news 2026/5/4 16:01:36

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/5/2 1:58:21

【极端天气应对指南】:基于AI Agent的7级预警阈值模型实战

第一章&#xff1a;气象灾害 Agent 的预警阈值在构建智能化的气象灾害监测系统时&#xff0c;Agent 的预警阈值设定是确保及时响应与减少误报的核心机制。合理的阈值不仅依赖于历史气象数据的统计分析&#xff0c;还需结合实时环境动态调整。预警参数配置 典型的气象灾害 Agent…

作者头像 李华
网站建设 2026/5/3 3:47:32

为什么你的MCP PL-600 Agent无法正常通信?深度剖析网络配置盲区

第一章&#xff1a;MCP PL-600 Agent通信故障的典型现象在部署和运维MCP PL-600 Agent的过程中&#xff0c;通信异常是影响系统稳定性的常见问题。当Agent无法与主控服务端建立有效连接时&#xff0c;通常会表现出一系列可观察的运行时症状&#xff0c;这些现象有助于快速定位问…

作者头像 李华
网站建设 2026/5/3 3:55:31

Claude Code如何重塑终端开发体验?

Claude Code如何重塑终端开发体验&#xff1f; 【免费下载链接】claude-code Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handlin…

作者头像 李华
网站建设 2026/5/3 23:35:53

Zettlr与LaTeX完美融合:让学术写作从此轻松自如 [特殊字符]

Zettlr与LaTeX完美融合&#xff1a;让学术写作从此轻松自如 &#x1f680; 【免费下载链接】Zettlr Your One-Stop Publication Workbench 项目地址: https://gitcode.com/GitHub_Trending/ze/Zettlr 还在为LaTeX的复杂命令而头疼吗&#xff1f;每次写论文都要在各种配置…

作者头像 李华
网站建设 2026/5/3 4:33:08

Ace-Translate:为什么它是你需要的终极本地离线翻译解决方案?

Ace-Translate&#xff1a;为什么它是你需要的终极本地离线翻译解决方案&#xff1f; 【免费下载链接】Ace-Translate 关于本地离线翻译程序&#xff0c;支持文本翻译&#xff0c;下划线翻译&#xff0c;屏幕截图翻译&#xff0c;语音&#xff08;音频文件&#xff09;翻译&…

作者头像 李华