news 2026/5/12 6:46:44

33. UVM TLM Analysis Port

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
33. UVM TLM Analysis Port

UVM TLM Analysis Port:一对多的"广播电台"

你已经掌握了点对点的Put/Get通信,现在我们来学习UVM TLM Analysis Port—— 这是一种特殊的"广播式"通信机制。它就像一个电台广播,发射塔(发送者)只管发送信号,不在乎有多少收音机(接收者)在听,甚至不在乎有没有人在听。

🎯 核心比喻:电台广播 vs 电话通话

想象两种通信方式:

  • 电话通话(Put/Get Port):一对一,必须有人接听才能通话
  • 电台广播(Analysis Port):一对多,不管有没有听众,照常广播

Analysis Port就是UVM的"广播电台",解决了Monitor需要广播数据给多个监听者(Scoreboard、Coverage Collector等)的需求。

⚙️ 工作原理:广播模式 vs 点对点模式

下图清晰展示了Analysis Port与传统Put/Get Port在通信模式上的根本区别:

📦 Analysis Port 的核心特性

1. 非阻塞通信
// Analysis Port使用function(不是task!),立即返回virtual functionvoidwrite(T t);// 在同一个仿真delta周期内完成// 不会阻塞发送者endfunction
2. 零到多个接收者
// 可以连接到0个、1个或多个接收者// 如果没有连接,调用write()不会出错,只是什么都不做ap.write(pkt);// 即使没有subscriber,也不会报错
3. 内置广播机制
// 内部自动循环调用所有连接的接收者的write()方法// 发送者只需调用一次write(),所有接收者都会收到

🔍 完整示例深度解析

让我们详细分析你提供的例子,理解Analysis Port如何工作:

第一步:定义事务类(simple_packet)
class simple_packet extends uvm_object;rand bit[7:0]addr;rand bit[7:0]data;bit rwb;// read/write bit`uvm_object_utils_begin(simple_packet)`uvm_field_int(addr,UVM_ALL_ON)`uvm_field_int(data,UVM_ALL_ON)`uvm_field_int(rwb,UVM_ALL_ON)`uvm_object_utils_end functionnew(string name="simple_packet");super.new(name);endfunction endclass
第二步:创建广播者(componentB)

这是"广播电台",只管发送,不管接收

class componentB extends uvm_component;`uvm_component_utils(componentB)// 1. 声明Analysis Port(广播端口)uvm_analysis_port #(simple_packet)ap;// 2. 传统Put接口(用于接收componentA的数据)uvm_blocking_put_imp #(simple_packet,componentB)put_export;functionnew(string name="componentB",uvm_component parent=null);super.new(name,parent);endfunction virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);// 创建Analysis Port(广播端口)ap=new("analysis_port",this);endfunction// 3. 实现Put接口(接收componentA的数据)virtual taskput(simple_packet pkt);`uvm_info("COMPB","从CompA收到Packet",UVM_LOW)pkt.print();// 关键:收到数据后立即广播给所有订阅者ap.write(pkt);// 广播!endtask// 4. 也可以主动生成数据广播(可选)virtual taskrun_phase(uvm_phase phase);// 这里可以主动生成数据广播// 但示例中是通过put()接收数据后广播endtask endclass
第三步:创建订阅者(sub)

这是"收音机",接收广播信号

注意:继承自uvm_subscriber基类,它已经内置了analysis_export

// UVM提供的订阅者基类(已经实现了大部分功能)virtual class uvm_subscriber #(type T=int)extends uvm_component;typedefuvm_subscriber #(T)this_type;// 内置的analysis_export,我们不需要自己声明uvm_analysis_imp #(T,this_type)analysis_export;// 纯虚函数,子类必须实现pure virtual functionvoidwrite(T t);endclass// 我们的具体订阅者类class sub #(type T=simple_packet)extends uvm_subscriber #(T);`uvm_component_utils(sub)functionnew(string name="sub",uvm_component parent=null);super.new(name,parent);endfunction// 必须实现的write()函数// 注意:参数名必须是t,这是UVM的约定virtual functionvoidwrite(T t);`uvm_info(get_full_name(),"订阅者收到事务",UVM_MEDIUM)// 这里可以进一步处理数据,比如:// - 发送给Scoreboard检查// - 收集覆盖率信息// - 记录日志endfunction endclass
第四步:创建发送者(componentA)

