diff --git a/sim/regression-wally b/sim/regression-wally index f58c8bdfe..a11679c2b 100755 --- a/sim/regression-wally +++ b/sim/regression-wally @@ -34,6 +34,7 @@ coverage = '-coverage' in sys.argv fp = '-fp' in sys.argv nightly = '-nightly' in sys.argv softfloat = '-softfloat' in sys.argv +soc = '-soc' in sys.argv TestCase = namedtuple("TestCase", ['name', 'variant', 'cmd', 'grepstr']) # name: the name of this test configuration (used in printing human-readable @@ -375,10 +376,20 @@ if (softfloat): cmd="vsim > {} -c < {} -c < +# Example: do wally-batch.do rv32imc imperas-32i + +# Use this wally-batch.do file to run this example. +# Either bring up ModelSim and type the following at the "ModelSim>" prompt: +# do wally-batch.do +# or, to run from a shell, type the following at the shell prompt: +# vsim -do wally-batch.do -c +# (omit the "-c" to see the GUI while running from the shell) + +onbreak {resume} + +# create library +if [file exists wkdir/work_${1}_${2}] { + vdel -lib wkdir/work_${1}_${2} -all +} +vlib wkdir/work_${1}_${2} +# Create directory for coverage data +mkdir -p cov + +set coverage 0 +set CoverageVoptArg "" +set CoverageVsimArg "" + +# Need to be able to pass arguments to vopt. Unforunately argv does not work because +# it takes on different values if vsim and the do file are called from the command line or +# if the do file isd called from questa sim directly. This chunk of code uses the $4 through $n +# variables and compacts into a single list for passing to vopt. +set configOptions "" +set from 4 +set step 1 +set lst {} +for {set i 0} true {incr i} { + set x [expr {$i*$step + $from}] + if {$x > $argc} break + set arg [expr "$$x"] + lappend lst $arg +} + +if {$argc >= 3} { + if {$3 eq "-coverage" || ($argc >= 7 && $7 eq "-coverage")} { + set coverage 1 + set CoverageVoptArg "+cover=sbecf" + set CoverageVsimArg "-coverage" + } elseif {$3 eq "configOptions"} { + set configOptions $lst + puts $configOptions + } +} + +# compile source files +# suppress spurious warnngs about +# "Extra checking for conflicts with always_comb done at vopt time" +# because vsim will run vopt + +# default to config/rv64ic, but allow this to be overridden at the command line. For example: +# do wally-pipelined-batch.do ../config/rv32imc rv32imc + +# Compile bsg files, then wally +set bsg_dir ../soc/src/basejump_stl +vlog -lint -work wkdir/work_${1}_${2} +define+den2048Mb +define+sg5 +define+x32 +incdir+$bsg_dir/bsg_clk_gen +incdir+$bsg_dir/bsg_dmc +incdir+$bsg_dir/bsg_noc +incdir+$bsg_dir/bsg_tag +incdir+$bsg_dir/testing/bsg_dmc/lpddr_verilog_model +incdir+$bsg_dir/bsg_misc +incdir+$bsg_dir/testing/bsg_dmc/lpddr_verilog_model $bsg_dir/bsg_misc/bsg_defines.sv $bsg_dir/bsg_tag/bsg_tag_pkg.sv $bsg_dir/bsg_dmc/bsg_dmc_pkg.sv $bsg_dir/bsg_noc/bsg_noc_pkg.sv $bsg_dir/bsg_noc/bsg_mesh_router_pkg.sv $bsg_dir/bsg_noc/bsg_wormhole_router_pkg.sv $bsg_dir/*/*.sv $bsg_dir/testing/bsg_dmc/lpddr_verilog_model/*.sv -suppress 2583,2596,2605,2902,7063,8885,13286,13314,13388 +vlog -lint -work wkdir/work_${1}_${2} +incdir+../config/$1 +incdir+../config/deriv/$1 +incdir+../config/shared +incdir+$bsg_dir/bsg_clk_gen +incdir+$bsg_dir/bsg_dmc +incdir+$bsg_dir/dmc_misc +incdir+$bsg_dir/bsg_misc +incdir+$bsg_dir/bsg_tag +incdir+$bsg_dir/testing/bsg_dmc/lpddr_verilog_model ../src/cvw.sv ../testbench/testbench-soc.sv ../testbench/common/*.sv ../src/*/*.sv ../src/*/*/*.sv ../soc/src/fifo/*.sv ../soc/src/*.sv -suppress 2583,2596,2605,2902,7063,8885,13286,13314,13388 + +# start and run simulation +# remove +acc flag for faster sim during regressions if there is no need to access internal signals +vopt wkdir/work_${1}_${2}.testbench -work wkdir/work_${1}_${2} -G TEST=$2 ${configOptions} -o testbenchopt ${CoverageVoptArg} +vsim -lib wkdir/work_${1}_${2} testbenchopt -fatal 7 -suppress 3009,3829,8885 ${CoverageVsimArg} + +# power add generates the logging necessary for said generation. +# power add -r /dut/core/* +run -all +# power off -r /dut/core/* + + +if {$coverage} { + echo "Saving coverage to ${1}_${2}.ucdb" + do coverage-exclusions-rv64gc.do # beware: this assumes testing the rv64gc configuration + coverage save -instance /testbench/dut/core cov/${1}_${2}.ucdb +} + +# These aren't doing anything helpful +#profile report -calltree -file wally-calltree.rpt -cutoff 2 +#power report -all -bsaif power.saif +quit diff --git a/testbench/testbench-soc.sv b/testbench/testbench-soc.sv new file mode 100644 index 000000000..320ffad41 --- /dev/null +++ b/testbench/testbench-soc.sv @@ -0,0 +1,922 @@ +/////////////////////////////////////////// +// 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 +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + +`include "config.vh" +`include "tests.vh" +`include "BranchPredictorType.vh" + +`ifdef USE_IMPERAS_DV + `include "idv/idv.svh" +`endif + +import cvw::*; + +module testbench; + /* verilator lint_off WIDTHTRUNC */ + /* verilator lint_off WIDTHEXPAND */ + parameter DEBUG=0; + parameter string TEST="arch64m"; + parameter PrintHPMCounters=0; + parameter BPRED_LOGGER=0; + parameter I_CACHE_ADDR_LOGGER=0; + parameter D_CACHE_ADDR_LOGGER=0; + parameter RISCV_DIR = "/opt/riscv"; + parameter INSTR_LIMIT = 0; + + `ifdef USE_IMPERAS_DV + import idvPkg::*; + import rvviApiPkg::*; + import idvApiPkg::*; + `endif + +`include "parameter-defs.vh" + + logic clk; + logic reset_ext, reset; + logic ResetMem; + + // DUT signals + logic [P.AHBW-1:0] HRDATAEXT; + logic HREADYEXT, HRESPEXT; + logic HSELEXTSDC; + logic [P.PA_BITS-1:0] HADDR; + logic [P.AHBW-1:0] HWDATA; + logic [P.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 [31:0] GPIOIN, GPIOOUT, GPIOEN; + logic UARTSin, UARTSout; + logic SPIIn, SPIOut; + logic [3:0] SPICS; + logic SDCIntr; + + logic HREADY; + logic HSELEXT; + + + string ProgramAddrMapFile, ProgramLabelMapFile; + integer ProgramAddrLabelArray [string]; + + int test, i, errors, totalerrors; + + string outputfile; + integer outputFilePointer; + + string tests[]; + logic DCacheFlushDone, DCacheFlushStart; + logic riscofTest; + logic Validate; + logic SelectTest; + logic TestComplete; + + // pick tests based on modes supported + initial begin + $display("TEST is %s", TEST); + //tests = '{}; + if (P.XLEN == 64) begin // RV64 + case (TEST) + "arch64i": tests = arch64i; + "arch64priv": tests = arch64priv; + "arch64c": if (P.C_SUPPORTED) + if (P.ZICSR_SUPPORTED) tests = {arch64c, arch64cpriv}; + else tests = {arch64c}; + "arch64m": if (P.M_SUPPORTED) tests = arch64m; + "arch64a": if (P.A_SUPPORTED) tests = arch64a; + "arch64f": if (P.F_SUPPORTED) tests = arch64f; + "arch64d": if (P.D_SUPPORTED) tests = arch64d; + "arch64f_fma": if (P.F_SUPPORTED) tests = arch64f_fma; + "arch64d_fma": if (P.D_SUPPORTED) tests = arch64d_fma; + "arch64f_divsqrt": if (P.F_SUPPORTED) tests = arch64f_divsqrt; + "arch64d_divsqrt": if (P.D_SUPPORTED) tests = arch64d_divsqrt; + "arch64zifencei": if (P.ZIFENCEI_SUPPORTED) tests = arch64zifencei; + "arch64zicond": if (P.ZICOND_SUPPORTED) tests = arch64zicond; + "imperas64i": tests = imperas64i; + "imperas64f": if (P.F_SUPPORTED) tests = imperas64f; + "imperas64d": if (P.D_SUPPORTED) tests = imperas64d; + "imperas64m": if (P.M_SUPPORTED) tests = imperas64m; + "wally64a": if (P.A_SUPPORTED) tests = wally64a; + "imperas64c": if (P.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; + "ahb64" : tests = ahb64; + "coverage64gc" : tests = coverage64gc; + "arch64zba": if (P.ZBA_SUPPORTED) tests = arch64zba; + "arch64zbb": if (P.ZBB_SUPPORTED) tests = arch64zbb; + "arch64zbc": if (P.ZBC_SUPPORTED) tests = arch64zbc; + "arch64zbs": if (P.ZBS_SUPPORTED) tests = arch64zbs; + "arch64zicboz": if (P.ZICBOZ_SUPPORTED) tests = arch64zicboz; + "arch64zcb": if (P.ZCB_SUPPORTED) tests = arch64zcb; + "arch64zfh": if (P.ZFH_SUPPORTED) tests = arch64zfh; + "arch64zfh_fma": if (P.ZFH_SUPPORTED) tests = arch64zfh_fma; + "arch64zfh_divsqrt": if (P.ZFH_SUPPORTED) tests = arch64zfh_divsqrt; + "arch64zfaf": if (P.ZFA_SUPPORTED) tests = arch64zfaf; + "arch64zfad": if (P.ZFA_SUPPORTED & P.D_SUPPORTED) tests = arch64zfad; + "buildroot": tests = buildroot; + endcase + end else begin // RV32 + case (TEST) + "arch32e": tests = arch32e; + "arch32i": tests = arch32i; + "arch32priv": tests = arch32priv; + "arch32c": if (P.C_SUPPORTED) + if (P.ZICSR_SUPPORTED) tests = {arch32c, arch32cpriv}; + else tests = {arch32c}; + "arch32m": if (P.M_SUPPORTED) tests = arch32m; + "arch32a": if (P.A_SUPPORTED) tests = arch32a; + "arch32f": if (P.F_SUPPORTED) tests = arch32f; + "arch32d": if (P.D_SUPPORTED) tests = arch32d; + "arch32f_fma": if (P.F_SUPPORTED) tests = arch32f_fma; + "arch32d_fma": if (P.D_SUPPORTED) tests = arch32d_fma; + "arch32f_divsqrt": if (P.F_SUPPORTED) tests = arch32f_divsqrt; + "arch32d_divsqrt": if (P.D_SUPPORTED) tests = arch32d_divsqrt; + "arch32zifencei": if (P.ZIFENCEI_SUPPORTED) tests = arch32zifencei; + "arch32zicond": if (P.ZICOND_SUPPORTED) tests = arch32zicond; + "imperas32i": tests = imperas32i; + "imperas32f": if (P.F_SUPPORTED) tests = imperas32f; + "imperas32m": if (P.M_SUPPORTED) tests = imperas32m; + "wally32a": if (P.A_SUPPORTED) tests = wally32a; + "imperas32c": if (P.C_SUPPORTED) tests = imperas32c; + else tests = imperas32iNOc; + "wally32i": tests = wally32i; + "wally32priv": tests = wally32priv; + "wally32periph": tests = wally32periph; + "ahb32" : tests = ahb32; + "embench": tests = embench; + "coremark": tests = coremark; + "arch32zba": if (P.ZBA_SUPPORTED) tests = arch32zba; + "arch32zbb": if (P.ZBB_SUPPORTED) tests = arch32zbb; + "arch32zbc": if (P.ZBC_SUPPORTED) tests = arch32zbc; + "arch32zbs": if (P.ZBS_SUPPORTED) tests = arch32zbs; + "arch32zicboz": if (P.ZICBOZ_SUPPORTED) tests = arch32zicboz; + "arch32zcb": if (P.ZCB_SUPPORTED) tests = arch32zcb; + "arch32zfh": if (P.ZFH_SUPPORTED) tests = arch32zfh; + "arch32zfh_fma": if (P.ZFH_SUPPORTED) tests = arch32zfh_fma; + "arch32zfh_divsqrt": if (P.ZFH_SUPPORTED) tests = arch32zfh_divsqrt; + "arch32zfaf": if (P.ZFA_SUPPORTED) tests = arch32zfaf; + "arch32zfad": if (P.ZFA_SUPPORTED & P.D_SUPPORTED) tests = arch32zfad; + endcase + end + if (tests.size() == 0) begin + $display("TEST %s not supported in this configuration", TEST); + $finish; + end + end // initial begin + + // Model the testbench as an fsm. + // Do this in parts so it easier to verify + // part 1: build a version which echos the same behavior as the below code, but does not drive anything + // part 2: drive some of the controls + // part 3: drive all logic and remove old inital and always @ negedge clk block + + typedef enum logic [3:0]{STATE_TESTBENCH_RESET, + STATE_INIT_TEST, + STATE_RESET_MEMORIES, + STATE_RESET_MEMORIES2, + STATE_LOAD_MEMORIES, + STATE_RESET_TEST, + STATE_RUN_TEST, + STATE_COPY_RAM, + STATE_CHECK_TEST, + STATE_CHECK_TEST_WAIT, + STATE_VALIDATE, + STATE_INCR_TEST} statetype; + statetype CurrState, NextState; + logic TestBenchReset; + logic [2:0] ResetCount, ResetThreshold; + logic LoadMem; + logic ResetCntEn; + logic ResetCntRst; + logic CopyRAM; + + string signame, memfilename, bootmemfilename, pathname; + integer begin_signature_addr, end_signature_addr, signature_size; + + assign ResetThreshold = 3'd5; + + initial begin + TestBenchReset = 1; + # 100; + TestBenchReset = 0; + end + + always_ff @(posedge clk) + if (TestBenchReset) CurrState <= #1 STATE_TESTBENCH_RESET; + else CurrState <= #1 NextState; + + // fsm next state logic + always_comb begin + // 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"; + pathname = tvpaths[tests[0].atoi()]; + + case(CurrState) + STATE_TESTBENCH_RESET: NextState = STATE_INIT_TEST; + STATE_INIT_TEST: NextState = STATE_RESET_MEMORIES; + STATE_RESET_MEMORIES: NextState = STATE_RESET_MEMORIES2; + STATE_RESET_MEMORIES2: NextState = STATE_LOAD_MEMORIES; // Give the reset enough time to ensure the bus is reset before loading the memories. + STATE_LOAD_MEMORIES: NextState = STATE_RESET_TEST; + STATE_RESET_TEST: if(ResetCount < ResetThreshold) NextState = STATE_RESET_TEST; + else NextState = STATE_RUN_TEST; + STATE_RUN_TEST: if(TestComplete) NextState = STATE_COPY_RAM; + else NextState = STATE_RUN_TEST; + STATE_COPY_RAM: NextState = STATE_CHECK_TEST; + STATE_CHECK_TEST: if (DCacheFlushDone) NextState = STATE_VALIDATE; + else NextState = STATE_CHECK_TEST_WAIT; + STATE_CHECK_TEST_WAIT: if(DCacheFlushDone) NextState = STATE_VALIDATE; + else NextState = STATE_CHECK_TEST_WAIT; + STATE_VALIDATE: NextState = STATE_INIT_TEST; + STATE_INCR_TEST: NextState = STATE_INIT_TEST; + default: NextState = STATE_TESTBENCH_RESET; + endcase + end // always_comb + // fsm output control logic + assign reset_ext = CurrState == STATE_TESTBENCH_RESET | CurrState == STATE_INIT_TEST | + CurrState == STATE_RESET_MEMORIES | CurrState == STATE_RESET_MEMORIES2 | + CurrState == STATE_LOAD_MEMORIES | CurrState ==STATE_RESET_TEST; + // this initialization is very expensive, only do it for coremark. + assign ResetMem = (CurrState == STATE_RESET_MEMORIES | CurrState == STATE_RESET_MEMORIES2) & TEST == "coremark"; + assign LoadMem = CurrState == STATE_LOAD_MEMORIES; + assign ResetCntRst = CurrState == STATE_INIT_TEST; + assign ResetCntEn = CurrState == STATE_RESET_TEST; + assign Validate = CurrState == STATE_VALIDATE; + assign SelectTest = CurrState == STATE_INIT_TEST; + assign CopyRAM = TestComplete & CurrState == STATE_RUN_TEST; + assign DCacheFlushStart = CurrState == STATE_COPY_RAM; + + // fsm reset counter + counter #(3) RstCounter(clk, ResetCntRst, ResetCntEn, ResetCount); + + //////////////////////////////////////////////////////////////////////////////// + // Find the test vector files and populate the PC to function label converter + //////////////////////////////////////////////////////////////////////////////// + logic [P.XLEN-1:0] testadr; + assign begin_signature_addr = ProgramAddrLabelArray["begin_signature"]; + assign end_signature_addr = ProgramAddrLabelArray["sig_end_canary"]; + assign signature_size = end_signature_addr - begin_signature_addr; + always @(posedge clk) begin + if(SelectTest) begin + if (riscofTest) memfilename = {pathname, tests[test], "/ref/ref.elf.memfile"}; + else if(TEST == "buildroot") begin + memfilename = {RISCV_DIR, "/linux-testvectors/ram.bin"}; + bootmemfilename = {RISCV_DIR, "/linux-testvectors/bootmem.bin"}; + end + else memfilename = {pathname, tests[test], ".elf.memfile"}; + if (riscofTest) begin + ProgramAddrMapFile = {pathname, tests[test], "/ref/ref.elf.objdump.addr"}; + ProgramLabelMapFile = {pathname, tests[test], "/ref/ref.elf.objdump.lab"}; + end else if (TEST == "buildroot") begin + ProgramAddrMapFile = {RISCV_DIR, "/buildroot/output/images/disassembly/vmlinux.objdump.addr"}; + ProgramLabelMapFile = {RISCV_DIR, "/buildroot/output/images/disassembly/vmlinux.objdump.lab"}; + end else begin + ProgramAddrMapFile = {pathname, tests[test], ".elf.objdump.addr"}; + ProgramLabelMapFile = {pathname, tests[test], ".elf.objdump.lab"}; + end + // declare memory labels that interest us, the updateProgramAddrLabelArray task will find + // the addr of each label and fill the array. To expand, add more elements to this array + // and initialize them to zero (also initilaize them to zero at the start of the next test) + updateProgramAddrLabelArray(ProgramAddrMapFile, ProgramLabelMapFile, ProgramAddrLabelArray); + end + + //////////////////////////////////////////////////////////////////////////////// + // Verify the test ran correctly by checking the memory against a known signature. + //////////////////////////////////////////////////////////////////////////////// + if(TestBenchReset) test = 1; + if (TEST == "coremark") + if (dut.core.priv.priv.EcallFaultM) begin + $display("Benchmark: coremark is done."); + $stop; + end + if(Validate) begin + if (TEST == "embench") begin + // Writes contents of begin_signature to .sim.output file + // this contains instret and cycles for start and end of test run, used by embench + // python speed script to calculate embench speed score. + // also, begin_signature contains the results of the self checking mechanism, + // which will be read by the python script for error checking + $display("Embench Benchmark: %s is done.", tests[test]); + if (riscofTest) outputfile = {pathname, tests[test], "/ref/ref.sim.output"}; + else outputfile = {pathname, tests[test], ".sim.output"}; + outputFilePointer = $fopen(outputfile, "w"); + i = 0; + testadr = ($unsigned(begin_signature_addr))/(P.XLEN/8); + while ($unsigned(i) < $unsigned(5'd5)) begin + $fdisplayh(outputFilePointer, DCacheFlushFSM.ShadowRAM[testadr+i]); + i = i + 1; + end + $fclose(outputFilePointer); + $display("Embench Benchmark: created output file: %s", outputfile); + end else if (TEST == "coverage64gc") begin + $display("Coverage tests don't get checked"); + end else begin + // for tests with no self checking mechanism, read .signature.output file and compare to check for errors + // clear signature to prevent contamination from previous tests + if (!begin_signature_addr) + $display("begin_signature addr not found in %s", ProgramLabelMapFile); + else if (TEST != "embench") begin // *** quick hack for embench. need a better long term solution + CheckSignature(pathname, tests[test], riscofTest, begin_signature_addr, errors); + if(errors > 0) totalerrors = totalerrors + 1; + end + end + test = test + 1; // *** this probably needs to be moved. + if (test == tests.size()) begin + if (totalerrors == 0) $display("SUCCESS! All tests ran without failures."); + else $display("FAIL: %d test programs had errors", totalerrors); + $stop; // if this is changed to $finish, wally-batch.do does not go to the next step to run coverage + end + end + end + + + //////////////////////////////////////////////////////////////////////////////// + // load memories with program image + //////////////////////////////////////////////////////////////////////////////// + + integer ShadowIndex; + integer LogXLEN; + integer StartIndex; + integer EndIndex; + integer BaseIndex; + integer memFile; + integer readResult; + if (P.SDC_SUPPORTED) begin + always @(posedge clk) begin + if (LoadMem) begin + string romfilename, sdcfilename; + romfilename = {"../tests/custom/fpga-test-sdc/bin/fpga-test-sdc.memfile"}; + sdcfilename = {"../testbench/sdc/ramdisk2.hex"}; + //$readmemh(romfilename, dut.uncore.uncore.bootrom.bootrom.memory.ROM); + //$readmemh(sdcfilename, sdcard.sdcard.FLASHmem); + // shorten sdc timers for simulation + //dut.uncore.uncore.sdc.SDC.LimitTimers = 1; + end + end + end else if (P.IROM_SUPPORTED) begin + always @(posedge clk) begin + if (LoadMem) begin + $readmemh(memfilename, dut.core.ifu.irom.irom.rom.ROM); + end + end + end else if (P.BUS_SUPPORTED) begin : bus_supported + always @(posedge clk) begin + if (LoadMem) begin + if (TEST == "buildroot") begin + memFile = $fopen(bootmemfilename, "rb"); + readResult = $fread(dut.uncore.uncore.bootrom.bootrom.memory.ROM, memFile); + $fclose(memFile); + memFile = $fopen(memfilename, "rb"); + readResult = $fread(dut.uncore.uncore.ram.ram.memory.RAM, memFile); + $fclose(memFile); + end else + $readmemh(memfilename, dut.uncore.uncore.ram.ram.memory.RAM); + if (TEST == "embench") $display("Read memfile %s", memfilename); + end + if (CopyRAM) begin + LogXLEN = (1 + P.XLEN/32); // 2 for rv32 and 3 for rv64 + StartIndex = begin_signature_addr >> LogXLEN; + EndIndex = (end_signature_addr >> LogXLEN) + 8; + BaseIndex = P.UNCORE_RAM_BASE >> LogXLEN; + for(ShadowIndex = StartIndex; ShadowIndex <= EndIndex; ShadowIndex++) begin + testbench.DCacheFlushFSM.ShadowRAM[ShadowIndex] = dut.uncore.uncore.ram.ram.memory.RAM[ShadowIndex - BaseIndex]; + end + end + end + end + if (P.DTIM_SUPPORTED) begin + always @(posedge clk) begin + if (LoadMem) begin + $readmemh(memfilename, dut.core.lsu.dtim.dtim.ram.RAM); + $display("Read memfile %s", memfilename); + end + if (CopyRAM) begin + LogXLEN = (1 + P.XLEN/32); // 2 for rv32 and 3 for rv64 + StartIndex = begin_signature_addr >> LogXLEN; + EndIndex = (end_signature_addr >> LogXLEN) + 8; + BaseIndex = P.UNCORE_RAM_BASE >> LogXLEN; + for(ShadowIndex = StartIndex; ShadowIndex <= EndIndex; ShadowIndex++) begin + testbench.DCacheFlushFSM.ShadowRAM[ShadowIndex] = dut.core.lsu.dtim.dtim.ram.RAM[ShadowIndex - BaseIndex]; + end + end + end + end + + integer adrindex; + if (P.UNCORE_RAM_SUPPORTED) + always @(posedge clk) + if (ResetMem) // program memory is sometimes reset (e.g. for CoreMark, which needs zeroed memory) + for (adrindex=0; adrindex<(P.UNCORE_RAM_RANGE>>1+(P.XLEN/32)); adrindex = adrindex+1) + dut.uncore.uncore.ram.ram.memory.RAM[adrindex] = '0; + + //////////////////////////////////////////////////////////////////////////////// + // Actual hardware + //////////////////////////////////////////////////////////////////////////////// + + // instantiate device to be tested + assign GPIOIN = 0; + assign UARTSin = 1; + assign SPIIn = 0; + + // Use bsg_dmc memory controller and LPDDR model + + logic ddr_ck_p; + logic ddr_ck_n; + logic ddr_cke; + logic [2:0] ddr_ba; + logic [13:0] ddr_addr; + logic ddr_cs; + logic ddr_ras; + logic ddr_cas; + logic ddr_we; + logic ddr_reset; + logic ddr_odt; + logic [P.XLEN/16-1:0] ddr_dm_oen_o; + logic [P.XLEN/16-1:0] ddr_dm; + logic [P.XLEN/16-1:0] ddr_dm_o; + logic [P.XLEN/16-1:0] ddr_dqs_p_oen_o; + logic [P.XLEN/16-1:0] ddr_dqs_p_ien_o; + logic [P.XLEN/16-1:0] ddr_dqs_p; + logic [P.XLEN/16-1:0] ddr_dqs_p_o; + logic [P.XLEN/16-1:0] ddr_dqs_p_i; + logic [P.XLEN/16-1:0] ddr_dqs_n_oen_o; + logic [P.XLEN/16-1:0] ddr_dqs_n_ien_o; + logic [P.XLEN/16-1:0] ddr_dqs_n; + logic [P.XLEN/16-1:0] ddr_dqs_n_o; + logic [P.XLEN/16-1:0] ddr_dqs_n_i; + logic [P.XLEN/2-1:0] ddr_dq_oen_o; + logic [P.XLEN/2-1:0] ddr_dq; + logic [P.XLEN/2-1:0] ddr_dq_o; + logic [P.XLEN/2-1:0] ddr_dq_i; + logic dfi_clk_2x_i; + logic dfi_clk_1x_o; + + bsg_dmc_ahb #(28, P.XLEN, 32) bsg_mem_controller ( + .HCLK, .HRESETn, .HSEL(HSELEXT), + .HADDR(HADDR[27:0]), .HWDATA, .HWSTRB, // FIXME: Double check that these are the right address bits + .HWRITE, .HTRANS, .HREADY(HREADYEXT), + .HRDATA(HRDATAEXT), .HRESP(HRESPEXT), .HREADYOUT(HREADYEXT), + .ddr_ck_p, .ddr_ck_n, .ddr_cke, .ddr_ba, .ddr_addr, + .ddr_cs, .ddr_ras, .ddr_cas, .ddr_we, .ddr_reset, .ddr_odt, + .ddr_dm_oen(ddr_dm_oen_o), .ddr_dm(ddr_dm_o), + .ddr_dqs_p_oen(ddr_dqs_p_oen_o), .ddr_dqs_p_ien(ddr_dqs_p_ien_o), + .ddr_dqs_p_out(ddr_dqs_p_o), .ddr_dqs_p_in(ddr_dqs_p_i), + .ddr_dqs_n_oen(ddr_dqs_n_oen_o), .ddr_dqs_n_ien(ddr_dqs_n_ien_o), + .ddr_dqs_n_out(ddr_dqs_n_o), .ddr_dqs_n_in(ddr_dqs_n_i), + .ddr_dq_oen(ddr_dq_oen_o), .ddr_dq_out(ddr_dq_o), .ddr_dq_in(ddr_dq_i), + .dfi_clk_2x(dfi_clk_2x_i), .dfi_clk_1x(dfi_clk_1x_o)); + + genvar ddr_i; + generate + for(ddr_i = 0; ddr_i < (P.XLEN/16); ddr_i++) begin: dm_io + assign ddr_dm[ddr_i] = !ddr_dm_oen_o[ddr_i]? ddr_dm_o[ddr_i]: 1'bz; + end + for(ddr_i = 0; ddr_i < (P.XLEN/16); ddr_i++) begin: dqs_io + assign ddr_dqs_p[ddr_i] = !ddr_dqs_p_oen_o[ddr_i]? ddr_dqs_p_o[ddr_i]: 1'bz; + assign ddr_dqs_p_i[ddr_i] = !ddr_dqs_p_ien_o[ddr_i]? ddr_dqs_p[ddr_i]: 1'b0; + assign ddr_dqs_n[ddr_i] = !ddr_dqs_n_oen_o[ddr_i]? ddr_dqs_n_o[ddr_i]: 1'bz; + assign ddr_dqs_n_i[ddr_i] = !ddr_dqs_n_ien_o[ddr_i]? ddr_dqs_n[ddr_i]: 1'b1; + end + for(ddr_i = 0; ddr_i < (P.XLEN/2); ddr_i++) begin: dq_io + assign ddr_dq[ddr_i] = !ddr_dq_oen_o[ddr_i]? ddr_dq_o[ddr_i]: 1'bz; + assign ddr_dq_i[ddr_i] = ddr_dq[ddr_i]; + end + for(ddr_i = 0; ddr_i < 1; ddr_i++) begin: lpddr + mobile_ddr mobile_ddr_inst + (.Dq (ddr_dq[32*ddr_i+31:32*ddr_i]) + ,.Dqs (ddr_dqs_p[4*ddr_i+3:4*ddr_i]) + ,.Addr (ddr_addr[13:0]) + ,.Ba (ddr_ba[1:0]) + ,.Clk (ddr_ck_p) + ,.Clk_n (ddr_ck_n) + ,.Cke (ddr_cke) + ,.Cs_n (ddr_cs) + ,.Ras_n (ddr_ras) + ,.Cas_n (ddr_cas) + ,.We_n (ddr_we) + ,.Dm (ddr_dm[4*ddr_i+3:4*ddr_i])); + end + endgenerate + + if(P.SDC_SUPPORTED) begin : sdcard + // *** fix later +/* -----\/----- EXCLUDED -----\/----- + sdModel sdcard + (.sdClk(SDCCLK), + .cmd(SDCCmd), + .dat(SDCDat)); + + assign SDCCmd = SDCCmdOE ? SDCCmdOut : 1'bz; + assign SDCCmdIn = SDCCmd; + assign SDCDat = sd_dat_reg_t ? sd_dat_reg_o : sd_dat_i; + assign SDCDatIn = SDCDat; + -----/\----- EXCLUDED -----/\----- */ + assign SDCIntr = '0; + end else begin + assign SDCIntr = '0; + end + + wallypipelinedsoc #(P) dut(.clk, .reset_ext, .reset, .HRDATAEXT, .HREADYEXT, .HRESPEXT, .HSELEXT, .HSELEXTSDC, + .HCLK, .HRESETn, .HADDR, .HWDATA, .HWSTRB, .HWRITE, .HSIZE, .HBURST, .HPROT, + .HTRANS, .HMASTLOCK, .HREADY, .TIMECLK(1'b0), .GPIOIN, .GPIOOUT, .GPIOEN, + .UARTSin, .UARTSout, .SDCIntr, .SPIIn, .SPIOut, .SPICS); + + // generate clock to sequence tests + always begin + clk = 1; # 5; clk = 0; # 5; + end + + /* + // Print key info each cycle for debugging + always @(posedge clk) begin + #2; + $display("PCM: %x InstrM: %x (%5s) WriteDataM: %x IEUResultM: %x", + dut.core.PCM, dut.core.InstrM, InstrMName, dut.core.WriteDataM, dut.core.ieu.dp.IEUResultM); + end + */ + + //////////////////////////////////////////////////////////////////////////////// + // Support logic + //////////////////////////////////////////////////////////////////////////////// + + // Duplicate copy of pipeline registers that are optimized out of some configurations + logic [31:0] NextInstrE, InstrM; + mux2 #(32) FlushInstrMMux(dut.core.ifu.InstrE, dut.core.ifu.nop, dut.core.ifu.FlushM, NextInstrE); + flopenr #(32) InstrMReg(clk, reset, ~dut.core.ifu.StallM, NextInstrE, InstrM); + + // Track names of instructions + string InstrFName, InstrDName, InstrEName, InstrMName, InstrWName; + logic [31:0] InstrW; + flopenr #(32) InstrWReg(clk, reset, ~dut.core.ieu.dp.StallW, InstrM, InstrW); + instrTrackerTB it(clk, reset, dut.core.ieu.dp.FlushE, + dut.core.ifu.InstrRawF[31:0], + dut.core.ifu.InstrD, dut.core.ifu.InstrE, + InstrM, InstrW, + InstrFName, InstrDName, InstrEName, InstrMName, InstrWName); + + // watch for problems such as lockup, reading unitialized memory, bad configs + watchdog #(P.XLEN, 1000000) watchdog(.clk, .reset); // check if PCW is stuck + ramxdetector #(P.XLEN, P.LLEN) ramxdetector(clk, dut.core.lsu.MemRWM[1], dut.core.lsu.LSULoadAccessFaultM, dut.core.lsu.ReadDataM, + dut.core.ifu.PCM, InstrM, dut.core.lsu.IEUAdrM, InstrMName); + riscvassertions #(P) riscvassertions(); // check assertions for a legal configuration + loggers #(P, TEST, PrintHPMCounters, I_CACHE_ADDR_LOGGER, D_CACHE_ADDR_LOGGER, BPRED_LOGGER) + loggers (clk, reset, DCacheFlushStart, DCacheFlushDone, memfilename); + + // track the current function or global label + if (DEBUG == 1 | ((PrintHPMCounters | BPRED_LOGGER) & P.ZICNTR_SUPPORTED)) begin : FunctionName + FunctionName #(P) FunctionName(.reset(reset_ext | TestBenchReset), + .clk(clk), .ProgramAddrMapFile(ProgramAddrMapFile), .ProgramLabelMapFile(ProgramLabelMapFile)); + end + + + // Termination condition + // terminate on a specific ECALL after li x3,1 for old Imperas tests, *** remove this when old imperas tests are removed + // or sw gp,-56(t0) for new Imperas tests + // or sd gp, -56(t0) + // or on a jump to self infinite loop (6f) for RISC-V Arch tests + logic ecf; // remove this once we don't rely on old Imperas tests with Ecalls + if (P.ZICSR_SUPPORTED) assign ecf = dut.core.priv.priv.EcallFaultM; + else assign ecf = 0; + assign TestComplete = ecf & + (dut.core.ieu.dp.regf.rf[3] == 1 | + (dut.core.ieu.dp.regf.we3 & + dut.core.ieu.dp.regf.a3 == 3 & + dut.core.ieu.dp.regf.wd3 == 1)) | + ((InstrM == 32'h6f | InstrM == 32'hfc32a423 | InstrM == 32'hfc32a823) & dut.core.ieu.c.InstrValidM ) | + ((dut.core.lsu.IEUAdrM == ProgramAddrLabelArray["tohost"]) & InstrMName == "SW" ); + //assign DCacheFlushStart = TestComplete; + + DCacheFlushFSM #(P) DCacheFlushFSM(.clk(clk), .reset(reset), .start(DCacheFlushStart), .done(DCacheFlushDone)); + + if(P.ZICSR_SUPPORTED & INSTR_LIMIT != 0) begin + logic [P.XLEN-1:0] Minstret; + assign Minstret = testbench.dut.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[2]; + always @(negedge clk) begin + if((Minstret != 0) && (Minstret % 'd100000 == 0)) $display("Reached %d instructions", Minstret); + if((Minstret == INSTR_LIMIT) & (INSTR_LIMIT!=0)) begin $stop; $stop; end + end +end + + //////////////////////////////////////////////////////////////////////////////// + // ImperasDV Co-simulator hooks + //////////////////////////////////////////////////////////////////////////////// +`ifdef USE_IMPERAS_DV + + rvviTrace #(.XLEN(P.XLEN), .FLEN(P.FLEN)) rvvi(); + wallyTracer #(P) wallyTracer(rvvi); + + trace2log idv_trace2log(rvvi); + // trace2cov idv_trace2cov(rvvi); + + // enabling of comparison types + trace2api #(.CMP_PC (1), + .CMP_INS (1), + .CMP_GPR (1), + .CMP_FPR (1), + .CMP_VR (0), + .CMP_CSR (1) + ) idv_trace2api(rvvi); + + initial begin + int iter; + #1; + IDV_MAX_ERRORS = 3; + + // Initialize REF (do this before initializing the DUT) + if (!rvviVersionCheck(RVVI_API_VERSION)) begin + $display($sformatf("%m @ t=%0t: Expecting RVVI API version %0d.", $time, RVVI_API_VERSION)); + $fatal; + end + + void'(rvviRefConfigSetString(IDV_CONFIG_MODEL_VENDOR, "riscv.ovpworld.org")); + void'(rvviRefConfigSetString(IDV_CONFIG_MODEL_NAME, "riscv")); + void'(rvviRefConfigSetString(IDV_CONFIG_MODEL_VARIANT, "RV64GC")); + void'(rvviRefConfigSetInt(IDV_CONFIG_MODEL_ADDRESS_BUS_WIDTH, 56)); + void'(rvviRefConfigSetInt(IDV_CONFIG_MAX_NET_LATENCY_RETIREMENTS, 6)); + + if (!rvviRefInit("")) begin + $display($sformatf("%m @ t=%0t: rvviRefInit failed", $time)); + $fatal; + end + + // Volatile CSRs + void'(rvviRefCsrSetVolatile(0, 32'hC00)); // CYCLE + void'(rvviRefCsrSetVolatile(0, 32'hB00)); // MCYCLE + void'(rvviRefCsrSetVolatile(0, 32'hC02)); // INSTRET + void'(rvviRefCsrSetVolatile(0, 32'hB02)); // MINSTRET + void'(rvviRefCsrSetVolatile(0, 32'hC01)); // TIME + + // User HPMCOUNTER3 - HPMCOUNTER31 + for (iter='hC03; iter<='hC1F; iter++) begin + void'(rvviRefCsrSetVolatile(0, iter)); // HPMCOUNTERx + end + + // Machine MHPMCOUNTER3 - MHPMCOUNTER31 + for (iter='hB03; iter<='hB1F; iter++) begin + void'(rvviRefCsrSetVolatile(0, iter)); // MHPMCOUNTERx + end + + // cannot predict this register due to latency between + // pending and taken + void'(rvviRefCsrSetVolatile(0, 32'h344)); // MIP + void'(rvviRefCsrSetVolatile(0, 32'h144)); // SIP + + // Privileges for PMA are set in the imperas.ic + // volatile (IO) regions are defined here + // only real ROM/RAM areas are BOOTROM and UNCORE_RAM + if (P.CLINT_SUPPORTED) begin + void'(rvviRefMemorySetVolatile(P.CLINT_BASE, (P.CLINT_BASE + P.CLINT_RANGE))); + end + if (P.GPIO_SUPPORTED) begin + void'(rvviRefMemorySetVolatile(P.GPIO_BASE, (P.GPIO_BASE + P.GPIO_RANGE))); + end + if (P.UART_SUPPORTED) begin + void'(rvviRefMemorySetVolatile(P.UART_BASE, (P.UART_BASE + P.UART_RANGE))); + end + if (P.PLIC_SUPPORTED) begin + void'(rvviRefMemorySetVolatile(P.PLIC_BASE, (P.PLIC_BASE + P.PLIC_RANGE))); + end + if (P.SDC_SUPPORTED) begin + void'(rvviRefMemorySetVolatile(P.SDC_BASE, (P.SDC_BASE + P.SDC_RANGE))); + end + if (P.SPI_SUPPORTED) begin + void'(rvviRefMemorySetVolatile(P.SPI_BASE, (P.SPI_BASE + P.SPI_RANGE))); + end + + if(P.XLEN==32) begin + void'(rvviRefCsrSetVolatile(0, 32'hC80)); // CYCLEH + void'(rvviRefCsrSetVolatile(0, 32'hB80)); // MCYCLEH + void'(rvviRefCsrSetVolatile(0, 32'hC82)); // INSTRETH + void'(rvviRefCsrSetVolatile(0, 32'hB82)); // MINSTRETH + end + + void'(rvviRefCsrSetVolatile(0, 32'h104)); // SIE - Temporary!!!! + + // Load memory + // *** RT: This section can probably be moved into the same chunk of code which + // loads the memories. However I'm not sure that ImperasDV supports reloading + // the memories without relaunching the simulator. + begin + longint x64; + int x32[2]; + longint index; + string memfilenameImperasDV, bootmemfilenameImperasDV; + + memfilenameImperasDV = {RISCV_DIR, "/linux-testvectors/ram.bin"}; + bootmemfilenameImperasDV = {RISCV_DIR, "/linux-testvectors/bootmem.bin"}; + + $display("RVVI Loading bootmem.bin"); + memFile = $fopen(bootmemfilenameImperasDV, "rb"); + index = 'h1000 - 8; + while(!$feof(memFile)) begin + index+=8; + readResult = $fread(x64, memFile); + if (x64 == 0) continue; + x32[0] = x64 & 'hffffffff; + x32[1] = x64 >> 32; + rvviRefMemoryWrite(0, index+0, x32[0], 4); + rvviRefMemoryWrite(0, index+4, x32[1], 4); + //$display("boot %08X x32[0]=%08X x32[1]=%08X", index, x32[0], x32[1]); + end + $fclose(memFile); + + $display("RVVI Loading ram.bin"); + memFile = $fopen(memfilenameImperasDV, "rb"); + index = 'h80000000 - 8; + while(!$feof(memFile)) begin + index+=8; + readResult = $fread(x64, memFile); + if (x64 == 0) continue; + x32[0] = x64 & 'hffffffff; + x32[1] = x64 >> 32; + rvviRefMemoryWrite(0, index+0, x32[0], 4); + rvviRefMemoryWrite(0, index+4, x32[1], 4); + //$display("ram %08X x32[0]=%08X x32[1]=%08X", index, x32[0], x32[1]); + end + $fclose(memFile); + + $display("RVVI Loading Complete"); + + void'(rvviRefPcSet(0, P.RESET_VECTOR)); // set BOOTROM address + end + end + + always @(dut.core.priv.priv.csr.csri.MIP_REGW[7]) void'(rvvi.net_push("MTimerInterrupt", dut.core.priv.priv.csr.csri.MIP_REGW[7])); + always @(dut.core.priv.priv.csr.csri.MIP_REGW[11]) void'(rvvi.net_push("MExternalInterrupt", dut.core.priv.priv.csr.csri.MIP_REGW[11])); + always @(dut.core.priv.priv.csr.csri.MIP_REGW[9]) void'(rvvi.net_push("SExternalInterrupt", dut.core.priv.priv.csr.csri.MIP_REGW[9])); + always @(dut.core.priv.priv.csr.csri.MIP_REGW[3]) void'(rvvi.net_push("MSWInterrupt", dut.core.priv.priv.csr.csri.MIP_REGW[3])); + always @(dut.core.priv.priv.csr.csri.MIP_REGW[1]) void'(rvvi.net_push("SSWInterrupt", dut.core.priv.priv.csr.csri.MIP_REGW[1])); + always @(dut.core.priv.priv.csr.csri.MIP_REGW[5]) void'(rvvi.net_push("STimerInterrupt", dut.core.priv.priv.csr.csri.MIP_REGW[5])); + + final begin + void'(rvviRefShutdown()); + end + +`endif + //////////////////////////////////////////////////////////////////////////////// + // END of ImperasDV Co-simulator hooks + //////////////////////////////////////////////////////////////////////////////// + + task automatic CheckSignature; + // This task must be declared inside this module as it needs access to parameter P. There is + // no way to pass P to the task unless we convert it to a module. + + input string pathname; + input string TestName; + input logic riscofTest; + input integer begin_signature_addr; + output integer errors; + int fd, code; + string line; + int siglines, sigentries; + + localparam SIGNATURESIZE = 5000000; + integer i; + logic [31:0] sig32[0:SIGNATURESIZE]; + logic [31:0] parsed; + logic [P.XLEN-1:0] signature[0:SIGNATURESIZE]; + string signame; + logic [P.XLEN-1:0] testadr, testadrNoBase; + + // read .signature.output file and compare to check for errors + if (riscofTest) signame = {pathname, TestName, "/ref/Reference-sail_c_simulator.signature"}; + else signame = {pathname, TestName, ".signature.output"}; + + // read signature file from memory and count lines. Can't use readmemh because we need the line count + // $readmemh(signame, sig32); + fd = $fopen(signame, "r"); + siglines = 0; + if (fd == 0) $display("Unable to read %s", signame); + else begin + while (!$feof(fd)) begin + code = $fgets(line, fd); + if (code != 0) begin + int errno; + string errstr; + errno = $ferror(fd, errstr); + if (errno != 0) $display("Error %d (code %d) reading line %d of %s: %s", errno, code, siglines, signame, errstr); + if (line.len() > 1) begin // skip blank lines + if ($sscanf(line, "%x", parsed) != 0) begin + sig32[siglines] = parsed; + siglines = siglines + 1; // increment if line is not blank + end + end + end + end + $fclose(fd); + end + + // Check valid number of lines were read + if (siglines == 0) begin + errors = 1; + $display("Error: empty test file %s", signame); + end else if (P.XLEN == 64 & (siglines % 2)) begin + errors = 1; + $display("Error: RV64 signature has odd number of lines %s", signame); + end else errors = 0; + + // copy lines into signature, converting to XLEN if necessary + sigentries = (P.XLEN == 32) ? siglines : siglines/2; // number of signature entries + for (i=0; i