NVDLA uvm验证环境深度解析

2023-10-27 18:50

本文主要是介绍NVDLA uvm验证环境深度解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.nvdla hw master github

  1. https://github.com/nvdla/hw/tree/master
  2. NVDLA Environment Setup Guide
  3. NVDLA Verification Suite User Guide

首先checkout nvdla hw master breach,然后按照第2,3步,完成build和health check,当nvdla tree build完之后,通过run testcase来check环境是否完善,build是否成功。

仿真结束后,在log的结尾会打印如下pass fail 信息:

*******************************
**        TEST PASS          **
*******************************PPPP     A     SSSSS  SSSSS
P   P   A A    S      S
PPPP   AAAAA   SSSSS  SSSSS
P     A     A      S      S
P     A     A  SSSSS  SSSSSor*******************************
**        TEST FAILED        **
*******************************FFFFF    A     IIIII  L
F       A A      I    L
FFFF   AAAAA     I    L
F     A     A    I    L
F     A     A  IIIII  LLLLL

2. 测试套件

主要有两类tests,一类是直接测试,一类是uvm的受约束的随机测试(constrained-random tests)。

 NV_SMALL project的直接测试路径为:TOT/verif/tests/trace_tests/nv_small

受约束的随机测试的路径为:TOT/verif/tests/trace_tests/uvm_tests

*目前为止,只有直接测试是官方ready的

3. 直接测试举例

以TOT/verif/tests/trace_tests/nv_small/cdp_1x1x1_lrn3_int8_0/为例,对直接测试进行简单讲解。

直接测试是以一种自定义的trace format的形式编写的,nvdla 定义了9种configuration commands。参考如下:

NameDescriptionSyntaxUse case
reg_writeWrite data to specific DUT registerreg_write(reg_n ame, reg_value);Fundamental operation for register configuration
reg_read_expect edRead data from specific DUT register, compare with expected valuereg_read_expect ed(addr, expected_data);For some special cases like register accessing tests
reg_readRead data from specific DUT registerreg_read(reg_na me, return_value);For specific cases which may need to do post-processing on read return value.
sync_notifySpecified player sequencer will send out synchronization eventsync_notify(tar get_resource, sync_id);CC pipeline, OP_EN configuration order, CACC->CMAC->CSC .
sync_waitSpecified player sequencer will wait on synchronization eventsync_wait(targe t_resource, sync_id);CC pipeline, OP_EN configuration order, CACC->CMAC->CSC .
intr_notify

Monitor DUT interrupt, catch and clear interrupt and send synchronization event.

There could be multiple intr_notify, all those intr_notify are processed sequentially. The processing order is the same as commands’ line order in configuration file.

intr_notify(int r_id, sync_id); // notify when specific interrupt fired

Hardware layer complete notification, informing test bench that test is ended.

Multi-layer test which is presumed containing layer 0 ~ N, for n >1 layers, they shall wait for interrupts.

poll

Continues poll register/field value from DUT, until one of the following conditions are met:

  1. Equal, polled value is equal to expected value
  2. Greater, polled value is greater than expected value
  3. Less, polled value is less than expected value
  4. Not equal, polled value is not equal to expected value
  5. Not greater, polled value is not greater than expected value
  6. Not less, polled value is not less than expected value

poll_field_equa l(target_resour ce, register_name, field_name, expected_value) ;

poll_reg_equal( target_resource , register_name, expected_value) ;

poll_field_grea ter(target_reso urce, register_name, field_name, expected_value) ;

poll_reg_less(t arget_resource, register_name, expected_value) ;

poll_field_nt_ greater(taget_ resource, register_name, field_name, expected_value) ;

poll_reg_not_le ss(target_resou rce, register_name, expected_value) ;

Convolution case, wait until CBUF flush has done
check

Invoke player result checking method.

When test bench works in RTL/CMOD cross checking mode, neither golden CRC nor golden files are necessary in this case. Method check_nothing() shall be added to trace file to indicated test end event.

check_crc(syn_ id, memory_type, base_address, size, golden_crc_valu e);

check_file(sync _id, memory_type, base_address, size, “golden_file_na me”);

