From 94f24d3f586f27df27cd873e76a5c945bee64dfb Mon Sep 17 00:00:00 2001 From: Ross Thompson Date: Thu, 12 Jan 2023 10:09:34 -0600 Subject: [PATCH] Added instruction logger. --- pipelined/testbench/testbench.sv | 195 ++++++- pipelined/testbench/testbench_imperas.sv | 709 +++++++++++++++++++++++ 2 files changed, 903 insertions(+), 1 deletion(-) create mode 100644 pipelined/testbench/testbench_imperas.sv diff --git a/pipelined/testbench/testbench.sv b/pipelined/testbench/testbench.sv index 8683af69..dcbebbf4 100644 --- a/pipelined/testbench/testbench.sv +++ b/pipelined/testbench/testbench.sv @@ -509,7 +509,53 @@ logic [3:0] dummy; $stop; end end - +/* -----\/----- EXCLUDED -----\/----- + + // rvvi tracer + localparam int ILEN = `XLEN; // Instruction length in bits + localparam int XLEN = `XLEN; // GPR length in bits + localparam int FLEN = `FLEN; // FPR length in bits + localparam int VLEN = 0; // Vector register size in bits + localparam int NHART = 1; // Number of harts reported + localparam int RETIRE = 1; // Number of instructions that can retire during valid event + + logic TraceClk; // Interface clock + + logic valid [(NHART-1):0][(RETIRE-1):0]; // Retired instruction + logic [63:0] order [(NHART-1):0][(RETIRE-1):0]; // Unique instruction order count (no gaps or reuse) + logic [(ILEN-1):0] insn [(NHART-1):0][(RETIRE-1):0]; // Instruction bit pattern + logic trap [(NHART-1):0][(RETIRE-1):0]; // Trapped instruction + logic halt [(NHART-1):0][(RETIRE-1):0]; // Halted instruction + logic intr [(NHART-1):0][(RETIRE-1):0]; // (RVFI Legacy) Flag first instruction of trap handler + logic [1:0] mode [(NHART-1):0][(RETIRE-1):0]; // Privilege mode of operation + logic [1:0] ixl [(NHART-1):0][(RETIRE-1):0]; // XLEN mode 32/64 bit + + logic [(XLEN-1):0] pc_rdata [(NHART-1):0][(RETIRE-1):0]; // PC of insn + logic [(XLEN-1):0] pc_wdata [(NHART-1):0][(RETIRE-1):0]; // PC of next instruction + + // X Registers + logic [31:0][(XLEN-1):0] x_wdata [(NHART-1):0][(RETIRE-1):0]; // X data value + logic [31:0] x_wb [(NHART-1):0][(RETIRE-1):0]; // X data writeback (change) flag + + // F Registers + logic [31:0][(FLEN-1):0] f_wdata [(NHART-1):0][(RETIRE-1):0]; // F data value + logic [31:0] f_wb [(NHART-1):0][(RETIRE-1):0]; // F data writeback (change) flag + + // V Registers + logic [31:0][(VLEN-1):0] v_wdata [(NHART-1):0][(RETIRE-1):0]; // V data value + logic [31:0] v_wb [(NHART-1):0][(RETIRE-1):0]; // V data writeback (change) flag + + // Control & State Registers + logic [4095:0][(XLEN-1):0] csr [(NHART-1):0][(RETIRE-1):0]; // Full CSR Address range + logic [4095:0] csr_wb [(NHART-1):0][(RETIRE-1):0]; // CSR writeback (change) flag + + logic lrsc_cancel[(NHART-1):0][(RETIRE-1):0]; // Implementation defined + rvviTrace #(`XLEN, `XLEN, `FLEN, 0, 1, 1) rvviTrace(.clk(TraceClk), .valid, .order, .insn, .trap, .halt, .intr, + .mode, .ixl, .pc_rdata, .pc_wdata, .x_wdata, .x_wb, .f_wdata, .f_wb, .v_wdata, .v_wb, + .csr, .csr_wb, .lrsc_cancel); + -----/\----- EXCLUDED -----/\----- */ + + rvviTrace rvviTrace(); endmodule @@ -692,3 +738,150 @@ task automatic updateProgramAddrLabelArray; $fclose(ProgramLabelMapFP); $fclose(ProgramAddrMapFP); endtask + +`define NUM_REGS 32 +`define NUM_CSRS 4096 + +module rvviTrace(); + + // wally specific signals + logic reset; + + logic [`XLEN-1:0] PCM, PCW; + logic [`XLEN-1:0] InstrRawD, InstrRawE, InstrRawM, InstrRawW; + logic InstrValidM, InstrValidW; + logic StallE, StallM, StallW; + logic FlushE, FlushM, FlushW; + + // tracer signals + logic clk; + logic valid; + logic [`XLEN-1:0] insn; + logic [`XLEN-1:0 ] pc_rdata; + + assign clk = testbench.dut.clk; + assign InstrValidM = testbench.dut.core.ieu.InstrValidM; + assign InstrRawD = testbench.dut.core.ifu.InstrRawD; + assign PCM = testbench.dut.core.ifu.PCM; + assign reset = testbench.reset; + assign StallE = testbench.dut.core.StallE; + assign StallM = testbench.dut.core.StallM; + assign StallW = testbench.dut.core.StallW; + assign FlushE = testbench.dut.core.FlushE; + assign FlushM = testbench.dut.core.FlushM; + assign FlushW = testbench.dut.core.FlushW; + + // pipeline to writeback stage + flopenrc #(`XLEN) InstrRawEReg (clk, reset, FlushE, ~StallE, InstrRawD, InstrRawE); + flopenrc #(`XLEN) InstrRawMReg (clk, reset, FlushM, ~StallM, InstrRawE, InstrRawM); + flopenrc #(`XLEN) InstrRawWReg (clk, reset, FlushW, ~StallW, InstrRawM, InstrRawW); + flopenrc #(`XLEN) PCWReg (clk, reset, FlushW, ~StallW, PCM, PCW); + flopenrc #(1) InstrValidMReg (clk, reset, FlushW, ~StallW, InstrValidM, InstrValidW); + + assign valid = InstrValidW; + assign insn = InstrRawW; + assign pc_rdata = PCW; + + always_ff @(posedge clk) begin + if(valid) begin + $display("PC = %x, insn = %x", pc_rdata, insn); + end + end + + +endmodule + +/* -----\/----- EXCLUDED -----\/----- +module rvviTrace #( + parameter int ILEN = `XLEN, // Instruction length in bits + parameter int XLEN = `XLEN, // GPR length in bits + parameter int FLEN = `FLEN, // FPR length in bits + parameter int VLEN = 0, // Vector register size in bits + parameter int NHART = 1, // Number of harts reported + parameter int RETIRE = 1 // Number of instructions that can retire during valid event + )( + // + // RISCV output signals + // + output logic clk, // Interface clock + + output logic valid [(NHART-1):0][(RETIRE-1):0], // Retired instruction + output logic [63:0] order [(NHART-1):0][(RETIRE-1):0], // Unique instruction order count (no gaps or reuse) + output logic [(ILEN-1):0] insn [(NHART-1):0][(RETIRE-1):0], // Instruction bit pattern + output logic trap [(NHART-1):0][(RETIRE-1):0], // Trapped instruction + output logic halt [(NHART-1):0][(RETIRE-1):0], // Halted instruction + output logic intr [(NHART-1):0][(RETIRE-1):0], // (RVFI Legacy) Flag first instruction of trap handler + output logic [1:0] mode [(NHART-1):0][(RETIRE-1):0], // Privilege mode of operation + output logic [1:0] ixl [(NHART-1):0][(RETIRE-1):0], // XLEN mode 32/64 bit + + output logic [(XLEN-1):0] pc_rdata [(NHART-1):0][(RETIRE-1):0], // PC of insn + output logic [(XLEN-1):0] pc_wdata [(NHART-1):0][(RETIRE-1):0], // PC of next instruction + + // X Registers + output logic [31:0][(XLEN-1):0] x_wdata [(NHART-1):0][(RETIRE-1):0], // X data value + output logic [31:0] x_wb [(NHART-1):0][(RETIRE-1):0], // X data writeback (change) flag + + // F Registers + output logic [31:0][(FLEN-1):0] f_wdata [(NHART-1):0][(RETIRE-1):0], // F data value + output logic [31:0] f_wb [(NHART-1):0][(RETIRE-1):0], // F data writeback (change) flag + + // V Registers + output logic [31:0][(VLEN-1):0] v_wdata [(NHART-1):0][(RETIRE-1):0], // V data value + output logic [31:0] v_wb [(NHART-1):0][(RETIRE-1):0], // V data writeback (change) flag + + // Control & State Registers + output logic [4095:0][(XLEN-1):0] csr [(NHART-1):0][(RETIRE-1):0], // Full CSR Address range + output logic [4095:0] csr_wb [(NHART-1):0][(RETIRE-1):0], // CSR writeback (change) flag + + output logic lrsc_cancel[(NHART-1):0][(RETIRE-1):0] // Implementation defined cancel + ); + + + assign clk = dut.clk; + // *** need to pipeline to writeback stage. + assign valid = dut.core.ieu.InstrValidM; + assign insn = dut.core.ifu.InstrM; + assign pc_rdata = dut.core.ifu.PCM; + + always_ff @(posedge clk) begin + if(valid) begin + $display("PC = %d, insn = %d", pc_rdata, insn); + end + end + + + // + // Synchronization of NETs + // + wire clkD; + assign #1 clkD = clk; + + string name[$]; + int value[$]; + longint tslot[$]; + int nets[string]; + + function automatic void net_push(input string vname, input int vvalue); + longint vslot = $time; + name.push_front(vname); + value.push_front(vvalue); + tslot.push_front(vslot); + endfunction + + function automatic int net_pop(output string vname, output int vvalue, output longint vslot); + int ok; + string msg; + if (name.size() > 0) begin + vname = name.pop_back(); + vvalue = value.pop_back(); + vslot = tslot.pop_back(); + nets[vname] = vvalue; + ok = 1; + end else begin + ok = 0; + end + return ok; + endfunction + +endmodule + -----/\----- EXCLUDED -----/\----- */ diff --git a/pipelined/testbench/testbench_imperas.sv b/pipelined/testbench/testbench_imperas.sv new file mode 100644 index 00000000..372257c5 --- /dev/null +++ b/pipelined/testbench/testbench_imperas.sv @@ -0,0 +1,709 @@ +/////////////////////////////////////////// +// testbench.sv +// +// Written: David_Harris@hmc.edu 9 January 2021 +// Modified: +// +// Purpose: Wally Testbench and helper modules +// Applies test programs from the riscv-arch-test and Imperas suites +// +// A component of the Wally configurable RISC-V project. +// +// Copyright (C) 2021 Harvey Mudd College & Oklahoma State University +// +// MIT LICENSE +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +//////////////////////////////////////////////////////////////////////////////////////////////// + +`include "wally-config.vh" +`include "tests.vh" + +`define PrintHPMCounters 0 +`define BPRED_LOGGER 0 + +module testbench_imperas; + parameter DEBUG=0; + parameter TEST="none"; + + logic clk; + logic reset_ext, reset; + + parameter SIGNATURESIZE = 5000000; + + int test, i, errors, totalerrors; + logic [31:0] sig32[0:SIGNATURESIZE]; + logic [`XLEN-1:0] signature[0:SIGNATURESIZE]; + logic [`XLEN-1:0] testadr, testadrNoBase; + string InstrFName, InstrDName, InstrEName, InstrMName, InstrWName; + logic [31:0] InstrW; + +string tests[]; +logic [3:0] dummy; + + logic [`AHBW-1:0] HRDATAEXT; + logic HREADYEXT, HRESPEXT; + logic [`PA_BITS-1:0] HADDR; + logic [`AHBW-1:0] HWDATA; + logic [`XLEN/8-1:0] HWSTRB; + logic HWRITE; + logic [2:0] HSIZE; + logic [2:0] HBURST; + logic [3:0] HPROT; + logic [1:0] HTRANS; + logic HMASTLOCK; + logic HCLK, HRESETn; + logic [`XLEN-1:0] PCW; + + string ProgramAddrMapFile, ProgramLabelMapFile; + integer ProgramAddrLabelArray [string] = '{ "begin_signature" : 0, "tohost" : 0 }; + + logic DCacheFlushDone, DCacheFlushStart; + logic riscofTest; + + flopenr #(`XLEN) PCWReg(clk, reset, ~dut.core.ieu.dp.StallW, dut.core.ifu.PCM, PCW); + flopenr #(32) InstrWReg(clk, reset, ~dut.core.ieu.dp.StallW, dut.core.ifu.InstrM, InstrW); + + // check assertions for a legal configuration + riscvassertions riscvassertions(); + + // pick tests based on modes supported + initial begin + $display("TEST is %s", TEST); + //tests = '{}; + if (`XLEN == 64) begin // RV64 + case (TEST) + "arch64i": tests = arch64i; + "arch64priv": tests = arch64priv; + "arch64c": if (`C_SUPPORTED) + if (`ZICSR_SUPPORTED) tests = {arch64c, arch64cpriv}; + else tests = {arch64c}; + "arch64m": if (`M_SUPPORTED) tests = arch64m; + "arch64f": if (`F_SUPPORTED) tests = arch64f; + "arch64d": if (`D_SUPPORTED) tests = arch64d; + "imperas64i": tests = imperas64i; + "imperas64f": if (`F_SUPPORTED) tests = imperas64f; + "imperas64d": if (`D_SUPPORTED) tests = imperas64d; + "imperas64m": if (`M_SUPPORTED) tests = imperas64m; + "wally64a": if (`A_SUPPORTED) tests = wally64a; + "imperas64c": if (`C_SUPPORTED) tests = imperas64c; + else tests = imperas64iNOc; + "custom": tests = custom; + "wally64i": tests = wally64i; + "wally64priv": tests = wally64priv; + "wally64periph": tests = wally64periph; + "coremark": tests = coremark; + "fpga": tests = fpga; + "ahb" : tests = ahb; + endcase + end else begin // RV32 + case (TEST) + "arch32i": tests = arch32i; + "arch32priv": tests = arch32priv; + "arch32c": if (`C_SUPPORTED) + if (`ZICSR_SUPPORTED) tests = {arch32c, arch32cpriv}; + else tests = {arch32c}; + "arch32m": if (`M_SUPPORTED) tests = arch32m; + "arch32f": if (`F_SUPPORTED) tests = arch32f; + "arch32d": if (`D_SUPPORTED) tests = arch32d; + "imperas32i": tests = imperas32i; + "imperas32f": if (`F_SUPPORTED) tests = imperas32f; + "imperas32m": if (`M_SUPPORTED) tests = imperas32m; + "wally32a": if (`A_SUPPORTED) tests = wally32a; + "imperas32c": if (`C_SUPPORTED) tests = imperas32c; + else tests = imperas32iNOc; + "wally32i": tests = wally32i; + "wally32e": tests = wally32e; + "wally32priv": tests = wally32priv; + "wally32periph": tests = wally32periph; + "embench": tests = embench; + "coremark": tests = coremark; + endcase + end + if (tests.size() == 0) begin + $display("TEST %s not supported in this configuration", TEST); + $stop; + end + end + + string signame, memfilename, pathname, objdumpfilename, adrstr, outputfile; + integer outputFilePointer; + + logic [31:0] GPIOPinsIn, GPIOPinsOut, GPIOPinsEn; + logic UARTSin, UARTSout; + + logic SDCCLK; + logic SDCCmdIn; + logic SDCCmdOut; + logic SDCCmdOE; + logic [3:0] SDCDatIn; + tri1 [3:0] SDCDat; + tri1 SDCCmd; + + logic HREADY; + logic HSELEXT; + + logic InitializingMemories; + integer ResetCount, ResetThreshold; + logic InReset; + + // instantiate device to be tested + assign GPIOPinsIn = 0; + assign UARTSin = 1; + + if(`EXT_MEM_SUPPORTED) begin + ram_ahb #(.BASE(`EXT_MEM_BASE), .RANGE(`EXT_MEM_RANGE)) + ram (.HCLK, .HRESETn, .HADDR, .HWRITE, .HTRANS, .HWDATA, .HSELRam(HSELEXT), + .HREADRam(HRDATAEXT), .HREADYRam(HREADYEXT), .HRESPRam(HRESPEXT), .HREADY, + .HWSTRB); + end else begin + assign HREADYEXT = 1; + assign HRESPEXT = 0; + assign HRDATAEXT = 0; + end + + if(`FPGA) begin : sdcard + sdModel sdcard + (.sdClk(SDCCLK), + .cmd(SDCCmd), + .dat(SDCDat)); + + assign SDCCmd = SDCCmdOE ? SDCCmdOut : 1'bz; + assign SDCCmdIn = SDCCmd; + assign SDCDatIn = SDCDat; + end else begin + assign SDCCmd = '0; + assign SDCDat = '0; + end + + wallypipelinedsoc dut(.clk, .reset_ext, .reset, .HRDATAEXT,.HREADYEXT, .HRESPEXT,.HSELEXT, + .HCLK, .HRESETn, .HADDR, .HWDATA, .HWSTRB, .HWRITE, .HSIZE, .HBURST, .HPROT, + .HTRANS, .HMASTLOCK, .HREADY, .TIMECLK(1'b0), .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, + .UARTSin, .UARTSout, .SDCCmdIn, .SDCCmdOut, .SDCCmdOE, .SDCDatIn, .SDCCLK); + + // Track names of instructions + instrTrackerTB it(clk, reset, dut.core.ieu.dp.FlushE, + dut.core.ifu.FinalInstrRawF[31:0], + dut.core.ifu.InstrD, dut.core.ifu.InstrE, + dut.core.ifu.InstrM, InstrW, + InstrFName, InstrDName, InstrEName, InstrMName, InstrWName); + + // initialize tests + localparam integer MemStartAddr = 0; + localparam integer MemEndAddr = `UNCORE_RAM_RANGE>>1+(`XLEN/32); + + initial + begin + ResetCount = 0; + ResetThreshold = 2; + InReset = 1; + test = 1; + totalerrors = 0; + testadr = 0; + testadrNoBase = 0; + // riscof tests have a different signature, tests[0] == "1" refers to RiscvArchTests and tests[0] == "2" refers to WallyRiscvArchTests + riscofTest = tests[0] == "1" | tests[0] == "2"; + // fill memory with defined values to reduce Xs in simulation + // Quick note the memory will need to be initialized. The C library does not + // guarantee the initialized reads. For example a strcmp can read 6 byte + // strings, but uses a load double to read them in. If the last 2 bytes are + // not initialized the compare results in an 'x' which propagates through + // the design. + if (TEST == "coremark") + for (i=MemStartAddr; i XLEN because AHB bus width is XLEN"); + assert (`I_SUPPORTED ^ `E_SUPPORTED) else $error("Exactly one of I and E must be supported"); + assert (`FLEN<=`XLEN | `DCACHE | `DTIM_SUPPORTED) else $error("Wally does not support FLEN > XLEN unleses data cache or DTIM is supported"); + assert (`DCACHE_WAYSIZEINBYTES <= 4096 | (!`DCACHE) | `VIRTMEM_SUPPORTED == 0) else $error("DCACHE_WAYSIZEINBYTES cannot exceed 4 KiB when caches and vitual memory is enabled (to prevent aliasing)"); + assert (`DCACHE_LINELENINBITS >= 128 | (!`DCACHE)) else $error("DCACHE_LINELENINBITS must be at least 128 when caches are enabled"); + assert (`DCACHE_LINELENINBITS < `DCACHE_WAYSIZEINBYTES*8) else $error("DCACHE_LINELENINBITS must be smaller than way size"); + assert (`ICACHE_WAYSIZEINBYTES <= 4096 | (!`ICACHE) | `VIRTMEM_SUPPORTED == 0) else $error("ICACHE_WAYSIZEINBYTES cannot exceed 4 KiB when caches and vitual memory is enabled (to prevent aliasing)"); + assert (`ICACHE_LINELENINBITS >= 32 | (!`ICACHE)) else $error("ICACHE_LINELENINBITS must be at least 32 when caches are enabled"); + assert (`ICACHE_LINELENINBITS < `ICACHE_WAYSIZEINBYTES*8) else $error("ICACHE_LINELENINBITS must be smaller than way size"); + assert (2**$clog2(`DCACHE_LINELENINBITS) == `DCACHE_LINELENINBITS | (!`DCACHE)) else $error("DCACHE_LINELENINBITS must be a power of 2"); + assert (2**$clog2(`DCACHE_WAYSIZEINBYTES) == `DCACHE_WAYSIZEINBYTES | (!`DCACHE)) else $error("DCACHE_WAYSIZEINBYTES must be a power of 2"); + assert (2**$clog2(`ICACHE_LINELENINBITS) == `ICACHE_LINELENINBITS | (!`ICACHE)) else $error("ICACHE_LINELENINBITS must be a power of 2"); + assert (2**$clog2(`ICACHE_WAYSIZEINBYTES) == `ICACHE_WAYSIZEINBYTES | (!`ICACHE)) else $error("ICACHE_WAYSIZEINBYTES must be a power of 2"); + assert (2**$clog2(`ITLB_ENTRIES) == `ITLB_ENTRIES | `VIRTMEM_SUPPORTED==0) else $error("ITLB_ENTRIES must be a power of 2"); + assert (2**$clog2(`DTLB_ENTRIES) == `DTLB_ENTRIES | `VIRTMEM_SUPPORTED==0) else $error("DTLB_ENTRIES must be a power of 2"); + assert (`UNCORE_RAM_RANGE >= 56'h07FFFFFF) else $warning("Some regression tests will fail if UNCORE_RAM_RANGE is less than 56'h07FFFFFF"); + assert (`ZICSR_SUPPORTED == 1 | (`PMP_ENTRIES == 0 & `VIRTMEM_SUPPORTED == 0)) else $error("PMP_ENTRIES and VIRTMEM_SUPPORTED must be zero if ZICSR not supported."); + assert (`ZICSR_SUPPORTED == 1 | (`S_SUPPORTED == 0 & `U_SUPPORTED == 0)) else $error("S and U modes not supported if ZISR not supported"); + assert (`U_SUPPORTED | (`S_SUPPORTED == 0)) else $error ("S mode only supported if U also is supported"); + assert (`VIRTMEM_SUPPORTED == 0 | (`DTIM_SUPPORTED == 0 & `IROM_SUPPORTED == 0)) else $error("Can't simultaneously have virtual memory and DTIM_SUPPORTED/IROM_SUPPORTED because local memories don't translate addresses"); + assert (`DCACHE | `VIRTMEM_SUPPORTED ==0) else $error("Virtual memory needs dcache"); + assert (`ICACHE | `VIRTMEM_SUPPORTED ==0) else $error("Virtual memory needs icache"); + assert ((`DCACHE == 0 & `ICACHE == 0) | `BUS) else $error("Dcache and Icache requires DBUS."); + assert (`DCACHE_LINELENINBITS <= `XLEN*16 | (!`DCACHE)) else $error("DCACHE_LINELENINBITS must not exceed 16 words because max AHB burst size is 1"); + assert (`DCACHE_LINELENINBITS % 4 == 0) else $error("DCACHE_LINELENINBITS must hold 4, 8, or 16 words"); + assert (`DCACHE | `A_SUPPORTED == 0) else $error("Atomic extension (A) requires cache on Wally."); + assert (`IDIV_ON_FPU == 0 | `F_SUPPORTED) else $error("IDIV on FPU needs F_SUPPORTED"); + end + + // *** DH 8/23/ +endmodule + + +/* verilator lint_on STMTDLY */ +/* verilator lint_on WIDTH */ + +module DCacheFlushFSM + (input logic clk, + input logic reset, + input logic start, + output logic done); + + genvar adr; + + logic [`XLEN-1:0] ShadowRAM[`UNCORE_RAM_BASE>>(1+`XLEN/32):(`UNCORE_RAM_RANGE+`UNCORE_RAM_BASE)>>1+(`XLEN/32)]; + + if(`DCACHE) begin + localparam integer numlines = testbench.dut.core.lsu.bus.dcache.dcache.NUMLINES; + localparam integer numways = testbench.dut.core.lsu.bus.dcache.dcache.NUMWAYS; + localparam integer linebytelen = testbench.dut.core.lsu.bus.dcache.dcache.LINEBYTELEN; + localparam integer linelen = testbench.dut.core.lsu.bus.dcache.dcache.LINELEN; + localparam integer sramlen = testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[0].SRAMLEN; + localparam integer cachesramwords = testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[0].NUMSRAM; + +//testbench.dut.core.lsu.bus.dcache.dcache.CacheWays.NUMSRAM; + localparam integer numwords = sramlen/`XLEN; + localparam integer lognumlines = $clog2(numlines); + localparam integer loglinebytelen = $clog2(linebytelen); + localparam integer lognumways = $clog2(numways); + localparam integer tagstart = lognumlines + loglinebytelen; + + + + genvar index, way, cacheWord; + logic [sramlen-1:0] CacheData [numways-1:0] [numlines-1:0] [cachesramwords-1:0]; + logic [sramlen-1:0] cacheline; + logic [`XLEN-1:0] CacheTag [numways-1:0] [numlines-1:0] [cachesramwords-1:0]; + logic CacheValid [numways-1:0] [numlines-1:0] [cachesramwords-1:0]; + logic CacheDirty [numways-1:0] [numlines-1:0] [cachesramwords-1:0]; + logic [`PA_BITS-1:0] CacheAdr [numways-1:0] [numlines-1:0] [cachesramwords-1:0]; + for(index = 0; index < numlines; index++) begin + for(way = 0; way < numways; way++) begin + for(cacheWord = 0; cacheWord < cachesramwords; cacheWord++) begin + copyShadow #(.tagstart(tagstart), + .loglinebytelen(loglinebytelen), .sramlen(sramlen)) + copyShadow(.clk, + .start, + .tag(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].CacheTagMem.RAM[index][`PA_BITS-1-tagstart:0]), + .valid(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].ValidBits[index]), + .dirty(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].DirtyBits[index]), + // these dirty bit selections would be needed if dirty is moved inside the tag array. + //.dirty(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].dirty.DirtyMem.RAM[index]), + //.dirty(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].CacheTagMem.RAM[index][`PA_BITS+tagstart]), + .data(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].word[cacheWord].CacheDataMem.RAM[index]), + .index(index), + .cacheWord(cacheWord), + .CacheData(CacheData[way][index][cacheWord]), + .CacheAdr(CacheAdr[way][index][cacheWord]), + .CacheTag(CacheTag[way][index][cacheWord]), + .CacheValid(CacheValid[way][index][cacheWord]), + .CacheDirty(CacheDirty[way][index][cacheWord])); + end + end + end + + integer i, j, k, l; + + always @(posedge clk) begin + if (start) begin #1 + #1 + for(i = 0; i < numlines; i++) begin + for(j = 0; j < numways; j++) begin + for(l = 0; l < cachesramwords; l++) begin + if (CacheValid[j][i][l] & CacheDirty[j][i][l]) begin + for(k = 0; k < numwords; k++) begin + //cacheline = CacheData[j][i][0]; + // does not work with modelsim + // # ** Error: ../testbench/testbench.sv(483): Range must be bounded by constant expressions. + // see https://verificationacademy.com/forums/systemverilog/range-must-be-bounded-constant-expressions + //ShadowRAM[CacheAdr[j][i][k] >> $clog2(`XLEN/8)] = cacheline[`XLEN*(k+1)-1:`XLEN*k]; + ShadowRAM[(CacheAdr[j][i][l] >> $clog2(`XLEN/8)) + k] = CacheData[j][i][l][`XLEN*k +: `XLEN]; + end + end + end + end + end + end + end + end + flop #(1) doneReg(.clk, .d(start), .q(done)); +endmodule + +module copyShadow + #(parameter tagstart, loglinebytelen, sramlen) + (input logic clk, + input logic start, + input logic [`PA_BITS-1:tagstart] tag, + input logic valid, dirty, + input logic [sramlen-1:0] data, + input logic [32-1:0] index, + input logic [32-1:0] cacheWord, + output logic [sramlen-1:0] CacheData, + output logic [`PA_BITS-1:0] CacheAdr, + output logic [`XLEN-1:0] CacheTag, + output logic CacheValid, + output logic CacheDirty); + + + always_ff @(posedge clk) begin + if(start) begin + CacheTag = tag; + CacheValid = valid; + CacheDirty = dirty; + CacheData = data; + CacheAdr = (tag << tagstart) + (index << loglinebytelen) + (cacheWord << $clog2(sramlen/8)); + end + end + +endmodule + +task automatic updateProgramAddrLabelArray; + input string ProgramAddrMapFile, ProgramLabelMapFile; + inout integer ProgramAddrLabelArray [string]; + // Gets the memory location of begin_signature + integer ProgramLabelMapFP, ProgramAddrMapFP; + ProgramLabelMapFP = $fopen(ProgramLabelMapFile, "r"); + ProgramAddrMapFP = $fopen(ProgramAddrMapFile, "r"); + + if (ProgramLabelMapFP & ProgramAddrMapFP) begin // check we found both files + while (!$feof(ProgramLabelMapFP)) begin + string label, adrstr; + integer returncode; + returncode = $fscanf(ProgramLabelMapFP, "%s\n", label); + returncode = $fscanf(ProgramAddrMapFP, "%s\n", adrstr); + if (ProgramAddrLabelArray.exists(label)) + ProgramAddrLabelArray[label] = adrstr.atohex(); + end + end + $fclose(ProgramLabelMapFP); + $fclose(ProgramAddrMapFP); +endtask + +`define NUM_REGS 32 +`define NUM_CSRS 4096 + +module rvviTrace(); + + // wally specific signals + logic reset; + + logic [`XLEN-1:0] PCM, PCW; + logic [`XLEN-1:0] InstrRawD, InstrRawE, InstrRawM, InstrRawW; + logic InstrValidM, InstrValidW; + logic StallE, StallM, StallW; + logic FlushE, FlushM, FlushW; + + // tracer signals + logic clk; + logic valid; + logic [`XLEN-1:0] insn; + logic [`XLEN-1:0 ] pc_rdata; + + assign clk = testbench.dut.clk; + assign InstrValidM = testbench.dut.core.ieu.InstrValidM; + assign InstrRawD = testbench.dut.core.ifu.InstrRawD; + assign PCM = testbench.dut.core.ifu.PCM; + assign reset = testbench.reset; + assign StallE = testbench.dut.core.StallE; + assign StallM = testbench.dut.core.StallM; + assign StallW = testbench.dut.core.StallW; + assign FlushE = testbench.dut.core.FlushE; + assign FlushM = testbench.dut.core.FlushM; + assign FlushW = testbench.dut.core.FlushW; + + // pipeline to writeback stage + flopenrc #(`XLEN) InstrRawEReg (clk, reset, FlushE, ~StallE, InstrRawD, InstrRawE); + flopenrc #(`XLEN) InstrRawMReg (clk, reset, FlushM, ~StallM, InstrRawE, InstrRawM); + flopenrc #(`XLEN) InstrRawWReg (clk, reset, FlushW, ~StallW, InstrRawM, InstrRawW); + flopenrc #(`XLEN) PCWReg (clk, reset, FlushW, ~StallW, PCM, PCW); + flopenrc #(1) InstrValidMReg (clk, reset, FlushW, ~StallW, InstrValidM, InstrValidW); + + assign valid = InstrValidW; + assign insn = InstrRawW; + assign pc_rdata = PCW; + + always_ff @(posedge clk) begin + if(valid) begin + $display("PC = %x, insn = %x", pc_rdata, insn); + end + end + + +endmodule +