日常记录(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!