日常记录(45)寄存器模型demo
寄存器模型
寄存器模型DUT
这里面只有与bus_*有关,与my_*,如my_transaction,my_sqr等无关。
DUT
UVM的寄存器模型适合于具有地址和数据总线之类的DUT,以下是一个可以使用寄存器模型的DUT
第二段always为写操作,当可写时,且写入地址为9号,则将bus_wr_data的最低位给invert(对应地址的寄存器)
第三段always为读操作,当可读时,且读取地址为9号,则将数据(invert)读取到bus_rd_data的最低位。
第一段alywas为数据传送操作,通常数据(rxd)和使能位(rx_dv)接收(rx)后,直接发送到txd(包括tx_en),若invert使能,则将rxd数据取反后发送。
module dut(clk,rst_n,bus_cmd_valid,bus_op,bus_addr,bus_wr_data,bus_rd_data,rxd,rx_dv,txd,tx_en);
input clk;
input rst_n;
input bus_cmd_valid;
input bus_op;
input [15:0] bus_addr;
input [15:0] bus_wr_data;
output [15:0] bus_rd_data;
input [7:0] rxd;
input rx_dv;
output [7:0] txd;
output tx_en;
reg[7:0] txd;
reg tx_en;
reg invert;
always @(posedge clk) begin
if(!rst_n) begin
txd <= 8'b0;
tx_en <= 1'b0;
end
else if(invert) begin
txd <= ~rxd;
tx_en <= rx_dv;
end
else begin
txd <= rxd;
tx_en <= rx_dv;
end
end
always @(posedge clk) begin
if(!rst_n)
invert <= 1'b0;
else if(bus_cmd_valid && bus_op) begin
case(bus_addr)
16'h9: begin
invert <= bus_wr_data[0];
end
default: begin
end
endcase
end
end
reg [15:0] bus_rd_data;
always @(posedge clk) begin
if(!rst_n)
bus_rd_data <= 16'b0;
else if(bus_cmd_valid && !bus_op) begin
case(bus_addr)
16'h9: begin
bus_rd_data <= {15'b0, invert};
end
default: begin
bus_rd_data <= 16'b0;
end
endcase
end
end
endmodule
bus_if
interface bus_if(input clk, input rst_n); logic bus_cmd_valid; logic bus_op; logic [15:0] bus_addr; logic [15:0] bus_wr_data; logic [15:0] bus_rd_data; endinterface
bus_transaction
typedef enum{BUS_RD, BUS_WR} bus_op_e;
class bus_transaction extends uvm_sequence_item;
rand bit[15:0] rd_data;
rand bit[15:0] wr_data;
rand bit[15:0] addr;
rand bus_op_e bus_op;
`uvm_object_utils_begin(bus_transaction)
`uvm_field_int(rd_data, UVM_ALL_ON)
`uvm_field_int(wr_data, UVM_ALL_ON)
`uvm_field_int(addr , UVM_ALL_ON)
`uvm_field_enum(bus_op_e, bus_op, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "bus_transaction");
super.new();
endfunction
endclass
bus_driver
class bus_driver extends uvm_driver#(bus_transaction);
virtual bus_if vif;
`uvm_component_utils(bus_driver)
function new(string name = "bus_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual bus_if)::get(this, "", "vif", vif))
`uvm_fatal("bus_driver", "virtual interface must be set for vif!!!")
endfunction
extern task run_phase(uvm_phase phase);
extern task drive_one_pkt(bus_transaction tr);
endclass
task bus_driver::run_phase(uvm_phase phase);
vif.bus_cmd_valid <= 1'b0;
vif.bus_op <= 1'b0;
vif.bus_addr <= 15'b0;
vif.bus_wr_data <= 15'b0;
while(!vif.rst_n)
@(posedge vif.clk);
while(1) begin
seq_item_port.get_next_item(req);
drive_one_pkt(req);
seq_item_port.item_done();
end
endtask
task bus_driver::drive_one_pkt(bus_transaction tr);
`uvm_info("bus_driver", "begin to drive one pkt", UVM_LOW);
repeat(1) @(posedge vif.clk);
vif.bus_cmd_valid <= 1'b1;
vif.bus_op <= ((tr.bus_op == BUS_RD) ? 0 : 1);
vif.bus_addr = tr.addr;
vif.bus_wr_data <= ((tr.bus_op == BUS_RD) ? 0 : tr.wr_data);
@(posedge vif.clk);
vif.bus_cmd_valid <= 1'b0;
vif.bus_op <= 1'b0;
vif.bus_addr <= 15'b0;
vif.bus_wr_data <= 15'b0;
@(posedge vif.clk);
if(tr.bus_op == BUS_RD) begin
tr.rd_data = vif.bus_rd_data;
//$display("@%0t, rd_data is %0h", $time, tr.rd_data);
end
`uvm_info("bus_driver", "end drive one pkt", UVM_LOW);
endtask
my_vsqr
class my_vsqr extends uvm_sequencer;
my_sequencer p_my_sqr;
bus_sequencer p_bus_sqr;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
`uvm_component_utils(my_vsqr)
endclass
my_case0中的读写操作
case0_bus_seq中写了如何发送,至于case0_sequence,无用。
class case0_sequence extends uvm_sequence #(my_transaction);
my_transaction m_trans;
function new(string name= "case0_sequence");
super.new(name);
endfunction
virtual task body();
repeat (10) begin
`uvm_do(m_trans)
end
endtask
`uvm_object_utils(case0_sequence)
endclass
class case0_bus_seq extends uvm_sequence #(bus_transaction);
bus_transaction m_trans;
function new(string name= "case0_bus_seq");
super.new(name);
endfunction
virtual task body();
`uvm_do_with(m_trans, {m_trans.addr == 16'h9;
m_trans.bus_op == BUS_RD;})
`uvm_info("case0_bus_seq", $sformatf("invert's initial value is %0h", m_trans.rd_data), UVM_LOW)
`uvm_do_with(m_trans, {m_trans.addr == 16'h9;
m_trans.bus_op == BUS_WR;
m_trans.wr_data == 16'h1;})
`uvm_do_with(m_trans, {m_trans.addr == 16'h9;
m_trans.bus_op == BUS_RD;})
`uvm_info("case0_bus_seq", $sformatf("after set, invert's value is %0h", m_trans.rd_data), UVM_LOW)
`uvm_do_with(m_trans, {m_trans.addr == 16'h9;
m_trans.bus_op == BUS_WR;
m_trans.wr_data == 16'h0;})
`uvm_do_with(m_trans, {m_trans.addr == 16'h9;
m_trans.bus_op == BUS_RD;})
`uvm_info("case0_bus_seq", $sformatf("after set, invert's value is %0h", m_trans.rd_data), UVM_LOW)
endtask
`uvm_object_utils(case0_bus_seq)
endclass
class case0_vseq extends uvm_sequence;
`uvm_object_utils(case0_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_vseq");
super.new(name);
endfunction
virtual task body();
case0_sequence dseq;
case0_bus_seq bseq;
if(starting_phase != null)
starting_phase.raise_objection(this);
bseq = case0_bus_seq::type_id::create("bseq");
bseq.start(p_sequencer.p_bus_sqr);
dseq = case0_sequence::type_id::create("dseq");
dseq.start(p_sequencer.p_my_sqr);
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
class my_case0 extends base_test;
function new(string name = "my_case0", uvm_component parent = null);
super.new(name,parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
`uvm_component_utils(my_case0)
endclass
function void my_case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"v_sqr.main_phase",
"default_sequence",
case0_vseq::type_id::get());
endfunction
寄存器模型1
uvm_reg中包含reg_field,并被block包含。
以下reg中包括了一个reg_field(reg_data),其中block需要一个default_map,把invert加入。其中填写了invert的地址为9,便于后续的读写。
class reg_invert extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_invert)
function new(input string name="reg_invert");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class reg_model extends uvm_reg_block;
rand reg_invert invert;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
invert = reg_invert::type_id::create("invert", , get_full_name());
invert.configure(this, null, "");
invert.build();
default_map.add_reg(invert, 'h9, "RW");
endfunction
`uvm_object_utils(reg_model)
function new(input string name="reg_model");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
寄存器模型需要的适配器
实现将reg的数据转换到bus,以及bus的数据转回reg。
class my_adapter extends uvm_reg_adapter;
string tID = get_type_name();
`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);
bus_transaction tr;
tr = new("tr");
tr.addr = rw.addr;
tr.bus_op = (rw.kind == UVM_READ) ? BUS_RD: BUS_WR;
if (tr.bus_op == BUS_WR)
tr.wr_data = rw.data;
return tr;
endfunction : reg2bus
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
bus_transaction tr;
if(!$cast(tr, bus_item)) begin
`uvm_fatal(tID,
"Provided bus_item is not of the correct type. Expecting bus_transaction")
return;
end
rw.kind = (tr.bus_op == BUS_RD) ? UVM_READ : UVM_WRITE;
rw.addr = tr.addr;
rw.byte_en = 'h3;
rw.data = (tr.bus_op == BUS_RD) ? tr.rd_data : tr.wr_data;
rw.status = UVM_IS_OK;
endfunction : bus2reg
endclass : my_adapter
base_test
被my_case0等继承
实例化寄存器模型、适配器,然后调用寄存器模型的default_map,设置bus_sqr与adapter。设置自动预测。
class base_test extends uvm_test;
my_env env;
my_vsqr v_sqr;
reg_model rm;
my_adapter reg_sqr_adapter;
function new(string name = "base_test", uvm_component parent = null);
super.new(name,parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
extern virtual function void report_phase(uvm_phase phase);
`uvm_component_utils(base_test)
endclass
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
v_sqr = my_vsqr::type_id::create("v_sqr", this);
rm = reg_model::type_id::create("rm", this);
rm.configure(null, "");
rm.build();
rm.lock_model();
rm.reset();
reg_sqr_adapter = new("reg_sqr_adapter");
env.p_rm = this.rm;
endfunction
function void base_test::connect_phase(uvm_phase phase);
super.connect_phase(phase);
v_sqr.p_my_sqr = env.i_agt.sqr;
v_sqr.p_bus_sqr = env.bus_agt.sqr;
v_sqr.p_rm = this.rm;
rm.default_map.set_sequencer(env.bus_agt.sqr, reg_sqr_adapter);
rm.default_map.set_auto_predict(1);
endfunction
function void base_test::report_phase(uvm_phase phase);
uvm_report_server server;
int err_num;
super.report_phase(phase);
server = get_report_server();
err_num = server.get_severity_count(UVM_ERROR);
if (err_num != 0) begin
$display("TEST CASE FAILED");
end
else begin
$display("TEST CASE PASSED");
end
endfunction
在my_model中声明和读取数据
p_rm.invert.read(status, value, UVM_FRONTDOOR);
reg_model p_rm;
task my_model::main_phase(uvm_phase phase);
my_transaction tr;
my_transaction new_tr;
uvm_status_e status;
uvm_reg_data_t value;
super.main_phase(phase);
p_rm.invert.read(status, value, UVM_FRONTDOOR);
while(1) begin
port.get(tr);
new_tr = new("new_tr");
new_tr.copy(tr);
//`uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
//new_tr.print();
if(value)
invert_tr(new_tr);
ap.write(new_tr);
end
endtask
在my_case0中写操作
class case0_cfg_vseq extends uvm_sequence;
`uvm_object_utils(case0_cfg_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_cfg_vseq");
super.new(name);
endfunction
virtual task body();
uvm_status_e status;
uvm_reg_data_t value;
if(starting_phase != null)
starting_phase.raise_objection(this);
p_sequencer.p_rm.invert.read(status, value, UVM_FRONTDOOR);
`uvm_info("case0_cfg_vseq", $sformatf("invert's initial value is %0h", value), UVM_LOW)
p_sequencer.p_rm.invert.write(status, 1, UVM_FRONTDOOR);
p_sequencer.p_rm.invert.read(status, value, UVM_FRONTDOOR);
`uvm_info("case0_cfg_vseq", $sformatf("after set, invert's value is %0h", value), UVM_LOW)
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
对应的my_vsqr
class my_vsqr extends uvm_sequencer;
my_sequencer p_my_sqr;
bus_sequencer p_bus_sqr;
reg_model p_rm;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
`uvm_component_utils(my_vsqr)
endclass
Le vent se lève! . . . il faut tenter de vivre!
Le vent se lève! . . . il faut tenter de vivre!

浙公网安备 33010602011771号