Significant refactoring of testbench.

This commit is contained in:
Ross Thompson 2023-06-14 17:02:49 -05:00
parent 4d2bb0ea83
commit 301d54fea8
6 changed files with 529 additions and 268 deletions

View File

@ -0,0 +1,26 @@
///////////////////////////////////////////
// checksignature.sv
//
// Written: David Harris David_Harris@hmc.edu
// Modified: 14 June 2023
//
// Purpose: Verifies the memory signature.
//
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -1,6 +1,8 @@
///////////////////////////////////////////
// functionName.sv
//
// Written: Ross Thompson ross1728@gmail.com
//
// Purpose: decode name of function
//
// A component of the Wally configurable RISC-V project.
@ -56,7 +58,6 @@ module FunctionName import cvw::*; #(parameter cvw_t P) (
flopenr #(P.XLEN) PCMReg(clk, reset, ~StallM, FlushD & FlushE & FlushM ? PCF : FlushE & FlushM ? PCE : FlushM ? PCM : PCE, PCM_temp);
flopenr #(P.XLEN) PCMOldReg(clk, reset, InstrValidM, PCM_temp, PCMOld);
assign PCM = InstrValidM ? PCM_temp : PCMOld;
task automatic bin_search_min;
input logic [P.XLEN-1:0] pc;
@ -169,7 +170,6 @@ module FunctionName import cvw::*; #(parameter cvw_t P) (
initial ProgramAddrIndex = '0;
assign FunctionName = AnyUnknown ? "Unknown!" : ProgramLabelMapMemory[ProgramAddrIndex];
endmodule // function_radix

234
testbench/common/loggers.sv Normal file
View File

