news 2026/5/15 4:40:12

36. UVM TLM Nonblocking Put Port

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
36. UVM TLM Nonblocking Put Port

UVM TLM 非阻塞Put端口:"敲门询问"式通信

你好!今天我们要学习UVM中非阻塞TLM通信。这是一种"先敲门,再进入"的通信方式,发送方不会傻等,而是先询问接收方是否准备好,再决定是否发送数据。

🎯 一句话理解非阻塞Put

非阻塞Put就像拜访朋友的礼貌方式:

  • 阻塞Put:直接去朋友家,敲门后一直等到开门(可能等很久)
  • 非阻塞Put:先打电话问"在家吗?",得到肯定回答再过去

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

场景对比:快递员送货

想象两种送货方式:

  • 阻塞方式(普通Put):快递员到你门口,一直敲门直到你开门取件
  • 非阻塞方式(非阻塞Put):快递员先打电话问"现在方便吗?",不方便就晚点再打

非阻塞通信的优势:

  1. 不浪费时间:发送方不会被无限期阻塞
  2. 灵活调度:发送方可以做其他事情
  3. 资源高效:避免无意义的等待

🔌 阻塞 vs 非阻塞对比图解

先通过一个流程图理解两种方式的根本区别:

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

非阻塞Put提供了三种与接收方交互的方式:

方法类型作用类比
try_put()函数尝试发送,立即返回成功/失败敲门问"能进来吗?"
can_put()函数仅查询是否准备好,不发送打电话问"在家吗?"
put()任务阻塞发送,等待完成直接进门等主人

🔍 完整代码深度解析

第一步:定义数据包类

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

注意:非阻塞通信使用函数(function),所以数据包在发送后不能被修改,因为函数立即返回,可能数据包还在传输中。

第二步:发送方实现(componentA)

版本1:基础try_put(示例1)
class componentA extends uvm_component;`uvm_component_utils(componentA)// 1. 声明非阻塞put端口uvm_nonblocking_put_port #(Packet)m_put_port;intm_num_tx=2;// 发送次数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_put_port=new("m_put_port",this);endfunction virtual taskrun_phase(uvm_phase phase);phase.raise_objection(this);repeat(m_num_tx)begin bit success;Packet pkt=Packet::type_id::create("pkt");assert(pkt.randomize());`uvm_info("COMPA","尝试发送数据包",UVM_LOW)pkt.print();// 3. 关键:尝试发送(非阻塞)success=m_put_port.try_put(pkt);if(success)`uvm_info("COMPA","发送成功",UVM_MEDIUM)else`uvm_info("COMPA","发送失败",UVM_MEDIUM)end phase.drop_objection(this);endtask endclass

关键点

  • try_put()函数,不是任务
  • 立即返回1(成功)或0(失败)
  • 发送方不会被阻塞
版本2:循环try_put模拟阻塞(示例2)
virtual taskrun_phase(uvm_phase phase);phase.raise_objection(this);repeat(m_num_tx)begin bit success;Packet pkt=Packet::type_id::create("pkt");assert(pkt.randomize());`uvm_info("COMPA","尝试发送数据包",UVM_LOW)pkt.print();// 循环尝试,直到成功dobegin success=m_put_port.try_put(pkt);if(success)`uvm_info("COMPA","发送成功",UVM_MEDIUM)elsebegin `uvm_info("COMPA","发送失败,1ns后重试",UVM_MEDIUM)#1;// 等待1ns后重试end endwhile(!success);// 直到成功才退出循环end phase.drop_objection(this);endtask

这种模式实现了"非阻塞API的阻塞行为"

  • 发送方主动等待,但不是被接收方阻塞
  • 可以控制重试间隔(如#1)
  • 更加灵活,可以添加超时机制
版本3:使用can_put查询(示例3)
virtual taskrun_phase(uvm_phase phase);phase.raise_objection(this);repeat(m_num_tx)begin bit ready;Packet pkt=Packet::type_id::create("pkt");assert(pkt.randomize());`uvm_info("COMPA","准备发送数据包",UVM_LOW)pkt.print();// 先查询接收方是否就绪`uvm_info("COMPA","等待接收方就绪...",UVM_MEDIUM)dobegin ready=m_put_port.can_put();// 仅查询,不发送endwhile(!ready);// 等待直到就绪`uvm_info("COMPA","接收方已就绪,开始发送",UVM_MEDIUM)// 确认就绪后发送(这时应该100%成功)m_put_port.try_put(pkt);end phase.drop_objection(this);endtask

can_put的优势

  • 纯粹的查询,不改变状态
  • 可以在发送前做其他准备工作
  • 适用于复杂的发送逻辑

第三步:接收方实现(componentB)

接收方需要实现两个函数:try_put()can_put()

