news 2026/4/13 8:45:41

38. UVM TLM Non-blocking Get Port

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
38. UVM TLM Non-blocking Get Port

UVM TLM 非阻塞Get端口:"主动询问取货"式通信

你好!今天我们要学习UVM中非阻塞Get通信。这是一种"主动上门取货"的通信方式,接收方主动去要数据,而不是被动等待数据送上门。

🎯 一句话理解非阻塞Get

非阻塞Get就像去商店买东西:

  • 阻塞Get:排队等店员服务,一直等到轮到你(可能等很久)
  • 非阻塞Get:进店先问"有货吗?",有就买,没有就离开或等会儿再来

⚡ 为什么需要非阻塞Get通信?

场景对比:客户取货

想象两种取货方式:

  • 阻塞方式(普通Get):客户到仓库等,直到货物准备好
  • 非阻塞方式(非阻塞Get):客户打电话问"货好了吗?",没好就挂断,等会儿再打

非阻塞Get的优势:

  1. 主动控制:接收方决定何时取数据
  2. 避免死等:不会无限期阻塞等待
  3. 灵活调度:可以在多个数据源间轮询

🔌 Get vs Put 本质区别图解

先通过一个流程图理解Get和Put的主动被动关系:

📦 核心概念:三个关键方法

非阻塞Get提供了三种与发送方交互的方式:

方法类型作用类比
try_get()函数尝试获取数据,立即返回成功/失败问店员"能给我货吗?"
can_get()函数仅查询是否有数据,不获取问店员"有货吗?"
get()任务阻塞获取,等待数据准备好排队等到有货

🔍 完整代码深度解析

第一步:定义数据包类

class Packet extends uvm_object;rand bit[7:0]addr;// 地址字段rand bit[7:0]data;// 数据字段`uvm_object_utils_begin(Packet)`uvm_field_int(addr,UVM_ALL_ON)`uvm_field_int(data,UVM_ALL_ON)`uvm_object_utils_end functionnew(string name="Packet");super.new(name);endfunction endclass

注意:Get通信是接收方主动,所以发送方需要准备好数据等待被取走

第二步:发送方实现(componentA - 数据提供者)

角色反转:发送方变成"被请求方"

在Get模式中,componentA不再是主动发送者,而是数据提供者,等待被请求。

class componentA extends uvm_component;`uvm_component_utils(componentA)// 1. 声明非阻塞Get实现端口(不是Port!)uvm_nonblocking_get_imp #(Packet,componentA)m_get_imp;functionnew(string name="componentA",uvm_component parent=null);super.new(name,parent);endfunction virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);// 2. 创建实现端口(等待被连接)m_get_imp=new("m_get_imp",this);endfunction// 3. 实现try_get:当接收方请求数据时调用virtual function bittry_get(output Packet pkt);// 创建新数据包pkt=new();assert(pkt.randomize());`uvm_info("COMPA","componentB请求数据包",UVM_LOW)pkt.print();return1;// 总是成功提供数据endfunction// 4. 实现can_get:查询是否可提供数据virtual function bitcan_get();// 总是可以提供服务return1;endfunction endclass

关键点

  • 发送方实现try_get()函数(不是任务!)
  • 参数output Packet pkt是输出参数,填充数据后返回
  • 返回1表示成功提供数据,0表示暂时无法提供

第三步:接收方实现(componentB - 数据请求者)

接收方主动请求数据,这是Get模式的核心。

