当前位置: 首页 > news >正文

TLM2.0

在数字芯片验证(SystemVerilog UVM)中,TLM 2.0(Transaction-Level Modeling)的通用净核类(Generic Payload)和套接字(Socket)是实现高效组件通信的核心机制。TLM2.0将模块间事务的传递抽象出发起者、目标、套接字和桥,发起者通过发起者套接字向目标套接字发送消息,目标从目标套接字接收消息并处理。

1 通用净核类 (Generic Payload - tlm_generic_payload)

为了在不同模块间实现标准化通信,TLM2.0定义了一个通用的事务包 tlm_generic_payload,它几乎包含了总线事务所需的所有信息:

  • m_address:地址(64位)。
  • m_command:操作类型(读 TLM_READ_COMMAND、写TLM_WRITE_COMMAND 或忽略)。
  • m_data:数据阵列(byte 数组)。
  • m_length:传输的数据长度。
  • m_response_status:返回状态(是否成功、是否发生错误,如TLM_OK_RESPONSE)。
  • m_byte_enable:字节使能(用于部分写操作)。
    通用净核类 (Generic Payload)是 TLM 2.0 统一的数据语言。大家都用同一种格式的数据包,组件之间才能无缝对接。

2 套接字 (Sockets)

Socket 是TLM2.0最重要的概念之一,它是 TLM 2.0 统一的硬件接口。它在内部封装了 port、export 和 imp,极大地简化了模块间接口的连接,实现了“即插即用”。主要分为:

  • tlm_initiator_socket:由事务发起方(Initiator)使用。通常留在 Driver 或 Master 组件中,用来发起事务。
  • tlm_target_socket:由事务接收方(Target)使用。通常留在 Monitor、Scoreboard 或 Slave 模型中,用来接收并处理事务。
    每个socket又根据传输方式分为阻塞(b)和非阻塞(nb)类型。套接字将发送端口(Port) 和 接收端口(Export) 组合成了一个双向的通道。它既能发送请求(Request),又能同时接收回应(Response)。

3 核心接口:阻塞与非阻塞传输

阻塞传输 (Blocking Transport):通过 b_transport 函数实现。调用会阻塞发起方线程,直到目标方完成事务处理并返回。适合对时序要求不高的快速模型。
非阻塞传输 (Non-blocking Transport):通过 nb_transport_fw (前向) 和 nb_transport_bw (后向) 函数实现。将一次完整事务拆分为请求和响应多个阶段,通过返回值 tlm_sync_enum 和引用参数 phase 来同步状态。适合需要精细时序建模的场景。

非阻塞传输

nb_transport_fw (Forward, 前向):由发起端Initiator 调用,把消息传给目标端Target。
nb_transport_bw (Backward, 后向):由目标端Target 调用,把消息传回发起端Initiator。

举例:
一个 Master(Initiator) 和一个 Slave(Target)之间要传输一个 tlm_generic_payload(简称 trans)。在传输时需要用到两个辅助参数:

  • phase(阶段):类型为 tlm_phase,用来标记当前走到哪一步了。
  • delay(延迟):时序控制。

(1)Phase1:发送请求 (Master -> Slave)
Master 准备好了数据,把 phase 设置为 BEGIN_REQ(开始请求),然后调用 nb_transport_fw 发送。

// Master 内部代码tlm_phase phase=BEGIN_REQ;uvm_tlm_time delay=new("delay",0);// Master发送请求status=initiator_socket.nb_transport_fw(trans,phase,delay);

(2)Phase2:确认请求 (Slave -> Master)
Slave 收到请求后,发现是 BEGIN_REQ。Slave 把 phase 改为 END_REQ(结束请求),通过 nb_transport_bw 发回给 Master。

// Slave 内部代码(在 nb_transport_fw 的实现中,或者稍后通过另一个 thread)phase=END_REQ;// Slave 确认请求status=target_socket.nb_transport_bw(trans,phase,delay);

