4. TLM适配器工程案例2:将非标准TLM接口(如APB总线协议)转换为标准TLM put/get接口
在复杂的SoC验证环境中,大量IP核使用总线协议(如APB、AHB、AXI)进行通信,而非标准的TLM接口。本案例聚焦于如何将APB从设备接口封装为UVM中标准的TLM put/get接口,使得验证组件(如激励发生器、记分板)能够通过统一的TLM调用与APB从设备交互,而无需关心底层总线时序。
4.1 案例背景与设计意图
APB(Advanced Peripheral Bus)是一种简单的、低功耗的总线协议,通常用于连接低速外设。其典型信号包括:PCLK、PRESETn、PADDR、PWRITE、PSEL、PENABLE、PWDATA、PRDATA 和 PREADY。APB从设备接口是一个典型的非标准TLM接口,因为它基于时钟周期和握手信号,而不是基于事务级的方法调用。
本案例的目标是创建一个 apb2tlm_adapter 组件,该组件在内部处理APB总线时序,对外则提供两个标准的TLM端口:
- put_port:用于接收写事务(
apb_write_txn)。 - get_port:用于请求读事务(
apb_read_txn)并返回读取的数据。
设计要点:适配器应作为被动组件存在,等待上游组件通过TLM端口发起事务。适配器内部维护一个有限状态机(FSM)来模拟APB总线的
SETUP 和 ACCESS 状态,从而将TLM方法调用转换为精确的APB时序波形。
4.2 适配器架构与SVG结构图
下图展示了 apb2tlm_adapter 在验证环境中的位置及其内部架构。适配器位于TLM激励发生器与APB从设备DUT之间,起到协议转换与桥接的作用。
4.3 核心实现:事务定义与适配器组件
4.3.1 定义APB事务对象
首先,需要定义一个继承自 uvm_sequence_item 的事务类,用于封装APB读写操作的所有必要信息。
class apb_txn extends uvm_sequence_item;
rand bit [31:0] addr;
rand bit [31:0] data;
rand bit write; // 1: write, 0: read
bit [31:0] rdata; // 用于存储读取返回的数据
bit error; // 用于指示APB传输错误(如SLVERR)
`uvm_object_utils_begin(apb_txn)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_field_int(write, UVM_ALL_ON)
`uvm_field_int(rdata, UVM_ALL_ON)
`uvm_field_int(error, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "apb_txn");
super.new(name);
endfunction
endclass
4.3.2 适配器组件实现
适配器 apb2tlm_adapter 继承自 uvm_component,并实现TLM put/get 方法。其核心是一个处理APB时序的有限状态机。
class apb2tlm_adapter extends uvm_component;
// TLM 端口
uvm_put_port #(apb_txn) put_port;
uvm_get_port #(apb_txn) get_port;
// 虚拟接口
virtual apb_if vif;
// 内部状态
typedef enum {IDLE, SETUP, ACCESS} apb_state_e;
apb_state_e state;
`uvm_component_utils(apb2tlm_adapter)
function new(string name, uvm_component parent);
super.new(name, parent);
put_port = new("put_port", this);
get_port = new("get_port", this);
endfunction
// 在run_phase中实现APB时序转换
virtual task run_phase(uvm_phase phase);
apb_txn txn;
state = IDLE;
forever begin
// 等待TLM端口有事务到达
fork
begin : put_handler
put_port.get(txn); // 阻塞等待写事务
drive_apb_write(txn);
end
begin : get_handler
get_port.get(txn); // 阻塞等待读事务请求
drive_apb_read(txn);
// 将读取的数据返回给请求者
// 注意:get_port的get方法通常需要返回数据,这里简化处理
// 实际工程中可能需要使用peek或额外的TLM返回路径
end
join_any
disable fork;
end
endtask
// 驱动APB写时序
task drive_apb_write(apb_txn txn);
@(posedge vif.pclk);
// SETUP phase
vif.psel <= 1;
vif.penable <= 0;
vif.paddr <= txn.addr;
vif.pwrite <= 1;
vif.pwdata <= txn.data;
@(posedge vif.pclk);
// ACCESS phase
vif.penable <= 1;
wait(vif.pready == 1);
@(posedge vif.pclk);
// 完成传输
vif.psel <= 0;
vif.penable <= 0;
txn.error <= (vif.pslverr === 1) ? 1 : 0;
endtask
// 驱动APB读时序
task drive_apb_read(apb_txn txn);
@(posedge vif.pclk);
// SETUP phase
vif.psel <= 1;
vif.penable <= 0;
vif.paddr <= txn.addr;
vif.pwrite <= 0;
@(posedge vif.pclk);
// ACCESS phase
vif.penable <= 1;
wait(vif.pready == 1);
@(posedge vif.pclk);
// 采样数据
txn.rdata <= vif.prdata;
txn.error <= (vif.pslverr === 1) ? 1 : 0;
// 完成传输
vif.psel <= 0;
vif.penable <= 0;
endtask
endclass
4.4 常见问题与解决方案
| 常见问题 | 现象 | 根因分析 | 解决方案 |
|---|---|---|---|
| TLM端口阻塞导致死锁 | 仿真挂起,适配器无法处理新事务 | put_port.get() 和 get_port.get() 同时阻塞,但只有一个事务到达,导致另一个分支永远等待。 |
使用 fork...join_any 配合 disable fork,或使用 uvm_tlm_fifo 解耦。 |
| APB时序不匹配 | DUT采样到错误的地址或数据 | 适配器未严格遵循APB的SETUP/ACCESS两周期握手,或未等待 PREADY 信号。 |
在 drive_apb_write/read 任务中严格按状态机实现,确保 PSEL 和 PENABLE 时序正确。 |
| 读事务数据返回路径缺失 | 激励发生器无法获取读取的数据 | get_port 仅用于请求,但适配器未将读取的数据写回事务对象或通过另一个TLM端口返回。 |
使用 uvm_get_peek_port 或增加一个 put_export 用于返回数据,确保双向数据流完整。 |
| 背压处理不当 | 适配器在DUT未准备好时持续驱动 | 未在 ACCESS 阶段等待 PREADY 信号,导致数据丢失或协议违规。 |
在驱动循环中插入 wait(vif.pready == 1),实现真正的握手。 |
4.5 适配器FSM状态转换图
为了更清晰地展示适配器内部的APB时序控制逻辑,下图给出了状态机的详细转换过程。该FSM确保每个TLM事务都能被正确地转换为符合APB规范的波形。
4.6 验证与调试建议
- 使用TLM FIFO解耦:在适配器内部引入
uvm_tlm_fifo #(apb_txn),将TLM端口的接收与APB驱动分离,可以有效避免死锁,并支持背压。 - 断言检查:在适配器内部添加APB协议断言(如
PSEL与PENABLE的时序关系),确保转换后的波形符合APB规范。 - 覆盖率收集:收集APB状态机状态转换覆盖率以及地址/数据范围的覆盖率,确保适配器在各种边界条件下都被充分测试。
- 仿真波形调试:在调试阶段,将适配器的内部状态(
state)和TLM事务内容(txn.addr,txn.write)通过$display或记录到日志中,便于快速定位协议转换错误。
工程陷阱提醒:不要试图在适配器内部处理复杂的APB流水线或Split传输。本案例的适配器设计为一次处理一个事务,适用于简单的APB从设备。对于高性能总线(如AXI),需要设计更复杂的适配器状态机,并考虑乱序传输和多个未完成事务的处理。