check_nothing(s ync_id);

CRC check for no CMOD simulation (usually generated by arch/inherit from previous project/eyeball gilded)

Golden memory result check for no CMOD simulation (usually generated by arch/inherit from previous project/eyeball gilded)

mem

Load memory from file.

Initialize memory by pattern.

mem_load(ram_ty pe, base_addr, file_path); // file_path shall be enclosed by “”

mem_init(ram_ty pe, base_addr, size, pattern);

 

*Some functions are not supported yet.

直接测试trace format如下:

mem_init(pri_mem, 0x80000c00, 0x800, ALL_ZERO);
mem_load(pri_mem, 0x80000c00, "cdp_1x1x1_lrn3_int8_0_in.dat");
mem_init(pri_mem, 0x80000020, 0x800, ALL_ZERO);
reg_write(NVDLA_CDP.S_POINTER_0, 0x1);
reg_read_check(NVDLA_CDP.S_POINTER_0, 0x1);
/*
some reg_write cfg cmd for dla reg config
/*
intr_notify(CDP_0, sync_id_0);
check_crc(sync_id_0, 1, 0x80000020, 0x800, 0xf1e8ba9e);add a new cfg command:
mem_reserve(pri_mem, 0x80000020, 0x800);
poll_reg_equal(NVDLA_GLB.S_INTR_STATUS_0, 0x40);

trace format 文件,在trace_player tb中经过nvdla_trace_parser.py脚本处理,会生成四个文件,四个文件分别对应四类不同的cfg cmds。

文件内容分别如下:

4. trace_player tb 关键组件解释

4.1 tb 验证组件一览:

.
├── Makefile                   # 编译makefile
├── nvdla_tb_base_test.sv      # base test, 例化 env  trace_parser seq result_ck
|                              # 并完成tlm port连接
├── nvdla_tb_common.svh        # 宏定义,以及用package封装tb中使用的参数
├── nvdla_tb_connect.sv        # tb用interface和dut各个模块的连接
├── nvdla_tb_env.sv            # 
├── nvdla_tb_intr_handler.sv   # 中断处理类
├── nvdla_tb_override.sv       # tlm generic payload 重载类
├── nvdla_tb_result_checker.sv # result check类,实现并行check,以及于其他cmds的同步
├── nvdla_tb_scoreboard.sv     # 很明显了,计分板
├── nvdla_tb_sequence.sv       # 寄存器读写以及check
├── nvdla_tb_top.sv            # tb top
├── nvdla_tb_trace_parser.sv   # trace format文件的parser类
└── nvdla_tb_txn.sv            # 定义tb中用到的对应四种cmds的四种sequence,# 以及define trace parser instruction command enum type

4.2 nvdla_tb_trace_parser

这一切都是在nvdla_tb_trace_parser.sv类中完成的,

class nvdla_tb_trace_parser extends uvm_component;string      tID = "nvdla_tb_trace_parser";string      trace_file_path;string      parser_core_path  = "nvdla_trace_parser.py";string      seq_cmd_file_path = "./trace_parser_cmd_sequence_command.log";string      ic_cmd_file_path  = "./trace_parser_cmd_interrupt_controller_command.log";string      rc_cmd_file_path  = "./trace_parser_cmd_result_checker_command.log";string      mm_cmd_file_path  = "./trace_parser_cmd_memory_model_command.log";uint32_t    echo_line_content =1;uvm_event_pool                              global_event_pool;uvm_analysis_port#(sequence_command)        sequence_command_port; uvm_analysis_port#(result_checker_command)  result_checker_command_port; uvm_analysis_port#(interrupt_command)       interrupt_handler_command_port; uvm_analysis_port#(memory_model_command)    primary_memory_model_command_port; 
`ifdef NVDLA_SECONDARY_MEMIF_ENABLEuvm_analysis_port#(memory_model_command)    secondary_memory_model_command_port; 
`endif变量声明部分代码

end_of_elaboration_phase中调用parse_trace函数,生成四种cmd file生成,然后分别读取其中内容,因为四种cmds每个字段的含义有些不同,请参考下图:

每一个cmds文件的处理大同小异,下面以sequence ctrl的处理为例:

    //---------- sequence controllerbeginstring      block_name;string      reg_name;string      field_name;uint32_t    data;string      sync_id;fh = $fopen(seq_cmd_file_path,"r");if(!fh) begin`uvm_fatal(tID, $sformatf("Cannot find sequence command file %s\n", seq_cmd_file_path) );endwhile (!$feof(fh)) begin// string kind_name,block_name,reg_name,field_name,data,sync_id;code = $fscanf(fh,"%s %s %s %s %h %s\n",kind_name,block_name,reg_name,field_name,data,sync_id);if (6 == code) begin`uvm_info(tID,$sformatf("kind_name=%s, block_name=%s, reg_name=%s, field_name=%s, data=%h, sync_id=%s",kind_name,block_name,reg_name,field_name,data,sync_id), UVM_HIGH);seq_cmd = new("seq_cmd");kind_wrapper::from_name(kind_name, seq_cmd.kind);seq_cmd.block_name  = block_name;seq_cmd.reg_name    = reg_name.substr(0, reg_name.len()-3); //FIXME: no "_0" suffix in ral register nameseq_cmd.field_name  = field_name;seq_cmd.data        = data;seq_cmd.sync_id     = sync_id;if ("NOTIFY" == kind_name) beginglobal_event_pool.get(sync_id);endsend_command_to_sequence(seq_cmd);endend$fclose(fh);end

主要调用verilog system api $fopen,  $fscanf, $sscanf, $feof, $fclose, 完成文件打开关闭,每行按格式读取和检查文件结尾标志。

文件中的一行,就是一个完整的sequence cmd,对应到tb中,sequence_command类中有对应每一行每个字段的变量,

这个kind_wrapper有点意思。

typedef enum {   WRITE, NOTIFY, WAIT, READ, READ_CHECK, POLL_REG_EQUAL, POLL_FIELD, SINGLE_SHOT, MULTI_SHOT, CHECK_CRC, CHECK_FILE, CHECK_NOTHING, MEM_RESERVE, MEM_LOAD, MEM_INIT_PATTERN, MEM_INIT_FILE, MEM_RELEASE} kind_e;typedef enum { PRI_MEM    = 0,SEC_MEM    = 1} memory_type_e;typedef uvm_enum_wrapper#(kind_e)           kind_wrapper;
typedef uvm_enum_wrapper#(memory_type_e)    memory_type_wrapper;// Class: uvm_enum_wrapper#(T)
//
// The ~uvm_enum_wrapper#(T)~ class is a utility mechanism provided
// as a convenience to the end user.  It provides a <from_name>
// method which is the logical inverse of the System Verilog ~name~ 
// method which is built into all enumerations.
// Function: from_name
// Attempts to convert a string ~name~ to an enumerated value.
//
// If the conversion is successful, the method will return
// 1, otherwise 0.
//
// Note that the ~name~ passed in to the method must exactly
// match the value which would be produced by ~enum::name~, and
// is case sensitive.kind_wrapper::from_name(kind_name, seq_cmd.kind);

意思将字符串转换为自定义的枚举类型变量。上图中kind_name是字符串,seq_cmd.kind是枚举类型变量。

然后调用send_command_to_sequence函数,通过uvm_analysis_port#(sequence_command)  sequence_command_port建cmd sequence发送到nvdla_tb_sequence类中取处理。

function void nvdla_tb_trace_parser::send_command_to_sequence(sequence_command cmd);`uvm_info(tID, $sformatf("seq cmd:%0s", cmd.sprint()), UVM_MEDIUM)sequence_command_port.write(cmd);
endfunction : send_command_to_sequence

4.3 nvdla_tb_common

package nvdla_tb_common_pkg;//Define global message print verbosity macros//Compatiable with UVM_VERBOSITY and extends to add two more print levelparameter  NVDLA_NONE   =  0;parameter  NVDLA_LOW    =  100;parameter  NVDLA_MEDIUM =  200;parameter  NVDLA_TRACE  =  250;parameter  NVDLA_VERIF  =  280;parameter  NVDLA_HIGH   =  300;parameter  NVDLA_FULL   =  400;parameter  NVDLA_DEBUG  =  500;// TODO: Remove hard-coded magic numbers and use macros from project.vh once VMOD add them.parameter SDP_DW = `NVDLA_BPE;parameter SDP_DS = `NVDLA_SDP_MAX_THROUGHPUT;parameter SDP_PW    = (`NVDLA_BPE*`NVDLA_SDP_MAX_THROUGHPUT);  // small:8, large:128parameter CACC_PW   = (32*`NVDLA_SDP_MAX_THROUGHPUT+2);   // small:34, large:514endpackage: nvdla_tb_common_pkg

用包封装tb中使用的参数,符合uvm的验证哲学,使用的时候,只需 import nvdla_tb_common_pkg::*;即可。

4.4 nvdla_tb_intr_handler

// TASK: main_phase
// Used to execure mainly run-time tasks of simulation
task nvdla_tb_intr_handler::main_phase(uvm_phase phase);super.main_phase(phase);`uvm_info(tID, $sformatf("main_phase begin ..."), UVM_HIGH)// `uvm_info(tID, $sformatf("raise objection"), UVM_MEDIUM)if((is_rm && ("RTL_ONLY" != work_mode)) || (!is_rm && ("CMOD_ONLY" != work_mode))) beginwhile(cmd_queue_size()>0) begin`uvm_info(tID, $sformatf("raise objection"), UVM_MEDIUM)phase.raise_objection(this);intr_process();phase.drop_objection(this);`uvm_info(tID, $sformatf("drop objection"), UVM_MEDIUM)endend
endtask : main_phasetask nvdla_tb_intr_handler::wait_intr(bit is_rm);if(is_rm) beginrm_intr_evt.wait_on();endelse begindut_intr_evt.wait_on();end`uvm_info(tID, $sformatf("intr_evt is on"), UVM_MEDIUM)
endtask : wait_intrtask nvdla_tb_intr_handler::intr_process();bit [`CSB_DATA_WIDTH-1:0]    intr_val;bit [`CSB_DATA_WIDTH-1:0]    mask_val;uvm_reg_field     flds[$];interrupt_command item;uvm_tlm_gp        gp;int               lsb;wait_intr(is_rm);get_intr_val(is_rm, intr_val, mask_val);flds.delete();ral.nvdla.NVDLA_GLB.S_INTR_STATUS.get_fields(flds);foreach(flds[i]) begin/*省略部分代码*/
endtask