此时,Master 收到 END_REQ,知道 Slave 已经成功接单,Master 就可以去准备下一个数据或者等待了。

(3)Phase3:发送响应 (Slave -> Master)
Slave 内部的存储器真正完成了写操作(或者准备好了读数据)。它把 phase 改为 BEGIN_RESP(开始响应),再次通过 nb_transport_bw 发给 Master。

// Slave 内部代码trans.set_response_status(TLM_OK_RESPONSE);// 写入成功phase=BEGIN_RESP;// Slave 发送响应status=target_socket.nb_transport_bw(trans,phase,delay);

(4)Phase4:确认响应 (Master -> Slave)
Master 收到 BEGIN_RESP,检查 trans 的状态发现成功了。
Master 把 phase 改为 END_RESP(结束响应),通过 nb_transport_fw 传给 Slave,宣告这次传输彻底结束。

// Master 内部代码(在 nb_transport_bw 的实现中)if(phase==BEGIN_RESP)begin phase=END_RESP;// Master 确认响应status=initiator_socket.nb_transport_fw(trans,phase,delay);end

核心返回值:tlm_sync_enum
在写代码时,不管是调用 fw 还是 bw,都会收到一个返回值(上面代码中的 status)。这个返回值决定了是不是立刻返回结果:

TLM_ACCEPTED:

含义:消息收到了,对方需要花时间处理,晚点会异步调用另一个方向的函数把结果返回来。

对应现实:上面的例子中,如果大家动作都慢,每一步都返回 TLM_ACCEPTED,那就是标准的 4 次函数调用。

TLM_UPDATED:

含义:对方是个快手,在调用函数把请求发过去的同时,他在同一个函数返回时就把 phase 改了并把结果返回。

好处:可以把 4 阶段缩短为 3 阶段甚至 2 阶段,极大地减少函数调用次数,提高仿真速度。

TLM_COMPLETED:

含义:传输结束,不需要后续的任何握手了。

阻塞传输

b_transport:发起方调用后阻塞,直到事务完成。

对比

特性b_transport(阻塞)nb_transport_fw (非阻塞前向)
完整函数名‌void b_transport(…)tlm_sync_enum nb_transport_fw(…)
‌执行行为‌调用后‌阻塞‌,等待目标处理完毕返回调用后‌立即返回‌,事务异步进行
‌ 参数特征‌(payload, delay)(payload, phase, delay)
‌建模风格‌LT (Loosely Timed),适合快速架构探索AT (Approximately Timed),适合精确时序/流水线建模
配套反向接口‌需配合 nb_transport_bw 实现双向通信

4 时间建模(Timing)

TLM2.0通过 sc_time 对象来携带时间信息。b_transport 函数中的 delay 参数按引用传递,允许目标方在返回前增加延时,从而模拟读写延迟或总线仲裁延迟

5 核心时间模式

TLM 2.0 主要有两种模式:

  1. LT 模式 (Loose-Timely,宽松时序模式)
    LT模式使用基于wait()的阻塞传送接口,当一个调用请求发出后,直到请求被处理完成后该调用才返回。LT模式支持“时间解耦”,用于平衡仿真速度与仿真精度。
    应用场景:只关心功能,不关心具体时钟周期(比如快速搭建的虚拟原型、软件算法模型)。
    工作原理:Initiator 通过 Socket 调用 b_transport(payload, delay)。这是一个阻塞(Blocking)方法。数据包(Payload)送过去后,由 Target 直接修改 Payload 中的数据或状态,函数返回后,Initiator 直接从同一个 Payload 里读取结果。

  2. AT 模式 (Approximately-Timely,近似时序模式)
    AT模式使用基于相位(phase)的非阻塞传送接口,当请求发出后,发送方只需获知接收方已经接收消息,接收方在处理完成事务后,主动通知发送方事务已完成。典型的相位变化如BEGIN_REQ -> END_REQ -> BEGIN_RESP -> END_RESP。
    应用场景:需要精确到时钟周期的总线仿真(比如 AXI 的 4 阶段握手)。
    工作原理:通过 Socket 调用 nb_transport_fw(前向)和 nb_transport_bw(后向)。这是一个非阻塞(Non-blocking)方法。数据包(Payload)在 Initiator 和 Target 之间像打乒乓球一样来回传送,每次传送代表一个状态(如开始地址、开始数据、结束等待等)。

