/////////////////////////////////////////// // 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" module testbench; 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; string ProgramAddrMapFile, ProgramLabelMapFile; logic [`AHBW-1:0] HRDATAEXT; logic HREADYEXT, HRESPEXT; logic [31:0] HADDR; logic [`AHBW-1:0] HWDATA; 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; logic DCacheFlushDone, DCacheFlushStart; 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(); string signame, memfilename, pathname, romfilename, sdcfilename; 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; sdModel sdcard (.sdClk(SDCCLK), .cmd(SDCCmd), .dat(SDCDat)); assign SDCCmd = SDCCmdOE ? SDCCmdOut : 1'bz; assign SDCCmdIn = SDCCmd; assign SDCDatIn = SDCDat; // instantiate device to be tested assign GPIOPinsIn = 0; assign UARTSin = 1; wallypipelinedsoc dut(.clk, .reset_ext, .reset, .HRDATAEXT,.HREADYEXT, .HRESPEXT,.HSELEXT, .HCLK, .HRESETn, .HADDR, .HWDATA, .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 = `RAM_BASE>>(1+`XLEN/32); localparam integer MemEndAddr = (`RAM_RANGE+`RAM_BASE)>>1+(`XLEN/32); initial begin test = 1; totalerrors = 0; testadr = 0; testadrNoBase = 0; // 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") // read test vectors into memory pathname = tvpaths[tests[0].atoi()]; /* if (tests[0] == `IMPERASTEST) pathname = tvpaths[0]; else pathname = tvpaths[1]; */ memfilename = {pathname, tests[test], ".elf.memfile"}; romfilename = {"../../tests/testsBP/fpga-test-sdc/bin/fpga-test-sdc.memfile"}; sdcfilename = {"../testbench/sdc/ramdisk2.hex"}; //$readmemh(romfilename, dut.wallypipelinedsoc.uncore.bootrom.bootrom.memory.RAM); $readmemh(sdcfilename, sdcard.FLASHmem); ProgramAddrMapFile = {pathname, tests[test], ".elf.objdump.addr"}; ProgramLabelMapFile = {pathname, tests[test], ".elf.objdump.lab"}; $display("Read memfile %s", memfilename); reset_ext = 0; #97 reset_ext = 1; # 1000; reset_ext = 0; end // generate clock to sequence tests always begin clk = 1; # 5; clk = 0; # 5; // if ($time % 100000 == 0) $display("Time is %0t", $time); end // check results always @(negedge clk) begin if (TEST == "coremark") if (dut.core.priv.priv.ecallM) begin $display("Benchmark: coremark is done."); $stop; end if (DCacheFlushDone) begin #600; // give time for instructions in pipeline to finish // clear signature to prevent contamination from previous tests for(i=0; i= 128 | (`DMEM != `MEM_CACHE)) 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 | (`IMEM != `MEM_CACHE) | `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 | (`IMEM != `MEM_CACHE)) 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 | (`DMEM != `MEM_CACHE)) else $error("DCACHE_LINELENINBITS must be a power of 2"); assert (2**$clog2(`DCACHE_WAYSIZEINBYTES) == `DCACHE_WAYSIZEINBYTES | (`DMEM != `MEM_CACHE)) else $error("DCACHE_WAYSIZEINBYTES must be a power of 2"); assert (2**$clog2(`ICACHE_LINELENINBITS) == `ICACHE_LINELENINBITS | (`IMEM != `MEM_CACHE)) else $error("ICACHE_LINELENINBITS must be a power of 2"); assert (2**$clog2(`ICACHE_WAYSIZEINBYTES) == `ICACHE_WAYSIZEINBYTES | (`IMEM != `MEM_CACHE)) 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 (`RAM_RANGE >= 56'h07FFFFFF) else $warning("Some regression tests will fail if 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 (`MEM_DCACHE == 0 | `MEM_DTIM == 0) else $error("Can't simultaneously have a data cache and TIM"); assert (`DMEM == `MEM_CACHE | `VIRTMEM_SUPPORTED ==0) else $error("Virtual memory needs dcache"); assert (`IMEM == `MEM_CACHE | `VIRTMEM_SUPPORTED ==0) else $error("Virtual memory needs icache"); assert (`DMEM == `MEM_CACHE | `DBUS ==0) else $error("Dcache rquires DBUS."); assert (`IMEM == `MEM_CACHE | `IBUS ==0) else $error("Icache rquires IBUS."); end 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[`RAM_BASE>>(1+`XLEN/32):(`RAM_RANGE+`RAM_BASE)>>1+(`XLEN/32)]; if(`DMEM == `MEM_CACHE) 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 numwords = testbench.dut.core.lsu.bus.dcache.dcache.LINELEN/`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 [`XLEN-1:0] CacheData [numways-1:0] [numlines-1:0] [numwords-1:0]; logic [`XLEN-1:0] CacheTag [numways-1:0] [numlines-1:0] [numwords-1:0]; logic CacheValid [numways-1:0] [numlines-1:0] [numwords-1:0]; logic CacheDirty [numways-1:0] [numlines-1:0] [numwords-1:0]; logic [`PA_BITS-1:0] CacheAdr [numways-1:0] [numlines-1:0] [numwords-1:0]; for(index = 0; index < numlines; index++) begin for(way = 0; way < numways; way++) begin for(cacheWord = 0; cacheWord < numwords; cacheWord++) begin copyShadow #(.tagstart(tagstart), .loglinebytelen(loglinebytelen)) copyShadow(.clk, .start, .tag(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].CacheTagMem.StoredData[index]), .valid(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].ValidBits[index]), .dirty(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].DirtyBits[index]), .data(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].word[cacheWord].CacheDataMem.StoredData[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; always @(posedge clk) begin if (start) begin #1 #1 for(i = 0; i < numlines; i++) begin for(j = 0; j < numways; j++) begin for(k = 0; k < numwords; k++) begin if (CacheValid[j][i][k] & CacheDirty[j][i][k]) begin ShadowRAM[CacheAdr[j][i][k] >> $clog2(`XLEN/8)] = CacheData[j][i][k]; end end end end end end end flop #(1) doneReg(.clk, .d(start), .q(done)); endmodule module copyShadow #(parameter tagstart, loglinebytelen) (input logic clk, input logic start, input logic [`PA_BITS-1:tagstart] tag, input logic valid, dirty, input logic [`XLEN-1:0] data, input logic [32-1:0] index, input logic [32-1:0] cacheWord, output logic [`XLEN-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(`XLEN/8)); end end endmodule