这是数据的原始来源

class componentA extends uvm_component;`uvm_component_utils(componentA)// Put端口,发送数据给componentBuvm_blocking_put_port #(simple_packet)put_port;functionnew(string name="componentA",uvm_component parent=null);super.new(name,parent);endfunction virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);put_port=new("put_port",this);endfunction virtual taskrun_phase(uvm_phase phase);for(inti=0;i<5;i++)begin simple_packet pkt=simple_packet::type_id::create("pkt");pkt.randomize();`uvm_info("COMPA","发送Packet给CompB",UVM_LOW)pkt.print();// 发送给componentBput_port.put(pkt);end endtask endclass
第五步:环境连接(my_env)

这是"连接电台和收音机"的地方

class my_env extends uvm_env;`uvm_component_utils(my_env)componentA compA;componentB compB;sub #(simple_packet)sub1,sub2,sub3;// 三个订阅者functionnew(string name="my_env",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);sub1=sub#(simple_packet)::type_id::create("sub1",this);sub2=sub#(simple_packet)::type_id::create("sub2",this);sub3=sub#(simple_packet)::type_id::create("sub3",this);endfunction virtual functionvoidconnect_phase(uvm_phase phase);// 1. 点对点连接:A -> BcompA.put_port.connect(compB.put_export);// 2. 广播连接:B -> 所有订阅者(关键!)compB.ap.connect(sub1.analysis_export);compB.ap.connect(sub2.analysis_export);compB.ap.connect(sub3.analysis_export);endfunction endclass

📊 数据流分析:看看广播如何工作

从输出日志可以看到清晰的广播模式:

1. ComponentA发送第一个Packet [COMPA] Packet sent to CompB pkt: { addr: 'h2f, data: 'h64, rwb: 'h0 } 2. ComponentB收到并广播 [COMPB] Packet received from CompA 同时广播给三个订阅者... 3. 所有订阅者同时收到 [sub1] Sub got transaction ← 同一时间! [sub2] Sub got transaction ← 同一时间! [sub3] Sub got transaction ← 同一时间! 4. 重复5次,每个Packet都被3个订阅者收到

关键发现:所有订阅者都在**同一仿真时间(0ns)**收到了事务,因为write()是函数,在同一个delta周期完成!

🎯 Analysis Port 的典型应用场景

场景1:Monitor广播事务(最经典的应用)
class monitor extends uvm_component;uvm_analysis_port #(bus_transaction)ap_mon;virtual taskrun_phase(uvm_phase phase);forever begin bus_transaction tr;// 监控总线,捕获事务capture_transaction(tr);// 广播给所有监听者ap_mon.write(tr);// Scoreboard、Coverage等都会收到end endtask endclass
场景2:配置信息广播
class config_manager extends uvm_component;uvm_analysis_port #(config_packet)ap_config;virtual taskupdate_config(config_packet cfg);// 更新配置current_config=cfg;// 广播新配置给所有组件ap_config.write(cfg);// 所有组件立即收到新配置endtask endclass
场景3:事件通知系统
class event_notifier extends uvm_component;uvm_analysis_port #(event_notification)ap_event;virtual tasknotify_event(string event_name,uvm_object data=null);event_notification evt=new(event_name,data);ap_event.write(evt);// 所有监听事件的组件都会收到endtask endclass

⚠️ Analysis Port 的独特特性

特性1:零连接也正常工作
// 即使没有连接任何订阅者,也不会出错ap.write(pkt);// ✅ 安全,不会崩溃// 这与Put/Get Port不同:// put_port.put(pkt); // ❌ 如果没有连接,会阻塞或出错
特性2:函数 vs 任务
// Analysis Port使用function(非阻塞)virtual functionvoidwrite(T t);// ✅ 立即返回// Put/Get使用task(可能阻塞)virtual taskput(T t);// ❌ 可能被接收方阻塞
特性3:自动广播循环
// 发送者只需调用一次write()ap.write(pkt);// 内部自动执行类似这样的操作:foreach(subscribers[i])begin subscribers[i].write(pkt);// 自动调用每个订阅者end

🔧 实际应用:构建完整监控系统

让我们看一个更实际的例子,Monitor监控DUT并广播给多个组件:

// 1. 事务定义class bus_transaction extends uvm_sequence_item;rand bit[31:0]addr;rand bit[31:0]data;rand bit write;// ... 其他字段endclass// 2. Monitor:监控DUT并广播class bus_monitor extends uvm_component;`uvm_component_utils(bus_monitor)uvm_analysis_port #(bus_transaction)ap;virtual bus_if vif;functionnew(string name,uvm_component parent);super.new(name,parent);endfunction virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);ap=new("ap",this);// 获取虚拟接口if(!uvm_config_db#(virtual bus_if)::get(this,"","vif",vif))`uvm_fatal("NOVIF","未找到虚拟接口")endfunction virtual taskrun_phase(uvm_phase phase);forever begin @(posedge vif.clk);if(vif.valid&&vif.ready)begin bus_transaction tr=bus_transaction::type_id::create("tr");tr.addr=vif.addr;tr.data=vif.data;tr.write=vif.write;// 关键:广播事务ap.write(tr);end end endtask endclass// 3. Scoreboard:检查功能正确性class scoreboard extends uvm_subscriber #(bus_transaction);`uvm_component_utils(scoreboard)// 内置了analysis_export,我们只需实现write()virtual functionvoidwrite(bus_transaction t);// 检查事务的正确性check_transaction(t);endfunction virtual functionvoidcheck_transaction(bus_transaction t);// 具体检查逻辑...`uvm_info("SCOREBOARD","检查事务",UVM_MEDIUM)endfunction endclass// 4. Coverage Collector:收集覆盖率class coverage_collector extends uvm_subscriber #(bus_transaction);`uvm_component_utils(coverage_collector)covergroup bus_cg;addr_cp:coverpoint tr.addr{bins low={[0:32'hFF]};bins mid={[32'h100:32'hFFF]};bins high={[32'h1000:32'hFFFF]};}data_cp:coverpoint tr.data;write_cp:coverpoint tr.write;endgroup virtual functionvoidwrite(bus_transaction t);bus_cg.sample();// 收集覆盖率endfunction endclass// 5. Logger:记录日志class transaction_logger extends uvm_subscriber #(bus_transaction);`uvm_component_utils(transaction_logger)virtual functionvoidwrite(bus_transaction t);// 记录到日志文件$fdisplay(log_file,"Time: %0t, Addr: 0x%h, Data: 0x%h, Write: %b",$time,t.addr,t.data,t.write);endfunction endclass// 6. 环境:连接所有组件class my_env extends uvm_env;bus_monitor monitor;scoreboard sb;coverage_collector cov;transaction_logger logger;virtual functionvoidconnect_phase(uvm_phase phase);// Monitor广播给所有订阅者monitor.ap.connect(sb.analysis_export);monitor.ap.connect(cov.analysis_export);monitor.ap.connect(logger.analysis_export);// 可以轻松添加更多订阅者...endfunction endclass

