UVM验证入门(4)-TLM1.0

UVM TLM 1.0:验证组件间通信的基石

1. 概述

在UVM验证方法学中,TLM(Transaction Level Modeling 事务级建模)是组件间通信的核心机制。TLM 1.0是起源于SystemC的一种通信方式,为验证工程师提供了一套标准化的接口,用于组件之间的数据传输和同步。本文将探讨TLM 1.0的核心概念、使用方法和实际应用。

什么是TLM?

TLM是一种基于事务的通信机制,与信号级的通信相比,它具有以下优势:

  • 抽象层次更高:不关心具体的信号时序
  • 仿真速度更快:减少不必要的事件触发
  • 可重用性更好:组件接口标准化
  • 调试更简单:事务级别的调试信息

2. TLM 1.0 核心概念

2.1 常用的操作方式

a) put操作

通信的发起者A把一个Transaction发送给B,在这个过程中,A成为"发起者",B成为"接收者"。A具有的端口称为PORT,B的端口称为EXPORT。此时数据流时从A到B。

b) get操作

A向B请求一个Transaction。在这个过程中,A依然是"发起者",B依然是"接收者",A上的端口依然是PORT,B上的端口依然是EXPORT。在这个过程中,数据流时从B流向A的。PORT和EXPORT体现的是控制流而不是数据流。

c) transport操作

Transport操作相当于一次PUT操作加一次GET操作,这两次操作A都是"发起者",B都是"接受者"。A上的端口依然是PORT,B的端口依然是EXPORT。在这个过程中,数据流先从A流向B,在从B流向A。相当于A向B提交了一个请求,而B返回给A一个应答。

2.2 端口类型

TLM 1.0定义了三种主要的端口类型:

a) 端口(Port)
  • 发起通信的组件
  • 调用TLM方法
b) 导出(Export)
  • 中间连接组件
  • 传递TLM方法调用
c) 实现(Implementation)
  • 最终处理通信的组件
  • 实现TLM方法的具体功能

3. 通信接口类型

3.1 常用的port

systemverilog 复制代码
// 阻塞接口 
uvm_blocking_put_port #(T)
uvm_blocking_get_port #(T)
uvm_blocking_peek_port #(T)
uvm_blocking_get_peek_port #(T)
uvm_blocking_transport_port #(REQ,RSP)

// 非阻塞接口
uvm_nonblocking_put_port #(T)
uvm_nonblocking_get_port #(T)
uvm_nonblocking_peek_port #(T)
uvm_nonblocking_get_peek_port #(T)
uvm_nonblocking_transport_port #(REQ,RSP)

// 组合接口
uvm_put_port #(T)
uvm_get_port #(T)
uvm_peek_port #(T)
uvm_get_peek_port #(T)
uvm_transport_port #(REQ,RSP)

其中参数T为要传输的事务类型,参数REQ为请求事务类型,参数RSP为响应事务类型。 阻塞端口操作发起后,会等待直到操作完成(像打电话,不挂断就一直等着对方回应) 非阻塞端口操作发起后,会立即返回(像发短信,发完就继续干别的,不等回复)

3.2 常用的export

与port类似,export有如下接口:

systemverilog 复制代码
// 阻塞接口 
uvm_blocking_put_export #(T)
uvm_blocking_get_export #(T)
uvm_blocking_peek_export #(T)
uvm_blocking_get_peek_export #(T)
uvm_blocking_transport_export #(REQ,RSP)

// 非阻塞接口
uvm_nonblocking_put_export #(T)
uvm_nonblocking_get_export #(T)
uvm_nonblocking_peek_export #(T)
uvm_nonblocking_get_peek_export #(T)
uvm_nonblocking_transport_export #(REQ,RSP)

// 组合接口
uvm_put_export #(T)
uvm_get_export #(T)
uvm_peek_export #(T)
uvm_get_peek_export #(T)
uvm_transport_export #(REQ,RSP)

3.3 常用的imp

imp有如下接口:

systemverilog 复制代码
// 阻塞接口 
uvm_blocking_put_imp #(T,IMP)
uvm_blocking_get_imp #(T,IMP)
uvm_blocking_peek_imp #(T,IMP)
uvm_blocking_get_peek_imp #(T,IMP)
uvm_blocking_transport_imp #(REQ,RSP,IMP)

