#if VM_TRACE Verilated::traceEverOn(true); // Verilator must compute traced signals VL_PRINTF("Enabling waves...\n"); tfp = new VerilatedVcdC; dut_ptr->trace(tfp, 99); // Trace 99 levels of hierarchy tfp->open("vlt_dump.vcd"); // Open the dump file #endif
while (!is_finish() && n > 0) { // 主执行循环 single_cycle(); n --;
if (lastcommit - n > stuck_limit && hascommit) { // 如果太久都没有指令提交,那说明有可能卡住了,强制停止 eprintf("No instruction commits for %d cycles, maybe get stuck\n" "(please also check whether a fence.i instruction requires more than %d cycles to flush the icache)\n", stuck_limit, stuck_limit); #if VM_TRACE tfp->close(); #endif set_abort(); }
externintdifftest_step(rtlreg_t *reg_scala, uint32_t this_inst, int isMMIO, int isRVC, int isRVC2, uint64_t intrNO, int priviledgeMode, int isMultiCommit); // 这里有一个参数,如果是双提交的话,需要检测两次 if (dut_ptr->io_difftestCtrl_enable) { if (difftest_step(reg, dut_ptr->io_difftest_thisINST, // <----------- PAY ATTENTION TO THIS FUNCTION !!! dut_ptr->io_difftest_isMMIO, dut_ptr->io_difftest_isRVC, dut_ptr->io_difftest_isRVC2, dut_ptr->io_difftest_intrNO, dut_ptr->io_difftest_priviledgeMode, dut_ptr->io_difftest_isMultiCommit)) { #if VM_TRACE tfp->close(); #endif set_abort(); } } lastcommit = n; }
uint32_t t = uptime(); if (t - lasttime > 100) { poll_event(); lasttime = t; } } }
intdifftest_step(rtlreg_t *reg_scala, uint32_t this_inst, int isMMIO, int isRVC, int isRVC2, uint64_t intrNO, int priviledgeMode, int isMultiCommit ){
// Note: // reg_scala[DIFFTEST_THIS_PC] is the first PC commited by CPU-WB // ref_r[DIFFTEST_THIS_PC] is NEMU's next PC // To skip the compare of an instruction, replace NEMU reg value with CPU's regfile value, // then set NEMU's PC to next PC to be run
#define DEBUG_RETIRE_TRACE_SIZE 16
rtlreg_t ref_r[DIFFTEST_NR_REG]; rtlreg_t this_pc = reg_scala[DIFFTEST_THIS_PC]; // ref_difftest_getregs() will get the next pc, // therefore we must keep track this one // 需要注意的是:NEMU的PC是NEMU将要执行的下一条指令的PC // ref_difftest_getregs() 拿到的PC是NEMU计算出的下一个PC // 为了避免对比出错,我们需要用当前CPU执行的PC去覆盖读出的PC // 为了避免丢失原有的PC,我们需要将NEMU拿出来的下一个PC进行保存,保存到nemu_this_pc中 staticrtlreg_t nemu_this_pc = 0x80000000; // 这个循环队列,是用于记录之前的轨迹用的 staticrtlreg_t pc_retire_queue[DEBUG_RETIRE_TRACE_SIZE] = {0}; staticuint32_t inst_retire_queue[DEBUG_RETIRE_TRACE_SIZE] = {0}; staticuint32_t multi_commit_queue[DEBUG_RETIRE_TRACE_SIZE] = {0}; staticuint32_t skip_queue[DEBUG_RETIRE_TRACE_SIZE] = {0}; staticint pc_retire_pointer = 7; staticint need_copy_pc = 0; #ifdef NO_DIFFTEST return0; #endif // Copy PC是什么机制? // 这个代码块的作用,猜测是仅在双提交+MMIO的情况下有用 // 通过在代码中添加assert断言,确定其确实是在双提交的时候才有用 // 在双提交,且为MMIO+跳转的模式下,下一个PC不应当是PC+8,而应当是跳转的目标 // 我们假设DUT计算的跳转的目标永远是正确的,直接将其拷贝给NEMU if (need_copy_pc) { need_copy_pc = 0; ref_difftest_getregs(&ref_r); nemu_this_pc = reg_scala[DIFFTEST_THIS_PC]; ref_r[DIFFTEST_THIS_PC] = reg_scala[DIFFTEST_THIS_PC]; ref_difftest_setregs(ref_r); } if (isMMIO) { // 对于MMIO,直接不对比,NEMU中的MMIO关系我们暂时无法干涉,所以直接跳过去,并且将执行完毕MMIO指令的CPU状态,拷贝到NEMU里面 // printf("diff pc: %x isRVC %x\n", this_pc, isRVC); // MMIO accessing should not be a branch or jump, just +2/+4 to get the next pc int pc_skip = 0; // 我们为什么很肯定地能认为NEMU的下一个PC是PC+4呢? // 在单提交的情况下,MMIO相关的不可能是跳转指令,所以PC+4一定是下一个PC // 所以直接将当前DUT的状态复制给NEMU,并且将NEMU的PC+4 // 双提交的情况下,一定会是PC+8吗?这里需要多加考虑。 pc_skip += isRVC ? 2 : 4; pc_skip += isMultiCommit ? (isRVC2 ? 2 : 4) : 0; reg_scala[DIFFTEST_THIS_PC] += pc_skip; nemu_this_pc += pc_skip; // to skip the checking of an instruction, just copy the reg state to reference design ref_difftest_setregs(reg_scala); // 把状态拷贝到里面去 // 设置相关的队列 pc_retire_pointer = (pc_retire_pointer+1) % DEBUG_RETIRE_TRACE_SIZE; pc_retire_queue[pc_retire_pointer] = this_pc; inst_retire_queue[pc_retire_pointer] = this_inst; multi_commit_queue[pc_retire_pointer] = isMultiCommit; skip_queue[pc_retire_pointer] = isMMIO; // 标记下一次需要覆盖PC(仅针对MMIO+跳转的双提交情形) need_copy_pc = 1; return0; // NEMU并不执行,直接return }
if (isMultiCommit) { ref_difftest_exec(1); // 如果是多提交,需要再运行一步 }
ref_difftest_getregs(&ref_r);
rtlreg_t next_pc = ref_r[32]; pc_retire_pointer = (pc_retire_pointer+1) % DEBUG_RETIRE_TRACE_SIZE; pc_retire_queue[pc_retire_pointer] = this_pc; inst_retire_queue[pc_retire_pointer] = this_inst; multi_commit_queue[pc_retire_pointer] = isMultiCommit; skip_queue[pc_retire_pointer] = isMMIO; int isCSR = ((this_inst & 0x7f) == 0x73); int isCSRMip = ((this_inst >> 20) == 0x344) && isCSR; if (isCSRMip) { // We can not handle NEMU.mip.mtip since it is driven by CLINT, // which is not accessed in NEMU due to MMIO. // Just sync the state of NEMU from NutCore. reg_scala[DIFFTEST_THIS_PC] = next_pc; nemu_this_pc = next_pc; ref_difftest_setregs(reg_scala); return0; }
// replace with "this pc" for checking // 同步NEMU的PC ref_r[DIFFTEST_THIS_PC] = nemu_this_pc; // 保存下一个PC nemu_this_pc = next_pc;
ref_r[0] = 0; // 检查上面获得的结果,如果有误,打印出最近DEBUG_RETIRE_TRACE_SIZE条 if (memcmp(reg_scala, ref_r, sizeof(ref_r)) != 0) { printf("\n==============Retire Trace==============\n"); int j; for(j = 0; j < DEBUG_RETIRE_TRACE_SIZE; j++){ printf("retire trace [%x]: pc %010lx inst %08x %s %s %s\n", j, pc_retire_queue[j], inst_retire_queue[j], (multi_commit_queue[j])?"MC":" ", (skip_queue[j])?"SKIP":" ", (j==pc_retire_pointer)?"<--":"" ); } printf("\n============== Reg Diff ==============\n"); ref_isa_reg_display(); printf("priviledgeMode = %d\n", priviledgeMode); puts(""); int i; for (i = 0; i < DIFFTEST_NR_REG; i ++) { if (reg_scala[i] != ref_r[i]) { printf("%s different at pc = 0x%010lx, right= 0x%016lx, wrong = 0x%016lx\n", reg_name[i], this_pc, ref_r[i], reg_scala[i]); } } return1; } return0; }
classUARTGetcextendsBlackBoxwithHasBlackBoxInline{ val io = IO(newBundle { val clk = Input(Clock()) val getc = Input(Bool()) val ch = Output(UInt(8.W)) })
classAXI4UARTextendsAXI4SlaveModule(new AXI4Lite) { val rxfifo = RegInit(0.U(32.W)) val txfifo = Reg(UInt(32.W)) val stat = RegInit(1.U(32.W)) val ctrl = RegInit(0.U(32.W))