diff --git a/linux/testvector-generation/genCheckpoint.sh b/linux/testvector-generation/genCheckpoint.sh index 447377b33..f12223f54 100755 --- a/linux/testvector-generation/genCheckpoint.sh +++ b/linux/testvector-generation/genCheckpoint.sh @@ -102,8 +102,8 @@ then # Priority Levels for sources 1 thru 63 x/63xw 0x0C000004 # Interrupt Enables for sources 1 thru 63 for contexts 0 and 1 - x/2xw 0x0C020000 - x/2xw 0x0C020080 + x/2xw 0x0C002000 + x/2xw 0x0C002080 # Global Priority Threshold for contexts 0 and 1 x/1xw 0x0C200000 x/1xw 0x0C201000 diff --git a/linux/testvector-generation/parsePlicState.py b/linux/testvector-generation/parsePlicState.py index b917bfdc3..aecc89bc3 100755 --- a/linux/testvector-generation/parsePlicState.py +++ b/linux/testvector-generation/parsePlicState.py @@ -26,11 +26,8 @@ def strip0x(num): return num[2:] def stripZeroes(num): - num = num.strip('0') - if num=='': - return '0' - else: - return num + num = int(num,16) + return hex(num)[2:] ############# # Main Code # @@ -84,11 +81,11 @@ with open(rawPlicStateFile, 'r') as rawPlicStateFile: # 0x0C020000 thru 0x0C020004 plicIntEnable = tokenize(rawPlicStateFile.readline())[1:] plicIntEnable = map(strip0x,plicIntEnable) - plicIntEnableArray.append(reduce(lambda x,y: x+y,plicIntEnable)) + plicIntEnableArray.append(reduce(lambda x,y: y+x,plicIntEnable)) # 0x0C020080 thru 0x0C020084 plicIntEnable = tokenize(rawPlicStateFile.readline())[1:] plicIntEnable = map(strip0x,plicIntEnable) - plicIntEnableArray.append(reduce(lambda x,y: x+y,plicIntEnable)) + plicIntEnableArray.append(reduce(lambda x,y: y+x,plicIntEnable)) plicIntPriorityThresholdArray = [] # iterates over number of different contexts # 0x0C200000 @@ -101,6 +98,7 @@ with open(outDir+'checkpoint-PLIC_INT_PRIORITY', 'w') as outFile: outFile.write(stripZeroes(word[2:])+'\n') with open(outDir+'checkpoint-PLIC_INT_ENABLE', 'w') as outFile: for word in plicIntEnableArray: + word = hex(int(word,16)>>1)[2:] # right shift by 1 because source 0 does not exist outFile.write(stripZeroes(word)+'\n') with open(outDir+'checkpoint-PLIC_THRESHOLD', 'w') as outFile: for word in plicIntPriorityThresholdArray: diff --git a/pipelined/testbench/testbench-linux.sv b/pipelined/testbench/testbench-linux.sv index b9111269f..5d50848ba 100644 --- a/pipelined/testbench/testbench-linux.sv +++ b/pipelined/testbench/testbench-linux.sv @@ -182,6 +182,7 @@ module testbench; `define UART_LCR `UART.LCR `define UART_MCR `UART.MCR `define UART_SCR `UART.SCR + `define UART_IP `UART.INTR `define PLIC dut.uncore.plic.plic `define PLIC_INT_PRIORITY `PLIC.intPriority `define PLIC_INT_ENABLE `PLIC.intEn @@ -376,7 +377,7 @@ module testbench; `INIT_CHECKPOINT_VAL(SATP, [`XLEN-1:0]); `INIT_CHECKPOINT_VAL(PRIV, [1:0]); `INIT_CHECKPOINT_PACKED_ARRAY(PLIC_INT_PRIORITY, [2:0],`PLIC_NUM_SRC,1); - `INIT_CHECKPOINT_PACKED_ARRAY(PLIC_INT_ENABLE, [`PLIC_NUM_SRC:1],1,0); + `MAKE_CHECKPOINT_INIT_SIGNAL(PLIC_INT_ENABLE, [`PLIC_NUM_SRC:0],1,0); `INIT_CHECKPOINT_PACKED_ARRAY(PLIC_THRESHOLD, [2:0],1,0); // UART checkpointing does not cover entire UART state // Many UART registers are difficult to initialize because under the hood @@ -399,6 +400,7 @@ module testbench; if(!NO_IE_MTIME_CHECKPOINT) begin force `MEIP = 0; force `SEIP = 0; + force `UART_IP = 0; force `MTIP = 0; end $sformat(testvectorDir,"%s/linux-testvectors/",RISCV_DIR); @@ -446,6 +448,7 @@ module testbench; force {`STATUS_SPP,`STATUS_MPIE} = initMSTATUS[0][8:7]; force {`STATUS_SPIE,`STATUS_UPIE,`STATUS_MIE} = initMSTATUS[0][5:3]; force {`STATUS_SIE,`STATUS_UIE} = initMSTATUS[0][1:0]; + force `PLIC_INT_ENABLE = {initPLIC_INT_ENABLE[1][`PLIC_NUM_SRC:1],initPLIC_INT_ENABLE[0][`PLIC_NUM_SRC:1]}; // would need to expand into a generate loop to cover an arbitrary number of contexts force `INSTRET = CHECKPOINT; while (reset!==1) #1; while (reset!==0) #1; @@ -455,6 +458,7 @@ module testbench; release {`STATUS_SPP,`STATUS_MPIE}; release {`STATUS_SPIE,`STATUS_UPIE,`STATUS_MIE}; release {`STATUS_SIE,`STATUS_UIE}; + release `PLIC_INT_ENABLE; release `INSTRET; end // Get the E-stage trace reader ahead of the M-stage trace reader @@ -481,6 +485,9 @@ module testbench; if(`DEBUG_TRACE >= 5) $display("Time %t, line %x", $time, line``STAGE); \ // extract PC, Instr \ matchCount``STAGE = $sscanf(line``STAGE, "%x %x %s", ExpectedPC``STAGE, ExpectedInstr``STAGE, text``STAGE); \ + if (`"STAGE`"=="M") begin \ + AttemptedInstructionCount += 1; \ + end \ \ // for the life of me I cannot get any build in C or C++ string parsing functions/methods to work. \ // strtok was the best idea but it cannot be used correctly as system verilog does not have null \ @@ -493,9 +500,6 @@ module testbench; for(index``STAGE = 0; index``STAGE < line``STAGE.len(); index``STAGE++) begin \ //$display("char = %s", line``STAGE[index]); \ if (line``STAGE[index``STAGE] == " " | line``STAGE[index``STAGE] == "\n") begin \ - if (line``STAGE[index``STAGE] == "\n" & `"STAGE`"=="M") begin \ - AttemptedInstructionCount += 1; \ - end \ EndIndex``STAGE = index``STAGE; \ ExpectedTokens``STAGE[TokenIndex``STAGE] = line``STAGE.substr(StartIndex``STAGE, EndIndex``STAGE-1); \ //$display("In Tokenizer %s", line``STAGE.substr(StartIndex, EndIndex-1)); \ @@ -560,14 +564,14 @@ module testbench; // ========== VALUE-CHECKING MACROS ========== `define checkEQ(NAME, VAL, EXPECTED) \ if(VAL != EXPECTED) begin \ - $display("%tns, %d instrs: %s %x differs from expected %x", $time, InstrCountW, NAME, VAL, EXPECTED); \ + $display("%tns, %d instrs: %s %x differs from expected %x", $time, AttemptedInstructionCount, NAME, VAL, EXPECTED); \ if ((NAME == "PCW") | (`DEBUG_TRACE >= 2)) fault = 1; \ end `define checkCSR(CSR) \ begin \ if (CSR != ExpectedCSRArrayValueW[NumCSRPostWIndex]) begin \ - $display("%tns, %d instrs: CSR %s = %016x, does not equal expected value %016x", $time, InstrCountW, ExpectedCSRArrayW[NumCSRPostWIndex], CSR, ExpectedCSRArrayValueW[NumCSRPostWIndex]); \ + $display("%tns, %d instrs: CSR %s = %016x, does not equal expected value %016x", $time, AttemptedInstructionCount, ExpectedCSRArrayW[NumCSRPostWIndex], CSR, ExpectedCSRArrayValueW[NumCSRPostWIndex]); \ if(`DEBUG_TRACE >= 3) fault = 1; \ end \ end @@ -627,12 +631,12 @@ module testbench; // override on special conditions if(~dut.core.StallW) begin if(textW.substr(0,5) == "rdtime") begin - //$display("%tns, %d instrs: Releasing force of MTIME_CLINT.", $time, InstrCountW); + //$display("%tns, %d instrs: Releasing force of MTIME_CLINT.", $time, AttemptedInstructionCount); if(!NO_IE_MTIME_CHECKPOINT) release dut.uncore.clint.clint.MTIME; end //if (ExpectedIEUAdrM == 'h10000005) begin - //$display("%tns, %d instrs: releasing force of ReadDataM.", $time, InstrCountW); + //$display("%tns, %d instrs: releasing force of ReadDataM.", $time, AttemptedInstructionCount); //release dut.core.ieu.dp.ReadDataM; //end end @@ -642,15 +646,16 @@ module testbench; // step2: make all checks in the write back stage. assign checkInstrW = InstrValidW & ~dut.core.StallW; // trapW will already be invalid in there was an InstrPageFault in the previous instruction. always @(negedge clk) begin + #1; // small delay allows interrupt spoofing to happen first // always check PC, instruction bits if (checkInstrW) begin InstrCountW += 1; // print progress message - if (InstrCountW % 'd100000 == 0) $display("Reached %d instructions", InstrCountW); + if (AttemptedInstructionCount % 'd100000 == 0) $display("Reached %d instructions", AttemptedInstructionCount); // turn on waves - if (InstrCountW == INSTR_WAVEON) $stop; + if (AttemptedInstructionCount == INSTR_WAVEON) $stop; // end sim - if ((InstrCountW == INSTR_LIMIT) & (INSTR_LIMIT!=0)) $stop; + if ((AttemptedInstructionCount == INSTR_LIMIT) & (INSTR_LIMIT!=0)) $stop; fault = 0; if (`DEBUG_TRACE >= 1) begin `checkEQ("PCW",PCW,ExpectedPCW) @@ -659,8 +664,8 @@ module testbench; `checkEQ("Instr Count",dut.core.priv.priv.csr.counters.counters.INSTRET_REGW,InstrCountW) #2; // delay 2 ns. if(`DEBUG_TRACE >= 5) begin - $display("%tns, %d instrs: Reg Write Address %02d ? expected value: %02d", $time, InstrCountW, dut.core.ieu.dp.regf.a3, ExpectedRegAdrW); - $display("%tns, %d instrs: RF[%02d] %016x ? expected value: %016x", $time, InstrCountW, ExpectedRegAdrW, dut.core.ieu.dp.regf.rf[ExpectedRegAdrW], ExpectedRegValueW); + $display("%tns, %d instrs: Reg Write Address %02d ? expected value: %02d", $time, AttemptedInstructionCount, dut.core.ieu.dp.regf.a3, ExpectedRegAdrW); + $display("%tns, %d instrs: RF[%02d] %016x ? expected value: %016x", $time, AttemptedInstructionCount, ExpectedRegAdrW, dut.core.ieu.dp.regf.rf[ExpectedRegAdrW], ExpectedRegValueW); end if (RegWriteW == "GPR") begin `checkEQ("Reg Write Address",dut.core.ieu.dp.regf.a3,ExpectedRegAdrW) @@ -703,6 +708,8 @@ module testbench; force `MEIP = 0; if ((ExpectedCSRArrayValueW[NumCSRPostWIndex] & 1<<09) == 0) force `SEIP = 0; + if ((ExpectedCSRArrayValueW[NumCSRPostWIndex] & ((1<<11) | (1<<09))) == 0) + force `UART_IP = 0; if ((ExpectedCSRArrayValueW[NumCSRPostWIndex] & 1<<07) == 0) force `MTIP = 0; end @@ -711,7 +718,7 @@ module testbench; end if (fault == 1) begin errorCount +=1; - $display("processed %0d instructions with %0d warnings", InstrCountW, warningCount); + $display("processed %0d instructions with %0d warnings", AttemptedInstructionCount, warningCount); $stop; end end // if (`DEBUG_TRACE >= 1) @@ -720,22 +727,39 @@ module testbench; // New IP spoofing - always @(posedge clk) begin - #1 + logic globalIntsBecomeEnabled; + assign globalIntsBecomeEnabled = (`CSR_BASE.csrm.WriteMSTATUSM || `CSR_BASE.csrs.WriteSSTATUSM) && (|(`CSR_BASE.CSRWriteValM & (~`CSR_BASE.csrm.MSTATUS_REGW) & 32'h22)); + always @(negedge clk) begin if(checkInstrM) begin if((interruptInstrCount+1) == AttemptedInstructionCount) begin if(!NO_IE_MTIME_CHECKPOINT) begin case (interruptCauseVal) - 11: force `MEIP = 1; - 09: force `SEIP = 1; + 11: begin + force `MEIP = 1; + force `UART_IP = 1; + end + 09: begin + force `SEIP = 1; + force `UART_IP = 1; + end 07: force `MTIP = 1; default: $display("Unsupported interrupt in interrupts.txt. cause = %0d",interruptCauseVal); endcase $display("Forcing interrupt."); end `SCAN_NEW_INTERRUPT - garbageInt = $fgets(garbageString,traceFileE); - garbageInt = $fgets(garbageString,traceFileM); + if (globalIntsBecomeEnabled) begin + $display("Enabled global interrupts"); + // The idea here is if a CSR instruction causes an interrupt by + // enabling interrupts, that CSR instruction will commit. + end else begin + // Other instructions, however, will get interrupted and not + // commit, so we don't want our W-stage checker to look for them + // and get confused when it doesn't find them. + garbageInt = $fgets(garbageString,traceFileE); + garbageInt = $fgets(garbageString,traceFileM); + AttemptedInstructionCount += 1; + end end end end