From 6c3d274970ae76343627126143be54d9fe6c57ba Mon Sep 17 00:00:00 2001 From: bbracker Date: Tue, 12 Apr 2022 21:22:08 -0700 Subject: [PATCH 1/6] change testbench-linux to by default use attempted instruction count for warning/error messages --- pipelined/testbench/testbench-linux.sv | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pipelined/testbench/testbench-linux.sv b/pipelined/testbench/testbench-linux.sv index b85c26e6..bc6bf2c1 100644 --- a/pipelined/testbench/testbench-linux.sv +++ b/pipelined/testbench/testbench-linux.sv @@ -558,14 +558,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 @@ -625,12 +625,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 @@ -644,11 +644,11 @@ module testbench; 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) @@ -657,8 +657,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) @@ -709,7 +709,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) From 67ef47b25b33c5e922499b816b793e4ff981ac26 Mon Sep 17 00:00:00 2001 From: bbracker Date: Wed, 13 Apr 2022 00:49:37 -0700 Subject: [PATCH 2/6] whoops forgot to update AttemptedInstructionCount in interrupt spoofing --- pipelined/testbench/testbench-linux.sv | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pipelined/testbench/testbench-linux.sv b/pipelined/testbench/testbench-linux.sv index bc6bf2c1..8988bcae 100644 --- a/pipelined/testbench/testbench-linux.sv +++ b/pipelined/testbench/testbench-linux.sv @@ -481,6 +481,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 +496,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)); \ @@ -734,6 +734,7 @@ module testbench; `SCAN_NEW_INTERRUPT garbageInt = $fgets(garbageString,traceFileE); garbageInt = $fgets(garbageString,traceFileM); + AttemptedInstructionCount += 1; end end end From 1bb5e1f35b28a607c12ede1aca2e2f1b3d818414 Mon Sep 17 00:00:00 2001 From: bbracker Date: Wed, 13 Apr 2022 01:36:09 -0700 Subject: [PATCH 3/6] whoops fix address for PLIC int enables in checkpoint generation --- linux/testvector-generation/genCheckpoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/testvector-generation/genCheckpoint.sh b/linux/testvector-generation/genCheckpoint.sh index 447377b3..f12223f5 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 From 0d4ec9b3f9c7bfd988a3a0544c659ebd9334ca61 Mon Sep 17 00:00:00 2001 From: bbracker Date: Wed, 13 Apr 2022 01:59:21 -0700 Subject: [PATCH 4/6] fix bugs in PLIC checkpoint state parsing --- linux/testvector-generation/parsePlicState.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/linux/testvector-generation/parsePlicState.py b/linux/testvector-generation/parsePlicState.py index b917bfdc..c05dd65e 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 From 3465d8cd322f9758ea54a165ad409c64a2492275 Mon Sep 17 00:00:00 2001 From: bbracker Date: Wed, 13 Apr 2022 03:37:53 -0700 Subject: [PATCH 5/6] improve testbench-linux.sv to correctly load in PLIC IntEnable checkpoint and to handle edge case where interrupt is caused by enabling interrupts in SSTATUS --- linux/testvector-generation/parsePlicState.py | 1 + pipelined/testbench/testbench-linux.sv | 29 +++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/linux/testvector-generation/parsePlicState.py b/linux/testvector-generation/parsePlicState.py index c05dd65e..aecc89bc 100755 --- a/linux/testvector-generation/parsePlicState.py +++ b/linux/testvector-generation/parsePlicState.py @@ -98,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 8988bcae..7d97249c 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 @@ -701,6 +705,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 @@ -724,17 +730,28 @@ module testbench; 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); - AttemptedInstructionCount += 1; + #1; + if ((`CSR_BASE.csrm.WriteMSTATUSM || `CSR_BASE.csrs.WriteSSTATUSM) && |(`CSR_BASE.CSRWriteValM & ~`CSR_BASE.csrm.MSTATUS_REGW & 32'h22)) begin + $display("Enabled global interrupts"); + end else begin + garbageInt = $fgets(garbageString,traceFileE); + garbageInt = $fgets(garbageString,traceFileM); + AttemptedInstructionCount += 1; + end end end end From 016e9604015ea9fa4514ed18eeeb9988634c19df Mon Sep 17 00:00:00 2001 From: bbracker Date: Wed, 13 Apr 2022 04:31:23 -0700 Subject: [PATCH 6/6] change interrupt spoofing to happen at negative clock edges --- pipelined/testbench/testbench-linux.sv | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pipelined/testbench/testbench-linux.sv b/pipelined/testbench/testbench-linux.sv index 7d97249c..50f0fcad 100644 --- a/pipelined/testbench/testbench-linux.sv +++ b/pipelined/testbench/testbench-linux.sv @@ -644,6 +644,7 @@ 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; @@ -724,8 +725,9 @@ 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 @@ -744,10 +746,14 @@ module testbench; $display("Forcing interrupt."); end `SCAN_NEW_INTERRUPT - #1; - if ((`CSR_BASE.csrm.WriteMSTATUSM || `CSR_BASE.csrs.WriteSSTATUSM) && |(`CSR_BASE.CSRWriteValM & ~`CSR_BASE.csrm.MSTATUS_REGW & 32'h22)) begin + 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;