版本1:基础try_get(示例1)
class componentB extends uvm_component;`uvm_component_utils(componentB)// 1. 声明非阻塞Get端口(主动请求方)uvm_nonblocking_get_port #(Packet)m_get_port;intm_num_tx=2;// 请求次数functionnew(string name="componentB",uvm_component parent=null);super.new(name,parent);endfunction virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);// 2. 创建Get端口m_get_port=new("m_get_port",this);endfunction virtual taskrun_phase(uvm_phase phase);phase.raise_objection(this);repeat(m_num_tx)begin Packet pkt;bit success;// 3. 关键:尝试获取数据(非阻塞)success=m_get_port.try_get(pkt);if(success)begin `uvm_info("COMPB","成功获取数据包",UVM_LOW)pkt.print();endelsebegin `uvm_info("COMPB","获取数据失败",UVM_LOW)end end phase.drop_objection(this);endtask endclass

关键点

  • try_get()立即返回,不阻塞
  • 输出参数pkt在成功时被填充
  • 接收方控制请求时机
版本2:循环try_get模拟阻塞(可选实现)
virtual taskrun_phase(uvm_phase phase);phase.raise_objection(this);repeat(m_num_tx)begin Packet pkt;bit success;// 循环尝试,直到成功获取dobegin success=m_get_port.try_get(pkt);if(!success)begin `uvm_info("COMPB","componentA暂无数据,10ns后重试",UVM_LOW)#10;// 等待后重试end endwhile(!success);`uvm_info("COMPB","成功获取数据包",UVM_LOW)pkt.print();end phase.drop_objection(this);endtask
版本3:使用can_get查询(示例2)
virtual taskrun_phase(uvm_phase phase);phase.raise_objection(this);repeat(m_num_tx)begin Packet pkt;// 先查询发送方是否有数据`uvm_info("COMPB","查询componentA是否有数据...",UVM_LOW)while(!m_get_port.can_get())begin #10;// 等待10ns后再次查询`uvm_info("COMPB","再次查询...",UVM_LOW)end `uvm_info("COMPB","componentA已就绪,开始获取数据",UVM_LOW)// 确认有数据后获取(这时应该100%成功)m_get_port.try_get(pkt);pkt.print();end phase.drop_objection(this);endtask

can_get的优势

  • 纯粹的查询,不改变状态
  • 可以优化重试策略
  • 避免频繁调用try_get的开销

第四步:发送方的高级实现(模拟真实场景)

版本2:随机就绪的发送方
class componentA extends uvm_component;// ... 其他代码不变// try_get实现:随机决定是否提供数据virtual function bittry_get(output Packet pkt);bit ready;std::randomize(ready);// 随机生成0或1if(ready)begin pkt=new();assert(pkt.randomize());`uvm_info("COMPA","提供数据包",UVM_LOW)pkt.print();return1;endelsebegin `uvm_info("COMPA","暂时无法提供数据",UVM_LOW)return0;end endfunction// can_get实现:随机返回就绪状态virtual function bitcan_get();bit ready;std::randomize(ready)with{ready dist{0:/70,1:/30};};// 70%概率返回0(未就绪),30%概率返回1(就绪)returnready;endfunction endclass

第五步:环境连接

