日常记录(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寄存器模型的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程