@ -0,0 +1,234 @@
///////////////////////////////////////////
// loggers.sv
//
// Written: Ross Thompson ross1728@gmail.com
// Modified: 14 June 2023
//
// Purpose: Log branch instructions, log instruction fetches,
// log I$ misses, log data memory accesses, log D$ misses, and
// log other related operations
//
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////
module loggers import cvw::*; #(parameter cvw_t P,
parameter TEST,
parameter PrintHPMCounters,
parameter I_CACHE_ADDR_LOGGER,
parameter D_CACHE_ADDR_LOGGER,
parameter BPRED_LOGGER) (
input logic clk,
input logic reset,
input logic DCacheFlushStart,
input logic DCacheFlushDone,
// input logic BeginSample,
// input logic StartSample,
// input logic EndSample,
input string memfilename
);
// performance counter logging
logic BeginSample;
logic StartSample, EndSample;
if(PrintHPMCounters & P.ZICOUNTERS_SUPPORTED) begin : HPMCSample
integer HPMCindex;
logic StartSampleFirst;
logic StartSampleDelayed, BeginDelayed;
logic EndSampleFirst, EndSampleDelayed;
logic [P.XLEN-1:0] InitialHPMCOUNTERH[P.COUNTERS-1:0];
string HPMCnames[] = '{"Mcycle",
"------",
"InstRet",
"Br Count",
"Jump Not Return",
"Return",
"BP Wrong",
"BP Dir Wrong",
"BP Target Wrong",
"RAS Wrong",
"Instr Class Wrong",
"Load Stall",
"Store Stall",
"D Cache Access",
"D Cache Miss",
"D Cache Cycles",
"I Cache Access",
"I Cache Miss",
"I Cache Cycles",
"CSR Write",
"FenceI",
"SFenceVMA",
"Interrupt",
"Exception",
"Divide Cycles"
};
if(TEST == "embench") begin
// embench runs warmup then runs start_trigger
// embench end with stop_trigger.
assign StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_trigger";
flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed);
assign StartSample = StartSampleFirst & ~ StartSampleDelayed;
assign EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_trigger";
flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed);
assign EndSample = EndSampleFirst & ~ EndSampleDelayed;
end else if(TEST == "coremark") begin
// embench runs warmup then runs start_trigger
// embench end with stop_trigger.
assign StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_time";
flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed);
assign StartSample = StartSampleFirst & ~ StartSampleDelayed;
assign EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_time";
flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed);
assign EndSample = EndSampleFirst & ~ EndSampleDelayed;
end else begin
// default start condiction is reset
// default end condiction is end of test (DCacheFlushDone)
assign StartSampleFirst = reset;
flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed);
assign StartSample = StartSampleFirst & ~ StartSampleDelayed;
assign EndSample = DCacheFlushStart & ~DCacheFlushDone;
flop #(1) BeginReg(clk, StartSampleFirst, BeginDelayed);
assign BeginSample = StartSampleFirst & ~BeginDelayed;
end
always @(negedge clk) begin
if(StartSample) begin
for(HPMCindex = 0; HPMCindex < 32; HPMCindex += 1) begin
InitialHPMCOUNTERH[HPMCindex] <= dut.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[HPMCindex];
end
end
if(EndSample) begin
for(HPMCindex = 0; HPMCindex < HPMCnames.size(); HPMCindex += 1) begin
// unlikely to have more than 10M in any counter.
$display("Cnt[%2d] = %7d %s", HPMCindex, dut.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[HPMCindex] - InitialHPMCOUNTERH[HPMCindex], HPMCnames[HPMCindex]);
end
end
end
end
if (P.ICACHE_SUPPORTED && I_CACHE_ADDR_LOGGER) begin : ICacheLogger
int file;
string LogFile;
logic resetD, resetEdge;
logic Enable;
logic InvalDelayed, InvalEdge;
assign Enable = dut.core.ifu.bus.icache.icache.cachefsm.LRUWriteEn &
dut.core.ifu.immu.immu.pmachecker.Cacheable &
~dut.core.ifu.bus.icache.icache.cachefsm.FlushStage &
~reset;
flop #(1) ResetDReg(clk, reset, resetD);
assign resetEdge = ~reset & resetD;
flop #(1) InvalReg(clk, dut.core.ifu.InvalidateICacheM, InvalDelayed);
assign InvalEdge = dut.core.ifu.InvalidateICacheM & ~InvalDelayed;
initial begin
LogFile = "ICache.log";
file = $fopen(LogFile, "w");
$fwrite(file, "BEGIN %s\n", memfilename);
end
string AccessTypeString, HitMissString;
assign HitMissString = dut.core.ifu.bus.icache.icache.CacheHit ? "H" :
dut.core.ifu.bus.icache.icache.vict.cacheLRU.AllValid ? "E" : "M";
always @(posedge clk) begin
if(resetEdge) $fwrite(file, "TRAIN\n");
if(BeginSample) $fwrite(file, "BEGIN %s\n", memfilename);
if(Enable) begin // only log i cache reads
$fwrite(file, "%h R %s\n", dut.core.ifu.PCPF, HitMissString);
end
if(InvalEdge) $fwrite(file, "0 I X\n");
if(EndSample) $fwrite(file, "END %s\n", memfilename);
end
end
if (P.DCACHE_SUPPORTED && D_CACHE_ADDR_LOGGER) begin : DCacheLogger
int file;
string LogFile;
logic resetD, resetEdge;
logic Enabled;
string AccessTypeString, HitMissString;
flop #(1) ResetDReg(clk, reset, resetD);
assign resetEdge = ~reset & resetD;
assign HitMissString = dut.core.lsu.bus.dcache.dcache.CacheHit ? "H" :
(!dut.core.lsu.bus.dcache.dcache.vict.cacheLRU.AllValid) ? "M" :
dut.core.lsu.bus.dcache.dcache.LineDirty ? "D" : "E";
assign AccessTypeString = dut.core.lsu.bus.dcache.FlushDCache ? "F" :
dut.core.lsu.bus.dcache.CacheAtomicM[1] ? "A" :
dut.core.lsu.bus.dcache.CacheRWM == 2'b10 ? "R" :
dut.core.lsu.bus.dcache.CacheRWM == 2'b01 ? "W" :
"NULL";
assign Enabled = dut.core.lsu.bus.dcache.dcache.cachefsm.LRUWriteEn &
~dut.core.lsu.bus.dcache.dcache.cachefsm.FlushStage &
dut.core.lsu.dmmu.dmmu.pmachecker.Cacheable &
(AccessTypeString != "NULL");
initial begin
LogFile = "DCache.log";
file = $fopen(LogFile, "w");
$fwrite(file, "BEGIN %s\n", memfilename);
end
always @(posedge clk) begin
if(resetEdge) $fwrite(file, "TRAIN\n");
if(BeginSample) $fwrite(file, "BEGIN %s\n", memfilename);
if(Enabled) begin
$fwrite(file, "%h %s %s\n", dut.core.lsu.PAdrM, AccessTypeString, HitMissString);
end
if(dut.core.lsu.bus.dcache.dcache.cachefsm.FlushFlag) $fwrite(file, "0 F X\n");
if(EndSample) $fwrite(file, "END %s\n", memfilename);
end
end
if (P.BPRED_SUPPORTED) begin : BranchLogger
if (BPRED_LOGGER) begin
string direction;
int file;
logic PCSrcM;
string LogFile;
logic resetD, resetEdge;
flopenrc #(1) PCSrcMReg(clk, reset, dut.core.FlushM, ~dut.core.StallM, dut.core.ifu.bpred.bpred.Predictor.DirPredictor.PCSrcE, PCSrcM);
flop #(1) ResetDReg(clk, reset, resetD);
assign resetEdge = ~reset & resetD;
initial begin
LogFile = "branch.log"; // will break some of Ross's research analysis scripts
//LogFile = $psprintf("branch_%s%0d.log", P.BPRED_TYPE, P.BPRED_SIZE);
file = $fopen(LogFile, "w");
end
always @(posedge clk) begin
if(resetEdge) $fwrite(file, "TRAIN\n");
if(StartSample) $fwrite(file, "BEGIN %s\n", memfilename);
if(dut.core.ifu.InstrClassM[0] & ~dut.core.StallW & ~dut.core.FlushW & dut.core.InstrValidM) begin
direction = PCSrcM ? "t" : "n";
$fwrite(file, "%h %s\n", dut.core.PCM, direction);
end
if(EndSample) $fwrite(file, "END %s\n", memfilename);
end
end
end
endmodule

