寄存器的作用:例如在一个ALU模块中可以通过配置不同类型的register model来改变使用什么类型的算法。
如果不适用UVM RAL的验证平台
class master_sequence extends uvm_sequence #(transaction); virutual taks body(); `uvm_do_with(req,{ addr == 'h110;data == `1;kind == transaction::WRITE;}); `uvm_do_with(req,{addr == 'h120;kind == transaction::READ;});...; endtask endclass
class master_sequence extends uvm_reg_sequene#(uvm_sequence#(transaction)); virtual taks body(); uvm_status_e status; uvm_reg_data_t data; regmodel.INFO.read(status,data,UVM_BACKDOOR,.parent(this)); regmodel.INFO.write(status,55,'1,UVM_FRONTDOOR,.parent(this)); endtask endclass
访问方式 | 说明 |
---|---|
前门访问 | 通过模拟cpu在总线上发出读指令,进行读写操作。在这个过程中,仿真时间($time函数得到的时间)是一直往前走的。 |
后门访问 | 直接通过寄存器所在的hierarchy path来直接访问寄存器,不需要消耗时间。 |
寄存器模型的组成:
寄存器模型的相关类及功能
类名 | 功能 |
---|---|
uvm_reg_field | 用来针对寄存器功能域来构建对应的比特位 |
uvm_reg | 与寄存器相匹配,其内部可以例化和配置多个uvm_reg_field对象 |
uvm_mem | 匹配硬件存储模型 |
uvm_reg_map | 用来指定寄存器列表中各个寄存器的偏移地址、访问属性以及对应的总线 ,并将其转换成可以访问的物理地址(因为寄存器模型中的地址一般都是偏移地址,而不是绝对地址) |
uvm_reg_block | 可以容纳多个寄存器(uvm_reg)、存储器(uvm_mem)和寄存器列表(uvm_reg_map) |
class ctrl_reg extends uvm_reg; `uvm_object_utils(ctrl_reg) uvm_reg_field reserved; ... function new(string name = "ctrl_reg"); super.new(name, 32, UVM_NO_COVERAGE);//实例化reg时的,32位的,无覆盖的 endfunction virtual function build(); //1、创建和实例化 reserved = uvm_reg_field::type_id::create("reserved"); //2、对该域进行参数配置参 reserved.configure(this, 26, 6, "RO", 0, 26'h0, 1, 0, 0); ... endclass
new中的标识 | 说明 |
---|---|
UVM_NO_COVERAGE | no coverage models,不收集覆盖率 |
UVM_CVR_REG_BITS | coverage models for the bits read or written in registers |
UVM_CVR_ADD_MAP | coverage models for the addresses read or written in an address map |
UVM_CVR_FIELD_VALS | coverage models for the values of fields |
UVM_CVR_ALL | All coverage models |
class mcdf_rgm extends uvm_reg_block; `uvm_object_utils(mcdf_rgm) rand ctrl_reg chnl0_ctrl_reg; ... function new(string name = "mcdf_rgm"); super.new(name, UVM_NO_COVERAGE);//这里的不需要位宽 endfunction virtual function build(); //1、容纳uvm_reg_block chnl0_ctrl_reg = ctrl_reg::type_id::create("chnl0_ctrl_reg"); chnl0_ctrl_reg.configure(this);//2、进行配置各个uvm_reg实例,将 uvm_reg 关联到 uvm_reg_block 上 chnl0_ctrl_reg.build();//3、手动调用 uvm_reg 的build(),实例化reg中的各个域 //容纳uvm_reg_map // map name, offset, number of bytes, endianess //default_map = creat_map("default_map",0,2,UVM_LITTLE_ENDIAN);//缺省default_map只需要在系统里实例化调用create_map map = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);//实例化map //配置map //map.configure(this,null,"map”); map.add_reg(chnl0_ctrl_reg, 32'h00000000, "RW");//将 uvm_reg 添加到 map 中 ... //lock_model( ),不允许外部对这个寄存器模型做访问;
uvm_reg_block
可以用来容纳多个uvm_reg
和uvm_mem
的实例,map的作用一方面是用来表示寄存器和存储对应的偏移地址,同时由一个reg_block包含的多个reg_map,各个map可以分别对应不同的总线或地址段。在reg_block中创建各个uvm_reg之后,需要调用uvm_reg::configure()去配置各个uvm_reg实例的属性。由于它能够对更大地系统做寄存器建模,这意味着uvm_reg_block之间可能存在层次关系。因为adapter的上层(UVM的寄存器模型transaction)与下层(总线DUT,例如不同的总线有不同的sequence item)的标准不同,它们不能共用,这里就是adapter去协调它们的沟通。
uvm_reg_bus_op类成员
typedef struct{ uvm_access_e kind; uvm_reg_addr_t addr; uvm_reg_data_t data; int n_bits; uvm_reg_byte_en_t byte_en; uvm_status_e status; }uvm_reg_bus_op
class reg2apb_adapter extends uvm_reg_adapter; `uvm_object_utils(reg2apb_adapter) virtual function uvm_sequence_item reg2bus(const reg uvm_reg_bus_op rw); apb_trans tr = apb_trans::type_id::create("tr"); tr.read = (rw.kind == UVM_READ)?1:0; tr.addr = rw.addr; tr.data = rw.data; return tr; endfunction virtual function void bus2reg(uvm_sequence_item bus_item,reg uvm_reg_bus_op rw); apb_trans tr; if(!$cast(tr,but_item)begin `uvm_fatal("NOT_APB_TYPE","Provided bus_item is not apb_trans type") return; end rw.status = UVM_IS_OK; rw.kind = tr.read?UVM_READ:UVM_WRITE; rw.addr = tr.addr; rw.data = tr.data; endfunction endclass
reg2bus()
和bus2reg()
两个函数实现了uvm_reg_bus_op与DUT在各自的数据transaction映射。adapter、sequencer、map三者的关系,首先需要在环境(一般在env层次时)需要连接三者通过map.set_sequencer(squencer, adapter)
,
因为本身regmodel充当sequence,adapter充当转换的桥梁,而regmodel中因为有lock_model只有Map能访问内部reg的field,而对于传到bus上也需要用bus的sequencer进行和drv的传递,所以三者是要关联的,这样adapter才能工作。
class base_test extends uvm_test; `uvm_component_utils(base_test) my_block rm;//reg_block reg2apb_adapter reg_sqr_adapter; function new(string name = "base_test",uvm_component parent = null); super.new(name,parent); endfunction virtual function void build_phase(uvm_phase phase); super.build_phase(phase); rm = my_block::type_id::create("rm",this); rm.configure(null,""); rm.build(); rm.lock_model(); rm.reset(); reg_sqr_adapter = new("reg_sqr_adapter"); endfunction endclass //connect_phase function void top_env::connect_phase(uvm_phase phase); rm.map.set_sequencer(env.agt.sqr,adapter);//将 map 与 sequencer 和 adapter 连接 endfunction
uvm_reg_map::set_sequencer(“bus sequencer句柄”,“adapter句柄”)
的方式,将寄存器模型的map组件与bus sequencer和adapter连接,这会将map(寄存器信息)、sequencer(总线侧激励驱动)和 adapter(寄存器级别和硬件总线级别的桥接)关联在一起。只有通过这一步,才能完成adapter的桥接功能。在寄存器模型上做的读写操作,通过bus实现bus上的物理时序访问,是真实地物理操作
class apb_seq extends uvm_reg_sequence; apb_rgm rgm; `uvm_object_utils(apb_seq) `uvm_declare_p_sequncer(apb_bus_sequencer0 ... task body(); uvm_status_e status; uvm_reg_data_t data; if(!uvm_config_db#(apb_rgm)::get(null,get_full_name(),"rgm",rgm))begin `uvm_errror("GETGM","no top-down RGM handle is assigned") end // 第一种register model access write()/read() rgm.chnl0_ctrl_reg.read (status, data, UVM_FRONTDOOR, .parent(this)); rgm.chnl0_ctrl_reg.write(status, 'h11, UVM_FRONTDOOR, .parent(this)); rgm.chnl0_ctrl_reg.read (status, data, UVM_FRONTDOOR, .parent(this)); //第二种 pre-defined methods access read_reg (rgm.chnl1_ctrl_reg, status, data, UVM_FRONTDOOR); write_reg(rgm.chnl1_ctrl_reg, status, 'h22, UVM_FRONTDOOR); read_reg (rgm.chnl1_ctrl_reg, status, data, UVM_FRONTDOOR); #1us; endtask endclass
有两种方式实现前门访问
后门访问,就是不经过DUT的接口总线,可以直接对DUT的寄存器值进行操作,类似于“走后门”。System verilog提供了这种接口,叫做DPI。跟前门访问相比,多了几步:
class base_test extends uvm_test; `uvm_component_utils(base_test) apb_environment apb_env; virtual_sequencer v_sqr; scoreboard sb; reg_model rm; apb_reg_adapter adapter; function new(string name = "base_test",uvm_component parent = null); super.new(name,parent); endfunction virtual function void build_phase(uvm_phase phase); super.build_phase(phase); apb_env = apb_environment::type_id::create("apb_env",this); v_sqr = virtaul_sequencer::type_id::create("v_sqr",this); sb = scorboard::type_id::create("sb",this); rm = reg_model::type_id::create("rm",this);//创建一个reg_block,然后指定容器是env rm.configure(null,""); rm.bulid(); rm.lock_model(); rm.reset(); rm.set_hdl_path_root("top.dut.regblock");//设置后面基访问路径 adapter = new("adapter"); endfunction virtual function void connect_phase(uvm_phase phase); super.connect_phase(phase); v_sqr.mst_sqr = apb_env.mst_agt.sqr; apb_env.mst_agt.mon.apb_mon_port.connect(sb.mst_imp); v_sqr.p_rm = this.rm;//在virtual sequence种定义 rm.default_map.set_sequencer(apb_env.mst_agt.sqr,adapter); rm.default_map.set_auto_predict(1); endfunction endclass
//========================transaction============== //事物数据建模中说明数据的操作、地址、数据和状态 class transaction extends uvm_sequence_item; ... typedef enum int unsigned{READ,WRITE}kind_e; rand kind_e kind; status_e status; rand bit [15:0]addr,data; endclass //========================transaction============== class master_driver extends uvm_driver#(transaction); .. task run_phase(uvm_phase phase); forever begin seq_item_port.get_next_item(req); wr_data(req); seq_item_port.item_done(); end endtask ... endclass //========================sequence============== //在seuquence中使用硬编码的方式,编写地址,并通过uvm_do的操作方式,进行读写操作 class bsm_sequence extends uvm_sequence#(transaction); ... task body(); `uvm_do_with(req,{addr == 'h000;kind == transactiion::READ;}); `uvm_do_with(req,{addr == 'h000;data == '1;kind == transactiion::READ;}); `uvm_do_with(req,{addr == 'h100;kind == transactiion::READ;}); if(starting_phase != null) starting_phase.drop_objection(this); endtask enclass //========================agent============== class master_agent extends uvm_agent; typedef uvm_sequencer#(transaction) master_sequncer; master_driver drv; master_monitor mon; master_sequencer sqr; endclass //========================env============== class master_env extends uvm_env; master_agent m_agent; function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db#(uvm_object_wrapper)::set(this, "m_agent.srq.configure_phase", "default_sequence", bfm_sequence::get_type()); endfunction endclass //========================base_test============== //需要看波形,看下是否是期望值 class base_test extends uvm_test; master_env m_env; function void build_phase (phase); `uvm_config_db#(virtual master_io)::set(this, "m_env.m_agent", "mater_io",DUV_test_top.master); endfunction endclass
使用VCS
使用VCS
见上面
//==============================RAL寄存器sequence========================= class master_ral_sequence extends uvm_reg_sequence#(master_sequence_base); ral_block_master_regmodel regmodel; virtual task pre_start(); super.pre_start();//raise_objection code; uvm_config_db#(ral_block_master_regmodel)::get(get_suequencer(), "", "regmodel", regmodel); endtask virtual task body(); regmodel.INFO.read(status,data,.parent(this));//can specify path regmodel.INT.write(status,8,'1,.parent(this));//default to frontdoor endtask endclass //==============================在验证环境中例化RAL========================= class master_env extends uvm_env; master_agent m_agent; ral_block_master_regmodel regmodel; virtual function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db#(ral_block_master_regmodel)::get(this, "", "regmodel", regmodel); if(regmodel == null)begin string hdl_path; //1、创建寄存器实例化 regmodel = ral_block_master_regmodel::type_id::create("regmodel",this); //2、创建UVM寄存器层次,注意没有使用build_phase() regmodel.build(); //3、锁定寄存器的层次结构并创建地址映射 regmodel.info_model(); //4、 if(!uvm_resource_db#(string)::read_by_name("master_regmodel", "hdl_path", hdl_path, this)) regmodel.set_hdl_path_root(hdl_path); else `uvm_warning("master_regmodel","Master XMR path is not set") //5、为sequence设置寄存器模型 uvm_config_db#(ral_block_master_regmodel)::set(this, "m_agent_sqr", "regmodel", regmodel); end endfunction
class base_test extends uvm_test; master_env env; virtual function void build_phase(uvm_phase phase); //super.build_phase and component construction not shown uvm_config_db#(string)::set(this,"m_env","hdl_path","DUT_test_top.duv"); endfunction virtual task configure_phase(uvm_phase phase); super.configure_phase(phase); master_ral_sequence master_seq; master_seq = master_ral_sequence::type_id::create("master_seq"); master_seq.regmodel = m_env.regmodel; master_seq.start(null); endtask endclass class test_ral_base extends base_test; virtual function void build_phase(uvm_phase phase); uvm_resource_db#(string)::set("master_regmodel","hdl_path","master_test_top.duv",this); endfunction endclass //======================隐式执行RALsequence============== class test_ral_implicit extends test_ral_base; virtual function void configure_phase(phase); uvm_config_db#(uvm_object_wrapper)::set(this,"*.seqr.configure_phase","default_sequence",master_ral_sequence::get_type()); endfunction endclass //======================显式执行RALsequence============== class test_ral_explicit extends test_ral_base; virtual function void configure_phase(uvm_phase phase); master_ral_sequence m_sqe; phase.raise_objection(this); m_seq = master_ral_sequence::type_id::create("m_seq",this); m_seq.start(env.m_agent.seqr); phase.drop_objection(this); endfunctio endclass
当使用后门访问寄存器时,sequence依赖于寄存器mirror值的更新
sequence name | description |
---|---|
uvm_reg_hw_reset_seq | 测试寄存器的硬复位值 |
uvm_reg_bit_bash_seq | 所有寄存器比特网 |
uvm_reg_access_seq | 验证所有寄存器的操作属性 |
uvm_mem_walk_seq | 使用遍历算法验证寄存器 |
uvm_mem_access_seq | 使用前门访问或者后门访问方式验证寄存器的读写操作 |
uvm_reg_mem_build_in_seq | 运行所有寄存器和存储器的测试用例 |
uvm_reg_mem_hdl_paths_seq | 验证寄存器和存储器的层次化路径 |
uvm_reg_mem_shared_access_seq | 验证共享寄存器和存储器的读写操作 |
加粗为常见
//======================隐式执行镜像预测============== //当对寄存器进行读写操作时,更新寄存器的镜像值,自动调用读写操作更新镜像值,不需要自己编写代码 //镜像值的更新时序不一定是精确到时钟周期,DUT内部的更改可能不会影响镜像值 class environment extends uvm_env; virtual function void connect_phase(uvm_phase phase); regmodel.default_map.set_auto_predict(1); endfunction endclass
class test_ral extends test_base; string seq_name="uvm_reg_bit_bash_seq"; uvm_reg_sequence selftest_seq; virtual reset_sequence rst_seq; virtual function void build_phase(uvm_phase phase); super.build_phase(); uvm_config_db#(uvm_object_wrapper)::set(this, "*", "default_sequence", null); endfunction virtual task run_phase(uvm_phase phase); rst_seq = virtual_reset_sequence::type_id::create("rst_seq",this); phase.raise_objection(this,"starting test"); v_reset_req.start(env.v_reset_seqr);//run reset clp.get_arg_value("+seq=",seq_name);//能够调用之前自定义的seq $cast(selftest_seq,factory.create_object_by_name(seq_name));//create test env.regmodel.default_map.set_auto_predict(1);//enable auto predict if not done in env selftest_seq.start(env.m_agent.seqr);//run test phase.drop_objection(this,"Done with tests"); endtask endclass
还可以通过命令行的方式实现
+UVM_TESTNAME=test_ral+seq=uvm_reg_hw_reset_seq
class environment extends uvm_env; typedef uvm_reg_predict#(master_data)mreg_predictor; mreg_predictor mreg_predict; virtual function void build_phase(uvm_phase phase); mreg_predict = mreg_predictor::type_id::create("mreg_predict",this); endfunction virtual function void connect_phase(uvm_phase phase); mreg_predict.map = regmodel.get_default_map(); mreg_predict.adapter = adapter; regmodel.default_map.set_auto_predict(0); m_agent.analysis_port.connect(mreg_predict.bus_in); endfunction endclass
class test_ral extends test_base; //code identical to auto predict test is left off virtual task run_phase(uvm_phase phase); phase.raise_objection(this,"starting reset test"); v_reset_seq = virtual_reset_sequence::type_id::create("v_reset_seq",this); v_reset_seq.start(env.v_reset_seqr); clp.get_arg_value("+seq=",seq_name); $cast(selftest_seq,factory.create_objection_by_name(seq_name)); env.regmodel.default_map.set_auto_predict(0);//这个可以省略 env.regmodel.default_map.set_auto_predict(1);//enable auto predict if not done in env selftest_seq.start(env.m_agent.seqr);//run test phase.drop_objection(this,"Done with tests"); endtask endclass //================例化UVM内建test_case进行自动测试 class hw_reset extends test_base; reset_sequence reset_seq; uvm_reg_hw_reset_seq reset_test; virtual task run_phase(uvm_phase phase); phase.raise_objection(this,"starting register test"); reset_seq = reset_sequence::type_id::create("reset_seq",this); reset_seq.start(env.reset_seqr); //创建并运行test_case reset_test = uvm_reg_hw_reset_seq::type_id::create("reset_test",this); reset_test.model = env.regmodel; reset_test.model.set_auto_predict(1); reset_test.start(null); phase.drop_objection(this,"Done with register test"); endtask endclass