日常记录(73)、241寄存器模型
2022/3/9 23:19:36
本文主要是介绍日常记录(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
这篇关于日常记录(73)、241寄存器模型的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27本地多文件上传的简单教程
- 2024-11-27低代码开发:初学者的简单教程
- 2024-11-27如何轻松掌握拖动排序功能
- 2024-11-27JWT入门教程:从零开始理解与实现
- 2024-11-27安能物流 All in TiDB 背后的故事与成果
- 2024-11-27低代码开发入门教程:轻松上手指南
- 2024-11-27如何轻松入门低代码应用开发
- 2024-11-27ESLint开发入门教程:从零开始使用ESLint
- 2024-11-27Npm 发布和配置入门指南
- 2024-11-27低代码应用课程:新手入门指南