日常记录(73)、241寄存器模型
作者:互联网
我的DUT
- 我只用了mem[0],它的地址是0x12345678。
- 信号线一共就这几条、时钟、复位、地址、写数据线、读数据线、数据使能线、写读方向线。
- 三段always,其中第一段没有用。
module dut (clk, rst_n, addr, w_data, r_data, data_valid, w_enable);
input clk, rst_n;
input [31:0] addr;
input [15:0] w_data;
input data_valid;
input w_enable;
output reg [15:0] r_data;
reg [15:0] mem[3];
reg [15:0] data;
// mem function to control data
always @(posedge clk) begin
if (!rst_n) begin
data <= '0;
end else if (mem[0]==4'ha) begin
data <= 16'haabb;
end else if (mem[1]==4'hb) begin
data <= 16'hbbcc;
end else if (mem[2]==4'hc) begin
data <= 16'hccdd;
end else begin
data <= '1;
end
end
// signal to control mem write
always @(posedge clk) begin
if(!rst_n) begin
mem[0] <= '0;
mem[1] <= '1;
mem[2] <= 16'b0101_0101_0101_0101;
end else if (data_valid && w_enable) begin
case (addr)
32'h12345678: begin
mem[0] <= w_data;
end
32'h12345679: begin
mem[1] <= w_data;
end
32'h1234567a: begin
end
default: begin
end
endcase
end
end
// signal to control mem read
always @(posedge clk) begin
if (!rst_n) begin
r_data <= '0;
end else if (data_valid && !w_enable) begin
case (addr)
32'h12345678: begin
r_data <= mem[0];
end
32'h12345679: begin
r_data <= '1;
end
32'h1234567a: begin
r_data <= mem[2];
end
default: begin
end
endcase
end
end
endmodule
test_top、interface
interface
interface bus_ctl (input bit clk);
// nets
logic rst_n, data_valid, w_enable;
logic [31:0] addr;
logic [15:0] w_data, r_data;
// clocking
clocking testbench_cb @(posedge clk);
output rst_n, data_valid, w_enable;
output addr;
output w_data;
input r_data;
endclocking: testbench_cb
// modports
modport my_testbench(clocking testbench_cb, output rst_n);
endinterface: bus_ctl
test_top
- 在testcase,或者env的调用接口上,是使用了test_top.bus_if的实例化调用。在使用过程中,直接可用cb,注意cb的非阻塞赋值。
- modport也可以使用,如test_top.bus_if.my_testbench.rst_n,这个可以阻塞或者非阻塞。
- 时钟生成与test_top即可,这个是dut和program公用。
`include "my_interface.sv"
module test_top ();
bit clk;
bus_ctl bus_if(clk);
dut dut_inst(.clk(clk),
.rst_n(bus_if.rst_n),
.addr(bus_if.addr),
.w_data(bus_if.w_data),
.r_data(bus_if.r_data),
.data_valid(bus_if.data_valid),
.w_enable(bus_if.w_enable));
test test_inst(bus_if);
initial begin
clk = 0;
forever begin
#1 clk = ~clk;
end
end
endmodule
program test (bus_ctl bus_if);
import uvm_pkg::*;
`include "testcase.sv"
initial begin
run_test("my_test");
end
endprogram: test
testcase
- 没有使用agent、env,直接上drv和seqr.用于测试reg_model
- 首先是需要测试seqr和drv的通信,以及drv的读写协议是否能够操作dut。注意这个通信的trans必须有一个读写方向控制变量。而drv的数据是返回给req的。
- 然后是建立寄存器模型进行读写控制。其中的发起者通过reg_model,使用read、write等,并配置,即可完成前后门访问。
- 前门访问:发起者调用reg_model,reg_model生成seq,然后通过adapter转换,生成drv可用的trans,根据trans的方向、地址、数据,进行读写,返回时候给req即可。
- 后门访问:发起者调用reg_model,reg_model可能是和DPI相关,然后通过路径(test_top.dut_inst.mem[0])等,直接就可以操作寄存器了。
- 因此在前门访问中,需要set_sequencer,指定好seqr和adapter。
`include "my_seqr.sv"
`include "my_driver.sv"
`include "reg_model.sv"
class my_test extends uvm_test;
`uvm_component_utils(my_test)
virtual bus_ctl my_if;
my_seq seq;
my_driver drv;
my_seqr seqr;
my_block rm;
my_block2 rm2;
my_adapter adp;
function new(string name="my_test", uvm_component parent);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = my_driver::type_id::create("drv", this);
seqr = my_seqr::type_id::create("seqr", this);
uvm_config_db#(virtual bus_ctl)::set(this, "drv", "bus_if", test_top.bus_if);
rm=my_block::type_id::create("name", this);
rm.configure(null, "");
rm.build();
rm.lock_model();
rm.reset();
rm.set_hdl_path_root("test_top.dut_inst");
rm2 = my_block2::type_id::create("rm2", this);
rm2.configure(null, "");
rm2.build();
rm2.lock_model();
rm2.reset();
rm2.set_hdl_path_root("test_top");
adp = new("apt");//type_id?
endfunction: build_phase
function void connect_phase(uvm_phase phase);
drv.seq_item_port.connect(seqr.seq_item_export);
rm.default_map.set_sequencer(seqr, adp);
rm.default_map.set_auto_predict(1);
rm2.default_map.set_sequencer(seqr, adp);
rm2.default_map.set_auto_predict(1);
check_config_usage();
endfunction: connect_phase
task run_phase(uvm_phase phase);
uvm_status_e status;
uvm_reg_data_t value;
phase.raise_objection(this);
seq = new("seq");
seq.start(seqr);
`uvm_info("INFO_ID", $sformatf("---------------------------------------------"), UVM_LOW)
rm.reg1.read(status, value, UVM_FRONTDOOR);
`uvm_info("INFO_ID", $sformatf("get data value %h", value), UVM_LOW)
value = 'hbcde;
rm.reg1.write(status, value, UVM_FRONTDOOR);
`uvm_info("INFO_ID", $sformatf("get data value %h", value), UVM_LOW)
rm.reg1.read(status, value, UVM_FRONTDOOR);
`uvm_info("INFO_ID", $sformatf("get data value %h", value), UVM_LOW)
`uvm_info("INFO_ID", $sformatf("---------------------------------------------"), UVM_LOW)
value = 'h2345;
rm.reg1.write(status, value, UVM_BACKDOOR);
rm.reg1.read(status, value, UVM_BACKDOOR);
`uvm_info("INFO_ID_READBACKDOOR_2345", $sformatf("get data value %h", value), UVM_LOW)
`uvm_info("INFO_ID", $sformatf("---------------------------------------------"), UVM_LOW)
value = 'h5678;
rm.reg1.poke(status, value);
rm.reg1.peek(status, value);
`uvm_info("INFO_ID_PEEK_5678", $sformatf("peek get data value %h", value), UVM_LOW)
`uvm_info("INFO_ID", $sformatf("---------------------------------------------"), UVM_LOW)
value = 'haabb;
rm2.blk2.reg1.write(status, value, UVM_FRONTDOOR);
rm2.blk2.reg1.read(status, value, UVM_FRONTDOOR);
`uvm_info("INFO_ID_FRONT2_aabb", $sformatf("get data value %h", value), UVM_LOW)
`uvm_info("INFO_ID", $sformatf("---------------------------------------------"), UVM_LOW)
value = 'hacbc;
rm2.blk2.reg1.poke(status, value);
rm2.blk2.reg1.peek(status, value);
`uvm_info("INFO_ID_PEEK2_acbc", $sformatf("get data value %h", value), UVM_LOW)
`uvm_info("INFO_ID", $sformatf("---------------------------------------------"), UVM_LOW)
phase.drop_objection(this);
endtask: run_phase
endclass: my_test
my_driver、my_sequencer
my_driver
class my_driver extends uvm_driver#(my_trans);
`uvm_component_utils(my_driver)
virtual bus_ctl my_if;
event start_drive;
function new(string name="my_driver", uvm_component parent);
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(virtual bus_ctl)::get(this, "", "bus_if", my_if);
endfunction: build_phase
task reset_phase(uvm_phase phase);
phase.raise_objection(this);
my_if.my_testbench.rst_n = 0;
@(my_if.my_testbench.testbench_cb);
my_if.my_testbench.rst_n = 1;
@(my_if.my_testbench.testbench_cb);
phase.drop_objection(this);
-> start_drive;
endtask: reset_phase
task run_phase(uvm_phase phase);
@(start_drive);
while (1) begin
seq_item_port.get_next_item(req);
`uvm_info("DRIVER_GET", $sformatf("get data: %h, %h", req.address, req.data), UVM_LOW)
drive();
seq_item_port.item_done(rsp);
end
endtask: run_phase
task drive();
if(req.w_enable == 1) begin
my_if.my_testbench.testbench_cb.w_data <= req.data;
my_if.my_testbench.testbench_cb.addr <= req.address;
my_if.my_testbench.testbench_cb.data_valid <= 1;
my_if.my_testbench.testbench_cb.w_enable <= 1;
@(my_if.my_testbench.testbench_cb);
`uvm_info("DRIVER_WRITE", $sformatf("write: addr: %h, data: %h", req.address, req.data), UVM_LOW)
end else begin
my_if.my_testbench.testbench_cb.addr <= req.address;
my_if.my_testbench.testbench_cb.data_valid <= 1;
my_if.my_testbench.testbench_cb.w_enable <= 0;
@(my_if.my_testbench.testbench_cb);
req.data = my_if.r_data;
`uvm_info("DRIVER_READ", $sformatf("read : addr: %h, data: %h", req.address, req.data), UVM_LOW)
end
endtask: drive
endclass: my_driver
my_sequencer\trans
-常规内容
class my_trans extends uvm_sequence_item;
`uvm_object_utils(my_trans)
rand int address;
rand bit [15:0] data;
rand bit w_enable;
function new(string name="my_trans");
super.new(name);
endfunction: new
endclass: my_trans
class my_seq extends uvm_sequence#(my_trans);
`uvm_object_utils(my_seq)
function new(string name="my_seq");
super.new(name);
endfunction: new
task body();
req = new("req");
`uvm_do_with(req, {address==32'h12345678; data==16'habcd; w_enable=='1;})
`uvm_do_with(req, {address==32'h12345678; data==16'habcd; w_enable=='0;})
endtask: body
endclass: my_seq
typedef uvm_sequencer#(my_trans) my_seqr;
reg_model
- 寄存器模型包括:field、reg、block、default_map至少四个要素。其中的field是一个位置,reg是多个寄存器位置的合集。block是reg的合集,需要一个default_map,同时可以包括其它的block。
- uvm_reg\uvm_reg_block\uvm_reg_field是直接继承于uvm_object的。它们的build方法,不是phase的,需要手动调用。
- my_reg是一个普通的16位reg,它被my_block包裹,形成一个reg_model。其中的reg1.configure配置了路径mem[0],后门访问用。reg进行build,是添加了reg_data,然后add_reg,说明了该reg的位置,前门访问用。
- reg_file的实现,用于对reg_model进行路径配置。my_block_sub,是一个block,里面有file1,配置了dut_inst,和设置后门访问的configure进行组合,形成完整路径。
- my_block2是包裹了my_block_sub的reg_model,它添加了my_block_sub以后,才应该进行block_model。里面有基地址,对应内部的偏移地址。
- adapter:常规转换。
class my_reg extends uvm_reg;
`uvm_object_utils(my_reg)
uvm_reg_field reg_data;
function new(string name="my_reg");
// width of reg, option
super.new(name, 16, UVM_NO_COVERAGE);
endfunction: new
function void build();
reg_data = uvm_reg_field::type_id::create("reg1");
// parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 16, 0, "RW", 1, 0, 1, 1, 0);
endfunction: build
endclass: my_reg
class my_block extends uvm_reg_block;
`uvm_object_utils(my_block)
my_reg reg1;
function new(string name="my_block");
super.new(name);
endfunction: new
function void build();
default_map = create_map("default_map", 0, 2, UVM_LITTLE_ENDIAN, 0);
reg1 = my_reg::type_id::create("reg1");//get_full_name
reg1.configure(this, null, "mem[0]");
reg1.build();
default_map.add_reg(reg1, 32'h12345678, "RW");
endfunction: build
endclass: my_block
class reg_file extends uvm_reg_file;
`uvm_object_utils(reg_file)
function new(string name="reg_file");
super.new(name);
endfunction: new
endclass: reg_file
class my_block_sub extends uvm_reg_block;
`uvm_object_utils(my_block_sub)
my_reg reg1;
rand reg_file file1;
function new(string name="my_block");
super.new(name);
endfunction: new
function void build();
/* file1 = reg_file::type_id::create("file1", ,get_full_name()); */
file1 = reg_file::type_id::create("file1");
file1.configure(this, null, "dut_inst");
default_map = create_map("default_map", 0, 2, UVM_LITTLE_ENDIAN, 0);
reg1 = my_reg::type_id::create("reg1");
reg1.configure(this, file1, "mem[0]");
reg1.build();
default_map.add_reg(reg1, 32'h8, "RW");
endfunction: build
endclass: my_block_sub
class my_block2 extends uvm_reg_block;
`uvm_object_utils(my_block2)
my_block_sub blk2;
function new(string name="my_block2");
super.new(name);
endfunction: new
function void build();
default_map = create_map("default_map", 0, 2, UVM_LITTLE_ENDIAN, 0);
blk2 = my_block_sub::type_id::create("blk2");
blk2.configure(this, "");
blk2.build();
default_map.add_submap(blk2.default_map, 32'h12345670);
blk2.lock_model();
endfunction: build
endclass: my_block2
class my_adapter extends uvm_reg_adapter;
`uvm_object_utils(my_adapter)
function new(string name="my_adapter");
super.new(name);
endfunction: new
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
my_trans tr;
tr = new("tr");
tr.address = rw.addr;
tr.data = rw.data;
tr.w_enable = rw.kind == UVM_WRITE ? 1:0;
return tr;
endfunction: reg2bus
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
my_trans tr;
$cast(tr, bus_item);
rw.addr = tr.address;
rw.kind = tr.w_enable == 1 ? UVM_WRITE:UVM_READ;
rw.data = tr.data;
rw.status = UVM_IS_OK;
endfunction: bus2reg
endclass: my_adapter
标签:data,new,73,uvm,寄存器,phase,241,my,reg 来源: https://www.cnblogs.com/bai2022/p/15986731.html