⚡ Analysis Port 的高级用法

用法1:使用analysis_fifo缓冲广播数据
// 当订阅者处理速度较慢时,可以使用analysis_fifoclass my_env extends uvm_env;bus_monitor monitor;uvm_tlm_analysis_fifo #(bus_transaction)fifo;scoreboard sb;virtual functionvoidconnect_phase(uvm_phase phase);// Monitor -> FIFO(缓冲)monitor.ap.connect(fifo.analysis_export);// FIFO -> Scoreboard(Scoreboard按自己速度读取)sb.get_port.connect(fifo.get_export);endfunction endclass
用法2:选择性广播
// 根据条件决定是否广播class smart_monitor extends uvm_component;uvm_analysis_port #(bus_transaction)ap;virtual taskrun_phase(uvm_phase phase);forever begin bus_transaction tr=capture_transaction();// 只广播特定类型的事务if(should_broadcast(tr))begin ap.write(tr);end end endtask virtual function bitshould_broadcast(bus_transaction tr);// 只广播写操作或地址在特定范围内的事务return(tr.write||(tr.addr inside{[32'h1000:32'h1FFF]}));endfunction endclass
用法3:多Analysis Port广播
// 一个组件有多个Analysis Port,广播不同类型的数据class advanced_monitor extends uvm_component;// 广播完整事务uvm_analysis_port #(bus_transaction)ap_full;// 只广播地址信息(轻量级)uvm_analysis_port #(addr_info)ap_addr;// 广播错误信息uvm_analysis_port #(error_packet)ap_error;virtual taskrun_phase(uvm_phase phase);forever begin bus_transaction tr=capture_transaction();// 广播完整事务ap_full.write(tr);// 广播地址信息(给只需要地址的订阅者)addr_info addr=new(tr.addr);ap_addr.write(addr);// 如果有错误,广播错误信息if(has_error(tr))begin error_packet err=new("总线错误",tr);ap_error.write(err);end end endtask endclass