首先在nvdla_tb_top.sv中有如下代码:

dla_intr即为dla中断输出信号,每当中断assert,需要将global event pool中的dut_intr_evt trigger,并且等待uvm_event的wait_off方法。 

其次在nvdla_tb_intr_handler的intr_process方法中,调用wait_intr等待dut_intr_evt trigger,说明此时有中断产生, 调用get_intr_val拿到S_INTR_STATUS和S_INTR_MASK寄存器的值,由于有两个reg group,需要判断是哪个类型的两个中断中的哪个。例如:

intr_notify(CDP_0, sync_id_0); intr_notify(CDP_1, sync_id_0); 遍历S_INTR_STATUS寄存器每个bit的值,得到reg model中每个reg field的名字,并且操作字符串得到act_id或者blk_name,与CDP_0 CDP_1中的CDP模块名比较,还有0或者1进行比较。

最后调用evt_trigger触发同步事件,并且调用intr_clear 清除中断标志位,调用evt_reset复位dut_intr_evt 事件,使上图中的evt.wait_off生效。

4.5 nvdla_tb_result_checker

task nvdla_tb_result_checker::main_phase(uvm_phase phase);super.main_phase(phase);`uvm_info(tID, $sformatf("main_phase begin ..."), UVM_MEDIUM)phase.raise_objection(this);warden_process();phase.drop_objection(this);`uvm_info(tID, $sformatf("main_phase end ..."), UVM_MEDIUM)endtask : main_phasetask nvdla_tb_result_checker::warden_process();uint32_t cmd_idx;for (cmd_idx = 0; cmd_idx < command_number; cmd_idx ++) beginautomatic uint32_t var_i = cmd_idx;forkbeginuvm_event evt;result_checker_command cmd_item;command_fifo.get(cmd_item);if(!global_event_pool.exists(cmd_item.sync_id)) begin`uvm_fatal(tID, $sformatf("sync_id %0s doesn't exist", cmd_item.sync_id))endevt = global_event_pool.get(cmd_item.sync_id);if (evt.is_off()) beginevt.wait_on();endif (CHECK_NOTHING == cmd_item.kind) begin`uvm_info(tID, $sformatf("No need to check on sync_id:%s.", cmd_item.sync_id), UVM_MEDIUM)end else begin`uvm_info(tID, $sformatf("Ready to send check command to memory model ..."), UVM_MEDIUM)if (PRI_MEM == cmd_item.memory_type) beginprimary_memory_check_command_port.write(cmd_item);end else beginsecondary_memory_check_command_port.write(cmd_item);endendendjoin_noneendwait fork;// Simulation complete notificationsim_done_evt.trigger();endtask : warden_process

nvdla_tb_intr_handler触发了同步事件sync_id_0, check_crc(sync_id_0, 1, 0x80000020, 0x800, 0xf1e8ba9e) 中第一个字段,就是需要同步等待的事件,说明模块done中断来了,可以进行结果比较了。再根据mem type是primary mem还是secondary mem将cmd item写到不同的组件进行处理(其实是一个组件例化两份)。再比较的进程都结束后,触发sim_done_evt,在nvdla_tb_top中有对sim_done_evt的应用。

注意:warden_process方法中用的fork join_none wait fork的语法

4.6 nvdla_tb_sequence

function void nvdla_tb_sequence::cmd_distribute();sequence_command  item_t;    sequence_command  item;    string            blk;uvm_event         evt;while(cmd_fifo.try_get(item_t))begin$cast(item, item_t.clone());`uvm_info(tID, $sformatf("cmd_distribute:\n%0s", item.sprint()), UVM_HIGH)if(item.kind == NOTIFY || item.kind == WAIT) beginevt = new(item.sync_id);glb_evts.add(item.sync_id, evt);endblk = item.block_name;blk_cmd[blk].push_back(item);end
endfunction : cmd_distributetask nvdla_tb_sequence::main_phase(uvm_phase phase);super.main_phase(phase);`uvm_info(tID, $sformatf("main_phase begin ..."), UVM_MEDIUM)phase.raise_objection(this);foreach(blk_cmd[i]) beginautomatic string j = i;forkbeginsequence_command cmd;while(blk_cmd[j].size() != 0) begincmd = blk_cmd[j].pop_front();cmd_issue(cmd);end`uvm_info(tID, $sformatf("main_phase::sequence %s, all commands have been procesdded.", j), UVM_MEDIUM)endjoin_noneendwait fork;`uvm_info(tID, $sformatf("main_phase complete ..."), UVM_MEDIUM)phase.drop_objection(this);
endtask : main_phasetask nvdla_tb_sequence::cmd_issue(sequence_command cmd);`uvm_info(tID, $sformatf("cmd_issue start:%0s", cmd.sprint()), UVM_MEDIUM)case(cmd.kind) WRITE:      write_i(cmd);NOTIFY:     notify_i(cmd);WAIT:       wait_i(cmd);READ:       read_i(cmd);READ_CHECK: read_check_i(cmd);POLL_REG_EQUAL:  poll_reg_equal_i(cmd);POLL_FIELD: poll_field_i(cmd);endcase`uvm_info(tID, $sformatf("cmd_issue done."), UVM_MEDIUM)
endtask : cmd_issuetask nvdla_tb_sequence::write_i(sequence_command cmd);string          blk_name;string          reg_name;uvm_reg         regs;uvm_reg_block   blks;uvm_status_e    status;blk_name = cmd.block_name;reg_name = cmd.reg_name;blks = ral.nvdla.get_block_by_name(blk_name.toupper);if(blks == null) begin`uvm_fatal(tID, $sformatf("No exists uvm_reg_block: %s", blk_name))endregs = blks.get_reg_by_name(reg_name.toupper);if(regs == null) begin`uvm_fatal(tID, $sformatf("No exists uvm_reg: %s", reg_name))endregs.write(status, cmd.data);endtask : write_i

在start_of_simulation_phase中调用cmd_distribute函数,将cmd_fifo中的cmd_item按照block_name为索引,扔进blk_cmd,一个队列类型的联合数组里。在main_phase()中,遍历blk_cmd,pop出cmd_item,然后调用cmd_issue,执行cmd。注意,使用fork join_none wait_fork。这样使得不同blk的寄存器同时进行配置,但是一个blk内的若干寄存器是按顺序进行配置的。

cmd_issue方法中有几种不同了类型的cmd的处理,我们以write_i为例进行说明。

根据cmd_item中的block_name,reg_name,分别调用get_block_by_name,get_reg_by_name拿到reg_block再拿到regs,最后调用 regs.write实现,寄存器的前门写。

4.7 与result check相关的mem_model

mem_model包主要有mem_core和mem_wrap两个类组成。

class mem_core extends uvm_component;typedef enum {RANDOM, ZEROS, ONES, X, AFIVE, FIVEA, ADDR} init_option_enum;string tID;rand init_option_enum init_option = RANDOM;rand bit dont_store_uninitialized_vals = 0;addr_t      base;addr_t      limit;protected bit [7:0] m_mem[addr_t];// Read functionsextern function bit [1023:0] read(addr_t addr, bit [10:0] size_in_bits);// Write functionsextern function void write(addr_t addr, bit [1023:0] data, bit [10:0] size_in_bits, bit [127:0] wstrb);// Surface Load, Dump & Release functionsextern function void load_surface(string filename, addr_t base);extern function void dump_surface(string filename, addr_t base, int unsigned len);extern function void init_surface_with_pattern(addr_t base, int unsigned len, string pattern);extern function void init_surface_with_pattern_and_file(addr_t base, string pattern, string filename);// Other APIsextern function bit mem_exists(addr_t addr);extern function bit has_addr(addr_t addr);extern function int unsigned calc_surface_crc(addr_t base, int len);// Private utility functionsextern protected function string m_trimed_string(string str);extern protected function int m_atov(string str);extern protected function void m_parse_surface_file(string filename, output surface_file_content content);endclass

mem_core中声明一个byte类型的联合数组,作为m_mem使用,用以存储数据。

init_option是为了读不存在的地址时,返回什么值的问题,同时由dont_store_uninitialized_vals变量决定将这个返回值,写到m_mem中对应地址。

base limit用来决定当前mem的可用基地址和大小

load_surface函数调用m_parse_surface_file函数,将mem surface格式的文件处理读取出有效的信息,例如offset地址,data信息。也就是往哪些地址写哪些数据,最后调用write8函数,将数据写到m_mem联合数组中。

dump_surface(string filename, addr_t base, int unsigned len)函数将base地址开始len个byte的数据,按照一定格式写到filename中。

init_surface_with_pattern(addr_t base, int unsigned len, string pattern)函数初始化base地址开始len个byte的m_mem数据,以pattern指定的格式(RANDOM, ALL_ZERO等)。

init_surface_with_pattern_and_file函数不给定len,有parse的file中信息,找到end_offset,然后调用init_surface_with_pattern函数完成初始化。

calc_surface_crc(addr_t base, int len)从base地址开始,读取len个byte的数据,计算crc值。

class mem_wrap extends uvm_component;typedef uvm_tlm_generic_payload gp_t;uvm_tlm_analysis_fifo#(memory_model_command) mmc_fifo;// result checker command fifouvm_tlm_analysis_fifo#(result_checker_command) rcc_fifo;// Memory read/write socketuvm_tlm_b_target_socket#(mem_wrap, gp_t) skt;// Store requested memory regionsmem_core mem_list[$];uvm_event_pool  global_event_pool;bit auto_dump_surface = 1;extern task b_transport(gp_t gp, uvm_tlm_time delay);extern protected task m_process_memory_model_command(memory_model_command tr);extern protected task m_process_result_checker_command(result_checker_command tr);extern local function void evaluate_memory_model_command(memory_model_command tr);extern local function string sprint_mem_list();extern local function mem_core locate_mem(addr_t addr);
endclasstask mem_wrap::run_phase(uvm_phase phase);super.run_phase(phase);`uvm_info(tID, $sformatf("run_phase begin ..."), UVM_HIGH)forkbeginmemory_model_command tr;forever beginmmc_fifo.get(tr);m_process_memory_model_command(tr);endendbeginresult_checker_command tr;forever beginrcc_fifo.get(tr);m_process_result_checker_command(tr);endendjoin`uvm_info(tID, $sformatf("run_phase complete ..."), UVM_HIGH)
endtask

skt是与dbb_slave_agent中driver中的mem_initiator连接,这样driver可以从mem_core读数据,然后发送给dut,或者从dut捕获数据,写到mem_core中的m_mem.

mem_list维护一个已经创建的mem_core的队列,以便重复访问时,可以直接调用。

在run_phase中并行循环调用m_process_memory_model_command和m_process_result_checker_command方法。

m_process_memory_model_command方法根据mem_cmd_item决定执行mem的哪种操作(MEM_RESERVE,MEM_LOAD,MEM_INIT_PATTERN,MEM_RELEASE),并调用mem_core中对应的函数完成m_mem的操作。

m_process_result_checker_command方法根据从result checker来的cmd_item的类型,来执行是CHECK_CRC还是CHECK_FILE操作。CHECK_CRC 调用mem_core的calc_surface_crc函数,计算得到的crc值和golden值进行比较,判断dla结果的正确与否。

总结:

本篇文章,主要还是讲解了trace_player tb的主要结构,并没有每个类每个函数都一一讲解,这有待大家主动学习,例如vip下的agent uvc都没有讲解,这块因为在dla集成的soc level的话,会有对应的vip来代替nvdla提供的uvc,所以并没有细看。至于trace_generator tb有空再更新吧。

这篇关于NVDLA uvm验证环境深度解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/287662

相关文章

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查

在Rust中要用Struct和Enum组织数据的原因解析

《在Rust中要用Struct和Enum组织数据的原因解析》在Rust中,Struct和Enum是组织数据的核心工具,Struct用于将相关字段封装为单一实体,便于管理和扩展,Enum用于明确定义所有... 目录为什么在Rust中要用Struct和Enum组织数据?一、使用struct组织数据:将相关字段绑

在Mysql环境下对数据进行增删改查的操作方法

《在Mysql环境下对数据进行增删改查的操作方法》本文介绍了在MySQL环境下对数据进行增删改查的基本操作,包括插入数据、修改数据、删除数据、数据查询(基本查询、连接查询、聚合函数查询、子查询)等,并... 目录一、插入数据:二、修改数据:三、删除数据:1、delete from 表名;2、truncate

使用Java实现一个解析CURL脚本小工具

《使用Java实现一个解析CURL脚本小工具》文章介绍了如何使用Java实现一个解析CURL脚本的工具,该工具可以将CURL脚本中的Header解析为KVMap结构,获取URL路径、请求类型,解析UR... 目录使用示例实现原理具体实现CurlParserUtilCurlEntityICurlHandler

深入解析Spring TransactionTemplate 高级用法(示例代码)

《深入解析SpringTransactionTemplate高级用法(示例代码)》TransactionTemplate是Spring框架中一个强大的工具,它允许开发者以编程方式控制事务,通过... 目录1. TransactionTemplate 的核心概念2. 核心接口和类3. TransactionT

数据库使用之union、union all、各种join的用法区别解析

《数据库使用之union、unionall、各种join的用法区别解析》:本文主要介绍SQL中的Union和UnionAll的区别,包括去重与否以及使用时的注意事项,还详细解释了Join关键字,... 目录一、Union 和Union All1、区别:2、注意点:3、具体举例二、Join关键字的区别&php

Spring IOC控制反转的实现解析

《SpringIOC控制反转的实现解析》:本文主要介绍SpringIOC控制反转的实现,IOC是Spring的核心思想之一,它通过将对象的创建、依赖注入和生命周期管理交给容器来实现解耦,使开发者... 目录1. IOC的基本概念1.1 什么是IOC1.2 IOC与DI的关系2. IOC的设计目标3. IOC