mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-02 17:55:19 +00:00
1c6ebb86a3
Removed the external reset of the phy and now it always reliably starts in the same way. The first 0x117 frames are always captured.
1028 lines
45 KiB
Systemverilog
1028 lines
45 KiB
Systemverilog
///////////////////////////////////////////
|
|
// 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
|
|
|
|
// *** bug replace with config?
|
|
`define RVVI_SYNTH_SUPPORTED 1
|
|
|
|
import cvw::*;
|
|
|
|
module testbench;
|
|
/* verilator lint_off WIDTHTRUNC */
|
|
/* verilator lint_off WIDTHEXPAND */
|
|
parameter DEBUG=0;
|
|
parameter PrintHPMCounters=0;
|
|
parameter BPRED_LOGGER=0;
|
|
parameter I_CACHE_ADDR_LOGGER=0;
|
|
parameter D_CACHE_ADDR_LOGGER=0;
|
|
|
|
`ifdef USE_IMPERAS_DV
|
|
import idvPkg::*;
|
|
import rvviApiPkg::*;
|
|
import idvApiPkg::*;
|
|
`endif
|
|
|
|
`ifdef VERILATOR
|
|
import "DPI-C" function string getenvval(input string env_name);
|
|
string RISCV_DIR = getenvval("RISCV"); // "/opt/riscv";
|
|
`elsif SIM_VCS
|
|
import "DPI-C" function string getenv(input string env_name);
|
|
string RISCV_DIR = getenv("RISCV"); // "/opt/riscv";
|
|
`else
|
|
string RISCV_DIR = "$RISCV"; // "/opt/riscv";
|
|
`endif
|
|
|
|
`include "parameter-defs.vh"
|
|
|
|
logic clk;
|
|
logic reset_ext, reset;
|
|
logic ResetMem;
|
|
|
|
// Variables that can be overwritten with $value$plusargs at start of simulation
|
|
string TEST, ElfFile;
|
|
integer INSTR_LIMIT;
|
|
|
|
// 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;
|
|
logic RVVIStall;
|
|
|
|
initial begin
|
|
// look for arguments passed to simulation, or use defaults
|
|
if (!$value$plusargs("TEST=%s", TEST))
|
|
TEST = "none";
|
|
if (!$value$plusargs("ElfFile=%s", ElfFile))
|
|
ElfFile = "none";
|
|
else begin
|
|
end
|
|
if (!$value$plusargs("INSTR_LIMIT=%d", INSTR_LIMIT))
|
|
INSTR_LIMIT = 0;
|
|
|
|
// pick tests based on modes supported
|
|
//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_amo": if (P.A_SUPPORTED | P.ZAAMO_SUPPORTED) tests = arch64a_amo;
|
|
"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;
|
|
"wally64q": if (P.Q_SUPPORTED) tests = wally64q;
|
|
"wally64a_lrsc": if (P.A_SUPPORTED | P.ZALRSC_SUPPORTED) tests = wally64a_lrsc;
|
|
"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;
|
|
"arch64zbkb": if (P.ZBKB_SUPPORTED) tests = arch64zbkb;
|
|
"arch64zbkc": if (P.ZBKC_SUPPORTED) tests = arch64zbkc;
|
|
"arch64zbkx": if (P.ZBKX_SUPPORTED) tests = arch64zbkx;
|
|
"arch64zknd": if (P.ZKND_SUPPORTED) tests = arch64zknd;
|
|
"arch64zkne": if (P.ZKNE_SUPPORTED) tests = arch64zkne;
|
|
"arch64zknh": if (P.ZKNH_SUPPORTED) tests = arch64zknh;
|
|
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_amo": if (P.A_SUPPORTED | P.ZAAMO_SUPPORTED) tests = arch32a_amo;
|
|
"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_lrsc": if (P.A_SUPPORTED | P.ZALRSC_SUPPORTED) tests = wally32a_lrsc;
|
|
"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;
|
|
"arch32zbkb": if (P.ZBKB_SUPPORTED) tests = arch32zbkb;
|
|
"arch32zbkc": if (P.ZBKC_SUPPORTED) tests = arch32zbkc;
|
|
"arch32zbkx": if (P.ZBKX_SUPPORTED) tests = arch32zbkx;
|
|
"arch32zknd": if (P.ZKND_SUPPORTED) tests = arch32zknd;
|
|
"arch32zkne": if (P.ZKNE_SUPPORTED) tests = arch32zkne;
|
|
"arch32zknh": if (P.ZKNH_SUPPORTED) tests = arch32zknh;
|
|
endcase
|
|
end
|
|
if (tests.size() == 0 & ElfFile == "none") begin
|
|
if (tests.size() == 0) begin
|
|
$display("TEST %s not supported in this configuration", TEST);
|
|
end else if(ElfFile == "none") begin
|
|
$display("ElfFile %s not found", ElfFile);
|
|
end
|
|
$finish;
|
|
end
|
|
`ifdef MAKEVCD
|
|
$dumpfile("testbench.vcd");
|
|
$dumpvars;
|
|
`endif
|
|
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, elffilename, memfilename, bootmemfilename, uartoutfilename, pathname;
|
|
integer begin_signature_addr, end_signature_addr, signature_size;
|
|
integer uartoutfile;
|
|
|
|
assign ResetThreshold = 3'd5;
|
|
|
|
initial begin
|
|
TestBenchReset = 1'b1;
|
|
# 100;
|
|
TestBenchReset = 1'b0;
|
|
end
|
|
|
|
always_ff @(posedge clk)
|
|
if (TestBenchReset) CurrState <= STATE_TESTBENCH_RESET;
|
|
else CurrState <= 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;
|
|
|
|
//VCS ignores the dynamic types while processing the implicit sensitivity lists of always @*, always_comb, and always_latch
|
|
//procedural blocks. VCS supports the dynamic types in the implicit sensitivity list of always @* block as specified in the Section 9.2 of the IEEE Standard SystemVerilog Specification 1800-2012.
|
|
//To support memory load and dump task verbosity: flag : -diag sys_task_mem
|
|
always @(*) begin
|
|
begin_signature_addr = ProgramAddrLabelArray["begin_signature"];
|
|
end_signature_addr = ProgramAddrLabelArray["sig_end_canary"];
|
|
signature_size = end_signature_addr - begin_signature_addr;
|
|
end
|
|
logic EcallFaultM;
|
|
if (P.ZICSR_SUPPORTED)
|
|
assign EcallFaultM = dut.core.priv.priv.EcallFaultM;
|
|
else
|
|
assign EcallFaultM = 0;
|
|
|
|
always @(posedge clk) begin
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Verify the test ran correctly by checking the memory against a known signature.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
if(TestBenchReset) test = 1;
|
|
if (P.ZICSR_SUPPORTED & TEST == "coremark")
|
|
if (EcallFaultM) begin
|
|
$display("Benchmark: coremark is done.");
|
|
$stop;
|
|
end
|
|
if (P.ZICSR_SUPPORTED & dut.core.ifu.PCM == 0 & dut.core.ifu.InstrM == 0 & dut.core.ieu.InstrValidM) begin
|
|
$display("Program fetched illegal instruction 0x00000000 from address 0x00000000. Might be fault with no fault handler.");
|
|
//$stop; // presently wally32/64priv tests trigger this for reasons not yet understood.
|
|
end
|
|
|
|
// modifications 4/3/24 kunlin & harris to speed up Verilator
|
|
// For some reason, Verilator runs ~100x slower when these SelectTest and Validate codes are in the posedge clk block
|
|
//end // added
|
|
//always @(posedge SelectTest) // added
|
|
if(SelectTest) begin
|
|
if (riscofTest) begin
|
|
memfilename = {pathname, tests[test], "/ref/ref.elf.memfile"};
|
|
elffilename = {pathname, tests[test], "ref/ref.elf"};
|
|
ProgramAddrMapFile = {pathname, tests[test], "/ref/ref.elf.objdump.addr"};
|
|
ProgramLabelMapFile = {pathname, tests[test], "/ref/ref.elf.objdump.lab"};
|
|
end else if(TEST == "buildroot") begin
|
|
memfilename = {RISCV_DIR, "/linux-testvectors/ram.bin"};
|
|
elffilename = "buildroot";
|
|
bootmemfilename = {RISCV_DIR, "/linux-testvectors/bootmem.bin"};
|
|
uartoutfilename = {"logs/", TEST, "_uart.out"};
|
|
uartoutfile = $fopen(uartoutfilename, "w"); // delete UART output file
|
|
ProgramAddrMapFile = {RISCV_DIR, "/buildroot/output/images/disassembly/vmlinux.objdump.addr"};
|
|
ProgramLabelMapFile = {RISCV_DIR, "/buildroot/output/images/disassembly/vmlinux.objdump.lab"};
|
|
end else if(ElfFile != "none") begin
|
|
elffilename = ElfFile;
|
|
memfilename = {ElfFile, ".memfile"};
|
|
ProgramAddrMapFile = {ElfFile, ".objdump.addr"};
|
|
ProgramLabelMapFile = {ElfFile, ".objdump.lab"};
|
|
end else begin
|
|
elffilename = {pathname, tests[test], ".elf"};
|
|
memfilename = {pathname, tests[test], ".elf.memfile"};
|
|
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
|
|
`ifdef VERILATOR // this macro is defined when verilator is used
|
|
// Simulator Verilator has an issue that the validate logic below slows runtime 110x if it is
|
|
// in the posedge clk block rather than a separate posedge Validate block.
|
|
// Until it is fixed, provide a silly posedge Validate block to keep Verilator happy.
|
|
// https://github.com/verilator/verilator/issues/4967
|
|
end // restored
|
|
always @(posedge Validate) // added
|
|
`endif
|
|
if(Validate) begin
|
|
if (TEST == "buildroot")
|
|
$fclose(uartoutfile);
|
|
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 if (ElfFile != "none") begin
|
|
$display("Single Elf file tests are not signatured verified.");
|
|
`ifdef VERILATOR // this macro is defined when verilator is used
|
|
$finish; // Simulator Verilator needs $finish to terminate simulation.
|
|
`elsif SIM_VCS // this macro is defined when vcs is used
|
|
$finish; // Simulator VCS needs $finish to terminate simulation.
|
|
`else
|
|
$stop; // if this is changed to $finish for Questa, wally-batch.do does not go to the next step to run coverage, and wally.do terminates without allowing GUI debug
|
|
`endif
|
|
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);
|
|
`ifdef VERILATOR // this macro is defined when verilator is used
|
|
$finish; // Simulator Verilator needs $finish to terminate simulation.
|
|
`elsif SIM_VCS // this macro is defined when vcs is used
|
|
$finish; // Simulator VCS needs $finish to terminate simulation.
|
|
`else
|
|
$stop; // if this is changed to $finish for Questa, wally-batch.do does not go to the next step to run coverage, and wally.do terminates without allowing GUI debug
|
|
`endif
|
|
end
|
|
end
|
|
`ifndef VERILATOR
|
|
// Remove this when issue 4967 is resolved and the posedge Validate logic above is removed
|
|
end
|
|
`endif
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 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.uncoregen.uncore.bootrom.bootrom.memory.ROM);
|
|
//$readmemh(sdcfilename, sdcard.sdcard.FLASHmem);
|
|
// shorten sdc timers for simulation
|
|
//dut.uncoregen.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.uncoregen.uncore.bootrom.bootrom.memory.ROM, memFile);
|
|
$fclose(memFile);
|
|
memFile = $fopen(memfilename, "rb");
|
|
readResult = $fread(dut.uncoregen.uncore.ram.ram.memory.RAM, memFile);
|
|
$fclose(memFile);
|
|
end else
|
|
$readmemh(memfilename, dut.uncoregen.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.uncoregen.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.uncoregen.uncore.ram.ram.memory.RAM[adrindex] = '0;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Actual hardware
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// instantiate device to be tested
|
|
assign GPIOIN = '0;
|
|
assign UARTSin = 1'b1;
|
|
assign SPIIn = 1'b0;
|
|
|
|
if(P.EXT_MEM_SUPPORTED) begin
|
|
ram_ahb #(.P(P), .BASE(P.EXT_MEM_BASE), .RANGE(P.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'b1;
|
|
assign {HRESPEXT, HRDATAEXT} = '0;
|
|
end
|
|
|
|
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 = 1'b0;
|
|
end else begin
|
|
assign SDCIntr = 1'b0;
|
|
end
|
|
|
|
wallypipelinedsoc #(P) dut(.clk, .reset_ext, .reset, .RVVIStall, .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'b1; # 5; clk = 1'b0; # 5;
|
|
end
|
|
|
|
if(`RVVI_SYNTH_SUPPORTED) begin : rvvi_synth
|
|
localparam MAX_CSRS = 3;
|
|
logic valid;
|
|
logic [187+(3*P.XLEN) + MAX_CSRS*(P.XLEN+12)-1:0] rvvi;
|
|
|
|
rvvisynth #(P, MAX_CSRS) rvvisynth(.clk, .reset, .valid, .rvvi);
|
|
|
|
// axi 4 write data channel
|
|
logic [31:0] RvviAxiWdata;
|
|
logic [3:0] RvviAxiWstrb;
|
|
logic RvviAxiWlast;
|
|
logic RvviAxiWvalid;
|
|
logic RvviAxiWready;
|
|
|
|
logic [3:0] mii_txd;
|
|
logic mii_tx_en, mii_tx_er;
|
|
|
|
logic tx_error_underflow, tx_fifo_overflow, tx_fifo_bad_frame, tx_fifo_good_frame, rx_error_bad_frame;
|
|
logic rx_error_bad_fcs, rx_fifo_overflow, rx_fifo_bad_frame, rx_fifo_good_frame;
|
|
|
|
packetizer #(P, MAX_CSRS) packetizer(.rvvi, .valid, .m_axi_aclk(clk), .m_axi_aresetn(~reset), .RVVIStall,
|
|
.RvviAxiWdata, .RvviAxiWstrb, .RvviAxiWlast, .RvviAxiWvalid, .RvviAxiWready);
|
|
|
|
eth_mac_mii_fifo #("GENERIC", "BUFG", 32) ethernet(.rst(reset), .logic_clk(clk), .logic_rst(reset),
|
|
.tx_axis_tdata(RvviAxiWdata), .tx_axis_tkeep(RvviAxiWstrb), .tx_axis_tvalid(RvviAxiWvalid), .tx_axis_tready(RvviAxiWready),
|
|
.tx_axis_tlast(RvviAxiWlast), .tx_axis_tuser('0), .rx_axis_tdata(), .rx_axis_tkeep(), .rx_axis_tvalid(), .rx_axis_tready(1'b1),
|
|
.rx_axis_tlast(), .rx_axis_tuser(),
|
|
|
|
// *** update these
|
|
.mii_rx_clk(clk), // *** need to be the mii clock
|
|
.mii_rxd('0),
|
|
.mii_rx_dv('0),
|
|
.mii_rx_er('0),
|
|
.mii_tx_clk(clk), // *** needs to be the mii clock
|
|
.mii_txd,
|
|
.mii_tx_en,
|
|
.mii_tx_er,
|
|
|
|
// status
|
|
.tx_error_underflow, .tx_fifo_overflow, .tx_fifo_bad_frame, .tx_fifo_good_frame, .rx_error_bad_frame,
|
|
.rx_error_bad_fcs, .rx_fifo_overflow, .rx_fifo_bad_frame, .rx_fifo_good_frame,
|
|
.cfg_ifg(8'd12), .cfg_tx_enable(1'b1), .cfg_rx_enable(1'b1)
|
|
);
|
|
|
|
logic MiiTxEnDelay;
|
|
logic EthernetTXCounterEn;
|
|
logic [31:0] EthernetTXCount;
|
|
flopr #(1) txedgereg(clk, reset, mii_tx_en, MiiTxEnDelay);
|
|
assign EthernetTXCounterEn = ~mii_tx_en & MiiTxEnDelay;
|
|
counter #(32) ethernexttxcounter(clk, reset, EthernetTXCounterEn, EthernetTXCount);
|
|
|
|
end else begin
|
|
assign RVVIStall = '0;
|
|
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, PrintHPMCounters, I_CACHE_ADDR_LOGGER, D_CACHE_ADDR_LOGGER, BPRED_LOGGER)
|
|
loggers (clk, reset, DCacheFlushStart, DCacheFlushDone, memfilename, TEST);
|
|
|
|
// track the current function or global label
|
|
if (DEBUG > 0 | ((PrintHPMCounters | BPRED_LOGGER) & P.ZICNTR_SUPPORTED)) begin : FunctionName
|
|
FunctionName #(P) FunctionName(.reset(reset_ext | TestBenchReset),
|
|
.clk(clk), .ProgramAddrMapFile(ProgramAddrMapFile), .ProgramLabelMapFile(ProgramLabelMapFile));
|
|
end
|
|
|
|
// Append UART output to file for tests
|
|
if (P.UART_SUPPORTED) begin: uart_logger
|
|
always @(posedge clk) begin
|
|
if (TEST == "buildroot") begin
|
|
if (~dut.uncoregen.uncore.uartgen.uart.MEMWb & dut.uncoregen.uncore.uartgen.uart.uartPC.A == 3'b000 & ~dut.uncoregen.uncore.uartgen.uart.uartPC.DLAB) begin
|
|
$fwrite(uartoutfile, "%c", dut.uncoregen.uncore.uartgen.uart.uartPC.Din); // append characters one at a time so we see a consistent log appearing during the run
|
|
$fflush(uartoutfile);
|
|
end
|
|
end
|
|
end
|
|
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;
|
|
always_comb begin
|
|
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"] & dut.core.lsu.IEUAdrM != 0) & InstrMName == "SW" );
|
|
end
|
|
|
|
DCacheFlushFSM #(P) DCacheFlushFSM(.clk, .start(DCacheFlushStart), .done(DCacheFlushDone));
|
|
|
|
if(P.ZICSR_SUPPORTED) 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 (INSTR_LIMIT > 0) begin
|
|
if((Minstret != 0) & (Minstret % 'd100000 == 0)) $display("Reached %d instructions", Minstret);
|
|
if((Minstret == INSTR_LIMIT) & (INSTR_LIMIT!=0)) begin $finish; end
|
|
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);
|
|
|
|
string filename;
|
|
initial begin
|
|
// imperasDV requires the elffile be defined at the begining of the simulation.
|
|
int iter;
|
|
longint x64;
|
|
int x32[2];
|
|
longint index;
|
|
string memfilenameImperasDV, bootmemfilenameImperasDV;
|
|
#1;
|
|
IDV_MAX_ERRORS = 3;
|
|
elffilename = ElfFile;
|
|
|
|
// 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(elffilename == "buildroot") filename = "";
|
|
else filename = elffilename;
|
|
|
|
// use the ImperasDV rvviRefInit to load the reference model with an elf file
|
|
if(elffilename != "none") begin
|
|
if (!rvviRefInit(filename)) begin
|
|
$display($sformatf("%m @ t=%0t: rvviRefInit failed", $time));
|
|
$fatal;
|
|
end
|
|
end else begin // for buildroot use the binary instead to load teh reference model.
|
|
if (!rvviRefInit("")) begin // still have to call with nothing
|
|
$display($sformatf("%m @ t=%0t: rvviRefInit failed", $time));
|
|
$fatal;
|
|
end
|
|
|
|
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
|
|
|
|
// 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!!!!
|
|
|
|
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;
|
|
|
|
//$display("Invoking CheckSignature %s %s %0t", pathname, TestName, $time);
|
|
|
|
// 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<sigentries; i++) begin
|
|
signature[i] = (P.XLEN == 32) ? sig32[i] : {sig32[i*2+1], sig32[i*2]};
|
|
//$display("XLEN = %d signature[%d] = %x", P.XLEN, i, signature[i]);
|
|
end
|
|
|
|
// Check errors
|
|
testadr = ($unsigned(begin_signature_addr))/(P.XLEN/8);
|
|
testadrNoBase = (begin_signature_addr - P.UNCORE_RAM_BASE)/(P.XLEN/8);
|
|
for (i=0; i<sigentries; i++) begin
|
|
if (signature[i] !== testbench.DCacheFlushFSM.ShadowRAM[testadr+i]) begin
|
|
errors = errors+1;
|
|
$display(" Error on test %s result %d: adr = %h sim (D$) %h signature = %h",
|
|
TestName, i, (testadr+i)*(P.XLEN/8), testbench.DCacheFlushFSM.ShadowRAM[testadr+i], signature[i]);
|
|
$stop; // if this is changed to $finish, wally-batch.do does not get to the next step to run coverage
|
|
end
|
|
end
|
|
if (errors) $display("%s failed with %d errors. :(", TestName, errors);
|
|
else $display("%s succeeded. Brilliant!!!", TestName);
|
|
endtask
|
|
|
|
`ifdef PMP_COVERAGE
|
|
test_pmp_coverage #(P) pmp_inst(clk);
|
|
`endif
|
|
/* verilator lint_on WIDTHTRUNC */
|
|
/* verilator lint_on WIDTHEXPAND */
|
|
|
|
endmodule
|
|
|
|
/* verilator lint_on STMTDLY */
|
|
/* verilator lint_on WIDTH */
|
|
|
|
task automatic updateProgramAddrLabelArray;
|
|
/* verilator lint_off WIDTHTRUNC */
|
|
/* verilator lint_off WIDTHEXPAND */
|
|
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
|
|
ProgramAddrLabelArray["begin_signature"] = 0;
|
|
ProgramAddrLabelArray["tohost"] = 0;
|
|
ProgramAddrLabelArray["sig_end_canary"] = 0;
|
|
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
|
|
|
|
// if(ProgramAddrLabelArray["begin_signature"] == 0) $display("Couldn't find begin_signature in %s", ProgramLabelMapFile);
|
|
// if(ProgramAddrLabelArray["sig_end_canary"] == 0) $display("Couldn't find sig_end_canary in %s", ProgramLabelMapFile);
|
|
|
|
$fclose(ProgramLabelMapFP);
|
|
$fclose(ProgramAddrMapFP);
|
|
/* verilator lint_on WIDTHTRUNC */
|
|
/* verilator lint_on WIDTHEXPAND */
|
|
endtask
|
|
|