1.nvdla hw master github
- https://github.com/nvdla/hw/tree/master
- NVDLA Environment Setup Guide
- 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 SSSSS
or
*******************************
** 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。参考如下:
Name | Description | Syntax | Use case |
---|---|---|---|
reg_write | Write data to specific DUT register | reg_write(reg_n ame, reg_value); | Fundamental operation for register configuration |
reg_read_expect ed | Read data from specific DUT register, compare with expected value | reg_read_expect ed(addr, expected_data); | For some special cases like register accessing tests |
reg_read | Read data from specific DUT register | reg_read(reg_na me, return_value); | For specific cases which may need to do post-processing on read return value. |
sync_notify | Specified player sequencer will send out synchronization event | sync_notify(tar get_resource, sync_id); | CC pipeline, OP_EN configuration order, CACC->CMAC->CSC . |
sync_wait | Specified player sequencer will wait on synchronization event | sync_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:
| 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_ENABLE
uvm_analysis_port#(memory_model_command) secondary_memory_model_command_port;
`endif
变量声明部分代码
end_of_elaboration_phase中调用parse_trace函数,生成四种cmd file生成,然后分别读取其中内容,因为四种cmds每个字段的含义有些不同,请参考下图:
每一个cmds文件的处理大同小异,下面以sequence ctrl的处理为例:
//---------- sequence controller
begin
string 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) );
end
while (!$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 name
seq_cmd.field_name = field_name;
seq_cmd.data = data;
seq_cmd.sync_id = sync_id;
if ("NOTIFY" == kind_name) begin
global_event_pool.get(sync_id);
end
send_command_to_sequence(seq_cmd);
end
end
$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 level
parameter 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:128
parameter CACC_PW = (32*`NVDLA_SDP_MAX_THROUGHPUT+2); // small:34, large:514
endpackage: 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))) begin
while(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)
end
end
endtask : main_phase
task nvdla_tb_intr_handler::wait_intr(bit is_rm);
if(is_rm) begin
rm_intr_evt.wait_on();
end
else begin
dut_intr_evt.wait_on();
end
`uvm_info(tID, $sformatf("intr_evt is on"), UVM_MEDIUM)
endtask : wait_intr
task 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_phase
task nvdla_tb_result_checker::warden_process();
uint32_t cmd_idx;
for (cmd_idx = 0; cmd_idx < command_number; cmd_idx ++) begin
automatic uint32_t var_i = cmd_idx;
fork
begin
uvm_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))
end
evt = global_event_pool.get(cmd_item.sync_id);
if (evt.is_off()) begin
evt.wait_on();
end
if (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) begin
primary_memory_check_command_port.write(cmd_item);
end else begin
secondary_memory_check_command_port.write(cmd_item);
end
end
end
join_none
end
wait fork;
// Simulation complete notification
sim_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) begin
evt = new(item.sync_id);
glb_evts.add(item.sync_id, evt);
end
blk = item.block_name;
blk_cmd[blk].push_back(item);
end
endfunction : cmd_distribute
task 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]) begin
automatic string j = i;
fork
begin
sequence_command cmd;
while(blk_cmd[j].size() != 0) begin
cmd = 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)
end
join_none
end
wait fork;
`uvm_info(tID, $sformatf("main_phase complete ..."), UVM_MEDIUM)
phase.drop_objection(this);
endtask : main_phase
task 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_issue
task 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))
end
regs = blks.get_reg_by_name(reg_name.toupper);
if(regs == null) begin
`uvm_fatal(tID, $sformatf("No exists uvm_reg: %s", reg_name))
end
regs.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 functions
extern function bit [1023:0] read(addr_t addr, bit [10:0] size_in_bits);
// Write functions
extern function void write(addr_t addr, bit [1023:0] data, bit [10:0] size_in_bits, bit [127:0] wstrb);
// Surface Load, Dump & Release functions
extern 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 APIs
extern 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 functions
extern 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 fifo
uvm_tlm_analysis_fifo#(result_checker_command) rcc_fifo;
// Memory read/write socket
uvm_tlm_b_target_socket#(mem_wrap, gp_t) skt;
// Store requested memory regions
mem_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);
endclass
task mem_wrap::run_phase(uvm_phase phase);
super.run_phase(phase);
`uvm_info(tID, $sformatf("run_phase begin ..."), UVM_HIGH)
fork
begin
memory_model_command tr;
forever begin
mmc_fifo.get(tr);
m_process_memory_model_command(tr);
end
end
begin
result_checker_command tr;
forever begin
rcc_fifo.get(tr);
m_process_result_checker_command(tr);
end
end
join
`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有空再更新吧。