// 非阻塞接口
uvm_nonblocking_put_imp #(T,IMP)
uvm_nonblocking_get_imp #(T,IMP)
uvm_nonblocking_peek_imp #(T,IMP)
uvm_nonblocking_get_peek_imp #(T,IMP)
uvm_nonblocking_transport_imp #(REQ,RSP,IMP)

// 组合接口
uvm_put_imp #(T,IMP)
uvm_get_imp #(T,IMP)
uvm_peek_imp #(T,IMP)
uvm_get_peek_imp #(T,IMP)
uvm_transport_imp #(REQ,RSP,IMP)

其中参数T、REQ、RSP与port、export定义想通,参数IMP为实现接口的Component。 3种接口的优先级:port->export->imp,高优先级可以连接到低优先级,但是低优先级不能连接到高优先级;同等级的也可以嵌套连接,例如port->port,export也可省略,但是目的端一定为imp。

实际传输时,在port中调用对应的方法,在imp中实现相应的方法,不同端口对应方法如下:

端口类型 方法
阻塞端口
uvm_blocking_put_port put(t)
uvm_blocking_get_port get(t)
uvm_blocking_peek_port peek(t)
uvm_blocking_get_peek_port get(t),peek(t)
uvm_blocking_transport_port transport(req,rsp)
非阻塞端口
uvm_nonblocking_put_port try_put(t),can_put(t)
uvm_nonblocking_get_port try_get(t),can_get(t)
uvm_nonblocking_peek_port try_peek(t),can_peek(t)
uvm_nonblocking_get_peek_port try_get(t),can_get(t),try_peek(t),can_peek(t)
uvm_nonblocking_transport_port nb_transport(req,rsp)

peek操作只查看数据,不消耗数据,后续可以继续操作,get_peek结合了get和peek的特性 try_*方法,尝试执行操作,成功返回1,失败返回0,can_*方法,查询是否可操作,可操作返回1,否则返回0

4. TLM1.0使用步骤

4.1 端口声明

声明方式如下:

systemverilog 复制代码
//port声明
class A extends uvm_component;
    `uvm_component_utils(A)

    uvm_blocking_put_port#(my_transaction) a_port;

    function new (string name = "A", uvm_component parent = null);
        super.new(name, parent);
    endfunction

    extern function void build_phase(uvm_phase phase);
    extern task main_phase(uvm_phase phase);

endclass

//export和imp声明
class B extends uvm_component;
    `uvm_component_utils(B)
	C C_inst;
    uvm_blocking_put_export#(my_transaction) b_export;

    function new (string name = "B", uvm_component parent = null);
        super.new(name, parent);
    endfunction

    extern function void build_phase(uvm_phase phase);
    extern task main_phase(uvm_phase phase);

endclass

class C extends uvm_component;
    `uvm_component_utils(C)

    uvm_blocking_put_export#(my_transaction) c_export;
    uvm_blocking_put_export#(my_transaction,C) c_imp;

    function new (string name = "C", uvm_component parent = null);
        super.new(name, parent);
    endfunction

    extern function void build_phase(uvm_phase phase);
    extern task main_phase(uvm_phase phase);

endclass

4.2 创建实例

创建实例方式如下:

systemverilog 复制代码
//port实例
    function void A::build_phase(uvm_phase phase);
        super.build_phase(phase);
        a_port = new("a_port",this);
    endfunction

//export实例
    function void B::build_phase(uvm_phase phase);
        super.build_phase(phase);
        b_export = new("b_export",this);
        C_inst = B::type_id::create("C_inst",this);
    endfunction

//export和imp实例
    function void C::build_phase(uvm_phase phase);
        super.build_phase(phase);
        c_export = new("c_export",this);
        c_imp = new("c_imp",this);
    endfunction

4.3 实现方法

如果实现的连接关系a_port->b_export->c_export->c_imp,则在类C中实现方法:

systemverilog 复制代码
    function void C::put(my_transaction tr);
        `uvm_info("C","put a transaction",UVM_LOW)
        tr.print();
    endfunction

4.4 连接端口

如果连接关系如下图,需要再my_env中连接port->export,在Class B 中连接b_export->c_export。

systemverilog 复制代码
function  void my_env::connect_phase(uvm_phase phase)
	super.connect_phase(phase);
	A_inst.a_port.connect(B_inst.b_export);
endfunction

function void B:connect_phase(uvm_phase phase);
	super.connect_phase(phase);
	this.b_export.connect(C_inst.c_export)
endfunction

5. TLM1.0常见的应用场景

5.1 analysis端口介绍