在发起者socket与目标socket中,注册forward或者backward的接口需要根据实际需要确定。通常发起者socket与目标socket相连接,发起方会调用目标socket的transport_fw()方法实现向前传输,目标方会调用发起者socket的transport_bw()方法实现向后传输。

6 代码示例

下面以一个发起方(Initiator)向一个目标方(Target Memory)发起写并读回验证的完整流程为例。

6.1 阻塞传输

1、目标方 (Target) 实现
目标方需要实现 b_transport 方法,并注册到其socket上

// target.h#include<systemc>#include<tlm.h>#include<tlm_utils/simple_target_socket.h>classMemory:publicsc_core::sc_module{public:// 1. 定义目标方套接字,接收来自发起方的事务tlm_utils::simple_target_socket<Memory>socket;SC_CTOR(Memory):socket("socket"){// 2. 将 b_transport 方法注册到套接字上socket.register_b_transport(this,&Memory::b_transport);// 初始化内存数组for(inti=0;i<256;++i)mem[i]=0;}// 3. 实现核心的阻塞传输函数voidb_transport(tlm::tlm_generic_payload&trans,sc_core::sc_time&delay){// 获取事务详情tlm::tlm_command cmd=trans.get_command();sc_dt::uint64 addr=trans.get_address();unsignedchar*ptr=trans.get_data_ptr();unsignedintlen=trans.get_data_length();// 模拟延迟 (例如 10ns)delay+=sc_core::sc_time(10,sc_core::SC_NS);if(cmd==tlm::TLM_WRITE_COMMAND){std::cout<<sc_core::sc_time_stamp()<<" [Target] WRITE to addr "<<addr<<std::endl;// 执行写操作for(unsignedinti=0;i<len;++i){mem[addr+i]=ptr[i];}}elseif(cmd==tlm::TLM_READ_COMMAND){std::cout<<sc_core::sc_time_stamp()<<" [Target] READ from addr "<<addr<<std::endl;// 执行读操作for(unsignedinti=0;i<len;++i){ptr[i]=mem[addr+i];}}// 设置事务完成状态trans.set_response_status(tlm::TLM_OK_RESPONSE);}private:unsignedcharmem[256];// 模拟内存};

2、发起方 (Initiator) 实现
发起方包含一个线程,持续发起事务

// initiator.h#include<systemc>#include<tlm.h>#include<tlm_utils/simple_initiator_socket.h>structInitiator:sc_core::sc_module{// 1. 定义发起方套接字tlm_utils::simple_initiator_socket<Initiator>socket;SC_CTOR(Initiator){SC_THREAD(thread_process);}voidthread_process(){// 初始化一个通用事务包tlm::tlm_generic_payload trans;sc_core::sc_timedelay(sc_core::SC_ZERO_TIME);// --- 执行一次写事务 ---unsignedintwrite_data=0xDEADBEEF;trans.set_command(tlm::TLM_WRITE_COMMAND);trans.set_address(0x10);trans.set_data_ptr(reinterpret_cast<unsignedchar*>(&write_data));trans.set_data_length(4);std::cout<<sc_core::sc_time_stamp()<<" [Initiator] Sending WRITE transaction"<<std::endl;// 2. 调用 socket 的 b_transport,这将最终调用目标方的 b_transportsocket->b_transport(trans,delay);std::cout<<sc_core::sc_time_stamp()<<" [Initiator] WRITE completed, status = "<<trans.get_response_status()<<std::endl;// --- 执行一次读事务 ---unsignedintread_data=0;trans.set_command(tlm::TLM_READ_COMMAND);trans.set_address(0x10);trans.set_data_ptr(reinterpret_cast<unsignedchar*>(&read_data));trans.set_data_length(4);std::cout<<sc_core::sc_time_stamp()<<" [Initiator] Sending READ transaction"<<std::endl;socket->b_transport(trans,delay);std::cout<<sc_core::sc_time_stamp()<<" [Initiator] READ completed, data = 0x"<<std::hex<<read_data<<std::endl;}};