View File

@ -0,0 +1,139 @@
///////////////////////////////////////////
// shadowmem.sv
//
// Written: David Harris David_Harris@hmc.edu and Ross Thompson ross1728@gmail.com
// Modified: 14 June 2023
//
// Purpose: The L1 data cache and any feature L2 or high cache will not necessary writeback all dirty
// line before the end of test. Signature based regression tests need to check the L1 data cache
// in addition to the main memory. These modules create a shadow memory with a projection of the
// D$ contents.
//
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////
module DCacheFlushFSM import cvw::*; #(parameter cvw_t P)
(input logic clk,
input logic reset,
input logic start,
output logic done);
genvar adr;
logic [P.XLEN-1:0] ShadowRAM[P.UNCORE_RAM_BASE>>(1+P.XLEN/32):(P.UNCORE_RAM_RANGE+P.UNCORE_RAM_BASE)>>1+(P.XLEN/32)];
logic startD;
if(P.DCACHE_SUPPORTED) begin
localparam numlines = testbench.dut.core.lsu.bus.dcache.dcache.NUMLINES;
localparam numways = testbench.dut.core.lsu.bus.dcache.dcache.NUMWAYS;
localparam linebytelen = testbench.dut.core.lsu.bus.dcache.dcache.LINEBYTELEN;
localparam linelen = testbench.dut.core.lsu.bus.dcache.dcache.LINELEN;
localparam sramlen = testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[0].SRAMLEN;
localparam cachesramwords = testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[0].NUMSRAM;
localparam numwords = sramlen/P.XLEN;
localparam lognumlines = $clog2(numlines);
localparam loglinebytelen = $clog2(linebytelen);
localparam lognumways = $clog2(numways);
localparam tagstart = lognumlines + loglinebytelen;
genvar index, way, cacheWord;
logic [sramlen-1:0] CacheData [numways-1:0] [numlines-1:0] [cachesramwords-1:0];
logic [sramlen-1:0] cacheline;
logic [P.XLEN-1:0] CacheTag [numways-1:0] [numlines-1:0] [cachesramwords-1:0];
logic CacheValid [numways-1:0] [numlines-1:0] [cachesramwords-1:0];
logic CacheDirty [numways-1:0] [numlines-1:0] [cachesramwords-1:0];
logic [P.PA_BITS-1:0] CacheAdr [numways-1:0] [numlines-1:0] [cachesramwords-1:0];
for(index = 0; index < numlines; index++) begin
for(way = 0; way < numways; way++) begin
for(cacheWord = 0; cacheWord < cachesramwords; cacheWord++) begin
copyShadow #(.P(P), .tagstart(tagstart),
.loglinebytelen(loglinebytelen), .sramlen(sramlen))
copyShadow(.clk,
.start,
.tag(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].CacheTagMem.RAM[index][P.PA_BITS-1-tagstart:0]),
.valid(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].ValidBits[index]),
.dirty(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].DirtyBits[index]),
// these dirty bit selections would be needed if dirty is moved inside the tag array.
//.dirty(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].dirty.DirtyMem.RAM[index]),
//.dirty(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].CacheTagMem.RAM[index][P.PA_BITS+tagstart]),
.data(testbench.dut.core.lsu.bus.dcache.dcache.CacheWays[way].word[cacheWord].wordram.CacheDataMem.RAM[index]),
.index(index),
.cacheWord(cacheWord),
.CacheData(CacheData[way][index][cacheWord]),
.CacheAdr(CacheAdr[way][index][cacheWord]),
.CacheTag(CacheTag[way][index][cacheWord]),
.CacheValid(CacheValid[way][index][cacheWord]),
.CacheDirty(CacheDirty[way][index][cacheWord]));
end
end
end
integer i, j, k, l;
always @(posedge clk) begin
if (startD) begin
for(i = 0; i < numlines; i++) begin
for(j = 0; j < numways; j++) begin
for(l = 0; l < cachesramwords; l++) begin
if (CacheValid[j][i][l] & CacheDirty[j][i][l]) begin
for(k = 0; k < numwords; k++) begin
//cacheline = CacheData[j][i][0];
// does not work with modelsim
// # ** Error: ../testbench/testbench.sv(483): Range must be bounded by constant expressions.
// see https://verificationacademy.com/forums/systemverilog/range-must-be-bounded-constant-expressions
//ShadowRAM[CacheAdr[j][i][k] >> $clog2(P.XLEN/8)] = cacheline[P.XLEN*(k+1)-1:P.XLEN*k];
ShadowRAM[(CacheAdr[j][i][l] >> $clog2(P.XLEN/8)) + k] = CacheData[j][i][l][P.XLEN*k +: P.XLEN];
end
end
end
end
end
end
end
end
flop #(1) doneReg1(.clk, .d(start), .q(startD));
flop #(1) doneReg2(.clk, .d(startD), .q(done));
endmodule
module copyShadow import cvw::*; #(parameter cvw_t P,
parameter tagstart, loglinebytelen, sramlen)
(input logic clk,
input logic start,
input logic [P.PA_BITS-1:tagstart] tag,
input logic valid, dirty,
input logic [sramlen-1:0] data,
input logic [32-1:0] index,
input logic [32-1:0] cacheWord,
output logic [sramlen-1:0] CacheData,
output logic [P.PA_BITS-1:0] CacheAdr,
output logic [P.XLEN-1:0] CacheTag,
output logic CacheValid,
output logic CacheDirty);
always_ff @(posedge clk) begin
if(start) begin
CacheTag = tag;
CacheValid = valid;
CacheDirty = dirty;
CacheData = data;
CacheAdr = (tag << tagstart) + (index << loglinebytelen) + (cacheWord << $clog2(sramlen/8));
end
end
endmodule

