diff --git a/wally-pipelined/regression/regression-wally.py b/wally-pipelined/regression/regression-wally.py index eac221cd..fcd6d4be 100755 --- a/wally-pipelined/regression/regression-wally.py +++ b/wally-pipelined/regression/regression-wally.py @@ -31,7 +31,7 @@ configs = [ TestCase( name="buildroot", cmd="vsim -do wally-buildroot-batch.do -c > {}", - grepstr="# loaded 2000000 instructions" + grepstr="# loaded 2500000 instructions" ), TestCase( name="rv32ic", diff --git a/wally-pipelined/regression/wally-buildroot-batch.do b/wally-pipelined/regression/wally-buildroot-batch.do index c16655e1..6eea258e 100644 --- a/wally-pipelined/regression/wally-buildroot-batch.do +++ b/wally-pipelined/regression/wally-buildroot-batch.do @@ -36,5 +36,4 @@ vsim workopt -suppress 8852,12070 run -all run -all -exec ./slack-notifier/slack-notifier.py quit diff --git a/wally-pipelined/regression/wally-buildroot.do b/wally-pipelined/regression/wally-buildroot.do index 452ba54d..c2312f75 100644 --- a/wally-pipelined/regression/wally-buildroot.do +++ b/wally-pipelined/regression/wally-buildroot.do @@ -39,5 +39,4 @@ vsim workopt -suppress 8852,12070 run -all do ./wave-dos/linux-waves.do run -all -exec ./slack-notifier/slack-notifier.py ##quit diff --git a/wally-pipelined/regression/wave-dos/linux-waves.do b/wally-pipelined/regression/wave-dos/linux-waves.do index b7dfd8c5..b3727644 100644 --- a/wally-pipelined/regression/wave-dos/linux-waves.do +++ b/wally-pipelined/regression/wave-dos/linux-waves.do @@ -19,12 +19,13 @@ add wave /testbench/dut/hart/FlushW add wave -divider F add wave -hex /testbench/dut/hart/ifu/PCF add wave -divider D -add wave -hex /testbench/pcExpected +add wave -hex /testbench/PCDexpected add wave -hex /testbench/dut/hart/ifu/PCD add wave -hex /testbench/PCtextD add wave /testbench/InstrDName add wave -hex /testbench/dut/hart/ifu/InstrD add wave -hex /testbench/dut/hart/ieu/c/InstrValidD +add wave -hex /testbench/PCDwrong add wave -divider E add wave -hex /testbench/dut/hart/ifu/PCE add wave -hex /testbench/PCtextE diff --git a/wally-pipelined/testbench/testbench-linux.sv b/wally-pipelined/testbench/testbench-linux.sv index df8fad8c..15e0e363 100644 --- a/wally-pipelined/testbench/testbench-linux.sv +++ b/wally-pipelined/testbench/testbench-linux.sv @@ -26,13 +26,15 @@ `include "wally-config.vh" module testbench(); - logic clk, reset; - logic [31:0] GPIOPinsIn; - logic [31:0] GPIOPinsOut, GPIOPinsEn; - - // instantiate device to be tested - logic [31:0] CheckInstrD; + + parameter waveOnICount = 2514000; // # of instructions at which to turn on waves in graphical sim + + /////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////// DUT ///////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + logic clk, reset; + logic [`AHBW-1:0] HRDATA; logic [31:0] HADDR; logic [`AHBW-1:0] HWDATA; @@ -45,155 +47,97 @@ module testbench(); logic HCLK, HRESETn; logic [`AHBW-1:0] HRDATAEXT; logic HREADYEXT, HRESPEXT; - logic UARTSout; - - logic ignoreRFwrite; - - parameter waveOnICount = 2060000; // # of instructions at which to turn on waves in graphical sim + logic [31:0] GPIOPinsIn; + logic [31:0] GPIOPinsOut, GPIOPinsEn; + logic UARTSin, UARTSout; assign GPIOPinsIn = 0; assign UARTSin = 1; - // instantiate processor and memories wallypipelinedsoc dut(.*); - /** - * Walk the page table stored in dtim according to sv39 logic and translate a - * virtual address to a physical address. - * - * See section 4.3.2 of the RISC-V Privileged specification for a full - * explanation of the below algorithm. - */ - function logic [`XLEN-1:0] adrTranslator( - input logic [`XLEN-1:0] adrIn); - begin - logic SvMode, PTE_R, PTE_X; - logic [`XLEN-1:0] SATP, PTE; - logic [55:0] BaseAdr, PAdr; - logic [8:0] VPN [2:0]; - logic [11:0] Offset; + /////////////////////////////////////////////////////////////////////////////// + //////////////////////// Signals & Shared Macros ////////////////////////// + //////////////////////// AKA stuff that comes first /////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // Sorry if these have gotten decontextualized. + // Verilog expects them to be defined before they are used. - int i; + // ------------------- + // Signal Declarations + // ------------------- + // Testbench Core + integer instrs; + integer warningCount = 0; + string trashString; // should never be read from + logic [31:0] InstrMask; + logic forcedInstr; + logic [63:0] lastPCD; + logic PCDwrong; + // PC, Instr Checking + logic [`XLEN-1:0] PCW; + logic [63:0] lastInstrDExpected, lastPC, lastPC2; + integer data_file_PCF, scan_file_PCF; + integer data_file_PCD, scan_file_PCD; + integer data_file_PCM, scan_file_PCM; + integer data_file_PCW, scan_file_PCW; + string PCtextF, PCtextF2; + string PCtextD, PCtextD2; + string PCtextE; + string PCtextM; + string PCtextW; + logic [31:0] InstrFExpected, InstrDExpected, InstrMExpected, InstrWExpected; + logic [63:0] PCFexpected, PCDexpected, PCMexpected, PCWexpected; + // RegFile Write Checking + logic ignoreRFwrite; + logic [63:0] regExpected; + integer regNumExpected; + integer data_file_rf, scan_file_rf; + // Bus Unit Read/Write Checking + logic [63:0] readMask; + logic [`XLEN-1:0] readAdrExpected, readAdrTranslated; + logic [`XLEN-1:0] writeDataExpected, writeAdrExpected, writeAdrTranslated; + integer data_file_memR, scan_file_memR; + integer data_file_memW, scan_file_memW; + // CSR Checking + integer totalCSR = 0; + logic [99:0] StartCSRexpected[63:0]; + string StartCSRname[99:0]; + integer data_file_csr, scan_file_csr; + + // ----------- + // Error Macro + // ----------- + `define ERROR \ + #10; \ + $display("processed %0d instructions with %0d warnings", instrs, warningCount); \ + $stop; - // Grab the SATP register from privileged unit - SATP = dut.hart.priv.csr.SATP_REGW; + // ---------------- + // PC Updater Macro + // ---------------- + `define SCAN_PC(DATAFILE,SCANFILE,PCTEXT,PCTEXT2,CHECKINSTR,PCEXPECTED) \ + SCANFILE = $fscanf(DATAFILE, "%s\n", PCTEXT); \ + PCTEXT2 = ""; \ + while (PCTEXT2 != "***") begin \ + PCTEXT = {PCTEXT, " ", PCTEXT2}; \ + SCANFILE = $fscanf(DATAFILE, "%s\n", PCTEXT2); \ + end \ + SCANFILE = $fscanf(DATAFILE, "%x\n", CHECKINSTR); \ + SCANFILE = $fscanf(DATAFILE, "%x\n", PCEXPECTED); - // Split the virtual address into page number segments and offset - VPN[2] = adrIn[38:30]; - VPN[1] = adrIn[29:21]; - VPN[0] = adrIn[20:12]; - Offset = adrIn[11:0]; - - // We do not support sv48; only sv39 - SvMode = SATP[63]; - - // Only perform translation if translation is on and the processor is not - // in machine mode - if (SvMode && (dut.hart.priv.PrivilegeModeW != `M_MODE)) begin - BaseAdr = SATP[43:0] << 12; - - for (i = 2; i >= 0; i--) begin - PAdr = BaseAdr + (VPN[i] << 3); - - // dtim.RAM is 64-bit addressed. PAdr specifies a byte. We right shift - // by 3 (the PTE size) to get the requested 64-bit PTE. - PTE = dut.uncore.dtim.RAM[PAdr >> 3]; - PTE_R = PTE[1]; - PTE_X = PTE[3]; - if (PTE_R || PTE_X) begin - // Leaf page found - break; - end else begin - // Go to next level of table - BaseAdr = PTE[53:10] << 12; - end - end - - // Determine which parts of the PTE page number to use based on the - // level of the page table we reached. - if (i == 2) begin - // Gigapage - assign adrTranslator = {8'b0, PTE[53:28], VPN[1], VPN[0], Offset}; - end else if (i == 1) begin - // Megapage - assign adrTranslator = {8'b0, PTE[53:19], VPN[0], Offset}; - end else begin - // Kilopage - assign adrTranslator = {8'b0, PTE[53:10], Offset}; - end - end else begin - // Direct translation if address translation is not on - assign adrTranslator = adrIn; - end - end - endfunction - - // initialize test + /////////////////////////////////////////////////////////////////////////////// + //////////////////////////////// Testbench Core /////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // -------------- + // Initialization + // -------------- initial begin - ignoreRFwrite <= 0; + instrs = 0; + PCDwrong = 0; reset <= 1; # 22; reset <= 0; end - - // read pc trace file - integer data_file_PC, scan_file_PC; - initial begin - data_file_PC = $fopen({`LINUX_TEST_VECTORS,"parsedPC.txt"}, "r"); - if (data_file_PC == 0) begin - $display("file couldn't be opened"); - $stop; - end - end - - integer data_file_PCW, scan_file_PCW; - initial begin - data_file_PCW = $fopen({`LINUX_TEST_VECTORS,"parsedPC.txt"}, "r"); - if (data_file_PCW == 0) begin - $display("file couldn't be opened"); - $stop; - end - end - - // read register trace file - integer data_file_rf, scan_file_rf; - initial begin - data_file_rf = $fopen({`LINUX_TEST_VECTORS,"parsedRegs.txt"}, "r"); - if (data_file_rf == 0) begin - $display("file couldn't be opened"); - $stop; - end - end - - // read CSR trace file - integer data_file_csr, scan_file_csr; - initial begin - data_file_csr = $fopen({`LINUX_TEST_VECTORS,"parsedCSRs.txt"}, "r"); - if (data_file_csr == 0) begin - $display("file couldn't be opened"); - $stop; - end - end - - // read memreads trace file - integer data_file_memR, scan_file_memR; - initial begin - data_file_memR = $fopen({`LINUX_TEST_VECTORS,"parsedMemRead.txt"}, "r"); - if (data_file_memR == 0) begin - $display("file couldn't be opened"); - $stop; - end - end - - // read memwrite trace file - integer data_file_memW, scan_file_memW; - initial begin - data_file_memW = $fopen({`LINUX_TEST_VECTORS,"parsedMemWrite.txt"}, "r"); - if (data_file_memW == 0) begin - $display("file couldn't be opened"); - $stop; - end - end - // initial loading of memories initial begin $readmemh({`LINUX_TEST_VECTORS,"bootmem.txt"}, dut.uncore.bootdtim.RAM, 'h1000 >> 3); @@ -201,49 +145,247 @@ module testbench(); $readmemb(`TWO_BIT_PRELOAD, dut.hart.ifu.bpred.bpred.Predictor.DirPredictor.PHT.memory); $readmemb(`BTB_PRELOAD, dut.hart.ifu.bpred.bpred.TargetPredictor.memory.memory); end - - integer warningCount = 0; - integer instrs; - - //logic[63:0] adrTranslation[4:0]; - //string translationType[4:0] = {"rf", "writeAdr", "PCW", "PC", "readAdr"}; - //initial begin - // for(int i=0; i<5; i++) begin - // adrTranslation[i] = 64'b0; - // end - //end - - //function logic equal(logic[63:0] adr, logic[63:0] adrExpected, integer func); - // if (adr[11:0] !== adrExpected[11:0]) begin - // equal = 1'b0; - // end else begin - // equal = 1'b1; - // if ((adr+adrTranslation[func]) !== adrExpected) begin - // adrTranslation[func] = adrExpected - adr; - // $display("warning: probably new address translation %x for %s at instr %0d", adrTranslation[func], translationType[func], instrs); - // warningCount += 1; - // end - // end - //endfunction - - // pretty sure this isn't necessary anymore, but keeping this for now since its easier - function logic equal(logic[63:0] adr, logic[63:0] adrExpected, integer func); - equal = adr === adrExpected; - endfunction - - - `define ERROR \ - #10; \ - $display("processed %0d instructions with %0d warnings", instrs, warningCount); \ - $stop; - - logic [63:0] pcExpected; - logic [63:0] regExpected; - integer regNumExpected; - logic [`XLEN-1:0] PCW; + // ------- + // Running + // ------- + always + begin + clk <= 1; # 5; clk <= 0; # 5; + end + + // ------------------------------------- + // Special warnings for important faults + // ------------------------------------- + always @(dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW) begin + if (dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW == 2 && instrs > 1) begin + $display("!!!!!! illegal instruction !!!!!!!!!!"); + $display("(as a reminder, MCAUSE and MEPC are set by this)"); + $display("at %0t ps, PCM %x, instr %0d, HADDR %x", $time, dut.hart.ifu.PCM, instrs, HADDR); + `ERROR + end + if (dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW == 5 && instrs != 0) begin + $display("!!!!!! illegal (physical) memory access !!!!!!!!!!"); + $display("(as a reminder, MCAUSE and MEPC are set by this)"); + $display("at %0t ps, PCM %x, instr %0d, HADDR %x", $time, dut.hart.ifu.PCM, instrs, HADDR); + `ERROR + end + end + + // ----------------------- + // RegFile Write Hijacking + // ----------------------- + always @(PCW or dut.hart.ieu.InstrValidW) begin + if(dut.hart.ieu.InstrValidW && PCW != 0) begin + // Hack to compensate for how Wally's MTIME may diverge from QEMU's MTIME (and that is okay) + if (PCtextW.substr(0,5) == "rdtime") begin + ignoreRFwrite <= 1; + scan_file_rf = $fscanf(data_file_rf, "%d\n", regNumExpected); + scan_file_rf = $fscanf(data_file_rf, "%x\n", regExpected); + force dut.hart.ieu.dp.regf.wd3 = regExpected; + // Hack to compensate for QEMU's incorrect MSTATUS + end else if (PCtextW.substr(0,3) == "csrr" && PCtextW.substr(10,16) == "mstatus") begin + force dut.hart.ieu.dp.regf.wd3 = dut.hart.ieu.dp.WriteDataW & ~64'ha00000000; + end else + release dut.hart.ieu.dp.regf.wd3; + end + end + + // ---------------- + // Big Chunky Block + // ---------------- + always @(reset or dut.hart.ifu.InstrRawD or dut.hart.ifu.PCD) begin// or negedge dut.hart.ifu.StallE) begin // Why do we care about StallE? Everything seems to run fine without it. + if(~HWRITE) begin // *** Should this need to consider HWRITE? + #2; + // If PCD/InstrD aren't garbage + if (~reset && dut.hart.ifu.InstrRawD[15:0] !== {16{1'bx}} && dut.hart.ifu.PCD !== 64'h0) begin // && ~dut.hart.ifu.StallE) begin + // If Wally's PCD has updated + if (dut.hart.ifu.PCD !== lastPCD) begin + lastInstrDExpected = InstrDExpected; + lastPC <= dut.hart.ifu.PCD; + lastPC2 <= lastPC; + // If PCD isn't going to be flushed + if (~PCDwrong || lastPC == PCDexpected) begin + + // Stop if we've reached the end + if($feof(data_file_PCF)) begin + $display("no more PC data to read... CONGRATULATIONS!!!"); + `ERROR + end + + // Increment PC + `SCAN_PC(data_file_PCF, scan_file_PCF, PCtextF, PCtextF2, InstrFExpected, PCFexpected); + `SCAN_PC(data_file_PCD, scan_file_PCD, PCtextD, PCtextD2, InstrDExpected, PCDexpected); + + // NOP out certain instructions + if(dut.hart.ifu.PCD===PCDexpected) begin + if((dut.hart.ifu.PCD == 32'h80001dc6) || // for now, NOP out any stores to PLIC + (dut.hart.ifu.PCD == 32'h80001de0) || + (dut.hart.ifu.PCD == 32'h80001de2)) begin + $display("warning: NOPing out %s at PCD=%0x, instr %0d, time %0t", PCtextD, dut.hart.ifu.PCD, instrs, $time); + force InstrDExpected = 32'b0010011; + force dut.hart.ifu.InstrRawD = 32'b0010011; + while (clk != 0) #1; + while (clk != 1) #1; + release dut.hart.ifu.InstrRawD; + release InstrDExpected; + warningCount += 1; + forcedInstr = 1; + end else begin + forcedInstr = 0; + end + end + + // Increment instruction count + if (instrs <= 10 || (instrs <= 100 && instrs % 10 == 0) || + (instrs <= 1000 && instrs % 100 == 0) || (instrs <= 10000 && instrs % 1000 == 0) || + (instrs <= 100000 && instrs % 10000 == 0) || (instrs % 100000 == 0)) begin + $display("loaded %0d instructions", instrs); + end + instrs += 1; + + // Stop before bugs so "do" file can turn on waves + if (instrs == waveOnICount) begin + $display("turning on waves at %0d instructions", instrs); + $stop; + end + + // Check if PCD is going to be flushed due to a branch or jump + if (`BPRED_ENABLED) begin + PCDwrong = dut.hart.ifu.bpred.bpred.BPPredWrongE; + end else begin + casex (lastInstrDExpected[31:0]) + 32'b00000000001000000000000001110011, // URET + 32'b00010000001000000000000001110011, // SRET + 32'b00110000001000000000000001110011, // MRET + 32'bXXXXXXXXXXXXXXXXXXXXXXXXX1101111, // JAL + 32'bXXXXXXXXXXXXXXXXXXXXXXXXX1100111, // JALR + 32'bXXXXXXXXXXXXXXXXXXXXXXXXX1100011, // B + 32'bXXXXXXXXXXXXXXXX110XXXXXXXXXXX01, // C.BEQZ + 32'bXXXXXXXXXXXXXXXX111XXXXXXXXXXX01, // C.BNEZ + 32'bXXXXXXXXXXXXXXXX101XXXXXXXXXXX01: // C.J + PCDwrong = 1; + 32'bXXXXXXXXXXXXXXXX1001000000000010, // C.EBREAK: + 32'bXXXXXXXXXXXXXXXXX000XXXXX1110011: // Something that's not CSRR* + PCDwrong = 0; // tbh don't really know what should happen here + 32'b000110000000XXXXXXXXXXXXX1110011, // CSR* SATP, * + 32'bXXXXXXXXXXXXXXXX1000XXXXX0000010, // C.JR + 32'bXXXXXXXXXXXXXXXX1001XXXXX0000010: // C.JALR //this is RV64 only so no C.JAL + PCDwrong = 1; + default: + PCDwrong = 0; + endcase + end + + // Check PCD, InstrD + if (~PCDwrong && ~(dut.hart.ifu.PCD === PCDexpected)) begin + $display("%0t ps, instr %0d: PC does not equal PC expected: %x, %x", $time, instrs, dut.hart.ifu.PCD, PCDexpected); + `ERROR + end + InstrMask = InstrDExpected[1:0] == 2'b11 ? 32'hFFFFFFFF : 32'h0000FFFF; + if ((~forcedInstr) && (~PCDwrong) && ((InstrMask & dut.hart.ifu.InstrRawD) !== (InstrMask & InstrDExpected))) begin + $display("%0t ps, PCD %x, instr %0d: InstrD %x %s does not equal InstrDExpected %x %s", $time, dut.hart.ifu.PCD, instrs, dut.hart.ifu.InstrRawD, InstrDName, InstrDExpected, PCtextD); + `ERROR + end + + // Repeated instruction means QEMU had an interrupt which we need to spoof + if (PCFexpected == PCDexpected) begin + $display("Note at %0t ps, PCM %x %s, instr %0d: spoofing an interrupt", $time, dut.hart.ifu.PCM, PCtextM, instrs); + // Increment file pointers past the repeated instruction. + `SCAN_PC(data_file_PCF, scan_file_PCF, PCtextF, PCtextF2, InstrFExpected, PCFexpected); + `SCAN_PC(data_file_PCD, scan_file_PCD, PCtextD, PCtextD2, InstrDExpected, PCDexpected); + scan_file_memR = $fscanf(data_file_memR, "%x\n", readAdrExpected); + scan_file_memR = $fscanf(data_file_memR, "%x\n", HRDATA); + // Next force a timer interrupt (*** this may later need generalizing) + force dut.uncore.genblk1.clint.MTIME = dut.uncore.genblk1.clint.MTIMECMP + 1; + while (clk != 0) #1; + while (clk != 1) #1; + release dut.uncore.genblk1.clint.MTIME; + end + end + end + lastPCD = dut.hart.ifu.PCD; + end + end + end + + /////////////////////////////////////////////////////////////////////////////// + ///////////////////////////// PC,Instr Checking /////////////////////////////// + /////////////////////// (outside of Big Chunky Block) ///////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // -------------- + // Initialization + // -------------- + initial begin + data_file_PCF = $fopen({`LINUX_TEST_VECTORS,"parsedPC.txt"}, "r"); + data_file_PCD = $fopen({`LINUX_TEST_VECTORS,"parsedPC.txt"}, "r"); + data_file_PCM = $fopen({`LINUX_TEST_VECTORS,"parsedPC.txt"}, "r"); + data_file_PCW = $fopen({`LINUX_TEST_VECTORS,"parsedPC.txt"}, "r"); + if (data_file_PCW == 0) begin + $display("file couldn't be opened"); + $stop; + end + // This makes sure PCF is one instr ahead of PCD + `SCAN_PC(data_file_PCF, scan_file_PCF, PCtextF, PCtextF2, InstrFExpected, PCFexpected); + // This makes sure PCM is one instr ahead of PCW + `SCAN_PC(data_file_PCM, scan_file_PCM, trashString, trashString, InstrMExpected, PCMexpected); + end + + // ------------------- + // Additional Hardware + // ------------------- flopenr #(`XLEN) PCWReg(clk, reset, ~dut.hart.ieu.dp.StallW, dut.hart.ifu.PCM, PCW); + // PCF stuff isn't actually checked + // it only exists for helping detecting duplicate instructions in PCD + // which are the result of interrupts hitting QEMU + // PCD checking already happens in "Big Chunky Block" + // PCM stuff isn't actually checked + // it only exists for helping detecting duplicate instructions in PCW + // which are the result of interrupts hitting QEMU + // ------------ + // PCW Checking + // ------------ + always @(PCW or dut.hart.ieu.InstrValidW) begin + if(dut.hart.ieu.InstrValidW && PCW != 0) begin + if($feof(data_file_PCW)) begin + $display("no more PC data to read"); + `ERROR + end + `SCAN_PC(data_file_PCM, scan_file_PCM, trashString, trashString, InstrMExpected, PCMexpected); + `SCAN_PC(data_file_PCW, scan_file_PCW, trashString, trashString, InstrWExpected, PCWexpected); + // If repeated instr + if (PCMexpected == PCWexpected) begin + // Increment file pointers past the repeated instruction. + `SCAN_PC(data_file_PCM, scan_file_PCM, trashString, trashString, InstrMExpected, PCMexpected); + `SCAN_PC(data_file_PCW, scan_file_PCW, trashString, trashString, InstrWExpected, PCWexpected); + end + if(~(PCW === PCWexpected)) begin + $display("%0t ps, instr %0d: PCW does not equal PCW expected: %x, %x", $time, instrs, PCW, PCWexpected); + `ERROR + end + end + end + + + /////////////////////////////////////////////////////////////////////////////// + /////////////////////////// RegFile Write Checking //////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // -------------- + // Initialization + // -------------- + initial begin + data_file_rf = $fopen({`LINUX_TEST_VECTORS,"parsedRegs.txt"}, "r"); + if (data_file_rf == 0) begin + $display("file couldn't be opened"); + $stop; + end + end + initial + ignoreRFwrite <= 0; + // -------- + // Checking + // -------- genvar i; generate for(i=1; i<32; i++) begin @@ -251,33 +393,32 @@ module testbench(); if ($time == 0) begin scan_file_rf = $fscanf(data_file_rf, "%x\n", regExpected); if (dut.hart.ieu.dp.regf.rf[i] != regExpected) begin - $display("%0t ps, instr %0d: rf[%0d] does not equal rf expected: %x, %x", $time, instrs, i, dut.hart.ieu.dp.regf.rf[i], regExpected); + $display("%0t ps, PCW %x, instr %0d: rf[%0d] does not equal rf expected: %x, %x", $time, PCW, instrs, i, dut.hart.ieu.dp.regf.rf[i], regExpected); `ERROR end end else begin - if (ignoreRFwrite) + if (ignoreRFwrite) // this allows other testbench elements to force WriteData to take on the next regExpected ignoreRFwrite <= 0; else begin scan_file_rf = $fscanf(data_file_rf, "%d\n", regNumExpected); scan_file_rf = $fscanf(data_file_rf, "%x\n", regExpected); end if (i != regNumExpected) begin - $display("%0t ps, instr %0d: wrong register changed: %0d, %0d expected to switch to %x from %x", $time, instrs, i, regNumExpected, regExpected, dut.hart.ieu.dp.regf.rf[regNumExpected]); + $display("%0t ps, PCW %x %s, instr %0d: wrong register changed: %0d, %0d expected to switch to %x from %x", $time, PCW, PCtextW, instrs, i, regNumExpected, regExpected, dut.hart.ieu.dp.regf.rf[regNumExpected]); `ERROR end - if (~equal(dut.hart.ieu.dp.regf.rf[i],regExpected, 0)) begin - $display("%0t ps, instr %0d: rf[%0d] does not equal rf expected: %x, %x", $time, instrs, i, dut.hart.ieu.dp.regf.rf[i], regExpected); + if (~(dut.hart.ieu.dp.regf.rf[i] === regExpected)) begin + $display("%0t ps, PCW %x %s, instr %0d: rf[%0d] does not equal rf expected: %x, %x", $time, PCW, PCtextW, instrs, i, dut.hart.ieu.dp.regf.rf[i], regExpected); `ERROR end - //if (dut.hart.ieu.dp.regf.rf[i] !== regExpected) begin - // force dut.hart.ieu.dp.regf.rf[i] = regExpected; - // release dut.hart.ieu.dp.regf.rf[i]; - //end end end end endgenerate + /////////////////////////////////////////////////////////////////////////////// + //////////////////////// Bus Unit Read/Write Checking ///////////////////////// + /////////////////////////////////////////////////////////////////////////////// // RAM and bootram are addressed in 64-bit blocks - this logic handles R/W // including subwords. Brief explanation on signals: // @@ -289,17 +430,33 @@ module testbench(); // In the linux boot, the processor spends the first ~5 instructions in // bootram, before jr jumps to main RAM - logic [63:0] readMask; + // -------------- + // Initialization + // -------------- + initial begin + data_file_memR = $fopen({`LINUX_TEST_VECTORS,"parsedMemRead.txt"}, "r"); + if (data_file_memR == 0) begin + $display("file couldn't be opened"); + $stop; + end + end + initial begin + data_file_memW = $fopen({`LINUX_TEST_VECTORS,"parsedMemWrite.txt"}, "r"); + if (data_file_memW == 0) begin + $display("file couldn't be opened"); + $stop; + end + end + + // ------------ + // Read Checker + // ------------ assign readMask = ((1 << (8*(1 << HSIZE))) - 1) << 8 * HADDR[2:0]; - - logic [`XLEN-1:0] readAdrExpected, readAdrTranslated; - always @(dut.HRDATA) begin #2; if (dut.hart.MemRWM[1] && (dut.hart.ebu.CaptureDataM) && dut.HRDATA !== {64{1'bx}}) begin - //$display("%0t", $time); if($feof(data_file_memR)) begin $display("no more memR data to read"); `ERROR @@ -307,31 +464,29 @@ module testbench(); scan_file_memR = $fscanf(data_file_memR, "%x\n", readAdrExpected); scan_file_memR = $fscanf(data_file_memR, "%x\n", HRDATA); assign readAdrTranslated = adrTranslator(readAdrExpected); - if (~equal(HADDR,readAdrTranslated,4)) begin - $display("%0t ps, instr %0d: HADDR does not equal readAdrExpected: %x, %x", $time, instrs, HADDR, readAdrTranslated); + if (~(HADDR === readAdrTranslated)) begin + $display("%0t ps, PCM %x %s, instr %0d: HADDR does not equal readAdrExpected: %x, %x", $time, dut.hart.ifu.PCM, PCtextM, instrs, HADDR, readAdrTranslated); `ERROR end if ((readMask & HRDATA) !== (readMask & dut.HRDATA)) begin if (HADDR inside `LINUX_FIX_READ) begin - //$display("warning %0t ps, instr %0d, adr %0d: forcing HRDATA to expected: %x, %x", $time, instrs, HADDR, HRDATA, dut.HRDATA); + if (HADDR != 'h10000005) // Suppress the warning for UART LSR so we can read UART output + $display("warning %0t ps, PCM %x %s, instr %0d, adr %0d: forcing HRDATA to expected: %x, %x", $time, dut.hart.ifu.PCM, PCtextM, instrs, HADDR, HRDATA, dut.HRDATA); force dut.uncore.HRDATA = HRDATA; #9; release dut.uncore.HRDATA; warningCount += 1; end else begin - $display("%0t ps, instr %0d: ExpectedHRDATA does not equal dut.HRDATA: %x, %x from address %x, %x", $time, instrs, HRDATA, dut.HRDATA, HADDR, HSIZE); + $display("%0t ps, PCM %x %s, instr %0d: ExpectedHRDATA does not equal dut.HRDATA: %x, %x from address %x, %x", $time, dut.hart.ifu.PCM, PCtextM, instrs, HRDATA, dut.HRDATA, HADDR, HSIZE); `ERROR end end - //end else if(dut.hart.MemRWM[1]) begin - // $display("%x, %x, %x, %t", HADDR, dut.PCF, dut.HRDATA, $time); - end - end - logic [`XLEN-1:0] writeDataExpected, writeAdrExpected, writeAdrTranslated; - + // ------------- + // Write Checker + // ------------- // this might need to change //always @(HWDATA or HADDR or HSIZE or HWRITE) begin always @(negedge HWRITE) begin @@ -346,20 +501,28 @@ module testbench(); assign writeAdrTranslated = adrTranslator(writeAdrExpected); if (writeDataExpected != HWDATA && ~dut.uncore.HSELPLICD) begin - $display("%0t ps, instr %0d: HWDATA does not equal writeDataExpected: %x, %x", $time, instrs, HWDATA, writeDataExpected); + $display("%0t ps, PCM %x %s, instr %0d: HWDATA does not equal writeDataExpected: %x, %x", $time, dut.hart.ifu.PCM, PCtextM, instrs, HWDATA, writeDataExpected); `ERROR end - if (~equal(writeAdrTranslated,HADDR,1) && ~dut.uncore.HSELPLICD) begin - $display("%0t ps, instr %0d: HADDR does not equal writeAdrExpected: %x, %x", $time, instrs, HADDR, writeAdrTranslated); + if (~(writeAdrTranslated === HADDR) && ~dut.uncore.HSELPLICD) begin + $display("%0t ps, PCM %x %s, instr %0d: HADDR does not equal writeAdrExpected: %x, %x", $time, dut.hart.ifu.PCM, PCtextM, instrs, HADDR, writeAdrTranslated); `ERROR end end end - integer totalCSR = 0; - logic [99:0] StartCSRexpected[63:0]; - string StartCSRname[99:0]; + /////////////////////////////////////////////////////////////////////////////// + //////////////////////////////// CSR Checking ///////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // -------------- + // Initialization + // -------------- initial begin + data_file_csr = $fopen({`LINUX_TEST_VECTORS,"parsedCSRs.txt"}, "r"); + if (data_file_csr == 0) begin + $display("file couldn't be opened"); + $stop; + end while(1) begin scan_file_csr = $fscanf(data_file_csr, "%s\n", StartCSRname[totalCSR]); if(StartCSRname[totalCSR] == "---") begin @@ -370,22 +533,10 @@ module testbench(); end end - always @(dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW) begin - if (dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW == 2 && instrs > 1) begin - $display("!!!!!! illegal instruction !!!!!!!!!!"); - $display("(as a reminder, MCAUSE and MEPC are set by this)"); - $display("at %0t ps, instr %0d, HADDR %x", $time, instrs, HADDR); - `ERROR - end - if (dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW == 5 && instrs != 0) begin - $display("!!!!!! illegal (physical) memory access !!!!!!!!!!"); - $display("(as a reminder, MCAUSE and MEPC are set by this)"); - $display("at %0t ps, instr %0d, HADDR %x", $time, instrs, HADDR); - `ERROR - end - end - - string MSTATUSstring = "MSTATUS"; + // -------------- + // Checker Macros + // -------------- + string MSTATUSstring = "MSTATUS"; //string variables seem to compare more reliably than string literals string SEPCstring = "SEPC"; string SCAUSEstring = "SCAUSE"; string SSTATUSstring = "SSTATUS"; @@ -394,7 +545,6 @@ module testbench(); string CSR; \ string ``CSR``name = `"CSR`"; \ string expected``CSR``name; \ - //CSR checking \ always @(``PATH``.``CSR``_REGW) begin \ if ($time > 1 && (`BUILDROOT != 1 || ``CSR``name != SSTATUSstring)) begin \ if (``CSR``name == SEPCstring) begin #1; end \ @@ -403,16 +553,16 @@ module testbench(); scan_file_csr = $fscanf(data_file_csr, "%s\n", expected``CSR``name); \ scan_file_csr = $fscanf(data_file_csr, "%x\n", expected``CSR``); \ if(expected``CSR``name.icompare(``CSR``name)) begin \ - $display("%0t ps, instr %0d: %s changed, expected %s", $time, instrs, `"CSR`", expected``CSR``name); \ + $display("%0t ps, PCM %x %s, instr %0d: %s changed, expected %s", $time, dut.hart.ifu.PCM, PCtextM, instrs, `"CSR`", expected``CSR``name); \ end \ if (``CSR``name == MSTATUSstring) begin \ if (``PATH``.``CSR``_REGW != ((``expected``CSR) | 64'ha00000000)) begin \ - $display("%0t ps, instr %0d: %s does not equal %s expected: %x, %x", $time, instrs, ``CSR``name, expected``CSR``name, ``PATH``.``CSR``_REGW, (``expected``CSR) | 64'ha00000000); \ + $display("%0t ps, PCM %x %s, instr %0d: %s (should be MSTATUS) does not equal %s expected: %x, %x", $time, dut.hart.ifu.PCM, PCtextM, instrs, ``CSR``name, expected``CSR``name, ``PATH``.``CSR``_REGW, (``expected``CSR) | 64'ha00000000); \ `ERROR \ end \ end else \ if (``PATH``.``CSR``_REGW != ``expected``CSR[$bits(``PATH``.``CSR``_REGW)-1:0]) begin \ - $display("%0t ps, instr %0d: %s does not equal %s expected: %x, %x", $time, instrs, ``CSR``name, expected``CSR``name, ``PATH``.``CSR``_REGW, ``expected``CSR); \ + $display("%0t ps, PCM %x %s, instr %0d: %s does not equal %s expected: %x, %x", $time, dut.hart.ifu.PCM, PCtextM, instrs, ``CSR``name, expected``CSR``name, ``PATH``.``CSR``_REGW, ``expected``CSR); \ `ERROR \ end \ end else begin \ @@ -420,7 +570,7 @@ module testbench(); for(integer j=0; j= 0; i--) begin + PAdr = BaseAdr + (VPN[i] << 3); + // dtim.RAM is 64-bit addressed. PAdr specifies a byte. We right shift + // by 3 (the PTE size) to get the requested 64-bit PTE. + PTE = dut.uncore.dtim.RAM[PAdr >> 3]; + PTE_R = PTE[1]; + PTE_X = PTE[3]; + if (PTE_R || PTE_X) begin + // Leaf page found + break; + end else begin + // Go to next level of table + BaseAdr = PTE[53:10] << 12; + end + end + // Determine which parts of the PTE page number to use based on the + // level of the page table we reached. + if (i == 2) begin + // Gigapage + assign adrTranslator = {8'b0, PTE[53:28], VPN[1], VPN[0], Offset}; + end else if (i == 1) begin + // Megapage + assign adrTranslator = {8'b0, PTE[53:19], VPN[0], Offset}; + end else begin + // Kilopage + assign adrTranslator = {8'b0, PTE[53:10], Offset}; + end + end else begin + // Direct translation if address translation is not on + assign adrTranslator = adrIn; + end + end + endfunction endmodule + + module instrTrackerTB( input logic clk, reset, input logic [31:0] InstrF,InstrD,InstrE,InstrM,InstrW, - output string InstrFName, InstrDName, InstrEName, InstrMName, InstrWName); - - // stage Instr to Writeback for visualization - //flopr #(32) InstrWReg(clk, reset, InstrM, InstrW); - + output string InstrFName, InstrDName, InstrEName, InstrMName, InstrWName); instrNameDecTB fdec(InstrF, InstrFName); instrNameDecTB ddec(InstrD, InstrDName); instrNameDecTB edec(InstrE, InstrEName);