Analysis端口是UVM TLM通信机制中的一种特殊类型。与传统的阻塞或非阻塞端口不同,analysis端口具有独特的广播特性,允许单个生产者向多个消费者同时发送数据,而无需知道消费者的具体身份和数量。

  • 单向广播:一个analysis端口可以连接多个接收端
  • 非阻塞:写入操作立即返回,不等待处理
  • 无返回值:不返回任何状态或数据
  • 常用于:监测数据分发、覆盖率收集、记分板更新
  • 接收方通过实现 write() 方法接收数据

5.2 monitor与reference model/scoreboard的传输

在UVM验证环境中,Monitor负责监视DUT的接口信号并将其转换为事务级数据,然后通过TLM端口将这些数据传输给其他验证组件。这种传输机制是验证环境数据流的核心环节。

Monitor到Reference Model的数据流   Monitor捕获DUT接口上的事务后,通过analysis端口将事务广播给Reference Model。Reference Model接收到这些激励事务后,会根据设计规范计算出预期的响应结果。这个过程模拟了理想情况下DUT应该具有的行为,为后续的验证比较提供基准。

systemverilog 复制代码
class my_monitor extends uvm_monitor;
    uvm_analysis_port #(my_transaction) mon_analysis_port;
    
    virtual task run_phase(uvm_phase phase);
        forever begin
            my_transaction tx;
            // 捕获DUT事务
            tx = capture_transaction();
            // 广播到所有连接的组件
            mon_analysis_port.write(tx);
        end
    endtask
endclass

class reference_model extends uvm_component;
    uvm_analysis_imp #(my_transaction, reference_model) analysis_imp;
    
    // 必须实现write方法
    virtual function void write(my_transaction tx);
        // 根据输入事务计算预期输出
        my_transaction expected = predict_output(tx);
        // 将预期结果发送到scoreboard
        result_analysis_port.write(expected);
    endfunction
endclass

Monitor到Scoreboard的数据流   Monitor同时也会将捕获的实际事务发送给Scoreboard。Scoreboard作为验证环境的核心检查组件,负责比较实际结果与预期结果。通常,Scoreboard会实现两个analysis imp端口:一个用于接收Monitor发送的实际事务,另一个用于接收Reference Model发送的预期事务。通过对比这两组数据,Scoreboard可以自动判断DUT的功能是否正确。

systemverilog 复制代码
class scoreboard extends uvm_component;
    uvm_analysis_imp #(my_transaction, scoreboard) actual_imp;
    uvm_analysis_imp #(my_transaction, scoreboard) expected_imp;
    
    // 存储实际和预期事务的队列
    my_transaction actual_queue[$];
    my_transaction expected_queue[$];
    
    virtual function void write(my_transaction tx);
        // 根据端口区分实际和预期数据
        if (this.actual_imp != null) 
            actual_queue.push_back(tx);
        else
            expected_queue.push_back(tx);
        
        // 进行比较检查
        check_match();
    endfunction
endclass

scoreboard需要接收两路数据,一路来自monitor,一路来自reference model,对于一个imp而言,必须在其实例化的uvm_component中定义一个write函数,现在scoreboard接收两路数据,但是write函数只有一个,通过使用uvm_analysis_imp_decl来解决这个问题

systemverilog 复制代码
`uvm_analysis_imp_decl(_monitor)
`uvm_analysis_imp_decl(_model)
class my_scoreboard extends uvm_scoreboard;
	my_transaction expect_queue[$];

	uvm_analysis_imp_monitor#(my_transaction, my_scoreboard) monitor_imp;
	uvm_analysis_imp_model#(my_transaction,my_scoreboard) model_imp;

	extern function void write_monitor(my_transaction tr);
	extern function void write_model(my_transaction tr);
	extern virtual task main_phase(uvm_phase phase);
endclass

数据同步机制   由于实际事务和预期事务可能在不同的时间到达Scoreboard,通常需要设计巧妙的同步机制。常见的做法包括使用事务ID进行匹配、基于时间戳的对比,或者使用TLM FIFO来缓冲数据以确保比较的准确性。

5.3 sequencer与driver的传输

Sequencer和Driver之间的TLM通信是UVM验证平台中sequence机制的核心,这种通信模式实现了测试用例与DUT驱动逻辑的分离,大大提高了测试的可重用性和灵活性。 sequencer与Driver的传输需具有以下特性:

  • 阻塞获取:Driver等待Sequencer提供序列项
  • 完成确认:Driver必须告知Sequencer事务已完成
  • 响应机制:可选地返回响应数据
  • 序列控制:支持序列的暂停、停止等控制