3、顶层连接 (Top-Level)
在顶层模块中将两个socket绑定在一起

// main.cpp#include<systemc>#include"initiator.h"#include"target.h"intsc_main(intargc,char*argv[]){Initiatorinitiator("initiator");Memorymemory("memory");// 将发起方套接字绑定到目标方套接字initiator.socket.bind(memory.socket);sc_core::sc_start();return0;}

进阶:引入时间标记
如果希望精确控制延迟,目标方可以修改传入的 delay 引用,而不是直接使用 # 等待。在目标方 b_transport 中添加如下代码:

// 模拟不同地址的访问延迟if(addr==0x00){delay+=sc_core::sc_time(20,sc_core::SC_NS);// 特殊地址慢一些}else{delay+=sc_core::sc_time(5,sc_core::SC_NS);}

6.2 非阻塞传输

1、目标方(Target)实现 —— 接受请求,延迟响应
Target 需要实现 nb_transport_fw(前向接口),并拥有一个内部线程来处理延迟响应。

// target_nb.h#include<systemc>#include<tlm.h>#include<tlm_utils/simple_target_socket.h>classTargetNB:publicsc_core::sc_module{public:// 目标方套接字tlm_utils::simple_target_socket<TargetNB>socket;SC_CTOR(TargetNB):socket("socket"),pending_trans(nullptr){// 注册前向非阻塞传输函数socket.register_nb_transport_fw(this,&TargetNB::nb_transport_fw);// 启动响应处理线程SC_THREAD(response_thread);sensitive<<resp_event;dont_initialize();}// 核心函数:接收来自 Initiator 的请求(前向路径)tlm::tlm_sync_enumnb_transport_fw(tlm::tlm_generic_payload&trans,tlm::tlm_phase&phase,sc_core::sc_time&delay){// 只处理请求开始阶段if(phase==tlm::BEGIN_REQ){std::cout<<sc_core::sc_time_stamp()<<" [Target] Received BEGIN_REQ, addr=0x"<<std::hex<<trans.get_address()<<", delay="<<delay.to_string()<<std::endl;// 保存事务指针,以便在响应线程中处理pending_trans=&trans;// 模拟总线延迟:假设需要 50ns 才能准备好数据// 注意:这里不阻塞当前调用,而是通知线程在 50ns 后触发resp_event.notify(50,sc_core::SC_NS);// 返回 TLM_ACCEPTED 表示:我收下了请求,但不会立即完成,// 发起方可以去做其他事情,不用等我。returntlm::TLM_ACCEPTED;}// 如果收到 END_RESP(来自发起方的确认),可以忽略或做清理returntlm::TLM_COMPLETED;}// 内部线程:延迟后通过后向路径发送响应voidresponse_thread(){while(true){// 等待事件触发(50ns 延时到达)wait(resp_event);if(pending_trans){std::cout<<sc_core::sc_time_stamp()<<" [Target] Processing response (DELAY expired)"<<std::endl;// 执行实际的读/写操作if(pending_trans->get_command()==tlm::TLM_WRITE_COMMAND){unsignedint*data_ptr=(unsignedint*)pending_trans->get_data_ptr();mem[pending_trans->get_address()/4]=*data_ptr;std::cout<<" [Target] WRITE completed"<<std::endl;}else{// TLM_READ_COMMANDunsignedintval=mem[pending_trans->get_address()/4];memcpy(pending_trans->get_data_ptr(),&val,4);std::cout<<" [Target] READ value = 0x"<<std::hex<<val<<std::endl;}pending_trans->set_response_status(tlm::TLM_OK_RESPONSE);// ----- 关键步骤:发起后向传输(Backward Path)-----tlm::tlm_phase phase=tlm::BEGIN_RESP;sc_core::sc_time delay=sc_core::SC_ZERO_TIME;// 此处不再额外延时// 通过套接字调用 Initiator 实现的 nb_transport_bw// 这会立即触发 Initiator 的接收函数tlm::tlm_sync_enum status=socket->nb_transport_bw(*pending_trans,phase,delay);// 如果 Initiator 返回 TLM_UPDATED,意味着它把 phase 改为了 END_RESP// 表示对方已收下响应,本目标可以释放资源了if(status==tlm::TLM_UPDATED){std::cout<<sc_core::sc_time_stamp()<<" [Target] Initiator confirmed END_RESP, transaction done."<<std::endl;}pending_trans=nullptr;}}}private:unsignedintmem[256];// 模拟内存tlm::tlm_generic_payload*pending_trans;// 当前待处理的事务sc_core::sc_event resp_event;// 触发响应的事件};

