文章目录

  • 一、reg_model的实现
  • 二、总线bus_agent的实现
  • 三、MCDF的REG硬件实现
  • 四、Adapter & Predictor实现
    • 4.1 Adapter实现
    • 4.2 Adapter集成
  • 五、前门访问&后门访问
    • 5.1 前门访问
    • 5.2 后门访问
  • 5.3对比
  • 六、寄存器各种方法
    • 6.1 mirror、desired、actual value
    • 6.2 prediction分类
      • 6.2.1自动预测:uvm_reg_map::set_auto_predict()
      • 6.2.2 显示预测:explicit(更准确)
    • 6.3 uvm_reg的访问方法
      • 6.3.1 uvm_reg_block\uvm_reg\uvm_reg_feild方法
      • 6.3.2 uvm_reg_sequence方法
      • 6.3.3 reset()/get_reset()
      • 6.3.3 mirror()
      • 6.3.4 set()和update()对寄存器做批量修改
    • 6.4 uvm_mem
    • 6.5 内建(built-in) sequences
    • 6.5 排除寄存器
  • 七、寄存器应用
    • 7.1 覆盖率自动收集
    • 7.2 自定义covergroup进行覆盖率收集
  • 7 创建寄存器.raif文件
    • 7.1 表单
    • 7.2 .ralf文件语法
    • 7.3 生成.sv
    • 7.4 集成到环境中
      • 7.4.1 uvm_reg_sequence
      • 7.4.2 uvm_env 创建实例

寄存器有关的设计流程。
寄存器模型的相关类。
如何将寄存器模型集成到现有环境,与总线UVC桥接,与DUT模型绑定。
寄存器模型的常用方法和预定义的sequence。
寄存器测试和功能覆盖率的实际用例。

一、reg_model的实现

uvm_reg_field 用来针对寄存器功能域来构建对应的比特位
uvm_reg 与寄存器相匹配,其内部可以例化和配置多个uvm_reg_field对象(uvm_object)
uvm_mem 匹配硬件存储模型
uvm_reg_map 用来指定寄存器列表中各个寄存器的偏移地址、访问属性以及对应的总线
uvm_reg_block 可以容纳多个寄存器(uvm_reg)、存储器(uvm_mem)和寄存器列表(uvm_reg_map)