版本1:总是就绪(示例1)
class componentB extends uvm_component;`uvm_component_utils(componentB)// 声明非阻塞put实现端口uvm_nonblocking_put_imp #(Packet,componentB)m_put_imp;functionnew(string name="componentB",uvm_component parent=null);super.new(name,parent);endfunction virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);m_put_imp=new("m_put_imp",this);endfunction// 实现try_put:接收数据virtual function bittry_put(Packet pkt);`uvm_info("COMPB","收到数据包",UVM_LOW)pkt.print();return1;// 总是成功endfunction// 实现can_put:查询是否就绪virtual function bitcan_put();// 总是就绪return1;endfunction endclass
版本2:模拟随机就绪(示例2)
// try_put实现:随机决定是否接收virtual function bittry_put(Packet pkt);bit ready;std::randomize(ready);// 随机生成0或1if(ready)begin `uvm_info("COMPB","接收数据包",UVM_LOW)pkt.print();return1;// 成功endelsebegin `uvm_info("COMPB","忙碌中,无法接收",UVM_LOW)return0;// 失败end endfunction
版本3:独立的can_put逻辑(示例3)
virtual function bittry_put(Packet pkt);// 收到数据包`uvm_info("COMPB","接收数据包",UVM_LOW)pkt.print();return1;endfunction virtual function bitcan_put();// 随机返回是否就绪(与实际try_put解耦)return$urandom_range(0,1);endfunction

重要区别

  • can_put():只查询状态,不改变状态
  • try_put():尝试改变状态(接收数据)

第四步:环境连接

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);compA.m_num_tx=2;// 配置发送次数endfunction virtual functionvoidconnect_phase(uvm_phase phase);// 连接非阻塞端口compA.m_put_port.connect(compB.m_put_imp);endfunction endclass

📊 三种模式输出对比分析

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

@0: [COMPA] 尝试发送数据包 @0: [COMPB] 收到数据包 ← 立即接收 @0: [COMPA] 发送成功 ← 立即返回成功

特点:发送立即完成,类似阻塞put但没有等待。

模式2:循环try_put(随机成功)

@0: [COMPA] 尝试发送数据包 @0: [COMPA] 发送失败,1ns后重试 ← 第一次失败 @1: [COMPB] 收到数据包 ← 1ns后成功 @1: [COMPA] 发送成功 ← 循环结束

特点:模拟真实场景,接收方可能忙碌。

模式3:can_put查询

@0: [COMPA] 准备发送数据包 @0: [COMPA] 等待接收方就绪... ← 开始查询 @0: [COMPA] 接收方已就绪,开始发送 ← can_put返回1 @0: [COMPB] 收到数据包 ← 发送成功

特点:确保发送时接收方100%就绪。

🎯 实际应用场景

场景1:总线仲裁器

class bus_arbiter extends uvm_component;uvm_nonblocking_put_imp #(bus_transaction,bus_arbiter)put_imp;bit busy=0;// 当前是否忙碌virtual function bitcan_put();return!busy;// 不忙碌时返回1endfunction virtual function bittry_put(bus_transaction tr);if(busy)return0;// 忙碌则拒绝busy=1;// 标记为忙碌fork beginprocess_transaction(tr);// 处理事务busy=0;// 处理完成,标记为空闲end join_nonereturn1;// 接收成功endfunction endclass

场景2:带缓冲的接收器

class buffered_receiver extends uvm_component;uvm_nonblocking_put_imp #(packet,buffered_receiver)put_imp;packet buffer[$];intmax_buffer_size=10;virtual function bitcan_put();// 缓冲区未满时才能接收return(buffer.size()<max_buffer_size);endfunction virtual function bittry_put(packet pkt);if(buffer.size()>=max_buffer_size)return0;// 缓冲区满,拒绝buffer.push_back(pkt);// 存入缓冲区return1;endfunction// 后台处理线程virtual taskrun_phase(uvm_phase phase);forever beginwait(buffer.size()>0);process_packet(buffer.pop_front());#10;// 模拟处理时间end endtask endclass

场景3:多优先级发送

class priority_sender extends uvm_component;uvm_nonblocking_put_port #(packet)high_pri_port;uvm_nonblocking_put_port #(packet)low_pri_port;virtual taskrun_phase(uvm_phase phase);forever begin packet pkt=get_next_packet();if(pkt.priority==HIGH)begin// 高优先级:尝试发送,失败则等待while(!high_pri_port.try_put(pkt))#1;endelsebegin// 低优先级:尝试发送,失败则丢弃if(!low_pri_port.try_put(pkt))`uvm_warning("LOW_PRI","低优先级包被丢弃")end end endtask endclass

⚠️ 注意事项和最佳实践

1. 函数 vs 任务

// ❌ 错误:非阻塞接口实现任务 virtual task try_put(packet pkt); // 应该是function! // ✅ 正确:非阻塞接口实现函数 virtual function bit try_put(packet pkt);

2. 返回值处理

