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

相关文章

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

Redis在windows环境下如何启动

《Redis在windows环境下如何启动》:本文主要介绍Redis在windows环境下如何启动的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Redis在Windows环境下启动1.在redis的安装目录下2.输入·redis-server.exe

Pytest多环境切换的常见方法介绍

《Pytest多环境切换的常见方法介绍》Pytest作为自动化测试的主力框架,如何实现本地、测试、预发、生产环境的灵活切换,本文总结了通过pytest框架实现自由环境切换的几种方法,大家可以根据需要进... 目录1.pytest-base-url2.hooks函数3.yml和fixture结论你是否也遇到过

浅谈配置MMCV环境,解决报错,版本不匹配问题

《浅谈配置MMCV环境,解决报错,版本不匹配问题》:本文主要介绍浅谈配置MMCV环境,解决报错,版本不匹配问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录配置MMCV环境,解决报错,版本不匹配错误示例正确示例总结配置MMCV环境,解决报错,版本不匹配在col

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

关于WebSocket协议状态码解析

《关于WebSocket协议状态码解析》:本文主要介绍关于WebSocket协议状态码的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录WebSocket协议状态码解析1. 引言2. WebSocket协议状态码概述3. WebSocket协议状态码详解3

CSS Padding 和 Margin 区别全解析

《CSSPadding和Margin区别全解析》CSS中的padding和margin是两个非常基础且重要的属性,它们用于控制元素周围的空白区域,本文将详细介绍padding和... 目录css Padding 和 Margin 全解析1. Padding: 内边距2. Margin: 外边距3. Padd

Oracle数据库常见字段类型大全以及超详细解析

《Oracle数据库常见字段类型大全以及超详细解析》在Oracle数据库中查询特定表的字段个数通常需要使用SQL语句来完成,:本文主要介绍Oracle数据库常见字段类型大全以及超详细解析,文中通过... 目录前言一、字符类型(Character)1、CHAR:定长字符数据类型2、VARCHAR2:变长字符数

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@