2、发起方(Initiator)实现 —— 发送请求,接收异步响应
Initiator 需要实现 nb_transport_bw(后向接口),并在内部线程中发起请求。

// initiator_nb.h#include<systemc>#include<tlm.h>#include<tlm_utils/simple_initiator_socket.h>classInitiatorNB:publicsc_core::sc_module{public:// 发起方套接字tlm_utils::simple_initiator_socket<InitiatorNB>socket;SC_CTOR(InitiatorNB):socket("socket"),response_received(false){// 注册后向非阻塞传输函数(接收 Target 发来的响应)socket.register_nb_transport_bw(this,&InitiatorNB::nb_transport_bw);SC_THREAD(run_thread);}// 核心函数:接收来自 Target 的响应(后向路径)tlm::tlm_sync_enumnb_transport_bw(tlm::tlm_generic_payload&trans,tlm::tlm_phase&phase,sc_core::sc_time&delay){if(phase==tlm::BEGIN_RESP){std::cout<<sc_core::sc_time_stamp()<<" [Initiator] Received BEGIN_RESP from Target!"<<std::endl;// 读取返回的数据(如果是读操作)if(trans.get_command()==tlm::TLM_READ_COMMAND){unsignedintval;memcpy(&val,trans.get_data_ptr(),4);read_result=val;std::cout<<" [Initiator] Data received = 0x"<<std::hex<<val<<std::endl;}// 告诉 Target:我已经收下响应,事务可以彻底结束phase=tlm::END_RESP;response_received=true;done_event.notify(sc_core::SC_ZERO_TIME);// 通知主线程任务完成returntlm::TLM_UPDATED;// 返回 UPDATED,告知 Target 阶段已变}returntlm::TLM_COMPLETED;}// 主线程:发起请求voidrun_thread(){// 等待仿真启动稳定wait(sc_core::SC_ZERO_TIME);// ----- 发起一次读请求 -----tlm::tlm_generic_payload trans;unsignedintread_data=0;trans.set_command(tlm::TLM_READ_COMMAND);trans.set_address(0x100);trans.set_data_ptr((unsignedchar*)&read_data);trans.set_data_length(4);trans.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);tlm::tlm_phase phase=tlm::BEGIN_REQ;sc_core::sc_time delay=sc_core::SC_ZERO_TIME;std::cout<<sc_core::sc_time_stamp()<<" [Initiator] Sending READ BEGIN_REQ (Non-blocking)"<<std::endl;// 调用 Target 的 nb_transport_fwtlm::tlm_sync_enum status=socket->nb_transport_fw(trans,phase,delay);if(status==tlm::TLM_ACCEPTED){// 对方接受了请求,但尚未完成。我们阻塞主线程等待 done_eventstd::cout<<sc_core::sc_time_stamp()<<" [Initiator] Request accepted, waiting for async response..."<<std::endl;wait(done_event);// 主线程挂起,等待后向路径触发}// 到这里,异步响应已完成std::cout<<sc_core::sc_time_stamp()<<" [Initiator] Transaction fully completed. Final data = 0x"<<std::hex<<read_result<<std::endl;sc_core::sc_stop();}private:boolresponse_received;unsignedintread_result;sc_core::sc_event done_event;};

