文章目录
- 原始代码
- 详细解读:Direct vs Layered UVM验证框架
- 一、第一段代码(Direct框架)详细解读
- 1. APB从设备模块(slave)
- 2. 寄存器模型(dut_regmodel)
- 3. 测试平台(tb_top)
- 4. 测试环境(tb_env)关键代码
- 二、第二段代码(Layered框架)详细解读
- 1. APB从设备模块(slave)
- 2. 寄存器模型(dut_regmodel)
- 3. 测试平台(tb_top)
- 4. 测试环境(tb_env)关键代码
- 三、关键组件详解
- 1. `uvm_sequencer#(uvm_reg_item) reg_seqr;`
- 2. `reg2apb_seq_t reg2apb_seq;`
- 3. `uvm_reg_sequence seq;`
- 4. `regmodel.set_hdl_path_root(hdl_root);`
- 5. `reg2apb_adapter reg2apb = new;`
- 6. `regmodel.default_map.set_sequencer(xxx, reg2apb);`
- 四、Direct vs Layered 的关键区别
- 1. 事务处理方式
- 2. 代码结构对比
- 3. 为什么需要Layered框架?
- 五、小白理解版
- Direct框架(直接点餐)
- Layered框架(分层点餐)
- 为什么Layered更好?
- 六、实际应用中的区别
- 七、总结
原始代码
`timescale1ns/1nsmoduleslave(apb_if apb,input bit rst);reg[31:0]pr_data;assign apb.prdata=(apb.psel&&apb.penable&&!apb.pwrite)?pr_data:'z;reg[31:0]DATA;reg[63:0]SOCKET[256];reg[31:0]DMA[1024];always @(posedge apb.pclk)beginif(rst)begin DATA<='h00;foreach(SOCKET[i])begin SOCKET[i]<=64'h0000_0000;end pr_data<=32'h0;endelsebegin// Wait for a SETUP+READ or ENABLE+WRITE cycleif(apb.psel==1'b1&&apb.penable==apb.pwrite)begin pr_data<=32'h0;if(apb.pwrite)begincasex(apb.paddr)16'h0024:DATA<=apb.pwdata;16'h1XX0:SOCKET[apb.paddr[11:4]][63:32]<=apb.pwdata;16'h1XX4:SOCKET[apb.paddr[11:4]][31:0]<=apb.pwdata;16'h2XXX:DMA[apb.paddr[11:2]]<=apb.pwdata;endcase endelsebegincasex(apb.paddr)16'h0000:pr_data<={4'h0,10'h176,8'h5A,8'h03};16'h0024:pr_data<=DATA;16'h1XX0:pr_data<=SOCKET[apb.paddr[11:4]][63:32];16'h1XX4:pr_data<=SOCKET[apb.paddr[11:4]][31:0];16'h2XXX:pr_data<=DMA[apb.paddr[11:2]];endcase end end end end endmoduleclassdut_IDextends uvm_reg;uvm_reg_field REVISION_ID;uvm_reg_field CHIP_ID;uvm_reg_field PRODUCT_ID;functionnew(string name="dut_ID");super.new(name,32,UVM_NO_COVERAGE);endfunctionvirtualfunctionvoidbuild();this.REVISION_ID=uvm_reg_field::type_id::create("REVISION_ID");this.CHIP_ID=uvm_reg_field::type_id::create("CHIP_ID");this.PRODUCT_ID=uvm_reg_field::type_id::create("PRODUCT_ID");this.REVISION_ID.configure(this,8,0,"RO",0,8'h03,1,0,1);this.CHIP_ID.configure(this,8,8,"RO",0,8'h5A,1,0,1);this.PRODUCT_ID.configure(this,10,16,"RO",0,10'h176,1,0,1);endfunction `uvm_object_utils(dut_ID)endclassclassdut_DATAextends uvm_reg;uvm_reg_field value;functionnew(string name="dut_DATA");super.new(name,32,UVM_NO_COVERAGE);endfunctionvirtualfunctionvoidbuild();this.value=uvm_reg_field::type_id::create("value");this.value.configure(this,32,0,"RW",1,32'h0,1,0,1);endfunction `uvm_object_utils(dut_DATA)endclassclassdut_SOCKETextends uvm_reg;rand uvm_reg_field IP;rand uvm_reg_field PORT;functionnew(string name="dut_ADDR");super.new(name,64,UVM_NO_COVERAGE);endfunction:newvirtualfunctionvoidbuild();this.IP=uvm_reg_field::type_id::create("value");this.PORT=uvm_reg_field::type_id::create("value");this.IP.configure(this,48,0,"RW",0,48'h0,1,0,1);this.PORT.configure(this,16,48,"RW",0,16'h0,1,0,1);endfunction `uvm_object_utils(dut_SOCKET)endclassclassdut_RAMextends uvm_mem;functionnew(string name="dut_RAM");super.new(name,'h400,32,"RW",UVM_NO_COVERAGE);endfunction `uvm_object_utils(dut_RAM)endclassclassdut_regmodelextends uvm_reg_block;rand dut_ID ID;rand dut_DATA DATA;rand dut_SOCKET SOCKET[256];rand dut_RAM RAM;functionnew(string name="slave");super.new(name,UVM_NO_COVERAGE);endfunctionvirtualfunctionvoidbuild();// createID=dut_ID::type_id::create("ID");DATA=dut_DATA::type_id::create("DATA");foreach(SOCKET[i])SOCKET