news 2026/8/8 2:52:01

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/8/7 23:35:04

QQScreenShot终极使用手册:10个提升效率的截图技巧

QQScreenShot是一款功能强大的电脑截图工具&#xff0c;集截图、文字识别、录屏等多项实用功能于一体。无论你是办公族、学生还是内容创作者&#xff0c;掌握这些技巧都能让你的工作效率翻倍&#xff01;&#x1f680; 【免费下载链接】QQScreenShot 电脑QQ截图工具提取版,支持…

作者头像 李华
网站建设 2026/8/8 4:25:07

如何用AI Agent实现护理任务100%准时提醒?:一线专家实战经验分享

第一章&#xff1a;护理任务准时提醒的挑战与AI Agent的介入价值在医疗护理场景中&#xff0c;护理任务的准时执行直接关系到患者的安全与治疗效果。然而&#xff0c;传统的人工排班与提醒机制常面临信息延迟、任务遗漏和响应不及时等问题。护士需同时处理多项任务&#xff0c;…

作者头像 李华
网站建设 2026/8/7 22:43:28

MCP SC-400合规报告配置全流程(从零到一键生成)

第一章&#xff1a;MCP SC-400合规报告概述Microsoft Compliance Portal&#xff08;MCP&#xff09;中的SC-400合规报告为组织提供了全面的数据治理与信息保护状态视图。该报告聚焦于敏感信息类型识别、数据分类准确性以及DLP&#xff08;数据丢失防护&#xff09;策略执行效果…

作者头像 李华
网站建设 2026/8/7 22:44:24

Kotaemon数学公式渲染:LaTeX支持配置方法

Kotaemon数学公式渲染&#xff1a;LaTeX支持配置方法 在构建现代智能问答系统时&#xff0c;我们常常会遇到一个看似简单却极具挑战的问题&#xff1a;如何让AI“写出”像教科书一样清晰、准确的数学表达式&#xff1f;尤其是在教育、科研和工程领域&#xff0c;用户不再满足于…

作者头像 李华
网站建设 2026/8/7 15:45:27

安卓投屏终极指南:3种方法让你的手机秒变电脑第二屏

想要在电脑上流畅操作安卓设备却不知从何入手&#xff1f;Scrcpy这款开源神器为你提供了完美的解决方案。无需安装任何手机应用&#xff0c;仅通过USB连接或WiFi网络&#xff0c;就能实现高质量的屏幕镜像和远程控制。无论是办公协作、游戏直播还是在线教学&#xff0c;这款工具…

作者头像 李华
网站建设 2026/8/7 22:20:28

MCP MS-720 Agent日志审计怎么做?90%安全团队忽略的4个高危盲区

第一章&#xff1a;MCP MS-720 Agent日志审计的核心价值在现代企业IT基础设施中&#xff0c;安全合规与异常行为追踪已成为运维管理的关键环节。MCP MS-720 Agent作为终端日志采集的核心组件&#xff0c;其日志审计能力不仅提升了系统可观测性&#xff0c;更为安全事件的溯源分…

作者头像 李华