📋 Analysis Port vs Put/Get Port 对比总结

特性Analysis PortPut/Get Port
通信模式广播(一对多)点对点(一对一)
连接要求0-N个接收者必须有1个接收者
阻塞性非阻塞(function)阻塞(task)
方法类型write()函数put()/get()任务
典型应用Monitor广播Generator→Driver
内部实现循环调用所有订阅者直接调用接收者
错误处理无连接也不报错无连接会阻塞或报错

🚀 实战练习建议

练习1:基础广播系统

  1. 创建一个Monitor,每100ns生成一个随机事务
  2. 创建3个不同的订阅者(Scoreboard、Coverage、Logger)
  3. 观察所有订阅者是否同时收到相同事务

练习2:选择性广播

  1. 修改Monitor,只广播特定条件的事务(如写操作)
  2. 观察订阅者是否只收到符合条件的事务

练习3:带缓冲的广播

  1. 使用uvm_tlm_analysis_fifo缓冲广播数据
  2. 创建慢速Scoreboard,每200ns处理一个事务
  3. 观察FIFO如何缓冲数据

练习4:实际场景模拟

  1. 实现一个完整的Monitor-Scoreboard-Coverage系统
  2. 添加事务过滤功能
  3. 实现多级广播(Monitor→多个分析组件)

💡 核心思想总结

UVM TLM Analysis Port是"只管广播,不问接收"的通信模式:

  1. 广播特性:一次发送,多个接收
  2. 零依赖:没有接收者也能正常工作
  3. 非阻塞:立即返回,不影响发送者
  4. 灵活扩展:轻松添加/移除订阅者

记住这个黄金法则

一对多用Analysis,广播数据最方便;
写用函数非阻塞,零个订阅也不怕;
Monitor广播最常见,Scoreboard收数据。

掌握了Analysis Port,你就能够构建高效、灵活的数据广播系统,让验证平台各个组件能够协同工作!现在,尝试在你的测试平台中添加一个Monitor,用它来广播事务给所有感兴趣的组件吧!

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

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

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

作者头像 李华
网站建设 2026/5/9 10:07:26

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

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

作者头像 李华
网站建设 2026/5/9 14:44:06

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/5/9 20:11:18

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

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

作者头像 李华
网站建设 2026/5/9 10:18:15

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

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

作者头像 李华