news 2026/8/21 12:52:20

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/8/21 19:46:57

JavaScript条码处理终极指南:ZXing.js实战从入门到精通

JavaScript条码处理终极指南&#xff1a;ZXing.js实战从入门到精通 【免费下载链接】library Multi-format 1D/2D barcode image processing library, usable in JavaScript ecosystem. 项目地址: https://gitcode.com/gh_mirrors/lib/library 在现代Web开发中&#xff…

作者头像 李华
网站建设 2026/8/21 16:51:25

Kotaemon老年陪伴机器人:慢病管理与生活提醒

Kotaemon老年陪伴机器人&#xff1a;慢病管理与生活提醒 在一间安静的客厅里&#xff0c;一位独居老人轻声问&#xff1a;“我今天能吃香蕉吗&#xff1f;”这不是一次普通的自言自语。几秒钟后&#xff0c;一个温和的声音回应&#xff1a;“您血糖偏高&#xff0c;建议暂时避免…

作者头像 李华
网站建设 2026/8/21 13:33:32

【物流量子Agent路径优化终极指南】:破解复杂配送网络的量子算法秘籍

第一章&#xff1a;物流量子 Agent 的路径优化在现代物流系统中&#xff0c;路径优化是提升运输效率、降低运营成本的核心环节。传统算法如Dijkstra或遗传算法在面对大规模节点网络时往往计算耗时较长&#xff0c;难以满足实时调度需求。近年来&#xff0c;结合量子计算思想与多…

作者头像 李华
网站建设 2026/8/21 16:43:51

从0到1搭建教育测评Agent:3个必须掌握的自动批改关键技术点

第一章&#xff1a;教育测评Agent自动批改的核心价值在现代教育技术的发展中&#xff0c;教育测评Agent的引入正深刻改变传统作业批改与学习反馈模式。通过自然语言处理、机器学习和规则引擎的协同工作&#xff0c;自动批改系统不仅能快速识别学生作答内容的语义结构&#xff0…

作者头像 李华
网站建设 2026/8/21 0:45:14

如何快速配置开源音频驱动:非官方声卡兼容终极指南

如何快速配置开源音频驱动&#xff1a;非官方声卡兼容终极指南 【免费下载链接】AppleALC 项目地址: https://gitcode.com/gh_mirrors/app/AppleALC AppleALC 是一款专为 macOS 系统设计的开源音频驱动解决方案&#xff0c;能够完美解决非官方声卡兼容问题&#xff0c;…

作者头像 李华