class my_test extends uvm_test;`uvm_component_utils(my_test)componentA compA;// 数据提供者componentB compB;// 数据请求者functionnew(string name="my_test",uvm_component parent=null);super.new(name,parent);endfunction virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);compA=componentA::type_id::create("compA",this);compB=componentB::type_id::create("compB",this);endfunction virtual functionvoidconnect_phase(uvm_phase phase);// 注意连接方向:Get端口连接到Get实现compB.m_get_port.connect(compA.m_get_imp);// 意思是:compB的请求端口连接到compA的实现端口endfunction endclass

重要!Get模式连接方向:

  • 接收方(请求者)的Port→ 发送方(提供者)的Imp
  • 与Put模式相反!

📊 两种模式输出对比分析

模式1:基础try_get(总是成功)

@0: [COMPA] componentB请求数据包 ← 被调用 @0: [COMPB] 成功获取数据包 ← 立即返回成功

特点:请求立即得到满足,类似阻塞get但没有等待。

模式2:can_get查询(随机就绪)

@0: [COMPB] 查询componentA是否有数据... @10: [COMPB] 再次查询... ← 等待10ns @20: [COMPB] 再次查询... ← 等待10ns @30: [COMPB] componentA已就绪,开始获取数据 @30: [COMPA] 提供数据包 ← 最终成功

特点:模拟真实场景,发送方可能暂时无法提供数据。

🎯 Get vs Put 对比总结

特性Get模式Put模式
主动方接收方(消费者)发送方(生产者)
控制权接收方决定何时取数据发送方决定何时发数据
接口get(),try_get(),can_get()put(),try_put(),can_put()
数据流向接收方 → 发送方(请求数据)发送方 → 接收方(发送数据)
典型应用处理器读取存储器驱动器发送事务
实现方法发送方实现try_get()接收方实现try_put()

🔧 实际应用场景

场景1:处理器读取指令缓存

class instruction_cache extends uvm_component;uvm_nonblocking_get_imp #(instruction,instruction_cache)get_imp;instruction cache[1024];virtual function bittry_get(output instruction instr);if(pc_valid&&cache_hit)begin instr=cache[pc];return1;endelsebeginreturn0;// 缓存未命中end endfunction endclass class cpu_fetch_unit extends uvm_component;uvm_nonblocking_get_port #(instruction)get_port;virtual taskrun_phase(uvm_phase phase);forever begin instruction instr;// 尝试从缓存获取指令if(get_port.try_get(instr))beginprocess_instruction(instr);pc=pc+4;endelsebegin// 缓存未命中,处理异常或等待handle_cache_miss();#10;// 等待缓存填充end end endtask endclass

场景2:DMA控制器读取数据

class dma_controller extends uvm_component;uvm_nonblocking_get_port #(data_chunk)get_port;virtual tasktransfer_data(intstart_addr,intsize);for(inti=0;i<size;i+=4)begin data_chunk chunk;// 尝试获取数据块while(!get_port.try_get(chunk))begin// 内存忙碌,等待并重试#5;end// 处理获取的数据process_data_chunk(chunk);end endtask endclass

场景3:仲裁器从多个源获取数据

class round_robin_arbiter extends uvm_component;uvm_nonblocking_get_port #(packet)port_a,port_b,port_c;virtual taskrun_phase(uvm_phase phase);forever begin packet pkt;// 轮询各个端口if(port_a.try_get(pkt))beginprocess_from_a(pkt);endelseif(port_b.try_get(pkt))beginprocess_from_b(pkt);endelseif(port_c.try_get(pkt))beginprocess_from_c(pkt);endelsebegin// 所有源都无数据,等待#10;end end endtask endclass

⚠️ 注意事项和最佳实践

1. 函数 vs 任务

// ❌ 错误:非阻塞接口实现任务virtual tasktry_get(output Packet pkt);// 应该是function!// ✅ 正确:非阻塞接口实现函数virtual function bittry_get(output Packet pkt);

2. 输出参数处理

// 必须分配新的对象virtual function bittry_get(output Packet pkt);// ❌ 错误:不分配对象// pkt.addr = 8'hFF; // 空指针错误!// ✅ 正确:创建新对象pkt=new();pkt.randomize();return1;endfunction

3. 连接方向

// ❌ 错误:Get模式连接反了compA.m_get_port.connect(compB.m_get_imp);// Put模式才这样// ✅ 正确:Get模式连接compB.m_get_port.connect(compA.m_get_imp);// 请求者→提供者

4. 竞态条件处理

// 多个接收方竞争同一发送方时class shared_data_source extends uvm_component;uvm_nonblocking_get_imp #(Packet,shared_data_source)get_imp;semaphore lock=new(1);// 互斥锁virtual function bittry_get(output Packet pkt);if(lock.try_get(1))begin// 获取锁成功pkt=create_packet();lock.put(1);return1;endelsebegin// 获取锁失败(其他线程正在使用)return0;end endfunction endclass

🔄 阻塞 vs 非阻塞Get对比

特性阻塞Get非阻塞Get
接口类型uvm_blocking_get_portuvm_nonblocking_get_port
实现类型uvm_blocking_get_impuvm_nonblocking_get_imp
方法类型任务(task)函数(function)
阻塞性接收方被阻塞接收方立即返回
主要方法get()try_get(),can_get()
返回值直接返回数据1(成功)/0(失败)
适用场景简单同步复杂异步、性能敏感
典型应用顺序数据读取总线访问、多线程读取

🚀 实战练习建议

练习1:基础非阻塞Get

  1. 实现基础非阻塞Get(示例1)
  2. 观察立即返回的特性
  3. 对比阻塞Get的时间消耗

练习2:模拟真实场景

  1. 让发送方随机忙碌(示例2)
  2. 实现接收方的轮询策略
  3. 添加超时机制

练习3:多源获取

  1. 实现一个接收方从两个发送方获取数据
  2. 使用轮询或优先级策略
  3. 处理数据源切换

练习4:性能优化

// 比较不同策略的性能virtual taskperformance_test();realtime start_time;intiterations=1000;// 策略1:频繁try_getstart_time=$realtime;for(inti=0;i<iterations;i++)begin Packet pkt;while(!get_port.try_get(pkt))#1;// 忙等待end realtime strategy1_time=$realtime-start_time;// 策略2:使用can_get减少尝试start_time=$realtime;for(inti=0;i<iterations;i++)begin Packet pkt;while(!get_port.can_get())#10;// 等待更长时间get_port.try_get(pkt);end realtime strategy2_time=$realtime-start_time;`uvm_info("PERF",$sformatf("策略1: %0t ns, 策略2: %0t ns",strategy1_time,strategy2_time),UVM_LOW)endtask

