文章目录 原始代码 详细解读: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更好? 六、实际应用中的区别 七、总结 原始代码 `timescale1 ns/ 1 nsmodule slave ( 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; endelse begin// Wait for a SETUP+READ or ENABLE+WRITE cycle if ( 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 endelse begincasex ( 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 endmoduleclass dut_ID extends 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) ; endfunctionvirtual functionvoid build ( ) ; 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) endclassclass dut_DATA extends uvm_reg; uvm_reg_field value; functionnew ( string name= "dut_DATA" ) ; super. new ( name, 32 , UVM_NO_COVERAGE) ; endfunctionvirtual functionvoid build ( ) ; 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) endclassclass dut_SOCKET extends 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: new virtual functionvoid build ( ) ; 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) endclassclass dut_RAM extends uvm_mem; functionnew ( string name= "dut_RAM" ) ; super. new ( name, 'h400, 32 , "RW" , UVM_NO_COVERAGE) ; endfunction `uvm_object_utils ( dut_RAM) endclassclass dut_regmodel extends 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) ; endfunctionvirtual functionvoid build ( ) ; // create ID= dut_ID:: type_id:: create ( "ID" ) ; DATA= dut_DATA:: type_id:: create ( "DATA" ) ; foreach ( SOCKET[ i] ) SOCKET