UVM-寄存器模型

目录

1.什么是寄存器

2.寄存器块

3.UVM寄存器模型

3.1 期望值

3.2 镜像值

 3.3 创建寄存器类

3.4 定义寄存器块

3.5 寄存器env

4.完整例子

4.1设计

4.2 interface

4.3 寄存器

4.4 寄存器block

4.5 适配器adapter

4.6 寄存器env

4.7 item 

4.8 driver

4.9 monitor

4.10 agent

4.11 env

4.11 reset sequence

4.12 test


1.什么是寄存器

寄存器可以理解为数字设计模块中,软件可以控制的配置单元。硬件在特定的配置下以特定的方式运行。

例如我们可以设计一个32位的寄存器,其中包含了多个独立的字段,每个字段代表特定的功能,在需要时,可以由软件对其进行配置。

偏移名字类型宽度含义
0enRW1使能,1表示使用,0表示禁用
1modeRW3

支持的模式:

000: xxx

001: xxx

……

4haltRW1暂停。1表示暂停,0表示继续
5autoRW1自动。1表示自动。
6reservedRO5保留
11speedRW5设置速度
16reservedRO16保留

2.寄存器块

将寄存器组合到一起,每个寄存器有自己的字段和配置,我们可以根据相关地址进行访问,这就是一个寄存器块。

因为我们的寄存器是32bit,所以每个的寄存器的偏移是0x4。

3.UVM寄存器模型

我们可以通过UVM RAL来定义单个字段,寄存器和寄存器块。寄存器模型是一个实体,它包含了我们需要描述的每个寄存器,以及每个寄存器的各个字段。我们可以通过寄存器模型对设计执行读写的操作。

3.1 期望值

字面意思。就是我们预期寄存器的值。在寄存器模型内部需要通过一个更新动作,更新对应寄存器的值。

3.2 镜像值

寄存器模型中对寄存器内部值的一个镜像。每次对寄存器的读写操作,都可能更新寄存器内部的值,有了镜像值后,我们不用每次都去读取寄存器的值,可以直接读取镜像值即可。

 3.3 创建寄存器类

