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有空再更新吧。