//1. 在定义单个寄存器时,需要将寄存器的各个域整理出来,在创建之后还应当通过uvm_reg_field::configure()函数来进一步配置各自属性。
class ctrl_reg extends uvm_reg;`uvm_object_utils(ctrl_reg)uvm_reg_field reserved;rand uvm_reg_field pkt_len;//为什么要随机化rand uvm_reg_field prio_level ;rand uvm_reg_field chnl_en;function new(string name = "ctrl_reg");super.new(name, 32, UVM_NO_COVERAGE);//endfunctionvirtual function build();////对各个field创建reserved = uvm_reg_field::type_id::create("reserved") ;pkt_len = uvm_reg_field::type_id::create("pkt_len") ;prio_level = uvm_reg_field::type_id::create("prio_level") ;chnl_en = uvm_reg_field::type_id::create ("chnl_en") ;//对各个field进行创建,位数,起点为,读写,reset值reserved.configure(this, 26, 6,"RO", 0, 26'h0, 1, 0, 0);//配置各个reg_field属性pkt_len.configure(this,3,3,"RW", 0, 3'h0, 1, 1, 0);prio_level.configure(this, 2, 1, "RW", 0, 2'h3, 1, 1, 0);chnl_en.configure(this, 1, 0, "RW", 0, 1'h0, 1, 1, 0);endfunction
endclassclass stat_reg extends uvm_reg;`uvm_object_utils(stat_reg)uvm_reg_field reserved;rand uvm_reg_field fifo_avail ;function new(string name = "stat_reg") ;super.new(name, 32, UVM_NO_COVERAGE) ;endfunctionvirtual function build() ;reserved = uvm_reg_field::type_id::create("reserved") ;fifo_avail = uvm_reg_field::type_id::create ("fifo_avail") ;reserved.configure(this, 24, 8, "RO", 0, 24'h0, 1, 0, 0) ;fifo_avail.configure(this, 8, 0, "RO", 0, 8'h0, 1, 1, 0) ;endfunction
endclass
//uvm_reg和uvm_mem分别对应着硬件中独立的寄存器或者存储,一个uvm_reg_block可以用来模拟一个功能模块的寄存器模型;map的作用一方面用来表示寄存器和存储对应的偏移地址,同时由于一个reg_block可以包含多个map,各个map可以分别对应不同总线或者不同地址段。在reg_block中创建了各个uvm_reg之后,需要调用uvm_reg::configure()去配置各个uvm_reg实例的属性。
class mcdf_rgm extends uvm_reg_block ;`uvm_object_utils(mcdf_rgm)rand ctrl_reg chn10_ctrl_reg ;rand ctrl_reg chn11_ctrl_reg;rand ctr1_reg chn12_ctrl_reg;rand stat_reg chn10_stat_reg;rand stat_reg chn11_stat_reg;rand stat_reg chn12_stat_reg;uvm_reg_map map;function new(string name = "mcdf_rgm") ;super.new(name, UVM_NO_COVERAGE) ;endfunctionvirtual function build();chn10_ctr1_reg = ctrl_reg::type_id::create("chn10_ctr1_reg");//例化//创建了各个uvm_reg之后,需要调用uvm_reg::configure()配置各个uvm_reg实例的属性。chn10_ctrl_reg.configure(this);//将uvm_reg与当前的uvm_reg_block关联起来chn10_ctrl_reg.build();//创建每个reg_field,需要自己调用,没有自动创建chn11_ctrl_reg = ctrl_reg::type_id::create("chn11_ctrl_reg");chn11_ctrl_reg.configure(this);chn11_ctrl_reg.build();chn12_ctrl_reg = ctr1_reg::type_id::create ("chn12_ctr1_reg");chn12_ctrl_reg.configure(this);chn12_ctr1_reg.bui1d() ;chn10_stat_reg = stat_reg::type_id::create("chn10_stat_reg");chn10_stat_reg.configure(this) ;chn10_stat_reg.build() ;chn11_stat_reg = stat_reg::type_id::create("chnl1_stat_reg") ;chn11_stat_reg.configure(this);chn11_stat_reg.build();chn12_stat_reg = stat_reg::type_id::create("chn12_stat_reg") ;chn12_stat_reg.configure(this);chn12_stat_reg.build();//map的作用一方面用来表示寄存器和存储对应的偏移地址,同时由于一个reg_block可以包含多个map,各个map可以分别对应不同总线或者不同地址段。// map name, offset, number of bytes, endianessmap = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);//base_addr + offset_addr//uvm_reg_map也会在uvm_reg_block中例化,在例化之后需要通过uvm_reg_map::add_reg()函数来添加各个uvm_reg对应的偏移地址和访问属性等。只有规定了这些属性,才可以在稍后的前门访问(frontdoor) 中给出正确的地址。map.add_reg(chn10_ctrl_reg, 32'h00000000, "RW") ;//map.add_reg(chn11_ctr1_reg, 32'h00000004, "RW") ;map.add_reg(chn12_ctrl_reg, 32'h00000008, "RW") ;map.add_reg(chn10_stat_reg, 32'h00000010, "RO") ;map.add_reg(chn11_stat_reg, 32'h00000014, "RO") ;map.add_reg(chn12_stat_reg, 32'h00000018, "RO") ;lock_model();//模型建立后锁住,不再改变endfunction
endclass: mcdf_rgm

二、总线bus_agent的实现

//1.trans
class mcdf_bus_trans extends uvm_sequence_item;rand bit[1:0] cmd;rand bit[7:0] addr;rand bit[31:0] wdata;bit[31:0] rdata;//从总线读出,不应随机化`uvm_object_utils_begin(mcdf_bus_trans)...`uvm_object_utils_end...
endclass
//2.sequencer
class mcdf_bus_sequencer extends uvm_sequencer;virtual mcdf_if vif;`uvm_component_utils(mcdf_bus_sequencer)function void build_phase(uvm_phase phase);if(!uvm_config_db#(virtual mcdf_if)::get(this, "", "vif", vif)) begin`uvm_error("GETVIF","no virtual interface is assigned")endendfunction
endclass
//3.monitor:ap除了连接到scoreboard上,还会连接到uvm_reg_predictor上
class mcdf_bus_monitor extends uvm_monitor;virtual mcdf_if vif;uvm_analysis_port #(mcdf_bus_trans) ap;`uvm_component_utils(mcdf_bus_monitor)function void build_phase(uvm_phase phase);if(!uvm_config_db#(virtual mcdf_if)::get(this,"","vif", vif)) beginuvm_error("GETVIF", "no virtual interface is assigned")endap = new("ap", this);//new,不是objectendfunctiontask run_phase(uvm_phase phase);forever beginmon_trans();endendtasktask mon_trans();mcdf_bus_trans t;@(posedge vif.clk);if(vif.cmd == `WRITE) begin//写,将接口的cmd,addr,data都写入,并发送出去t = new();t.cmd = `WRITE;t.addr = vif.addr;t.wdata = vif.wdata;ap.write(t);endelse if(vif.cmd == `READ) begin//读,先将接口的addr放入trans,等下个周期的数据写入,并发送出去t = new();t.cmd = `READ;t.addr = vif.addr;forkbegin@(posedge vif.cl k);#10ps;t.rdata = vif.rdata;ap.write(t);endjoin_none//join_none避免错过下一拍的addrendendtask
endclass: mcdf_bus_monitor
//4.driver:实现总线的驱动和复位
class mcdf_bus_driver extends uvm_driver;virtual mcdf_if vif;`uvm_component_utils(mcdf_bus_driver)function void build_phase(uvm_phase phase);if(!uvm_config_db#(virtual mcdf_if)::get(this, "", "vif", vif)) begin`uvm_error("GETVIF", "no virtual interface is assigned")endendfunctiontask run_phase(uvm_phase phase);REQ tmp; mcdf_bus_trans req, rsp;reset_listener();//?听复位forever beginseq_item_port.get_next_item(tmp);void'($cast(req,tmp));`uvm_info("DRV", $sformatf("got a item \n 8s", req.sprint()), UVM_LOW)void'($cast(rsp,req.clone()));rsp.set_sequence_id(req.get_sequence_id());//sequence_idrsp.set_transaction_id(req.get_transaction_id());//transaction_iddrive_bus(rsp);//驱动seq_item_port.item_done(rsp);`uvm_info("DRV", $sformatf("sent a item \n 8s", rsp.sprint()),UVM_LOW)endendtasktask reset_listener() ;forkforever begin@(negedge vif.rstn) drive_idle() ;endjoin_noneendtasktask drive_bus(mcdf_bus_trans t) ;case(t.cmd)`WRITE: drive_write(t) ;`READ: drive_read(t) ;`IDLE: drive_idle(1) ;default:`uvm_error("DRIVE", " invalid mcdf command type received!")endcaseendtasktask drive_write(mcdf_bus_trans t) ;@(posedge vif.clk) ;//等一拍vif.cmd <= t.cmd;vif.addr <= t.addr ;vif.wdata <= t.wdata ;endtasktask drive_read(mcdf_bus_trans t);@(posedge vif.clk);//第一拍把命令和地址写到接口上vif.cmd <= t.cmd;vif.addr <= t.addr;@(posedge vif.clk);//第二拍把数据读回来#10ps;t.rdata = vif.rdata;endtasktask drive_idle(bit is_sync = 0);if(is_sync) @(posedge vif.clk);vif.cmd <='h0;vif.addr <= 'h0;vif.wdata <='h0;endtask
endclass
//5.agent
class mcdf_bus_agent extends uvm_agent;mcdf_bus_driver driver;mcdf_bus_sequencer sequencer;mcdf_bus_monitor monitor;`uvm_component_utils(mcdf_bus_agent)function void build_phase(uvm_phase phase);driver = mcdf_bus_driver::type_id::create("driver", this);sequencer = mcdf_bus_sequencer::type_id::create("sequencer", this);monitor = mcdf_bus_monitor::type_id::create("monitor", this);endfunctionfunction void connect_phase(uvm_phase phase);driver.seq_item_port.connect(sequencer.seq_item_export);endfunction
endclass

三、MCDF的REG硬件实现

`define IDLE         2'b00//cmd
`define WRITE       2'b01
`define READ        2'b10
`define SLV0_RW_ADDR 8'h00//addr
`define SLV1_RW_ADDR 8'h04
`define SLV2_RW_ADDR 8'h08
`define SLV0_R_ADDR  8'h10
`define SLV1_R_ADDR  8'h14
`define SLV2_R_ADDR  8'h18
`define SLV0_RW_REG  0//位置
`define SLV1_RW_REG  1
`define SLV2_RW_REG  2
`define SLV0_R_REG   3
`define SLV1_R_REG   4
`define SLV2_R_REG   5module ctr1_regs(clk_i,rstn i,cmd_i,cmd_addr_i,cmd_data_i,cmd_data_o,s1v0_len_o,s1v1_len_o,s1v2_len_o,s1v0_prio_o,s1v1_prio_o,s1v2_prio_o,slv0_margin_i,s1v1_margin_i,s1v2_margin_i,s1v0_en_o,s1v1_en_o,s1v2_en_o);input clk_i,rstn_i;//系统信号input[1:0] cmd_i;//访问寄存器cmdinput[7:0] cmd_addr_i;//访问寄存器addrinput[31:0] cmd_data_i;//访问寄存器datainput[7:0] slv0_margin_i;//三个chnl表示fifo余量input[7:0] slv1_margin i;input[7:0] s1v2_margin_i;output[31:0] cmd_data_o;//访问寄存器data_out//三个输出output[2:0] s1v0_len_o;//输出给formatteroutput[2:0]   s1v1_len_o;output[2:0] slv2_len_o;output[1:0] s1v0_prio_o;//输出给arbiteroutput[1:0] s1v1_prio_o;output[1:0] s1v2_prio_o;output s1v0_en_o;//输出给chnloutput s1v1_en_o;output s1v2_en_o;reg[31:0] regs[5:0];//低六位reg[31:0] cmd_data_reg;always@(posedge clk_i or negedge rstn_i) beginif(!rstn_i) begin//异步复位regs[`SLV0_RW_REG] <= 32'h00000007;regs[`SLV1_RW_REG] <= 32'h00000007;regs[`SLV2_RW_REG] <= 32'h00000007;regs[`SLV0_R_REG]  <= 32'h00000010;regs[`SLV1_R_REG]  <= 32'h00000010;regs[`SLV2_R_REG]  <= 32'h00000010;endelse beginif(cmd_i == `WRITE) begincase(cmd_addr_i)//对读写寄存器的低6位操作`SLV0_RW_ADDR: regs[`SLV0_RW_REG][5:0]<= cmd_data_i;`SLV1_RW_ADDR: regs[`SLV1_RW_REG][5:0]<= cmd_data_i;`SLV2_RW_ADDR: regs[`SLV2_RW_REG][5:0]<= cmd_data_i;endcaseendelse if(cmd_i == `READ) begincase(cmd_addr_i)//读出6个寄存器的数据`SLV0_RW_ADDR: cmd_data_reg <= regs[`SLV0_RW_REG];`SLV1_RW_ADDR: cmd_data_reg <= regs[`SLV1_RW_REG];`SLV2_RW_ADDR: cmd_data_reg <= regs[`SLV2_RW_REG];`SLV0_R_ADDR:  cmd_data_reg <= regs[`SLV0_R_REG];`SLV1_R_ADDR:  cmd_data_reg <= regs[`SLV1_R_REG];`SLV2_R_ADDR:  cmd_data_reg <= regs[`SLV2_R_REG];endcaseendregs[`SLV0_R_REG][7:0] <= slv0_margin_i;//每一拍都将寄存器余量反应到只读寄存器里,有1拍延迟regs[`SLV1_R_REG][7:0] <= s1v1_margin_i;regs[`SLV2_R_REG][7:0] <= s1v2_margin_i;end
end
assign cmd_data_o = cmd_data_reg;//将读出的数据驱动到端口上
assign slv0_len_o = regs[`SLV0_RW_REG][5:3];//读写寄存器驱动到端口上
assign s1v1_len_o = regs[`SLV1_RW_REG][5:3];
assign s1v2_len_o = regs[`SLV2_RW_REG][5:3];
assign s1v0_prio_o = regs[`SLV0_RW_REG][2:1];
assign s1v1_prio_o = regs[`SLV1_RW_REG][2:1];
assign s1v2_prio_o = regs[`SLV2_RW_REG][2:1];
assign s1v0_en_o = regs[`SLV0_RW_REG][0];
assign s1v1_en_o = regs[`SLV1_RW_REG][0];
assign s1v2_en_o = regs[`SLV2_RW_REG][0];
endmodule: ctr1_regs

四、Adapter & Predictor实现

4.1 Adapter实现

adapter所完成的桥接功能即是在uvm_reg_bus_op和总线transaction之间的转换。
●实现reg2bus()和bus2reg()两个函数,这两个函数即实现了两种transaction的数据映射。
●如果总线支持byte访问,可以使能supports_byte_enable; 如果总线UVC要返回response数据,则应当使能provides_responses。在本例中,mcdf_bus_driver在读数时会将读回的数据填入到RSP并返回至sequencer,因此需要在adapter中使能provides_responses。 由此使得bus2reg()函数调用时得到的数据是总线返回时的transaction,但如果总线UVC不支持返回RSP(没有调用put_response(RSP)或者item_done(RSP)) ,那么不应该置此位,否则adapter将会使得验证环境挂起。默认情况下,上述的两个成员的复位值都是0。
●uvm_reg_bus_op:

class reg2mcdf_adapter extends uvm_reg_adapter ;`uvm_object_utils(reg2mcdf_adapter)//objectfunction new(string name = "mcdf_bus_trans");super.new(name);provides_responses = 1; //有response;driver到sequencer有一个response,继而给到adapterendfunctionfunction uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);mcdf_bus_trans t = mcdf_bus_trans::type_id::create("t");t.cmd =(rw.kind == UVM_WRITE)?`WRITE :`READ;t.addr = rw.addr;t.wdata = rw.data;return t;//右边到左边的转换,子类句柄隐式转换为父类endfunctionfunction void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);mcdf_bus_trans t;if(!$cast(t, bus_item))begin`uvm_fatal("NOT_MCDF_BUS_TYPE", "Provided bus_item is not of the correct type")return;endrw.kind =(t.cmd ==`WRITE)? UVM_WRITE : UVM_READ ;rw.addr = t.addr;rw.data =(t.cmd ==`WRITE)? t.wdata : t.rdata;rw.status = UVM_IS_OK;//状态位,成功endfunction
endclass

4.2 Adapter集成

class mcdf_bus_env extends uvm_env ;mcdf_bus_agent agent;//bus_agentmcdf_rgm rgm; //register_modelreg2mcdf_adapter reg2mcdf ;//adapter`uvm_component_utils(mcdf_bus_env)function void build_phase(uvm_phase phase);agent = mcdf_bus_agent::type_id::create("agent",this);if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm" ,rgm))begin//get了一个reference modeluvm_info("GETRGM","no top-down RGM handle is assigned" ,UVM_LOW)rgm = mcdf_rgm::type_id::create("rgm",this);//没有的话,创建一个uvm_info("NEWRGM"," created rgm instance locally" ,UVM_LOW)endrgm.build();//uvm_reg,configure,buildrgm.map.set_auto_predict();//reg2mcdf = reg2mcdf_adapter::type_id::create("reg2mcdf");//adapter在env中endfunctionfunction void connect_phase(uvm_phase phase);rgm.map.set_sequencer(agent.sequencer, reg2mcdf);//与reg_model中的map做关联,访问到所有的寄存器,map与adapter关联,adapter与sequencer关联,三者关联 endfunction
endclass class test1 extends uvm_test ;mcdf_rgm rgm;mcdf_bus_env env ;`uvm_component_utils(test1)function void build_phase(uvm_phase phase);rgm = mcdf_rgm::type_id::create("rgm",this);uvm_config_db#(mcdf_rgm)::set(this, "env*" ,"rgm",rgm);env = mcdf_bus_env::type_id::create("env",this);endfunctiontask run_phase(uvm_phase phase);...endtask
endclass

五、前门访问&后门访问

5.1 前门访问

在寄存器模型上做读写操作,最终会通过总线来实现总线上的物理时序访问
1. uvm_reg::read()/write()。 在传递时,用户需要注意将参数path指定为UVM_FRONTDOOR。
2. uvm_reg::read()/write()方法可传入的参数较多,除了status和value两个参数需要传入,其它参数如果不指定,可采用默认值。
3. uvm_reg_sequence::read_reg()/write_reg()。 在使用时,也需要将path指定为UVM_ FRONTDOOR。
class mcdf_example_seq extends uvm_reg_sequence;mcdf_rgm rgm;`uvm_object_utils(mcdf_example_seq)`uvm_declare_p_sequencer(mcdf_bus_sequencer)task body();uvm_status_e status;uvm_reg_data_t data;if(!uvm_config_db#(mcdf_rgm)::get(null, get_full_name() ,"rgm" ,rgm)begin//1.拿到register_model的句柄`uvm_error("GETRGM","no top-down RGM handle is assigned")end// register model access write() /read()rgm.chn10_ctrl_reg.read(status, data, UVM FRONTDOOR, .parent(this));rgm.chn10_ctrl_reg.write(status,'h11, UVM FRONTDOOR, .parent(this));rgm.chn10_ctrl_reg.read(status, data, UVM_FRONTDOOR,.parent(this));// pre-defined me thods accessread_reg(rgm.chn11_ctr1_reg,status, data, UVM_FRONTDOOR);write_reg(rgm.chn11_ctrl_reg, status, 'h22, UVM_FRONTDOOR);read_reg(rgm.chn11_ctrl_reg, status, data, UVM_FRONTDOOR);endtask
endclass

5.2 后门访问

利用UVM_DPI(uvm_hdl_read()、uvm_hdl_deposit()、将寄存器的操作直接作用到DUT内的寄存器变量,而不通过物理总线访问。

  1. uvm_reg::read()/write()。
  2. uvm_reg::peek()/poke()。
class mcdf_rgm extends uvm_reg_block;...//寄存器成员和map声明virtual function build();...//寄存器成员和map创建//关联寄存器模型和HDLadd_hdl_path("reg_backdoor_access.dut");chn10_ctrl_reg.add_hdl_path_slice($sformatf("regs [%0d]", `SLV0_RW_REG), 0, 32);//完成地址映射chn11_ctrl_reg.add_hdl_path_slice($sformatf("regs [%0d]", `SLV1_RW_REG), 0, 32);chn12_ctr1_reg.add_hdl_path_slice($sformatf("regs [%0d]", `SLV2_RW_REG), 0, 32);chn10_stat_reg.add_hdl_path_slice($sformatf("regs [%0d]", `SLV0_R_REG ), 0, 32);chn11_stat_reg.add_hdl_path_slice($sformatf("regs [%0d]", `SLV1_R_REG ), 0, 32);chn12_stat_reg.add_hdl_path_slice($sformatf("regs [%0d]", `SLV2_R_REG ), 0, 32);lock_model();//防止外部修改模型endfunction
endclass
class mcdf_example_seq extends uvm_reg_sequence;mcdf_rgm rgm;`uvm_object_utils(mcdf_examp1e_seq)`uvm_declare_p_sequencer(mcdf_bus_sequencer)task body();uvm_status_e status;uvm_reg_data_t data ;if(!uvm_config_db#(mcdf_rgm)::get(nu11, get_full_name() ,"rgm", rgm)) begin`uvm_error("GETRGM", "no top-down RGM handle is assigned")end// register model access write() /read()rgm.chn10_ctrl_reg.read(status, data, UVM_BACKDOOR,.parent(this));rgm.chnl0_ctrl_reg.write(status, 'h11, UVM_BACKDOOR,.parent(this));rgm.chn10_ctrl_reg.read(status, data, UVM_BACKDOOR,.parent(this));// register model access poke() /peed()rgm.chn11_ctrl_reg.peek(status, data, .parent(this));rgm.chn11_ctrl_reg.poke(status, 'h22, .parent(this));rgm.chnl1_ctrl_reg.peek(status, data, .parent(this));// pre-defined methods read_reg() /write_reg() .read_reg(rgm.chn12_ctrl_reg, status, data, UVM_BACKDOOR);write_reg(rgm.chn12_ctrl_reg, status, 'h22, UVM_BACKDOOR);read_reg(rgm.chn12_ctrl_reg, status, data, UVM_BACKDOOR);// pre-defined methods peek_reg()/poke_reg()peek_reg(rgm.chn12_ctrl_reg,status, data);poke_reg(rgm.chn12_ctrl_reg, status, 'h33);peek_reg(rgm.chn12_ctrl_reg, status, data);endtask
endclass

5.3对比

六、寄存器各种方法

6.1 mirror、desired、actual value

mirror: 前门观察总线或者后门通过自动预测给出;表示当前硬件的已知状态值
desired:利用寄存器模型修改软件对象值,利用该值更新硬件值
actual: 镜像值可能和actual不一致,状态寄存器的两个值无法保持同步更新;若其他访问寄存器的通路修改了寄存器,但可能没有检测那一路的总线,也会存在无法及时更新

6.2 prediction分类

6.2.1自动预测:uvm_reg_map::set_auto_predict()

  1. 如果用户没有在环境中集成独立的predictor,而是利用寄存器的操作来自动记录每一次寄存器的读写数值,并在后台自动调用predict()方法的话,这种方式被称之为自动预测。
  2. 如果出现了其它一些sequence直接在总线层面上对寄存器进行操作(跳过寄存器级别的write()/read()操作),或者通过其它总线来访问寄存器等这些额外的情况,都无法自动得到寄存器的镜像值和预期值。

6.2.2 显示预测:explicit(更准确)

  1. 在物理总线上通过监视器来捕捉总线事务,并将捕捉到的事务传递给外部例化的predictor,该predictor由UVM参数化类uvm_reg_predictor(component)例化并集成在顶层环境中。
  2. 在集成的过程中需要将adapter与map的句柄也并传递给predictor,同时将monitor采集的事务通过analysis_port接入到predictor一侧。
  3. 这种集成关系可以使得,monitor一旦捕捉到有效事务,会发送给predictor,再由其利用adapter的桥接方法,实现事务信息转换,并将转化后的寄存器模型有关信息更新到map中。
  4. 默认情况下,系统将采用显式预测的方式,这就要求集成到环境中的总线monitor需要具备捕捉事务的功能和对应的analysis_port,以便于同predictor连接。
class mcdf_bus_env extends uvm_env;mcdf_bus_agent agent;mcdf_rgm rgm;reg2mcdf_adapter reg2mcdf;uvm_reg_predictor #(mcdf_bus_trans) mcdf2reg_predictor;//匹配类型`uvm_component_utils(mcdf_bus_env)function void build_phase(uvm_phase phase);agent =   mcdf_bus_agent::type_id::create("agent", this);if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin//获取register model句柄uvm_info("GETRGM","no top-down RGM handle is assigned", UVM_LOW)rgm = mcdf_rgm::type_id::create("rgm", this);uvm_info("NEWRGM","created rgm instance locally", UVM_LOW)endrgm.build();reg2mcdf = reg2mcdf_adapter::type_id::create("reg2mcdf");mcdf2reg_predictor = uvm_reg_predictor#(mcdf_bus_trans)::type_id::create( "mcdf2reg_predcitor" ,this);//参数类型一致mcdf2reg_predictor.map = rgm.map;//map和adapter的指针传入mcdf2reg_predictor.adapter = reg2mcdf;endfunctionfunction void connect_phase(uvm_phase phase);rgm.map.set_sequencer(agent.sequencer, reg2mcdf);agent.monitor.ap.connect(mcdf2reg_predictor.bus_in);//默认的analysis portendfunction
endclass

6.3 uvm_reg的访问方法

6.3.1 uvm_reg_block\uvm_reg\uvm_reg_feild方法

6.3.2 uvm_reg_sequence方法

  1. 对于前门访问的read()和write(),在总线事务完成时,镜像值和期望值才会更新为与总线上相同的值,这种预测方式是显式预测。
  2. 对于后门访问模式下的peek()和poke()以及read()和write(),由于不通过总线,默认采取自动预测的方式,因此在零时刻方法调用返回后,镜像值和期望值也相应修改
    针对寄存器对象的方法

6.3.3 reset()/get_reset()

  1. 硬件在复位触发时,会将内部寄存器值复位,而寄存器模型在捕捉到复位事件时,为了保持同硬件行为一致,也应当
    对其复位。这里复位的对象是寄存器模型,而不是硬件。
 @(negedge p_sequencer.vif.rstn) ;rgm.reset() ; // register block reset for mirrored value and desired valuergm.chn10_ctrl_reg.reset() ; // register level resetrgm.chn10_ctr1_reg.pkt_len.reset() ; // register field reset
  1. 在复位之后,可以通过读取寄存器模型的复位值(与寄存器描述文件一致),与前门访问获取的寄存器复位值进行比较,以此来判断硬件各个寄存器的复位值是否按照寄存器描述去实现。这里的get_reset()方法指的也是寄存器模型的复位值,而不是硬件
// register model reset value get and check
rstval = rgm.chn10_ctrl_reg.get_reset();
rgm.chn10_ctrl_reg.read(status, data, UVM_ BACKDOOR, .parent (this));
if (rstval != data)`uvm_error ( "RSTERR","reset value read is not the desired reset value")

6.3.3 mirror()

  1. mirror()不会返回读回的数值,但是会将对应的镜像值修改。
  2. 在修改镜像值之前,用户还可以选择是否将读回的值与模型中的原镜像值进行比较。
  3. 对于配置寄存器,可以采用检查上一次的配置是否生效,又或者对于状态寄存器,可以选择只更新镜像值不做比较,这是因为状态寄存器随时可能被硬件内部逻辑修改。
    // get register value and check
    rgm.chn10_ctrl_reg.mirror(status, UVM_CHECK, UVM_FRONTDOOR, .parent(this)) ;//check 在更新镜像值之前,首先将读回的值与上一次镜像值做了比对,随后再更新镜像值

6.3.4 set()和update()对寄存器做批量修改

1.set()方法的对象是寄存器模型自身,通过set()可以修改期望值,而在寄存器配置时先对其模型随机化,再配置个别寄存器或者域,当寄存器的期望值与镜像值不相同时,可以通过update()方法来将不相同的寄存器通过前门门访问或者后门访问的方式做全部修改
2. 这种set()和update()的方式较write()和poke()的写寄存器方式更为灵活的是,它可以实现随机化寄存器配置值(先随机化寄存器模型,后将随机值结合某些域的指定值写入到寄存器), 继而模拟更多不可预知的寄存器应用场景,另外update()强大的批量操作寄存器功能使得修改寄存器更为便捷。

// randomize register mode1, set register/ field value and update to
// hardware actual value
void'(rgm.chnl0_ctrl_reg.randomize());
rgm.chn10_ctr1_reg.pkt_len.set('h3);
rgm.chn10_ctrl_reg.update(status, UVM FRONTDOOR,.parent(this));//对单个reg进行检查和更新
void'(rgm.chn11_ctrl_reg.randomize());
rgm.chn10_ctrl_reg.set('h22);//6个寄存器的desired value进行检查和更新
rgm.update(status, UVM_FRONTDOOR, .parent(this));

6.4 uvm_mem

● UVM寄存器模型也可以用来对存储建模。uvm_mem类可以用来模拟RW (读写)、RO(只读)和WO(只写)类型的存储,并且可以配置存储模型的数据宽度和地址范围。
● uvm_mem没有镜像值和期望值。
● uvm_mem可以提供的功能就是利用自带的方法去访问硬件存储。
● 与uvm_reg相比,uvm_mem不但拥有常规的访问方法read()、write()、peek()和poke(),也提供了burst_read()和burst_write()。busrt操作不但可以更高速通过总线BURST方式连续存储,也是为了贴合实际访问存储中的场景。
● 要实现BURST访问形式,需要考虑下面这些因素:
a. 目前挂载的总线UVC是否支持BURST形式访问,例如APB不能支持BURST访问模式。
b. 与read()、write()方法相比,burst_read()和burst_write()的参数列表中的一项uvm_reg_data_t value[]采用的是数组形式,不再是单一变量,即表示用户可以传递多个数据。而在后台,这些数据首先需要装载到uvm_reg_item对象中,装载时value数组可以直接写入,另外两个成员需要分别指定为element_kind = UVM_MEM,kind = UVM_BURST_READ
c. 在adapter实现中,也需要考虑到存储模型BURST访问的情形,实现四种访问类型的转换,即UVM_READ、UVM_WRITE、UVM_BURST_READ和UVM_BURST_WRITE。
d. 对于UVM_READ和UVM_WRITE的桥接,已经在寄存器模型访问中实现,而UVM_BURST_READ和UVM_BURST_WRITE的转换,往往需要考虑写入的数据长度,例如长度是否是4、8、16或者其它。
e. 对于更为复杂的BURST形式,例如AHB支持WRAP模式,AXI支持out-of-order模式等,如果需要实现更多的协议配置要求,那么推荐直接在总线UVC层面去驱动。这样做的灵活性更大,且更能充分全面的测试存储接口的协议层完备性。

6.5 内建(built-in) sequences


class mcdf_example_seq extends uvm_reg_sequence;mcdf_rgm rgm;`uvm_object_utils(mcdf_example_seq)`uvm_declare_p_sequencer(mcdf_bus_sequencer)task body();uvm_status_e status;uvm_reg_data_t data;uvm_reg_hw_reset_seq reg_rst_seq = new();//1.检查寄存器模型复位值与硬件复位值一致性uvm_reg_bit_bash_seq reg_bit_bash_seq = new();//2.检查所有支持读写访问的域uvm_reg_access_seq reg_acc_seq = new();//3.对所有uvm_reg执行前门写后门读和后门写前门读,寄存器地址映射关系if(!uvm_config_db#(mcdf_rgm)::get(null, get_full_name() ,"rgm", rgm)) begin//get register model句柄`uvm_error("GETRGM", "no top-down RGM handle is assigned" )end// wait reset asserted and release@(negedge p_sequencer.vif.rstn);@(posedge P_sequencer.vif.rstn);`uvm_info("BLTINSEQ", "register reset sequence started", UVM_LOW)reg_rst_seq.model = rgm;//传入reg_block句柄reg_rst_seq.start(m_sequencer);//开始执行,sequencer为bus_sequencer`uvm_info("BLTINSEQ", "register reset sequence finished", UVM_LOW)`uvm_info("BLTINSEQ","register bit bash sequence started" ,UVM_LOW)// reset hardware register and register modelreg_bit_bash_seq.model = rgm;reg_bit_bash_seq.start(m_sequencer);`uvm_info("BLTINSEQ", "register bit bash sequence finished", UVM_LOW)`uvm_info("BLTINSEQ", "register access sequence started", UVM_LoW)// reset hardware register and register modelreg_acc_seq.model = rgm;reg_acc_seq.start(m_sequencer);`uvm_info("BLTINSEQ", "register access sequence finished", UVM_LOW)endtask
endclass

6.5 排除寄存器

在做测试时,时钟等寄存器不能直接写01,需要排除出去

class mcdf_rgm extends uvm_reg_block;virtual function build();// disable built-in seq attributesuvm_resource_db# (bit)::set({"REG::",this.chn10_stat_reg.get_full_name() } ,"NO REG ACCESS_TEST" ,1);//排除状态寄存器uvm_resource_db# (bit)::set({"REG::",this.chn11_stat_reg.get_fu11_name() } ,"NO_REG_ACCESS_TEST" ,1);uvm_resource_db# (bit)::set({"REG::",this.chn12_stat_reg.get_full_name() } ,"NO_REG ACCESS_TEST" ,1);endfunction
endclass

七、寄存器应用

覆盖率收集
get_coverage(UVM_CVR_FIELD_VALS)//是否允许覆盖率采样
has_coverage(UVM_CVR_FIELD_VALS)//has_coverage有条件例化covergroup

7.1 覆盖率自动收集

class ctrl_reg extends uvm_reg;`uvm_object_utils(ctrl_reg)uvm_reg_field reserved;rand uvm_reg_field pkt_len; rand uvm_reg_field prio_leve1;rand uvm_reg_field chnl_en; covergroup value_cg;option.per_instance = 1;//4个field的coverpoint关心的value//reserved: coverpoint reserved.value[25:0];pkt_len: coverpoint pkt_len.value[2:0];prio_level: coverpoint prio_level.value[1:0];chn1_en: coverpoint chnl_en.value[0:0];endgroupfunction new(string name = "ctrl_reg");super.new(name,32, UVM_CVR_ALL);set_coverage(UVM_CVR_FIELD_VALS);if(has_coverage(UVM_CVR_FIELD_VALS)) begin//has_coverage有条件例化covergroupvalue_cg = new();endendfunctionvirtual function build();reserved = uvm_reg_field::type_id::create("reserved");pkt_len = uvm_reg_field::type_id::create("pkt_len");prio_level = uvm_reg_field::type_id::create("prio_level");chnl_en = uvm_reg_field::type_id::create("chnl_en");reserved.configure(this, 26,6,"RO", 0,26'h0,1, 0,0);pkt_len.configure(this, 3,3,"RW", 0,3'h0,1,1, 0);prio_level.configure(this, 2, 1,"RW", 0, 2'h3,1, 1, 0);chnl_en.configure(this, 1, 0,"RW", 0,1'h1,1, 1, 0);endfunction//采样部分,实现的时候形式要保持一致//read()\write()方法的回调函数,需要自己填充使之自动采样数据//read()\write()->sample->sample_value->coveragefunction void sample(uvm_reg_data_t data,uvm_reg_data_t byte_en,bit    is_read,uvm_reg_map map);super.sample(data, byte_en, is_read, map);sample_values();endfunction//供外部调用的方法function void sample_values();//采样的值super.sample_values();if(get_coverage(UVM_CVR_FIELD_VALS)) begin//是否允许覆盖率采样value_cg.sample();endendfunction
endclass

7.2 自定义covergroup进行覆盖率收集

class mcdf_coverage extends uvm_subscriber #(mcdf bus trans);//只监听monitormcdf_rgm rgm;`ucm_component_utils(mcdf_coverage)covergroup reg_value_cg;option.per_instance = 1;CH0LEN: coverpoint rgm.chnl0_ctrl_reg.pkt_len.value[2:0] {bins len[] =   {0,1,2,3,[4:7]};}//mirror valueCH0PRI: coverpoint rgm.chn10_ctrl_reg.prio_level.value[1:0];CH0CEN: coverpoint rgm.chnl0_ctrl_reg.chnl_en.value[0:0];CH1LEN: coverpoint rgm.chnll_ctrl_reg.pkt len.value[2:0] {bins len[] = {0,1,2,3,[4:7]};}CH1PRI: coverpoint rgm.chnl1_ctrl_reg.prio_level.value[1:0];CH1CEN: coverpoint rgm.chnl1_ctrl_reg.chnl_en.value[0:0];CH2LEN: coverpoint rgm.chn12__ctrl_reg.pkt_len.value[2:0] {bins len[]=   {0,1,2,3,[4:7]};}CH2PRI: coverpoint rgm.chn12_ctrl_reg.prio_level.value[1:0];CH2CEN: coverpoint rgm.chn12_ctrl_reg.chnl_en.value[0:0];CH0AVL: coverpoint rgm.chn10_stat_reg.fifo_avail.value[7:0] {bins avail[] = {0,1,[2:7], [8:55], [56:61], 62, 63};}//7个binCH1AVL: coverpoint rgm.chn11_stat_reg.fifo_avail.value[7:0] {bins avail[] = {0,1,[2:7], [8:55], [56:61], 62, 63};}CH2AVL: coverpoint rgm.chn12_stat_reg.fifo_avail.value[7:0] {bins avail[] = {0,1,[2:7], [8:55], [56:61], 62, 63};}LEN_COMB: cross CH0LEN, CH1LEN, CH2LEN;PRI_COMB: cross CH0PRI, CH1PRI, CH2PRI;CEN COMB: cross CH0CEN, CH1CEN, CH2CEN;AVL_COMB: cross CH0AVL, CH1AVL, CH2AVL;endgroupfunction new(string name, uvm_component parent);super.new(name,parent);reg_value_cg = new();endfunctionfunction void build_phase(uvm_phase phase);if(!uvm_config_db#(mcdf_rgm)::get(this, "","rgm", rgm)) begin`uvm_info("GETRGM", "no top-down RGM handle is assigned", UVM_LOW)endendfunctionfunction void write(T t);//有analysis port,要实现writereg_value_cg.sample();//bus_monitor写入后,进行采样endfunction
endclass

7 创建寄存器.raif文件

表单->.ralf->.sv->集成到环境中,调用
为tcl格式

7.1 表单

7.2 .ralf文件语法


7.3 生成.sv

用ralgen将RAL文件内容转换为UVM注册类:
ralgen -uvm -t host_regmodel host.ralf
生成ral_host_regmodel.sv

7.4 集成到环境中

7.4.1 uvm_reg_sequence

class host_ral_sequence extends uvm_reg_sequence #(uvm_sequence #(host_data));//1.父类为uvm_reg_sequenceral_block_host_regmodel regmodel;//2.声明`uvm_object_utils(host_ral_sequence)function new(string name = "host_ral_sequence");super.new(name);`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);endfunctionvirtual task body();uvm_status_e status;uvm_reg_data_t data;`uvm_info("TRACE",$sformatf("%m"),UVM_HIGH)if (starting_phase != null)starting_phase.raise_objection(this);regmodel.PORT_LOCK.read(.status(status), .value(data), .path(UVM_FRONTDOOR), .parent(this));//3.调用,可以不用输入地址了`uvm_info("RAL READ", $sformatf("PORT_LOCK= %2h",data),UVM_MEDIUM)regmodel.PORT_LOCK.write(.status(status), .value('1), .path(UVM_FRONTDOOR), .parent(this));`uvm_info("RAL WRITE", $sformatf("PORT_LOCK= %2h",'1),UVM_MEDIUM)regmodel.PORT_LOCK.read(.status(status), .value(data), .path(UVM_FRONTDOOR), .parent(this));`uvm_info("RAL READ", $sformatf("PORT_LOCK= %2h",data),UVM_MEDIUM)if (starting_phase != null)starting_phase.drop_objection(this);endtask
endclass

7.4.2 uvm_env 创建实例

//build
if (regmodel == null)begin//createstring hdl_path;//for backdoorif(!uvm_config_db#(string)::get(this,"","hdl_path", hdl_path)) begin`uvm_warning("HOSTCFG", "HDL path for backdoor is not set");end regmodel = ral_block_host_regmodel::type_id::create("regmodel", this);//instanceregmodel.build();//instance reg_block;regmodel.lock_model();//Lock and create mapregmodel.set_hdl_path_root(hdl_path);//configregmodel.reset();//reset mirror and desired value
end
//connect
reg_adapter adapter;
adapter = reg_adapter::type_id::create("adapter", this);
regmodel.default_map.set_sequencer(h_agent.seqr, adapter);

UVM学习笔记(五)寄存器相关推荐

  1. 哈工大操作系统学习笔记五——内核级线程实现

    哈工大os学习笔记五(内核级线程实现) 文章目录 哈工大os学习笔记五(内核级线程实现) 一. 中断入口.中断出口(前后两段) 1. 从int中断进入内核(中断入口第一段) 2.中断出口(最后一段) ...

  2. UVM学习笔记—前门访问/后门访问

    目录 0.前言 1.前门访问 2.后门访问 2.1路径设置 2.2操作类型 0.前言 在UVM学习笔记-寄存器模型的搭建及使用中提到过前门访问和后门访问的概念,这篇文章将会详细的阐述一下这2种访问方式 ...

  3. python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用

    本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...

  4. Ethernet/IP 学习笔记五

    Ethernet/IP 学习笔记五 Accessing data within a device using a non-time critical message (an explicit mess ...

  5. StackExchange.Redis学习笔记(五) 发布和订阅

    StackExchange.Redis学习笔记(五) 发布和订阅 原文:StackExchange.Redis学习笔记(五) 发布和订阅 Redis命令中的Pub/Sub Redis在 2.0之后的版 ...

  6. 吴恩达《机器学习》学习笔记五——逻辑回归

    吴恩达<机器学习>学习笔记五--逻辑回归 一. 分类(classification) 1.定义 2.阈值 二. 逻辑(logistic)回归假设函数 1.假设的表达式 2.假设表达式的意义 ...

  7. 好程序员教程分析Vue学习笔记五

    好程序员教程分析Vue学习笔记五,上次我们学习了Vue的组件,这次我们来学习一下路由的使用.在Vue中,所谓的路由其实跟其他的框架中的路由的概念差不多,即指跳转的路径. 注意:在Vue中,要使用路由, ...

  8. 【AngularJs学习笔记五】AngularJS从构建项目开始

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# AngularJs学习笔记 [AngularJs学习笔记一]Bower解决js的依赖管理 [AngularJs学习笔 ...

  9. ROS学习笔记五:理解ROS topics

    ROS学习笔记五:理解ROS topics 本节主要介绍ROS topics并且使用rostopic和rqt_plot命令行工具. 例子展示 roscore 首先运行roscore系列服务,这是使用R ...

  10. Spring Boot 框架学习笔记(五)( SpringSecurity安全框架 )

    Spring Boot 框架学习笔记(五) SpringSecurity安全框架 概述 作用 开发示例: 1. 新建项目 2. 引入依赖 3. 编写`SecurityConfig`类,实现认证,授权, ...

最新文章

  1. python之路-day11-迭代器闭包
  2. flutter ios打包_Flutter项目之iOS应用的打包发布流程
  3. 画蛇添足之error of activesync over usb link to pc
  4. vs2005 + ASP.NET 页面布局应注意问题及方法步骤
  5. 七十七、React中的propTypes,defaultProps和生命周期函数
  6. 解决 /usr/share/git-cola/bin/ssh-askpass: 3: exec: wish: not found
  7. 无聊的一天_一人互联网公司背后的无聊技术
  8. test.php.bak,记一次phpmyadmin 4.8.1 远程文件包含漏洞(BUUCTF web)
  9. android系统开发实验,基于Android智能手机的实验管理系统的设计与实现
  10. 如何让putty像secureCRT一样支持多标签 - 趁我还年轻 - 博客频道 - CSDN.NET - Opera
  11. [原创]全球首款不使用ViewState的Asp.Net2.0控件库
  12. activemq消息丢失_基于Redis实现消息队列的典型方案
  13. (转)我40个比特币失而复得的经历
  14. linux下view如何修改字符串,Linux下view命令的使用
  15. EasyCVR视频融合共享平台作为国标上级平台接入紫光华智云平台的流程解析
  16. 企业微信 事件接收服务器,授权通知事件
  17. 线性表——链表的应用
  18. 云测试(cloud testing)之我见
  19. Linux 搭建 discuz 论坛
  20. [杂谈] 机器学习与优化算法的对比

热门文章

  1. Java、JSP足球俱乐部管理系统
  2. okcc 呼叫中心系统最常见的三种搭建方案
  3. 设备驱动中的ioctl函数详解
  4. SQL查询一个表中类别字段中Max()最大值对应的记录
  5. 给一个数组,把偶数放到左边,奇数放到右边(有序和无序)
  6. Python对excel单元格着色
  7. 基于Cesium的通视分析的实现
  8. 基于python命令流及代码的Plaxis自动化建模
  9. 220kV变电站AR智能巡检系统-广州华锐互动
  10. 20230306xgs