mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-02 17:55:19 +00:00
soc: Add testbench and integrate into regression tests
This commit is contained in:
parent
ce25816e44
commit
3dbb777d4d
@ -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 <<!\ndo testfloat-batch.do " + config + " cvtfp \n!",
|
||||
grepstr="All Tests completed with 0 errors"
|
||||
)
|
||||
configs.append(cvtfptest)
|
||||
|
||||
configs.append(cvtfptest)
|
||||
|
||||
if (soc):
|
||||
print('Running soc coverage')
|
||||
configs = [] # Delete all configs, just use test64gc
|
||||
coverStr = '-coverage' # Include coverage report
|
||||
for test in tests64gc:
|
||||
tc = TestCase(
|
||||
name=test,
|
||||
variant="rv64gc",
|
||||
cmd="vsim > {} -c <<!\ndo wally-soc.do rv64gc "+test+" " + coverStr + "\n!",
|
||||
grepstr="All tests ran without failures")
|
||||
configs.append(tc)
|
||||
|
||||
|
||||
|
||||
import os
|
||||
from multiprocessing import Pool, TimeoutError
|
||||
@ -439,6 +450,9 @@ def main():
|
||||
configs.append(getBuildrootTC(boot=False))
|
||||
elif '-softfloat' in sys.argv:
|
||||
TIMEOUT_DUR = 60*60 # seconds
|
||||
elif '-soc' in sys.argv:
|
||||
TIMEOUT_DUR = 20*60 # seconds
|
||||
# FIXME: Do we need to run buildroot?
|
||||
else:
|
||||
TIMEOUT_DUR = 10*60 # seconds
|
||||
configs.append(getBuildrootTC(boot=False))
|
||||
@ -458,7 +472,7 @@ def main():
|
||||
print(f"{bcolors.FAIL}%s_%s: Timeout - runtime exceeded %d seconds{bcolors.ENDC}" % (config.variant, config.name, TIMEOUT_DUR))
|
||||
|
||||
# Coverage report
|
||||
if coverage:
|
||||
if coverage or soc:
|
||||
os.system('make coverage')
|
||||
# Count the number of failures
|
||||
if num_fail:
|
||||
|
94
sim/wally-soc.do
Normal file
94
sim/wally-soc.do
Normal file
@ -0,0 +1,94 @@
|
||||
# wally-batch.do
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||
#
|
||||
# Modification by Oklahoma State University & Harvey Mudd College
|
||||
# Use with Testbench
|
||||
# James Stine, 2008; David Harris 2021
|
||||
# Go Cowboys!!!!!!
|
||||
#
|
||||
# Takes 1:10 to run RV64IC tests using gui
|
||||
|
||||
# Usage: do wally-batch.do <config> <testcases>
|
||||
# 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
|
922
testbench/testbench-soc.sv
Normal file
922
testbench/testbench-soc.sv
Normal file
@ -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<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
|
||||
logic [P.XLEN-1:0] sig;
|
||||
// **************************************
|
||||
// ***** BUG BUG BUG make sure RT undoes this.
|
||||
//if (P.DTIM_SUPPORTED) sig = testbench.dut.core.lsu.dtim.dtim.ram.RAM[testadrNoBase+i];
|
||||
//else if (P.UNCORE_RAM_SUPPORTED) sig = testbench.dut.uncore.uncore.ram.ram.memory.RAM[testadrNoBase+i];
|
||||
if (P.UNCORE_RAM_SUPPORTED) sig = testbench.dut.uncore.uncore.ram.ram.memory.RAM[testadrNoBase+i];
|
||||
//if (P.UNCORE_RAM_SUPPORTED) sig = testbench.dut.uncore.uncore.ram.ram.memory.RAM[testadrNoBase+i];
|
||||
//$display("signature[%h] = %h sig = %h", i, signature[i], sig);
|
||||
//if (signature[i] !== sig & (signature[i] !== testbench.DCacheFlushFSM.ShadowRAM[testadr+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 sim (DTIM_SUPPORTED) = %h, signature = %h",
|
||||
TestName, i, (testadr+i)*(P.XLEN/8), testbench.DCacheFlushFSM.ShadowRAM[testadr+i], sig, 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
|
||||
|
||||
/* 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
|
||||
|
Loading…
Reference in New Issue
Block a user