View File

@ -0,0 +1,52 @@
///////////////////////////////////////////
// watchdog.sv
//
// Written: Ross Thompson ross1728@gmail.com
// Modified: 14 June 2023
//
// Purpose: Detects if the processor is stuck and halts the simulation
//
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////
module watchdog #(parameter XLEN, WatchDogTimerThreshold)
(input clk,
input reset
);
// check for hang up.
logic [XLEN-1:0] PCW;
flopenr #(XLEN) PCWReg(clk, reset, ~dut.core.ieu.dp.StallW, dut.core.ifu.PCM, PCW);
logic [XLEN-1:0] OldPCW;
integer WatchDogTimerCount;
logic WatchDogTimeOut;
always_ff @(posedge clk) begin
OldPCW <= PCW;
if(OldPCW == PCW) WatchDogTimerCount = WatchDogTimerCount + 1'b1;
else WatchDogTimerCount = '0;
end
always_comb begin
WatchDogTimeOut = WatchDogTimerCount >= WatchDogTimerThreshold;
if(WatchDogTimeOut) begin
$display("FAILURE: Watch Dog Time Out triggered. PCW stuck at %x for more than %d cycles", PCW, WatchDogTimerCount);
$stop;
end
end
endmodule

View File

@ -25,7 +25,6 @@
// and limitations under the License.
////////////////////////////////////////////////////////////////////////////////////////////////
`include "wally-config.vh"
`include "config.vh"
`include "tests.vh"
@ -85,7 +84,6 @@ module testbench;
string tests[];
logic DCacheFlushDone, DCacheFlushStart;
logic riscofTest;
logic StartSample, EndSample;
logic Validate;
logic SelectTest;
@ -396,11 +394,8 @@ module testbench;
$display("Read memfile %s", memfilename);
end
end
logic BeginSample;
// instantiate device to be tested
assign GPIOIN = 0;
assign UARTSin = 1;
@ -451,89 +446,7 @@ module testbench;
clk = 1; # 5; clk = 0; # 5;
// if ($time % 100000 == 0) $display("Time is %0t", $time);
end
if(PrintHPMCounters & P.ZICOUNTERS_SUPPORTED) begin : HPMCSample
integer HPMCindex;
logic StartSampleFirst;
logic StartSampleDelayed, BeginDelayed;
logic EndSampleFirst, EndSampleDelayed;
logic [P.XLEN-1:0] InitialHPMCOUNTERH[P.COUNTERS-1:0];
string HPMCnames[] = '{"Mcycle",
"------",
"InstRet",
"Br Count",
"Jump Not Return",
"Return",
"BP Wrong",
"BP Dir Wrong",
"BP Target Wrong",
"RAS Wrong",
"Instr Class Wrong",
"Load Stall",
"Store Stall",
"D Cache Access",
"D Cache Miss",
"D Cache Cycles",
"I Cache Access",
"I Cache Miss",
"I Cache Cycles",
"CSR Write",
"FenceI",
"SFenceVMA",
"Interrupt",
"Exception",
"Divide Cycles"
};
if(TEST == "embench") begin
// embench runs warmup then runs start_trigger
// embench end with stop_trigger.
assign StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_trigger";
flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed);
assign StartSample = StartSampleFirst & ~ StartSampleDelayed;
assign EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_trigger";
flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed);
assign EndSample = EndSampleFirst & ~ EndSampleDelayed;
end else if(TEST == "coremark") begin
// embench runs warmup then runs start_trigger
// embench end with stop_trigger.
assign StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_time";
flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed);
assign StartSample = StartSampleFirst & ~ StartSampleDelayed;
assign EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_time";
flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed);
assign EndSample = EndSampleFirst & ~ EndSampleDelayed;
end else begin
// default start condiction is reset
// default end condiction is end of test (DCacheFlushDone)
assign StartSampleFirst = reset;
flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed);
assign StartSample = StartSampleFirst & ~ StartSampleDelayed;
assign EndSample = DCacheFlushStart & ~DCacheFlushDone;
flop #(1) BeginReg(clk, StartSampleFirst, BeginDelayed);
assign BeginSample = StartSampleFirst & ~BeginDelayed;
end
always @(negedge clk) begin
if(StartSample) begin
for(HPMCindex = 0; HPMCindex < 32; HPMCindex += 1) begin
InitialHPMCOUNTERH[HPMCindex] <= dut.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[HPMCindex];
end
end
if(EndSample) begin
for(HPMCindex = 0; HPMCindex < HPMCnames.size(); HPMCindex += 1) begin
// unlikely to have more than 10M in any counter.
$display("Cnt[%2d] = %7d %s", HPMCindex, dut.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[HPMCindex] - InitialHPMCOUNTERH[HPMCindex], HPMCnames[HPMCindex]);
end
end
end
end
// track the current function or global label
if (DEBUG == 1 | (PrintHPMCounters & P.ZICOUNTERS_SUPPORTED)) begin : FunctionName
@ -564,120 +477,87 @@ module testbench;
.start(DCacheFlushStart),
.done(DCacheFlushDone));
if (P.ICACHE_SUPPORTED && I_CACHE_ADDR_LOGGER) begin : ICacheLogger
int file;
string LogFile;
logic resetD, resetEdge;
logic Enable;
logic InvalDelayed, InvalEdge;
assign Enable = dut.core.ifu.bus.icache.icache.cachefsm.LRUWriteEn &
dut.core.ifu.immu.immu.pmachecker.Cacheable &
~dut.core.ifu.bus.icache.icache.cachefsm.FlushStage &
~reset;
flop #(1) ResetDReg(clk, reset, resetD);
assign resetEdge = ~reset & resetD;
flop #(1) InvalReg(clk, dut.core.ifu.InvalidateICacheM, InvalDelayed);
assign InvalEdge = dut.core.ifu.InvalidateICacheM & ~InvalDelayed;
initial begin
LogFile = "ICache.log";
file = $fopen(LogFile, "w");
$fwrite(file, "BEGIN %s\n", memfilename);
end
string AccessTypeString, HitMissString;
assign HitMissString = dut.core.ifu.bus.icache.icache.CacheHit ? "H" :
dut.core.ifu.bus.icache.icache.vict.cacheLRU.AllValid ? "E" : "M";
always @(posedge clk) begin
if(resetEdge) $fwrite(file, "TRAIN\n");
if(BeginSample) $fwrite(file, "BEGIN %s\n", memfilename);
if(Enable) begin // only log i cache reads
$fwrite(file, "%h R %s\n", dut.core.ifu.PCPF, HitMissString);
end
if(InvalEdge) $fwrite(file, "0 I X\n");
if(EndSample) $fwrite(file, "END %s\n", memfilename);
end
end
if (P.DCACHE_SUPPORTED && D_CACHE_ADDR_LOGGER) begin : DCacheLogger
int file;
string LogFile;
logic resetD, resetEdge;
logic Enabled;
string AccessTypeString, HitMissString;
flop #(1) ResetDReg(clk, reset, resetD);
assign resetEdge = ~reset & resetD;
assign HitMissString = dut.core.lsu.bus.dcache.dcache.CacheHit ? "H" :
(!dut.core.lsu.bus.dcache.dcache.vict.cacheLRU.AllValid) ? "M" :
dut.core.lsu.bus.dcache.dcache.LineDirty ? "D" : "E";
assign AccessTypeString = dut.core.lsu.bus.dcache.FlushDCache ? "F" :
dut.core.lsu.bus.dcache.CacheAtomicM[1] ? "A" :
dut.core.lsu.bus.dcache.CacheRWM == 2'b10 ? "R" :
dut.core.lsu.bus.dcache.CacheRWM == 2'b01 ? "W" :
"NULL";
assign Enabled = dut.core.lsu.bus.dcache.dcache.cachefsm.LRUWriteEn &
~dut.core.lsu.bus.dcache.dcache.cachefsm.FlushStage &
dut.core.lsu.dmmu.dmmu.pmachecker.Cacheable &
(AccessTypeString != "NULL");
initial begin
LogFile = "DCache.log";
file = $fopen(LogFile, "w");
$fwrite(file, "BEGIN %s\n", memfilename);
end
always @(posedge clk) begin
if(resetEdge) $fwrite(file, "TRAIN\n");
if(BeginSample) $fwrite(file, "BEGIN %s\n", memfilename);
if(Enabled) begin
$fwrite(file, "%h %s %s\n", dut.core.lsu.PAdrM, AccessTypeString, HitMissString);
end
if(dut.core.lsu.bus.dcache.dcache.cachefsm.FlushFlag) $fwrite(file, "0 F X\n");
if(EndSample) $fwrite(file, "END %s\n", memfilename);
end
end
if (P.BPRED_SUPPORTED) begin : BranchLogger
if (BPRED_LOGGER) begin
string direction;
int file;
logic PCSrcM;
string LogFile;
logic resetD, resetEdge;
flopenrc #(1) PCSrcMReg(clk, reset, dut.core.FlushM, ~dut.core.StallM, dut.core.ifu.bpred.bpred.Predictor.DirPredictor.PCSrcE, PCSrcM);
flop #(1) ResetDReg(clk, reset, resetD);
assign resetEdge = ~reset & resetD;
initial begin
LogFile = "branch.log"; // will break some of Ross's research analysis scripts
//LogFile = $psprintf("branch_%s%0d.log", P.BPRED_TYPE, P.BPRED_SIZE);
file = $fopen(LogFile, "w");
end
always @(posedge clk) begin
if(resetEdge) $fwrite(file, "TRAIN\n");
if(StartSample) $fwrite(file, "BEGIN %s\n", memfilename);
if(dut.core.ifu.InstrClassM[0] & ~dut.core.StallW & ~dut.core.FlushW & dut.core.InstrValidM) begin
direction = PCSrcM ? "t" : "n";
$fwrite(file, "%h %s\n", dut.core.PCM, direction);
end
if(EndSample) $fwrite(file, "END %s\n", memfilename);
end
end
end
watchdog #(P.XLEN, 1000000) watchdog(.clk, .reset);
loggers #(P, TEST, PrintHPMCounters, I_CACHE_ADDR_LOGGER, D_CACHE_ADDR_LOGGER, BPRED_LOGGER)
loggers (clk, reset, DCacheFlushStart, DCacheFlushDone, memfilename);
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;
localparam SIGNATURESIZE = 5000000;
integer i;
logic [31:0] sig32[0:SIGNATURESIZE];
logic [P.XLEN-1:0] signature[0:SIGNATURESIZE];
string signame, pathname;
logic [P.XLEN-1:0] testadr, testadrNoBase;
// 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
for(i=0; i<SIGNATURESIZE; i=i+1) begin
sig32[i] = 'bx;
end
if (riscofTest) signame = {pathname, TestName, "/ref/Reference-sail_c_simulator.signature"};
else signame = {pathname, TestName, ".signature.output"};
// read signature, reformat in 64 bits if necessary
$readmemh(signame, sig32);
i = 0;
while (i < SIGNATURESIZE) begin
if (P.XLEN == 32) begin
signature[i] = sig32[i];
i = i+1;
end else begin
signature[i/2] = {sig32[i+1], sig32[i]};
i = i + 2;
end
if (i >= 4 & sig32[i-4] === 'bx) begin
if (i == 4) begin
i = SIGNATURESIZE+1; // flag empty file
$display(" Error: empty test file");
end else i = SIGNATURESIZE; // skip over the rest of the x's for efficiency
end
end
// Check errors
errors = (i == SIGNATURESIZE+1); // error if file is empty
i = 0;
testadr = ($unsigned(begin_signature_addr))/(P.XLEN/8);
testadrNoBase = (begin_signature_addr - P.UNCORE_RAM_BASE)/(P.XLEN/8);
/* verilator lint_off INFINITELOOP */
while (signature[i] !== 'bx) begin
logic [P.XLEN-1:0] sig;
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];
//$display("signature[%h] = %h sig = %h", i, signature[i], sig);
if (signature[i] !== sig & (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; //***debug
end
i = i + 1;
end
/* verilator lint_on INFINITELOOP */
if (errors == 0) begin
$display("%s succeeded. Brilliant!!!", TestName);
end else begin
$display("%s failed with %d errors. :(", TestName, errors);
//totalerrors = totalerrors+1;
end
endtask //
endmodule
/* verilator lint_on STMTDLY */
/* verilator lint_on WIDTH */
task automatic updateProgramAddrLabelArray;
input string ProgramAddrMapFile, ProgramLabelMapFile;
inout integer ProgramAddrLabelArray [string];
@ -703,73 +583,3 @@ task automatic updateProgramAddrLabelArray;
$fclose(ProgramAddrMapFP);
endtask
task automatic CheckSignature;
input string pathname;
input string TestName;
input logic riscofTest;
input integer begin_signature_addr;
output integer errors;
localparam SIGNATURESIZE = 5000000;
integer i;
logic [31:0] sig32[0:SIGNATURESIZE];
logic [`XLEN-1:0] signature[0:SIGNATURESIZE];
string signame, pathname;
logic [`XLEN-1:0] testadr, testadrNoBase;
// 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
for(i=0; i<SIGNATURESIZE; i=i+1) begin
sig32[i] = 'bx;
end
if (riscofTest) signame = {pathname, TestName, "/ref/Reference-sail_c_simulator.signature"};
else signame = {pathname, TestName, ".signature.output"};
// read signature, reformat in 64 bits if necessary
$readmemh(signame, sig32);
i = 0;
while (i < SIGNATURESIZE) begin
if (`XLEN == 32) begin
signature[i] = sig32[i];
i = i+1;
end else begin
signature[i/2] = {sig32[i+1], sig32[i]};
i = i + 2;
end
if (i >= 4 & sig32[i-4] === 'bx) begin
if (i == 4) begin
i = SIGNATURESIZE+1; // flag empty file
$display(" Error: empty test file");
end else i = SIGNATURESIZE; // skip over the rest of the x's for efficiency
end
end
// Check errors
errors = (i == SIGNATURESIZE+1); // error if file is empty
i = 0;
testadr = ($unsigned(begin_signature_addr))/(`XLEN/8);
testadrNoBase = (begin_signature_addr - `UNCORE_RAM_BASE)/(`XLEN/8);
/* verilator lint_off INFINITELOOP */
while (signature[i] !== 'bx) begin
logic [`XLEN-1:0] sig;
if (`DTIM_SUPPORTED) sig = testbench.dut.core.lsu.dtim.dtim.ram.RAM[testadrNoBase+i];
else if (`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
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)*(`XLEN/8), testbench.DCacheFlushFSM.ShadowRAM[testadr+i], sig, signature[i]);
$stop; //***debug
end
i = i + 1;
end
/* verilator lint_on INFINITELOOP */
if (errors == 0) begin
$display("%s succeeded. Brilliant!!!", TestName);
end else begin
$display("%s failed with %d errors. :(", TestName, errors);
//totalerrors = totalerrors+1;
end
endtask //