class my_reg extends uvm_reg;
    //申明寄存器字段
    rand vum_reg_field en;
    rand uvm_reg_field mode;
    rand uvm_reg_field halt;
    rand uvm_reg_field auto;
    rand uvm_reg_field speed;

    function new (string name = "my_reg");
        super.new(name);
    endfunction

    //定义寄存器相关内容
    virtual function void build();
        //创建每个字段
        this.en    =uvm_reg_field::type_id::create("en");
        this.mode  =uvm_reg_field::type_id::create("mode");
        this.halt  =uvm_reg_field::type_id::create("halt");
        this.auto  =uvm_reg_field::type_id::create("auto");
        this.speed =uvm_reg_field::type_id::create("speed");

        //配置每个字段
        this.en.configure(this, 1, 0, "RW", 0, 1'h0, 1, 1, 1);
        this.mode.configure(this, 3, 1, "RW", 0, 3'h2, 1, 1, 1);
        this.halt.configure(this, 1, 4, "RW", 0, 1'h1, 1, 1, 1);
        this.auto.configure(this, 1, 5, "RW", 0, 1'h0, 1, 1, 1);
        this.speed.configure(this, 5, 11, "RW", 0, 5'h1c, 1, 1, 1);
    endfunction
endclass

 类似的,我们可以对每个寄存器进行定义。

3.4 定义寄存器块

寄存器块就是寄存器模型,可以用于对寄存器的访问。

class my_reg_block extends uvm_reg_block;
    rand reg_1 reg1;
    rand reg_2 reg2;
    
    `uvm_object_utils(my_reg_block )

    function new(string name = "my_reg_block ");
        super.new(name, build_coverage(UVM_NO_COVERAGE));        
    endfunction

    virtual function void build();
        this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
        this.reg1 = reg_ctl::type_id::create("reg1", get_full_name());
        this.reg1.configure(this, null, "");
        this.reg1.build();
        this.default_map.add_reg(this.reg1, `UVM_REG_ADDR_WIDTH'h0, "RW", 0);

        this.reg2 = reg_stat::type_id::create("reg2", get_full_name());
        this.reg2.configure(this, null, "");
        this.reg2.build();
        this.default_map.add_reg(this.reg2, `UVM_REG_ADDR_WIDTH'hc, "RO", 0);
    endfunction

3.5 寄存器env

class my_env extends uvm_env;
    `uvm_component_utils(my_env)

    function new(string name = "my_env",  uvm_component parent);
        super.new(name, parent);
    endfunction

    reg_env       m_reg_env;
    my_agent      m_agent;

    virtual function void build_phase (uvm_phase phase);
        super.build_phase(phase);
        m_reg_env = reg_env::type_id::create("m_reg_env", this);
        m_agent = my_agent::type_id::create("m_agent", this);
    endfunction

    virtual function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);

        m_agent.m_mon.mon_ap.connect(m_reg_env.m_apb2reg_predictor.bus_in);
        m_reg_env.m_ral_model.default_map.set_sequencer(m_agent.m_seqr, m_reg_env.m_reg2apb);
    endfunction
endclass

4.完整例子

4.1设计

基于APB协议的一个模型

module traffic (
    input           pclk,
    input           presetn,
    input[31:0]     paddr,
    input[31:0]     pwdata,
    input           psel,
    input           pwrite,
    input           penable,
    output[31:0]    prdata
);
    reg [3:0]       ctl_reg;
    reg [1:0]       stat_reg;
    reg [31:0]      timer_0;
    reg [31:0]      timer_1;

    reg [31:0]      data_in;
    reg [31:0]      rdata_tmp;

    always @(posedge pclk) begin
        if (!presetn) begin
            data_in <= 0;
            ctl_reg <= 0;
            stat_reg <= 0;
            timer_0 <= 32'hcafe_1234;
            timer_1 <= 32'hface_5678;
        end
    end

    always @(posedge pclk) begin
        if (presetn & psel & penable) begin
            if (pwrite) begin
                case (paddr)
                    'h0 : ctl_reg   <= pwdata;
                    'h4 : timer_0   <= pwdata;
                    'h8 : timer_1   <= pwdata;
                    'hc : stat_reg  <= pwdata; 
                    // default: 
                endcase
            end
        end
    end

    always @(penable) begin
        if (psel & !pwrite) begin
            case(paddr)
                'h0 : rdata_tmp <= ctl_reg  ;
                'h4 : rdata_tmp <= timer_0  ;
                'h8 : rdata_tmp <= timer_1  ;
                'hc : rdata_tmp <= stat_reg ; 
            endcase
        end
    end

    assign prdata = (psel & penable & !pwrite) ? rdata_tmp : 'hz;
endmodule

4.2 interface

interface bus_if (input pclk);
    logic[31:0] paddr;
    logic[31:0] pwdata;
    logic[31:0] prdata;

    logic       pwrite;
    logic       psel;
    logic       penable;
    logic       presetn;
endinterface //interfacename

4.3 寄存器

class reg_ctl extends uvm_reg;
    rand uvm_reg_field mod_en;
    rand uvm_reg_field bl_yellow;
    rand uvm_reg_field bl_red;
    rand uvm_reg_field profile;

    `uvm_object_utils(reg_ctl)

    function new(string name = "reg_ctl");
        super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
    endfunction

    virtual function void build();
        this.mod_en     = uvm_reg_field::type_id::create("mod_en", , get_full_name());
        this.bl_yellow  = uvm_reg_field::type_id::create("bl_yellow", , get_full_name());
        this.bl_red     = uvm_reg_field::type_id::create("bl_red", , get_full_name());
        this.profile    = uvm_reg_field::type_id::create("profile", , get_full_name());
        
        this.mod_en.configure(this, 1, 0, "RW", 0, 0, 1, 0, 0);   
        this.bl_yellow.configure(this, 1, 1, "RW", 0, 0, 1, 0, 0);
        this.bl_red.configure(this, 1, 2, "RW", 0, 0 , 1, 0, 0); 
        this.profile.configure(this, 1, 3, "RW", 0, 0, 1, 0, 0);
    endfunction
endclass

class reg_stat extends uvm_reg;

    uvm_reg_field state;
    `uvm_object_utils(reg_stat)

    function new(string name = "reg_stat");
        super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
    endfunction //new()

    virtual function void build();
        this.state = uvm_reg_field::type_id::create("state", , get_full_name());

        this.state.configure(this, 2, 0, "RO", 0, 0, 0, 0, 0);
    endfunction
endclass //reg_stat

class reg_timer extends uvm_reg;

    uvm_reg_field timer;

    `uvm_object_utils(reg_timer)
    function new(string name = "reg_timer");
        super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
    endfunction //new()

    virtual function void build();
        this.timer = uvm_reg_field::type_id::create("timer", , get_full_name());

        this.timer.configure(this, 32, 0, "RW", 0, 32'hcafe1234, 1, 0, 1);
        this.timer.set_reset(0, "SOFT");
    endfunction
endclass //reg_timer

4.4 寄存器block

class reg_traffic_block extends uvm_reg_block;
    rand reg_ctl ctrl;
    rand reg_timer timer[2];
         reg_stat stat;
    
    `uvm_object_utils(reg_traffic_block)

    function new(string name = "reg_traffic_block");
        super.new(name, build_coverage(UVM_NO_COVERAGE));        
    endfunction

    virtual function void build();
        this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
        this.ctrl = reg_ctl::type_id::create("ctrl", ,get_full_name());
        this.ctrl.configure(this, null, "");
        this.ctrl.build();
        this.default_map.add_reg(this.ctrl, `UVM_REG_ADDR_WIDTH'h0, "RW", 0);

        this.timer[0] = reg_timer::type_id::create("timer[0]", , get_full_name());
        this.timer[0].configure(this, null, "");
        this.timer[0].build();
        this.default_map.add_reg(this.timer[0], `UVM_REG_ADDR_WIDTH'h4, "RW", 0);

        this.timer[1] = reg_timer::type_id::create("timer[1]", , get_full_name());
        this.timer[1].configure(this, null, "");
        this.timer[1].build();
        this.default_map.add_reg(this.timer[1], `UVM_REG_ADDR_WIDTH'h8, "RW", 0);

        this.stat = reg_stat::type_id::create("stat", , get_full_name());
        this.stat.configure(this, null, "");
        this.stat.build();
        this.default_map.add_reg(this.stat, `UVM_REG_ADDR_WIDTH'hc, "RO", 0);
    endfunction
endclass

class reg_sys_block extends uvm_reg_block;
    rand reg_traffic_block traffic;

    `uvm_object_utils(reg_sys_block)

    function new(string name = "reg_sys_block");
        super.new(name);
    endfunction

    virtual function void build();
        this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
        this.traffic = reg_traffic_block::type_id::create("traffic",,get_full_name());
        this.traffic.configure(this, "top.pB0");
        this.traffic.build();
        this.default_map.add_submap(this.traffic.default_map, `UVM_REG_ADDR_WIDTH'h0);
    endfunction
endclass

4.5 适配器adapter

adapter依赖总线协议的实现。可以在总线协议和模型数据之间做一个转化。继承自uvm_reg_adapter,主要实现2个方法:reg2bus和bus2reg。

class reg_adapter extends uvm_reg_adapter;
    `uvm_object_utils(reg_adapter)

    function new(string name = "reg_adapter");
        super.new(name);
    endfunction //new()

    virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
        bus_pkt pkt = bus_pkt::type_id::create("pkt");
        pkt.write = rw.kind == UVM_WRITE ? 1 : 0;
        pkt.addr = rw.addr;
        pkt.data = rw.data;

        return pkt;
    endfunction

    virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
        bus_pkt pkt;

        if (!$cast(pkt, bus_item)) begin
            `uvm_fatal("reg_adapter", "failed to case bus_item to pkt");
        end

        rw.kind = pkt.write ? UVM_WRITE : UVM_READ;
        rw.addr = pkt.addr;
        rw.data = pkt.data;
    endfunction
endclass //reg_adapter extends uvm_reg_adapter

4.6 寄存器env

class reg_env extends uvm_env;
    `uvm_component_utils(reg_env)

    function new(string name = "reg_env",  uvm_component parent);
        super.new(name, parent);
    endfunction

    reg_sys_block m_reg_model;
    reg_adapter   m_adapter;
    uvm_reg_predictor#(bus_pkt) m_predictor;
    reg_agent      m_agent;

    virtual function void build_phase (uvm_phase phase);
        super.build_phase(phase);
        m_reg_model = reg_sys_block::type_id::create("m_reg_model", this);
        m_adapter = reg_adapter::type_id::create("m_adapter", this);
        m_predictor = uvm_reg_predictor#(bus_pkt)::type_id::create("m_predictor", this);

        m_reg_model.build();
        m_reg_model.lock_model();

        uvm_config_db #(reg_sys_block)::set(null, "uvm_test_top", "m_reg_model", m_reg_model);
    endfunction

    virtual function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);

        m_predictor.map = m_reg_model.default_map;
        m_predictor.adapter = m_adapter;
    endfunction
endclass

4.7 item 

class bus_pkt extends uvm_sequence_item;
    rand bit[31:0] addr;
    rand bit[31:0] data;
    rand bit       write;

    `uvm_object_utils_begin(bus_pkt)
        `uvm_field_int(addr, UVM_ALL_ON)
        `uvm_field_int(data, UVM_ALL_ON)
        `uvm_field_int(write, UVM_ALL_ON)
    `uvm_object_utils_end

    function new(string name = "bus_pkt");
        super.new(name);
    endfunction

    constraint c_addr {addr inside {0, 4, 8};}
endclass

4.8 driver

class reg_driver extends uvm_driver#(bus_pkt);
    `uvm_component_utils(reg_driver)

    bus_pkt pkt;

    virtual bus_if vif;

    function new(string name = "reg_driver", uvm_component parent);
        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, "*", "bus_if", vif)) begin
            `uvm_error("reg_driver", "do not get bus if handle");
        end
    endfunction

    virtual task run_phase(uvm_phase phase);
        bit[31:0] data;

        vif.psel <= 0;
        vif.penable <= 0;
        vif.pwrite <= 0;
        vif.paddr <= 0;
        vif.pwdata <= 0;

        forever begin
            seq_item_port.get_next_item(pkt);
            if (pkt.write) begin
                write(pkt.addr, pkt.data);
            end
            else begin
                read(pkt.addr, data);
                pkt.data = data;
            end
            seq_item_port.item_done();
        end
    endtask

    virtual task read(input bit[31:0] addr, output logic[31:0] data);
        vif.paddr <= addr;
        vif.pwrite <= 0;
        vif.psel <= 1;

        @(posedge vif.pclk);
        vif.penable <= 1;

        @(posedge vif.pclk);
        data = vif.prdata;
        vif.psel <= 0;
        vif.penable <= 0;
    endtask

    virtual task write(input bit[31:0] addr, input [31:0] data);
        vif.paddr <= addr;
        vif.pwdata <= data;
        vif.pwrite <= 1;
        vif.psel <= 1;

        @(posedge vif.pclk);
        vif.penable <= 1;

        @(posedge vif.pclk);
        vif.psel <= 0;
        vif.penable <= 0;
    endtask
endclass

4.9 monitor

class reg_monitor extends uvm_monitor;
    `uvm_component_utils(reg_monitor)

    function new(string name = "reg_monitor", uvm_component parent);
        super.new(name, parent);
    endfunction

    uvm_analysis_port#(bus_pkt) mon_ap;
    virtual bus_if vif;

    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);

        mon_ap = new("mon_ap", this);
        uvm_config_db#(virtual bus_if)::get(null, "uvm_test_top.*", "bus_if", vif);
    endfunction

    virtual task run_phase(uvm_phase phase);
        fork
            forever begin
                @(posedge vif.pclk);
                if (vif.psel &vif.penable & vif.penable) begin
                    bus_pkt pkt = bus_pkt::type_id::create("pkt");
                    pkt.addr = vif.paddr;
                    if (vif.pwrite) begin
                        pkt.data = vif.pwdata;
                    end
                    else begin
                        pkt.data = vif.prdata;
                    end

                    pkt.write = vif.pwrite;
                    mon_ap.write(pkt);
                end
            end
        join_none
    endtask
endclass

4.10 agent

class reg_agent extends uvm_agent;
    `uvm_component_utils(reg_agent)

    function new(string name = "reg_agent", uvm_component parent);
        super.new(name, parent);
    endfunction

    reg_driver m_drv;
    reg_monitor m_mon;
    uvm_sequencer#(bus_pkt) m_sqr;

    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);

        m_drv = reg_driver::type_id::create("m_drv", this);
        m_mon = reg_monitor::type_id::create("m_mon", this);
        m_sqr = uvm_sequencer#(bus_pkt)::type_id::create("m_sqr", this);
    endfunction

    virtual function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        m_drv.seq_item_port.connect(m_sqr.seq_item_export);
    endfunction
endclass

4.11 env

class reg_test_env extends uvm_env;
    `uvm_component_utils(reg_test_env)

    reg_agent m_agt;
    reg_env   m_reg_env;

    function new(string name= "reg_test_env", uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        m_agt = reg_agent::type_id::create("m_agt", this);
        m_reg_env = reg_env::type_id::create("m_reg_env", this);
    endfunction

    virtual function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        m_reg_env.m_agent = m_agt;
        m_agt.m_mon.mon_ap.connect(m_reg_env.m_predictor.bus_in);
        m_reg_env.m_reg_model.default_map.set_sequencer(m_agt.m_sqr, m_reg_env.m_adapter);
    endfunction
endclass

4.11 reset sequence

用于在跑case前的reset

class reset_sequence extends uvm_sequence;
    `uvm_object_utils(reset_sequence)

    function new(string name = "reset_sequence");
        super.new(name);
    endfunction

    virtual bus_if vif;

    task body();
        if(!uvm_config_db#(virtual bus_if)::get(null, "uvm_test_top.*", "bus_if", vif)) begin
            `uvm_fatal("reset_sequence", "No vif");
        end

        vif.presetn <= 0;
        @(posedge vif.pclk);
        vif.presetn <= 1;
        @(posedge vif.pclk);
    endtask
endclass

4.12 test

class reg_base_test extends uvm_test;
    `uvm_component_utils(reg_base_test)

    reg_test_env m_env;
    reset_sequence m_reset_seq;
    uvm_status_e status;

    function new(string name = "reg_base_test", uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        m_env = reg_test_env::type_id::create("m_env", this);
        m_reset_seq = reset_sequence::type_id::create("m_reset_seq", this);
    endfunction

    virtual task reset_phase(uvm_phase phase);
        super.reset_phase(phase);
        phase.raise_objection(this);
        m_reset_seq.start(m_env.m_agt.m_sqr);
        phase.drop_objection(this);
    endtask
endclass

class reg_rw_test extends reg_base_test;
    `uvm_component_utils(reg_rw_test)

    function new(string name = "reg_rw_test", uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual task main_phase(uvm_phase phase);
        reg_sys_block reg_model;
        uvm_status_e status;
        int rdata;

        phase.raise_objection(this);
        m_env.m_reg_env.set_report_verbosity_level(UVM_HIGH);

        uvm_config_db#(reg_sys_block)::get(null, "uvm_test_top", "m_reg_model", reg_model);

        reg_model.traffic.timer[1].write(status, 32'hcafe_feed);
        reg_model.traffic.timer[1].read(status, rdata);

        reg_model.traffic.timer[1].set(32'hface);

        reg_model.traffic.timer[1].predict(32'hcafe_feed);
        reg_model.traffic.timer[1].mirror(status, UVM_CHECK);

        reg_model.traffic.ctrl.bl_yellow.set(1);
        reg_model.traffic.update(status);

        reg_model.traffic.stat.write(status, 32'h12345678);
        phase.drop_objection(this);
    endtask

    virtual task shutdown_phase(uvm_phase phase);
        super.shutdown_phase(phase);
        phase.raise_objection(this);
        #100ns;
        phase.drop_objection(this);
    endtask
endclass

4.13 仿真结果

完整代码路径:verify/uvm_reghttps://github.com/zljuft/verify/tree/main/uvm_reg

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UVM中,如果不使用寄存器模型,可以通过使用UVM提供的其他功能来扫描寄存器。一种常见的方法是使用UVM提供的register_sequence来实现寄存器的扫描操作。 首先,你需要创建一个继承自uvm_sequence类的自定义sequence类,用于定义寄存器扫描的操作序列。在这个自定义sequence类中,你可以使用UVM提供的register_sequence_item类来定义每个寄存器的扫描操作。 接下来,在你的测试中,你需要创建一个继承自uvm_test类的自定义test类,并在其中实例化你之前定义的自定义sequence类。然后,你可以在test的run_phase中使用uvm_do_with方法来执行寄存器扫描操作。 下面是一个示例代码,演示了如何在UVM中实现寄存器的扫描操作: ```systemverilog class my_register_sequence extends uvm_sequence #(uvm_sequence_item); `uvm_object_utils(my_register_sequence) virtual task body(); // 定义寄存器扫描操作 my_register_sequence_item scan_item; scan_item.operation = SCAN; scan_item.address = 0; // 设置寄存器地址 scan_item.data = 0; // 设置扫描数据 // 执行寄存器扫描操作 uvm_do_with(scan_item, {repeat = 10;}); endtask endclass class my_test extends uvm_test; `uvm_component_utils(my_test) my_register_sequence reg_sequence; function new(string name, uvm_component parent); super.new(name, parent); reg_sequence = my_register_sequence::type_id::create("reg_sequence"); endfunction virtual task run_phase(uvm_phase phase); // 执行寄存器扫描操作 reg_sequence.start(null); reg_sequence.wait_for_sequence_state(UVM_FINISHED); endtask endclass // 在你的测试环境中实例化my_test类,并运行测试 initial begin my_test test = my_test::type_id::create("test"); run_test("test"); end ``` 通过以上代码,你可以在UVM中实现寄存器的扫描操作,而不使用寄存器模型

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值