From b6e2710f5defc27efc059da60dc723eeebc0fdac Mon Sep 17 00:00:00 2001 From: Ross Thompson Date: Sun, 22 Aug 2021 21:35:59 -0500 Subject: [PATCH] Confirmed David's changes to the interrupt code. When a timer interrupt occurs it should be routed to the machine interrupt pending MTIP even if MIDELEG[5] = 1 when the current privilege mode is Machine. This is true for all the interrupts. The interrupt should not be masked even though it is delegated to a lower privilege. Since the CPU is currently in machine mode the interrupt must be taken if MIE. Additionally added a new qemu script which pipes together all the parsing and post processing scripts to produce the singular all.txt trace without the massivie intermediate files. --- .../testvector-generation/CreateTrace.sh | 18 +++ .../testvector-generation/parseNew.py | 109 +++++++++--------- .../testvector-generation/remove_dup.awk | 20 ++++ wally-pipelined/src/privileged/csri.sv | 11 +- wally-pipelined/testbench/testbench-linux.sv | 69 ++++++----- 5 files changed, 137 insertions(+), 90 deletions(-) create mode 100755 wally-pipelined/linux-testgen/testvector-generation/CreateTrace.sh create mode 100755 wally-pipelined/linux-testgen/testvector-generation/remove_dup.awk diff --git a/wally-pipelined/linux-testgen/testvector-generation/CreateTrace.sh b/wally-pipelined/linux-testgen/testvector-generation/CreateTrace.sh new file mode 100755 index 000000000..65d626a6e --- /dev/null +++ b/wally-pipelined/linux-testgen/testvector-generation/CreateTrace.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Oftentimes this script runs so long you'll go to sleep. +# But you don't want the script to die when your computer goes to sleep. +# So consider invoking this with nohup (i.e. "nohup ./logAllBuildroot.sh") +# You can run "tail -f nohup.out" to see what would've +# outputted to the terminal if you didn't use nohup + +#customQemu="/courses/e190ax/qemu_sim/rv64_initrd/qemu_experimental/qemu/build/qemu-system-riscv64" +customQemu="qemu-system-riscv64" +imageDir="../buildroot-image-output" +intermedDir="../linux-testvectors/intermediate-outputs" +outDir="../linux-testvectors" + +# - Logs info needed by buildroot testbench +#($customQemu -M virt -nographic -bios $imageDir/fw_jump.elf -kernel $imageDir/Image -append "root=/dev/vda ro" -initrd $imageDir/rootfs.cpio -d nochain,cpu,in_asm -serial /dev/null -singlestep -gdb tcp::1236 -S 2>&1 >/dev/null | ./parseNew.py "$outDir") & riscv64-unknown-elf-gdb -x gdbinit_qemulog +#./fix_csrs.py "$outDir" + +($customQemu -M virt -nographic -bios $imageDir/fw_jump.elf -kernel $imageDir/Image -append "root=/dev/vda ro" -initrd $imageDir/rootfs.cpio -d nochain,cpu,in_asm -serial /dev/null -singlestep -gdb tcp::1236 -S 2>&1 >/dev/null | ./parse_qemu.py | ./parseNew.py | ./remove_dup.awk > allNew2.txt) & riscv64-unknown-elf-gdb -x gdbinit_qemulog diff --git a/wally-pipelined/linux-testgen/testvector-generation/parseNew.py b/wally-pipelined/linux-testgen/testvector-generation/parseNew.py index a52555fc1..719286a2f 100755 --- a/wally-pipelined/linux-testgen/testvector-generation/parseNew.py +++ b/wally-pipelined/linux-testgen/testvector-generation/parseNew.py @@ -9,9 +9,10 @@ import sys, fileinput, re InstrStartDelim = '=>' InstrEndDelim = '-----' -InputFile = 'noparse.txt' +#InputFile = 'noparse.txt' +#InputFile = sys.stdin #InputFile = 'temp.txt' -OutputFile = 'parsedAll.txt' +#OutputFile = 'parsedAll.txt' HUMAN_READABLE = False @@ -134,67 +135,67 @@ RegNumber = {'zero': 0, 'ra': 1, 'sp': 2, 'gp': 3, 'tp': 4, 't0': 5, 't1': 6, 't # initial state CurrentInstr = ['0', '0', None, 'other', {'zero': 0, 'ra': 0, 'sp': 0, 'gp': 0, 'tp': 0, 't0': 0, 't1': 0, 't2': 0, 's0': 0, 's1': 0, 'a0': 0, 'a1': 0, 'a2': 0, 'a3': 0, 'a4': 0, 'a5': 0, 'a6': 0, 'a7': 0, 's2': 0, 's3': 0, 's4': 0, 's5': 0, 's6': 0, 's7': 0, 's8': 0, 's9': 0, 's10': 0, 's11': 0, 't3': 0, 't4': 0, 't5': 0, 't6': 0, 'mhartid': 0, 'mstatus': 0, 'mip': 0, 'mie': 0, 'mideleg': 0, 'medeleg': 0, 'mtvec': 0, 'stvec': 0, 'mepc': 0, 'sepc': 0, 'mcause': 0, 'scause': 0, 'mtval': 0, 'stval': 0}, {}, None, None, None] -with open (InputFile, 'r') as InputFileFP: - #lines = InputFileFP.readlines() - lineNum = 0 - StartLine = 0 - EndLine = 0 - #instructions = [] - MemAdr = 0 - lines = [] - for line in InputFileFP: - lines.insert(lineNum, line) - if InstrStartDelim in line: - lineNum = 0 - StartLine = lineNum - elif InstrEndDelim in line: - EndLine = lineNum - (InstrBits, text) = lines[StartLine].split(':') - InstrBits = int(InstrBits.strip('=> '), 16) - text = text.strip() - PC = int(lines[StartLine+1].split(':')[0][2:], 16) - Regs = toDict(lines[StartLine+2:EndLine]) - (Class, Addr, WriteReg, ReadReg) = whichClass(text, Regs) - #print("CWR", Class, WriteReg, ReadReg) - PreviousInstr = CurrentInstr +#with open (InputFile, 'r') as InputFileFP: +#lines = InputFileFP.readlines() +lineNum = 0 +StartLine = 0 +EndLine = 0 +#instructions = [] +MemAdr = 0 +lines = [] +for line in fileinput.input('-'): + lines.insert(lineNum, line) + if InstrStartDelim in line: + lineNum = 0 + StartLine = lineNum + elif InstrEndDelim in line: + EndLine = lineNum + (InstrBits, text) = lines[StartLine].split(':') + InstrBits = int(InstrBits.strip('=> '), 16) + text = text.strip() + PC = int(lines[StartLine+1].split(':')[0][2:], 16) + Regs = toDict(lines[StartLine+2:EndLine]) + (Class, Addr, WriteReg, ReadReg) = whichClass(text, Regs) + #print("CWR", Class, WriteReg, ReadReg) + PreviousInstr = CurrentInstr - Changed = whatChanged(PreviousInstr[4], Regs) + Changed = whatChanged(PreviousInstr[4], Regs) - if (ReadReg !=None): ReadData = ReadReg - else: ReadData = None + if (ReadReg !=None): ReadData = ReadReg + else: ReadData = None - if (WriteReg !=None): WriteData = WriteReg - else: WriteData = None + if (WriteReg !=None): WriteData = WriteReg + else: WriteData = None - CurrentInstr = [PC, InstrBits, text, Class, Regs, Changed, Addr, WriteData, ReadData] + CurrentInstr = [PC, InstrBits, text, Class, Regs, Changed, Addr, WriteData, ReadData] - #print(CurrentInstr[0:4], PreviousInstr[5], CurrentInstr[6:7], PreviousInstr[8]) + #print(CurrentInstr[0:4], PreviousInstr[5], CurrentInstr[6:7], PreviousInstr[8]) - # pc, instrbits, text and class come from the last line. - MoveInstrToRegWriteLst = PreviousInstr[0:4] - # updated registers come from the current line. - MoveInstrToRegWriteLst.append(CurrentInstr[5]) # destination regs - # memory address if present comes from the last line. - MoveInstrToRegWriteLst.append(PreviousInstr[6]) # MemAdrM - # write data from the previous line - #MoveInstrToRegWriteLst.append(PreviousInstr[7]) # WriteDataM + # pc, instrbits, text and class come from the last line. + MoveInstrToRegWriteLst = PreviousInstr[0:4] + # updated registers come from the current line. + MoveInstrToRegWriteLst.append(CurrentInstr[5]) # destination regs + # memory address if present comes from the last line. + MoveInstrToRegWriteLst.append(PreviousInstr[6]) # MemAdrM + # write data from the previous line + #MoveInstrToRegWriteLst.append(PreviousInstr[7]) # WriteDataM - if (PreviousInstr[7] != None): - MoveInstrToRegWriteLst.append(Regs[PreviousInstr[7]]) # WriteDataM - else: - MoveInstrToRegWriteLst.append(None) + if (PreviousInstr[7] != None): + MoveInstrToRegWriteLst.append(Regs[PreviousInstr[7]]) # WriteDataM + else: + MoveInstrToRegWriteLst.append(None) - # read data from the current line - #MoveInstrToRegWriteLst.append(PreviousInstr[8]) # ReadDataM - if (PreviousInstr[8] != None): - MoveInstrToRegWriteLst.append(Regs[PreviousInstr[8]]) # ReadDataM - else: - MoveInstrToRegWriteLst.append(None) + # read data from the current line + #MoveInstrToRegWriteLst.append(PreviousInstr[8]) # ReadDataM + if (PreviousInstr[8] != None): + MoveInstrToRegWriteLst.append(Regs[PreviousInstr[8]]) # ReadDataM + else: + MoveInstrToRegWriteLst.append(None) - lines.clear() - #instructions.append(MoveInstrToRegWriteLst) - PrintInstr(MoveInstrToRegWriteLst, sys.stdout) - lineNum += 1 + lines.clear() + #instructions.append(MoveInstrToRegWriteLst) + PrintInstr(MoveInstrToRegWriteLst, sys.stdout) + lineNum += 1 #for instruction in instructions[1::]: diff --git a/wally-pipelined/linux-testgen/testvector-generation/remove_dup.awk b/wally-pipelined/linux-testgen/testvector-generation/remove_dup.awk new file mode 100755 index 000000000..7963d76a6 --- /dev/null +++ b/wally-pipelined/linux-testgen/testvector-generation/remove_dup.awk @@ -0,0 +1,20 @@ +#!/usr/bin/awk -f + +BEGIN{ + old = "first" +} + +{ + if($1 != old){ + if(old != "first"){ + print oldAll + } + } + old=$1 + oldAll=$0 +} + +END{ + print oldAll +} + diff --git a/wally-pipelined/src/privileged/csri.sv b/wally-pipelined/src/privileged/csri.sv index c34de1263..bf7e5f541 100644 --- a/wally-pipelined/src/privileged/csri.sv +++ b/wally-pipelined/src/privileged/csri.sv @@ -52,17 +52,12 @@ module csri #(parameter always_comb begin IntInM = 0; - IntInM[11] = ExtIntM & ~MIDELEG_REGW[9]; // MEIP + IntInM[11] = ExtIntM;; // MEIP IntInM[9] = ExtIntM & MIDELEG_REGW[9]; // SEIP - IntInM[7] = TimerIntM & ~MIDELEG_REGW[5]; // MTIP + IntInM[7] = TimerIntM; // MTIP IntInM[5] = TimerIntM & MIDELEG_REGW[5]; // STIP - IntInM[3] = SwIntM & ~MIDELEG_REGW[1]; // MSIP + IntInM[3] = SwIntM; // MSIP IntInM[1] = SwIntM & MIDELEG_REGW[1]; // SSIP - /* maybe only machine mode interrupts should be directly triggered: - IntInM[11] = ExtIntM; // MEIP - IntInM[7] = TimerIntM; // MTIP - IntInM[3] = SwIntM; // MSIP - */ end // Interrupt Write Enables diff --git a/wally-pipelined/testbench/testbench-linux.sv b/wally-pipelined/testbench/testbench-linux.sv index ba2860318..64b5d349e 100644 --- a/wally-pipelined/testbench/testbench-linux.sv +++ b/wally-pipelined/testbench/testbench-linux.sv @@ -128,7 +128,7 @@ module testbench(); integer NumCSRMIndex; integer NumCSRWIndex; integer NumCSRPostWIndex; - logic CurrentInterruptForce; +// logic CurrentInterruptForce; // ----------- // Error Macro @@ -141,11 +141,13 @@ module testbench(); data_file_all = $fopen({`LINUX_TEST_VECTORS,"all.txt"}, "r"); end +/* -----\/----- EXCLUDED -----\/----- initial begin CurrentInterruptForce = 1'b0; end + -----/\----- EXCLUDED -----/\----- */ - assign checkInstrM = dut.hart.ieu.InstrValidM & ~dut.hart.priv.trap.InstrPageFaultM & ~dut.hart.StallM; + assign checkInstrM = dut.hart.ieu.InstrValidM & ~dut.hart.priv.trap.InstrPageFaultM & ~dut.hart.priv.trap.InterruptM & ~dut.hart.StallM; // trapW will already be invalid in there was an InstrPageFault in the previous instruction. assign checkInstrW = dut.hart.ieu.InstrValidW & ~dut.hart.StallW; @@ -228,6 +230,29 @@ module testbench(); MarkerIndex += 2; + // if we get an xcause with the interrupt bit set we must generate an interrupt as interrupts + // are imprecise. Forcing the trap at this time will allow wally to track what qemu does. + // the msb of xcause will be set. + // bits 1:0 select mode; 0 = user, 1 = superviser, 3 = machine + // bits 3:2 select the type of interrupt, 0 = software, 1 = timer, 2 = external + if(ExpectedCSRArrayM[NumCSRM].substr(1, 5) == "cause" && (ExpectedCSRArrayValueM[NumCSRM][`XLEN-1] == 1'b1)) begin + //what type? + ExpectedIntType = ExpectedCSRArrayValueM[NumCSRM] & 64'h0000_000C; + $display("%t: CSR = %s. Forcing interrupt of cause = %x", $time, ExpectedCSRArrayM[NumCSRM], ExpectedCSRArrayValueM[NumCSRM]); + + if(ExpectedIntType == 0) begin + force dut.hart.priv.SwIntM = 1'b1; + $display("Force SwIntM"); + end + else if(ExpectedIntType == 4) begin + force dut.hart.priv.TimerIntM = 1'b1; + $display("Force TimeIntM"); + end + else if(ExpectedIntType == 8) begin + force dut.hart.priv.ExtIntM = 1'b1; + $display("Force ExtIntM"); + end + end NumCSRM++; end end @@ -305,7 +330,18 @@ module testbench(); //$display("%t: releasing force of ReadDataM.", $time); release dut.hart.ieu.dp.ReadDataM; end - + + // remove forces on interrupts + for(NumCSRMIndex = 0; NumCSRMIndex < NumCSRM; NumCSRMIndex++) begin + if(ExpectedCSRArrayM[NumCSRMIndex].substr(1, 5) == "cause" && (ExpectedCSRArrayValueM[NumCSRMIndex][`XLEN-1] == 1'b1)) begin + //what type? + $display("%t: Releasing all forces on interrupts", $time); + + release dut.hart.priv.SwIntM; + release dut.hart.priv.TimerIntM; + release dut.hart.priv.ExtIntM; + end + end end end end @@ -507,6 +543,7 @@ module testbench(); end endcase // case (ExpectedCSRArrayW[NumCSRPostWIndex]) +/* -----\/----- EXCLUDED -----\/----- if(CurrentInterruptForce) begin CurrentInterruptForce = 1'b0; // remove forces on interrupts @@ -516,32 +553,8 @@ module testbench(); release dut.hart.priv.TimerIntM; release dut.hart.priv.ExtIntM; end + -----/\----- EXCLUDED -----/\----- */ - // if we get an xcause with the interrupt bit set we must generate an interrupt as interrupts - // are imprecise. Forcing the trap at this time will allow wally to track what qemu does. - // the msb of xcause will be set. - // bits 1:0 select mode; 0 = user, 1 = superviser, 3 = machine - // bits 3:2 select the type of interrupt, 0 = software, 1 = timer, 2 = external - if(ExpectedCSRArrayW[NumCSRPostWIndex].substr(1, 5) == "cause" && (ExpectedCSRArrayValueW[NumCSRPostWIndex][`XLEN-1] == 1'b1)) begin - //what type? - ExpectedIntType = ExpectedCSRArrayValueW[NumCSRPostWIndex] & 64'h0000_000C; - $display("%t: CSR = %s. Forcing interrupt of cause = %x", $time, ExpectedCSRArrayW[NumCSRPostWIndex], ExpectedCSRArrayValueW[NumCSRPostWIndex]); - - CurrentInterruptForce = 1'b1; - - if(ExpectedIntType == 0) begin - force dut.hart.priv.SwIntM = 1'b1; - $display("Force SwIntM"); - end - else if(ExpectedIntType == 4) begin - force dut.hart.priv.TimerIntM = 1'b1; - $display("Force TimeIntM"); - end - else if(ExpectedIntType == 8) begin - force dut.hart.priv.ExtIntM = 1'b1; - $display("Force ExtIntM"); - end - end end // for (NumCSRPostWIndex = 0; NumCSRPostWIndex < NumCSRW; NumCSRPostWIndex++) if (fault == 1) begin `ERROR