3、顶层连接(Top-Level)

// main.cpp#include"initiator_nb.h"#include"target_nb.h"intsc_main(intargc,char*argv[]){InitiatorNBinitiator("initiator");TargetNBmemory("memory");// 绑定套接字initiator.socket.bind(memory.socket);sc_core::sc_start();return0;}
http://www.jsqmd.com/news/1036061/

相关文章:

  • 2026多品牌大电流微欧计实测:青岛普锐思高口碑生产商测评 - 品牌推荐大师
  • 2026武汉真人发假发定制推荐:武汉三星速美假发超市实力全盘点 - 行业深度观察C
  • 基于MC68HC908MR32的永磁同步电机正弦波驱动与死区补偿技术详解
  • NXP T4240RDB参考设计板硬件架构解析与设计实践
  • 百度网盘秒传解决方案:高效文件管理与分享终极指南
  • 外贸快车怎么样?实力测评解析 - 栗子测评
  • 如何三步快速解密Navicat数据库连接密码的完整免费解决方案
  • 终极跨平台Access数据库处理方案:MDB Tools实战指南
  • Motorola Suite56 ADS调试器:OnCE与MFAX技术深度解析与实战指南
  • 5分钟搞定Windows和Office激活:KMS智能脚本终极指南
  • 嵌入式功能安全实战:基于NXP IEC60730库的GPIO短路与Flash CRC校验
  • 2026年6月18日海安车灯维修本地走访记:裂痕位置、进水情况和灯壳状态先核对哪几项 - Ayu8888
  • B站视频解析技术深度解析:多协议支持与智能缓存实现
  • 杰理之USB SPK位宽设置24bit,插PC会死机【篇】
  • 嵌入式异构多核硬件设计实战:TWR-VF65GS10开发板深度解析
  • Mythos能力跃迁:系统级推理与具身叙事的工程落地
  • 3个必装理由:为什么你需要PowerToys中文版来提升Windows效率?
  • 3步掌握OpenSlide:从零开始高效处理虚拟切片图像
  • 2026拉力机试验机品牌推荐榜:技术派vs性价比派,你选哪一派? - 品牌推荐大师1
  • 2026年采购合同风险意识不足,咨询众智商学院CPPM前应该先看哪些条款和案例 - 众智商学院官方
  • 杭州思亿欧智能体科技有限公司靠谱么?公司综合实力深度解析 - 栗子测评
  • 考软考中项报培训班一般多少钱?哪家性价比高
  • Poppins字体终极指南:如何高效运用这款现代几何无衬线字体提升你的设计质感
  • 【2027最新】基于SpringBoot+Vue的汽车维修预约服务系统管理系统源码+MyBatis+MySQL
  • 计算机Java毕设实战-基于 Spring Boot 的二手房交易信息管理系统的设计与实现 基于 Spring Boot 的房屋买卖供需对接系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • zip slip目录遍历加n1例题
  • 哈尔滨本土门窗厂家排行:适配寒地需求的实力之选 - 起跑123
  • 2026年合肥市肥西县眼镜店哪家好?资质、设备与专项服务综合较优的10家门店概览 - 每日行业榜
  • 并发编程(c++)——5.事件驱动
  • CodeWarrior IDE编译与链接实战:从源码到可执行文件的构建全解析