// 必须处理返回值bit success=port.try_put(pkt);if(!success)begin// 处理失败情况:重试、记录、丢弃等handle_failure(pkt);end

3. can_put与try_put的竞态条件

// 潜在问题:查询后状态可能改变bit ready=port.can_put();// 返回1(就绪)// 在这期间,其他线程可能占用接收方bit success=port.try_put(pkt);// 可能失败!// 解决方案:循环尝试dobeginif(port.can_put())begin success=port.try_put(pkt);endif(!success)#1;// 等待后重试endwhile(!success);

4. 超时机制

virtual function bittry_put_with_timeout(packet pkt,inttimeout_ns);realtime start_time=$realtime;while($realtime-start_time<timeout_ns)beginif(m_put_port.try_put(pkt))return1;// 成功#1;// 等待1ns后重试end `uvm_warning("TIMEOUT","发送超时")return0;// 超时失败endfunction

🔄 阻塞 vs 非阻塞完整对比

特性阻塞Put非阻塞Put
接口类型uvm_blocking_put_portuvm_nonblocking_put_port
实现类型uvm_blocking_put_impuvm_nonblocking_put_imp
方法类型任务(task)函数(function)
阻塞性发送方被阻塞发送方立即返回
主要方法put()try_put(),can_put()
返回值1(成功)/0(失败)
适用场景简单同步复杂异步、性能敏感
典型应用顺序数据流总线通信、多线程

🚀 实战练习建议

练习1:基础非阻塞通信

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

练习2:模拟真实场景

  1. 让接收方随机忙碌(示例2)
  2. 实现发送方的重试机制
  3. 添加重试次数限制

练习3:高级应用

  1. 实现带缓冲的接收器
  2. 添加优先级机制
  3. 实现超时和错误处理

练习4:性能对比

// 测试代码:比较阻塞和非阻塞的性能virtual taskperformance_test();realtime start_time;intiterations=1000;// 测试阻塞putstart_time=$realtime;for(inti=0;i<iterations;i++)blocking_port.put(pkt);// 可能被阻塞realtime blocking_time=$realtime-start_time;// 测试非阻塞put(循环尝试)start_time=$realtime;for(inti=0;i<iterations;i++)beginwhile(!nonblocking_port.try_put(pkt))#1;// 忙等待end realtime nonblocking_time=$realtime-start_time;`uvm_info("PERF",$sformatf("阻塞: %0t ns, 非阻塞: %0t ns",blocking_time,nonblocking_time),UVM_LOW)endtask

💡 设计模式推荐

模式1:生产者-消费者带流控

class producer extends uvm_component;uvm_nonblocking_put_port #(data)put_port;virtual taskrun_phase(uvm_phase phase);forever begin data item=generate_data();// 使用can_put避免忙等待if(put_port.can_put())begin put_port.try_put(item);endelsebegin// 接收方忙碌,做其他工作do_something_else();#10;// 等待一段时间end end endtask endclass

模式2:带超时的发送

class timeout_sender extends uvm_component;uvm_nonblocking_put_port #(packet)put_port;virtual function bitsend_with_timeout(packet pkt,intmax_retries);for(inti=0;i<max_retries;i++)beginif(put_port.try_put(pkt))return1;// 成功#10;// 等待后重试end// 重试次数用尽`uvm_error("SEND_FAIL","发送失败")return0;endfunction endclass

模式3:批量发送优化

class batch_sender extends uvm_component;uvm_nonblocking_put_port #(packet)put_port;virtual tasksend_batch(packet batch[$]);foreach(batch[i])begin// 尝试发送,失败则等待并重试while(!put_port.try_put(batch[i]))begin// 接收方忙碌,可以:// 1. 发送其他数据// 2. 等待固定时间// 3. 调整发送策略if(i<batch.size()-1)begin// 尝试发送下一个包i++;endelsebegin #10;// 等待后重试当前包end end end endtask endclass

🎓 总结

非阻塞TLM Put是"礼貌、高效、灵活"的通信方式:

  1. 立即返回:发送方不会被无限期阻塞
  2. 状态感知:通过返回值知道发送结果
  3. 灵活控制:可以重试、等待、放弃或做其他事情
  4. 性能友好:避免无意义的等待时间

记住核心区别:

阻塞put用任务,发送方会等待;
非阻塞用函数,立即知成败;
try_put尝试发,can_put查状态;
灵活又高效,复杂场景爱。

掌握了非阻塞Put,你就能构建响应更快、资源利用率更高的验证平台!现在尝试在你的测试中用非阻塞通信替换一些阻塞调用,体验性能提升吧!

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

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

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

作者头像 李华
网站建设 2026/5/7 16:58:54

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

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

作者头像 李华
网站建设 2026/5/11 7:26:59

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/13 0:01:01

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

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

作者头像 李华
网站建设 2026/5/11 23:55:54

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

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

作者头像 李华