如果在构建更加复杂的协议总线传输,例如PCie、USB3.0等,那么通过一个单一的传输层次会对以后的激励复用、上层控制不那么友好。对于这钟更深层次化的数据传输,在实际中无论是VIP还是自开发的环境,都倾向于通过若干抽象层次的sequence群落来模拟协议层次。通过层次化的sequence可以分别构建transaction layer、transport layer和physical layer等从高抽象级到低抽象级的transaction转化。这种层次化的sequence构建方式,称之为layering sequence。例如在进行寄存器级别的访问操作,其需要通过transport layer转化,最终映射为具体的总线传输。
typedef enum {CLKON, CLKOFF, RESET, WRREG, RDREG} phy_cmd_t; // 定义物理层命令枚举类型
typedef enum {FREQ_LOW_TRANS, FREQ_MED_TRANS, FREQ_HIGH_TRANS} layer_cmd_t; // 定义数据层命令枚举类型
class bus_trans extends uvm_sequence_item; // 定义物理层事务类
rand phy_cmd_t cmd; // 随机物理层命令
rand int addr; // 随机地址
rand int data; // 随机数据
constraint cstr { // 定义约束
soft addr == 'h0; // 限制地址为 0
soft data == 'h0; // 限制数据为 0
}
...
endclass
class packet_seq extends uvm_sequence; // 定义数据层序列类
rand int len; // 随机包长度
rand int addr; // 随机地址
rand int data[]; // 随机数据数组
rand phy_cmd_t cmd; // 随机物理层命令
constraint cstr { // 定义约束
soft len inside {[30:50]}; // 包长度范围为 30 到 50
soft addr[31:16] == 'hFF00; // 地址的高 16 位固定为 'hFF00
data.size() == len; // 数据数组大小等于包长度
}
...
task body(); // 序列执行逻辑
bus_trans req;
foreach(data[i]) // 遍历数据数组
`uvm_do_with(req, {cmd == local::cmd; // 设置物理层命令
addr == local::addr; // 设置地址
data == local::data[i];}) // 设置数据
endtask
endclass
class layer_trans extends uvm_sequence_item; // 定义数据层事务类
rand layer_cmd_t layer_cmd; // 随机数据层命令
rand int pkt_len; // 随机包数量
rand int pkt_idle; // 随机包之间间隔时间
constraint cstr { // 定义约束
soft pkt_len inside {[10:20]}; // 包数量范围为 10 到 20
layer_cmd == FREQ_LOW_TRANS -> pkt_idle inside {[300:400]}; // 低速率时,间隔时间范围为 300 到 400
layer_cmd == FREQ_MED_TRANS -> pkt_idle inside {[100:200]}; // 中速率时,间隔时间范围为 100 到 200
layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]}; // 高速率时,间隔时间范围为 20 到 40
}
...
endclass
class adapter_seq extends uvm_sequence; // 定义数据层到物理层适配器序列类
`uvm_object_utils(adapter_seq) // 使用 `uvm_object_utils` 宏定义字段
`uvm_declare_p_sequencer(phy_master_sequencer) // 声明父 sequencer 类型
...
task body(); // 序列执行逻辑
layer_trans trans; // 声明数据层事务
packet_seq pkt; // 声明数据层包序列
forever begin
p_sequencer.up_sqr.get_next_item(req); // 获得高抽象层的 transaction
void'h(trans, req); // 将高抽象层事务转换为数据层事务
repeat(trans.pkt_len) begin // 循环发送指定数量的包
`uvm_do(pkt) // 发送一个数据层包
delay(trans.pkt_idle); // 延迟指定时间
end
p_sequencer.up_sqr.item_done(); // 通知高抽象层事务完成
end
endtask
virtual task delay(int delay); // 延迟任务
...
endtask
endclass
class top_seq extends uvm_sequence; // 定义顶层序列类
...
task body(); // 序列执行逻辑
layer_trans trans; // 声明数据层事务
`uvm_do_with(trans, {layer_cmd == FREQ_LOW_TRANS;}) // 发送低速率事务
`uvm_do_with(trans, {layer_cmd == FREQ_HIGH_TRANS;}) // 发送高速率事务
endtask
endclass
class layering_sequencer extends uvm_sequencer; // 定义数据层 sequencer 类
...
endclass
class phy_master_sequencer extends uvm_sequencer; // 定义物理层 sequencer 类
layering_sequencer up_sqr; // 声明上层 sequencer
...
endclass
class phy_master_driver extends uvm_driver; // 定义物理层 driver 类
...
task run_phase(uvm_phase phase); // 驱动程序运行逻辑
REQ tmp; // 声明临时变量
bus_trans req; // 声明物理层事务
forever begin
seq_item_port.get_next_item(tmp); // 获取序列项
void'($cast(req, tmp)); // 将序列项转换为物理层事务
`uvm_info("DRV", $sformatf("got a item \n %s", req.sprint()), UVM_LOW) // 打印信息
seq_item_port.item_done(); // 通知 sequencer 序列项完成
end
endtask
endclass
class phy_master_agent extends uvm_agent; // 定义物理层 agent 类
phy_master_sequencer sqr; // 声明物理层 sequencer
phy_master_driver drv; // 声明物理层 driver
...
function void build_phase(uvm_phase phase); // 创建物理层 sequencer 和 driver
sqr = phy_master_sequencer::type_id::create("sqr", this);
drv = phy_master_driver::type_id::create("drv", this);
endfunction
function void connect_phase(uvm_phase phase); // 连接物理层 sequencer 和 driver
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
class test1 extends uvm_test; // 定义测试用例类
layering_sequencer layer_sqr; // 声明数据层 sequencer
phy_master_agent phy_agt; // 声明物理层 agent
...
function void build_phase(uvm_phase phase); // 创建数据层 sequencer 和 物理层 agent
layer_sqr = layering_sequencer::type_id::create("layer_sqr", this);
phy_agt = phy_master_agent::type_id::create("phy_agt", this);
endfunction
function connect_phase(uvm_phase phase); // 连接数据层 sequencer 和 物理层 agent
phy_agt.sqr.up_sqr = layer_sqr;
endfunction
task run_phase(uvm_phase phase); // 测试用例运行逻辑
top_seq seq; // 声明顶层序列
adapter_seq adapter; // 声明数据层到物理层适配器序列
phase.raise_objection(phase); // 声明阻塞
seq = new(); // 创建顶层序列
adapter = new(); // 创建数据层到物理层适配器序列
fork
adapter.start(phy_agt.sqr); // 启动数据层到物理层适配器序列
join_none
seq.start(layer_sqr); // 启动顶层序列
phase.drop_objection(phase); // 解除阻塞
endtask
endclass
代码功能:
bus_trans 类定义了物理层的事务,包含命令、地址和数据。
packet_seq 类定义了一个数据层序列,它会发送一系列物理层事务,模拟一个数据包的传输。
layer_trans 类定义了数据层的事务,包含数据层命令、包数量和包之间间隔时间。
adapter_seq 类定义了一个数据层到物理层适配器序列,它会将数据层事务转换为物理层事务,并发送给物理层 driver。
top_seq 类定义了顶层序列,它会发送数据层事务给数据层 sequencer。
layering_sequencer 类定义了数据层 sequencer,它会将数据层事务发送给适配器序列。
phy_master_sequencer 类定义了物理层 sequencer,它会将物理层事务发送给物理层 driver。
phy_master_driver 类定义了物理层 driver,它会将物理层事务发送到 DUT。
phy_master_agent 类定义了物理层 agent,它管理物理层 sequencer 和 driver。
test1 类定义了测试用例,它会启动顶层序列,并连接数据层和物理层 agent。
测试流程:
测试用例 test1 创建数据层 sequencer 和 物理层 agent。
测试用例启动顶层序列 seq。
顶层序列发送数据层事务给数据层 sequencer。
数据层 sequencer 将数据层事务发送给适配器序列 adapter。
适配器序列将数据层事务转换为物理层事务,并发送给物理层 sequencer。
物理层 sequencer 将物理层事务发送给物理层 driver。
物理层 driver 将物理层事务发送到 DUT。
这个简单的测试环境展示了 UVM 如何使用 sequence、sequencer 和 driver 来验证分层协议模型。