💡 设计模式推荐

模式1:带超时的获取

class timeout_getter extends uvm_component;uvm_nonblocking_get_port #(Packet)get_port;virtual function bitget_with_timeout(output Packet pkt,inttimeout_ns);realtime start_time=$realtime;while($realtime-start_time<timeout_ns)beginif(get_port.try_get(pkt))return1;// 成功#5;// 等待5ns后重试end `uvm_warning("TIMEOUT","获取数据超时")return0;// 超时失败endfunction endclass

模式2:批量获取优化

class batch_getter extends uvm_component;uvm_nonblocking_get_port #(data_item)get_port;virtual taskget_batch(data_item batch[$],intbatch_size);for(inti=0;i<batch_size;i++)begin data_item item;// 尝试获取,失败则等待while(!get_port.try_get(item))begin// 可以在这里做其他工作#10;end batch.push_back(item);end endtask endclass

模式3:优先级多源获取

class priority_getter extends uvm_component;uvm_nonblocking_get_port #(packet)high_pri_port,low_pri_port;virtual taskrun_phase(uvm_phase phase);forever begin packet pkt;// 优先尝试高优先级源if(high_pri_port.try_get(pkt))beginprocess_high_priority(pkt);endelseif(low_pri_port.try_get(pkt))beginprocess_low_priority(pkt);endelsebegin// 都无数据,等待#20;end end endtask endclass

🎓 总结

非阻塞TLM Get是"主动、灵活、高效"的拉取数据方式:

  1. 接收方主动:控制何时获取数据
  2. 立即返回:不会被无限期阻塞
  3. 状态感知:通过返回值知道获取结果
  4. 灵活控制:可以重试、等待、放弃或切换数据源

记住核心特点:

Get模式接收方主动,请求数据不阻塞;
try_get函数立即返,成功失败即刻知;
can_get查询状态用,优化性能减开销;
连接方向要注意,请求端口连实现。

掌握了非阻塞Get,你就能构建更加主动、高效的验证平台!现在尝试在你的测试中用Get模式替换一些被动等待的场景,让组件更加智能吧!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/10 7:26:03

大内存通过mmap分配,释放后什么情况不能通过munmap直接归还OS

大于等于128KB的内存分配通常使用mmap&#xff0c;其释放一般能通过munmap直接归还操作系统&#xff0c;但在某些特定情况下&#xff0c;可能无法立即或完整地归还。下面这个表格汇总了这些情况及其原因。情况分类具体场景原因简析系统资源限制​进程的虚拟内存区域&#xff08…

作者头像 李华
网站建设 2026/4/10 15:46:19

系统发育树可视化新体验:TreeViewer功能全解析

系统发育树可视化新体验&#xff1a;TreeViewer功能全解析 【免费下载链接】TreeViewer Cross-platform software to draw phylogenetic trees 项目地址: https://gitcode.com/gh_mirrors/tr/TreeViewer 在生物信息学研究中&#xff0c;系统发育树的可视化是理解物种进化…

作者头像 李华
网站建设 2026/4/10 18:16:39

MediaPipe边缘部署终极指南:避坑手册与性能优化指南

为什么你的MediaPipe在Jetson上总是安装失败&#xff1f; 【免费下载链接】mediapipe Cross-platform, customizable ML solutions for live and streaming media. 项目地址: https://gitcode.com/gh_mirrors/me/mediapipe MediaPipe作为Google开源的多媒体机器学习框架…

作者头像 李华
网站建设 2026/4/9 17:49:59

智能赋能绿色共生:智慧园区的发展逻辑与实践路径

城市化进程加速推动下&#xff0c;城市空间的高效利用与可持续发展已成为核心议题。智慧园区作为现代城市发展的关键载体&#xff0c;正通过智能技术与绿色理念的深度融合&#xff0c;突破传统园区资源浪费、管理粗放的瓶颈&#xff0c;为城市高质量发展注入强劲动能。这种“智…

作者头像 李华
网站建设 2026/4/10 16:29:07

南京大学学位论文LaTeX模板:智能排版系统完整使用指南

南京大学学位论文LaTeX模板&#xff1a;智能排版系统完整使用指南 【免费下载链接】NJUThesis 南京大学学位论文模板 项目地址: https://gitcode.com/gh_mirrors/nj/NJUThesis 南京大学学位论文LaTeX模板是专为南大学子设计的智能排版系统&#xff0c;能够自动处理复杂的…

作者头像 李华