无法使用简单的get()或put()实现,因此设计了专用的通信端口seq_item_port,其包含的方法:get_next_item(),item_done(),put_response()。   sequencer与Driver的通过seq_item_port传输,可通过以下步骤实现

  1. 声明,定义Driver组件时已经从父类继承,可以直接使用
systemverilog 复制代码
// 在 uvm_driver 中自动拥有:
// uvm_seq_item_pull_port #(REQ, RSP) seq_item_port
class my_driver extends uvm_driver #(my_transaction);
    // 不需要手动声明 seq_item_port
    // 它已经从 uvm_driver 基类继承
endclass
  1. 连接
systemverilog 复制代码
class my_agent extends uvm_agent;
    my_driver    driver;
    my_sequencer sequencer;
    
    function void connect_phase(uvm_phase phase);
        // 连接 driver 的 seq_item_port 到 sequencer 的 seq_item_export
        driver.seq_item_port.connect(sequencer.seq_item_export);
    endfunction
endclass
  1. 实现方法
systemverilog 复制代码
class my_driver extends uvm_driver #(my_transaction);
    virtual task run_phase(uvm_phase phase);
        forever begin
            // 实现协议方法调用
            seq_item_port.get_next_item(req);
            // ... 驱动逻辑 ...
            seq_item_port.item_done();
        end
    endtask
endclass

5.4 tlm_fifo的使用场景

TLM FIFO是UVM提供的一种特殊组件,它在TLM端口之间充当缓冲区,主要用于解决验证组件间速度不匹配、实现数据暂存和提供额外的同步点。

systemverilog 复制代码
class my_env extends uvm_env;
    my_monitor monitor;
    scoreboard sb;
    uvm_tlm_fifo #(my_transaction) tlm_fifo;
    
    virtual function void build_phase(uvm_phase phase);
        monitor = my_monitor::type_id::create("monitor", this);
        sb = scoreboard::type_id::create("sb", this);
        tlm_fifo = new("tlm_fifo", this);
    endfunction
    
    virtual function void connect_phase(uvm_phase phase);
        // Monitor快速写入FIFO
        monitor.mon_analysis_port.connect(tlm_fifo.analysis_export);
        
        // Scoreboard按自己的速度从FIFO读取
        tlm_fifo.blocking_get_port.connect(sb.get_export);
    endfunction
endclass
  • 作用:解决生产者和消费者速率不匹配的问题
  • 特点:提供数据缓冲功能,自动处理读写同步,支持多种访问方式
  • 应用场景:Monitor 采集数据快于 Scoreboard 处理速度时,不同时钟域组件间的数据传递,需要数据排队处理的场景

6 总结

TLM 1.0作为UVM验证环境中的通信骨干,提供了灵活而强大的组件间交互机制。通过合理使用不同类型的TLM接口,可以构建出高效、可重用的验证组件。掌握TLM 1.0不仅有助于理解UVM的通信机制,也是构建复杂验证环境的基础技能。   在实际项目中,建议根据具体的通信需求选择合适的TLM接口,并遵循统一的连接规范,这样才能构建出稳定可靠的验证环境。

上一篇:UVM验证入门(3)-factory工厂机制 下一篇:UVM验证入门(5)-Phase机制

参考文档:UVM_Cl ass_Reference_Manual_1.0.pdf

相关推荐
今天没有盐1 天前
Python 数据分析实战:多场景数据处理与可视化全解析
python·pycharm·编程语言
今天没有盐4 天前
Python数据分析实战:从超市销售到教学评估
python·pycharm·编程语言
avi91115 天前
Lua高级语法-第二篇
lua·游戏开发·编程语言·语法糖
今天没有盐5 天前
Python算法实战:从滑动窗口到数学可视化
python·pycharm·编程语言
今天没有盐7 天前
Scala Map集合完全指南:从入门到实战应用
后端·scala·编程语言
有意义8 天前
栈数据结构全解析:从实现原理到 LeetCode 实战
javascript·算法·编程语言
HyperAI超神经12 天前
【TVM 教程】优化大语言模型
人工智能·语言模型·自然语言处理·cpu·gpu·编程语言·tvm
吴名氏.19 天前
电子书《ASP.NET MVC企业级实战》
后端·asp.net·mvc·编程语言
Mr_Dwj20 天前
【Python】Python 基本概念
开发语言·人工智能·python·大模型·编程语言