2023-10-13 20:10:58 +00:00
///////////////////////////////////////////
// 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 "
2024-05-17 19:45:37 +00:00
`include " tests.vh "
`include " BranchPredictorType.vh "
2023-10-13 20:10:58 +00:00
`ifdef USE_IMPERAS_DV
`include " idv/idv.svh "
`endif
2024-05-17 19:45:37 +00:00
2023-10-13 20:10:58 +00:00
import cvw::* ;
module testbench ;
2024-05-17 19:45:37 +00:00
/* verilator lint_off WIDTHTRUNC */
/* verilator lint_off WIDTHEXPAND */
2023-10-13 20:10:58 +00:00
parameter DEBUG = 0 ;
2024-05-17 19:45:37 +00:00
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
2023-10-13 20:10:58 +00:00
`include " parameter-defs.vh "
logic clk ;
logic reset_ext , reset ;
2024-05-17 19:45:37 +00:00
logic ResetMem ;
// Variables that can be overwritten with $value$plusargs at start of simulation
string TEST ;
string 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 ;
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
string outputfile ;
integer outputFilePointer ;
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
string tests [ ] ;
logic DCacheFlushDone , DCacheFlushStart ;
logic riscofTest ;
logic Validate ;
logic SelectTest ;
logic TestComplete ;
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 ;
ElfFile = " /home/rose/repos/active/cvw2/cvw/tests/riscof/work/riscv-arch-test/rv64i_m/I/src/add-01.S/ref/ref.elf " ;
// 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
2024-05-17 21:08:14 +00:00
// 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
2024-05-17 19:45:37 +00:00
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 ] ResetCountNew , ResetThresholdNew ;
logic LoadMem ;
logic ResetCntEn ;
logic ResetCntRst ;
logic CopyRAM ;
2024-05-17 21:08:14 +00:00
string signame , elffilename , memfilename , bootmemfilename , uartoutfilename , pathname ;
2024-05-17 19:45:37 +00:00
integer begin_signature_addr , end_signature_addr , signature_size ;
integer uartoutfile ;
2024-05-17 21:08:14 +00:00
2024-05-17 19:45:37 +00:00
logic reset_extNew ;
logic DCacheFlushStartNew ;
2024-05-17 21:08:14 +00:00
assign ResetThresholdNew = 3 'd5 ;
2023-10-13 20:10:58 +00:00
2024-05-17 21:08:14 +00:00
initial begin
TestBenchReset = 1 'b1 ;
# 100 ;
TestBenchReset = 1 'b0 ;
end
2023-10-13 20:10:58 +00:00
2024-05-17 21:08:14 +00:00
always_ff @ ( posedge clk )
if ( TestBenchReset ) CurrState < = STATE_TESTBENCH_RESET ;
else CurrState < = NextState ;
2024-05-17 19:45:37 +00:00
2024-05-17 21:08:14 +00:00
// 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
//pathname = tvpaths[tests[0].atoi()];
2023-10-13 20:10:58 +00:00
2024-05-17 21:08:14 +00:00
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 ( ResetCountNew < ResetThresholdNew ) 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 ) ;
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 DCacheFlushStartNew = CurrState = = STATE_COPY_RAM ;
2023-10-13 20:10:58 +00:00
2024-05-17 21:08:14 +00:00
// fsm reset counter
counter # ( 3 ) RstCounter ( clk , ResetCntRst , ResetCntEn , ResetCountNew ) ;
////////////////////////////////////////////////////////////////////////////////
// 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
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
////////////////////////////////////////////////////////////////////////////////
// load memories with program image
////////////////////////////////////////////////////////////////////////////////
integer ShadowIndex ;
integer LogXLEN ;
integer StartIndex ;
integer EndIndex ;
integer BaseIndex ;
2024-05-17 21:08:14 +00:00
integer memFile ;
integer readResult ;
2024-05-17 19:45:37 +00:00
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 ;
2023-10-13 20:10:58 +00:00
2024-05-17 21:08:14 +00:00
////////////////////////////////////////////////////////////////////////////////
// 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 , . 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
/ *
// 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
logic [ P . XLEN - 1 : 0 ] testadrNoBase ;
string testName ;
string testDir , adrstr ;
logic InitializingMemories ;
integer ResetCount , ResetThreshold ;
logic InReset ;
2023-10-13 20:10:58 +00:00
// Imperas look here.
initial
begin
ResetCount = 0 ;
2024-05-17 17:36:00 +00:00
ResetThreshold = 21 ;
2023-10-13 20:10:58 +00:00
InReset = 1 ;
testadr = 0 ;
testadrNoBase = 0 ;
if ( $value$plusargs ( " testDir=%s " , testDir ) ) begin
memfilename = { testDir , " /ref/ref.elf.memfile " } ;
elffilename = { testDir , " /ref/ref.elf " } ;
$display ( $sformatf ( " %m @ t=%0t: loading testDir %0s " , $time , testDir ) ) ;
end else begin
$error ( " Must specify test directory using plusarg testDir " ) ;
end
2024-05-17 19:45:37 +00:00
# 130 ;
2023-10-13 20:10:58 +00:00
ProgramAddrMapFile = { testDir , " /ref/ref.elf.objdump.addr " } ;
ProgramLabelMapFile = { testDir , " /ref/ref.elf.objdump.lab " } ;
// 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 ) ;
$display ( " Read memfile %s " , memfilename ) ;
end
2024-05-17 19:45:37 +00:00
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
`ifdef USE_IMPERAS_DV
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
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
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 ( elffilename = = " buildroot " ) filename = " " ;
else filename = elffilename ;
if ( ! rvviRefInit ( filename ) ) begin
$display ( $sformatf ( " %m @ t=%0t: rvviRefInit failed " , $time ) ) ;
$fatal ;
end
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
// 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
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
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
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
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.
if ( elffilename = = " buildroot " ) begin
longint x64 ;
int x32 [ 2 ] ;
longint index ;
string memfilenameImperasDV , bootmemfilenameImperasDV ;
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
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]);
2023-10-13 20:10:58 +00:00
end
2024-05-17 19:45:37 +00:00
$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]);
2023-10-13 20:10:58 +00:00
end
2024-05-17 19:45:37 +00:00
$fclose ( memFile ) ;
2023-10-13 20:10:58 +00:00
2024-05-17 19:45:37 +00:00
$display ( " RVVI Loading Complete " ) ;
void ' ( rvviRefPcSet ( 0 , P . RESET_VECTOR ) ) ; // set BOOTROM address
2023-10-13 20:10:58 +00:00
end
2024-05-17 19:45:37 +00:00
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
2023-10-13 20:10:58 +00:00
`endif
2024-05-17 21:08:14 +00:00
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
2023-10-13 20:10:58 +00:00
end
end
2024-05-17 21:08:14 +00:00
$fclose ( fd ) ;
end
2023-10-13 20:10:58 +00:00
2024-05-17 21:08:14 +00:00
// 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
2023-10-13 20:10:58 +00:00
2024-05-17 21:08:14 +00:00
// 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
2023-10-13 20:10:58 +00:00
end
end
2024-05-17 21:08:14 +00:00
if ( errors ) $display ( " %s failed with %d errors. :( " , TestName , errors ) ;
else $display ( " %s succeeded. Brilliant!!! " , TestName ) ;
endtask
2023-10-13 20:10:58 +00:00
endmodule
/* verilator lint_on STMTDLY */
/* verilator lint_on WIDTH */
task automatic updateProgramAddrLabelArray ;
input string ProgramAddrMapFile , ProgramLabelMapFile ;
inout integer ProgramAddrLabelArray [ string ] ;
// Gets the memory location of begin_signature
integer ProgramLabelMapFP , ProgramAddrMapFP ;
ProgramLabelMapFP = $fopen ( ProgramLabelMapFile , " r " ) ;
ProgramAddrMapFP = $fopen ( ProgramAddrMapFile , " r " ) ;
if ( ProgramLabelMapFP & ProgramAddrMapFP ) begin // check we found both files
while ( ! $feof ( ProgramLabelMapFP ) ) begin
string label , adrstr ;
integer returncode ;
returncode = $fscanf ( ProgramLabelMapFP , " %s \n " , label ) ;
returncode = $fscanf ( ProgramAddrMapFP , " %s \n " , adrstr ) ;
if ( ProgramAddrLabelArray . exists ( label ) )
ProgramAddrLabelArray [ label ] = adrstr . atohex ( ) ;
end
end
$fclose ( ProgramLabelMapFP ) ;
$fclose ( ProgramAddrMapFP ) ;
endtask