2021-01-24 22:10:00 +00:00
|
|
|
`include "wally-config.vh"
|
2021-01-21 21:17:34 +00:00
|
|
|
|
2021-04-26 16:48:58 +00:00
|
|
|
|
2021-04-24 13:32:09 +00:00
|
|
|
module testbench();
|
2021-01-21 22:55:05 +00:00
|
|
|
logic clk, reset;
|
|
|
|
logic [31:0] GPIOPinsIn;
|
|
|
|
logic [31:0] GPIOPinsOut, GPIOPinsEn;
|
2021-01-21 21:17:34 +00:00
|
|
|
|
|
|
|
// instantiate device to be tested
|
2021-03-30 19:25:07 +00:00
|
|
|
logic [31:0] CheckInstrD;
|
2021-01-23 22:52:05 +00:00
|
|
|
|
2021-01-29 17:46:50 +00:00
|
|
|
logic [`AHBW-1:0] HRDATA;
|
|
|
|
logic [31:0] HADDR;
|
|
|
|
logic [`AHBW-1:0] HWDATA;
|
|
|
|
logic HWRITE;
|
|
|
|
logic [2:0] HSIZE;
|
|
|
|
logic [2:0] HBURST;
|
|
|
|
logic [3:0] HPROT;
|
|
|
|
logic [1:0] HTRANS;
|
|
|
|
logic HMASTLOCK;
|
2021-01-30 17:38:18 +00:00
|
|
|
logic HCLK, HRESETn;
|
2021-02-23 18:59:06 +00:00
|
|
|
logic [`AHBW-1:0] HRDATAEXT;
|
|
|
|
logic HREADYEXT, HRESPEXT;
|
|
|
|
logic UARTSout;
|
2021-01-29 17:46:50 +00:00
|
|
|
|
|
|
|
assign GPIOPinsIn = 0;
|
|
|
|
assign UARTSin = 1;
|
2021-02-24 05:28:33 +00:00
|
|
|
|
2021-01-21 22:55:05 +00:00
|
|
|
// instantiate processor and memories
|
2021-02-28 06:02:21 +00:00
|
|
|
wallypipelinedsoc dut(.*);
|
2021-01-21 21:17:34 +00:00
|
|
|
|
2021-04-24 00:12:20 +00:00
|
|
|
/**
|
|
|
|
* Walk the page table stored in dtim according to sv39 logic and translate a
|
|
|
|
* virtual address to a physical address.
|
|
|
|
*
|
|
|
|
* See section 4.3.2 of the RISC-V Privileged specification for a full
|
|
|
|
* explanation of the below algorithm.
|
|
|
|
*/
|
|
|
|
function logic [`XLEN-1:0] adrTranslator(
|
|
|
|
input logic [`XLEN-1:0] adrIn);
|
|
|
|
begin
|
|
|
|
logic SvMode, PTE_R, PTE_X;
|
|
|
|
logic [`XLEN-1:0] SATP, PTE;
|
|
|
|
logic [55:0] BaseAdr, PAdr;
|
|
|
|
logic [8:0] VPN [0:2];
|
|
|
|
logic [11:0] Offset;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// Grab the SATP register from privileged unit
|
|
|
|
SATP = dut.hart.priv.csr.SATP_REGW;
|
|
|
|
|
|
|
|
// Split the virtual address into page number segments and offset
|
|
|
|
VPN[2] = adrIn[38:30];
|
|
|
|
VPN[1] = adrIn[29:21];
|
|
|
|
VPN[0] = adrIn[20:12];
|
|
|
|
Offset = adrIn[11:0];
|
|
|
|
|
|
|
|
// We do not support sv48; only sv39
|
|
|
|
SvMode = SATP[63];
|
|
|
|
|
|
|
|
// Only perform translation if translation is on and the processor is not
|
|
|
|
// in machine mode
|
|
|
|
if (SvMode && (dut.hart.priv.PrivilegeModeW != `M_MODE)) begin
|
|
|
|
BaseAdr = SATP[43:0] << 12;
|
|
|
|
|
|
|
|
for (i = 2; i >= 0; i--) begin
|
|
|
|
PAdr = BaseAdr + (VPN[i] << 3);
|
|
|
|
|
|
|
|
// dtim.RAM is 64-bit addressed. PAdr specifies a byte. We right shift
|
|
|
|
// by 3 (the PTE size) to get the requested 64-bit PTE.
|
|
|
|
PTE = dut.uncore.dtim.RAM[PAdr >> 3];
|
|
|
|
PTE_R = PTE[1];
|
|
|
|
PTE_X = PTE[3];
|
|
|
|
if (PTE_R || PTE_X) begin
|
|
|
|
// Leaf page found
|
|
|
|
break;
|
|
|
|
end else begin
|
|
|
|
// Go to next level of table
|
|
|
|
BaseAdr = PTE[53:10] << 12;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
// Determine which parts of the PTE page number to use based on the
|
|
|
|
// level of the page table we reached.
|
|
|
|
if (i == 2) begin
|
|
|
|
// Gigapage
|
|
|
|
assign adrTranslator = {8'b0, PTE[53:28], VPN[1], VPN[0], Offset};
|
|
|
|
end else if (i == 1) begin
|
|
|
|
// Megapage
|
|
|
|
assign adrTranslator = {8'b0, PTE[53:19], VPN[0], Offset};
|
|
|
|
end else begin
|
|
|
|
// Kilopage
|
|
|
|
assign adrTranslator = {8'b0, PTE[53:10], Offset};
|
|
|
|
end
|
|
|
|
end else begin
|
|
|
|
// Direct translation if address translation is not on
|
|
|
|
assign adrTranslator = adrIn;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
endfunction
|
2021-02-24 01:51:18 +00:00
|
|
|
|
2021-01-21 21:17:34 +00:00
|
|
|
// initialize test
|
|
|
|
initial
|
|
|
|
begin
|
2021-01-21 22:55:05 +00:00
|
|
|
reset <= 1; # 22; reset <= 0;
|
2021-01-21 21:17:34 +00:00
|
|
|
end
|
2021-02-24 05:28:33 +00:00
|
|
|
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
// read pc trace file
|
|
|
|
integer data_file_PC, scan_file_PC;
|
2021-01-22 19:11:17 +00:00
|
|
|
initial begin
|
2021-04-17 03:27:29 +00:00
|
|
|
data_file_PC = $fopen({`BUSYBEAR_TEST_VECTORS,"parsedPC.txt"}, "r");
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
if (data_file_PC == 0) begin
|
2021-01-22 19:11:17 +00:00
|
|
|
$display("file couldn't be opened");
|
2021-02-12 17:21:56 +00:00
|
|
|
$stop;
|
2021-02-24 05:28:33 +00:00
|
|
|
end
|
2021-01-22 19:11:17 +00:00
|
|
|
end
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
|
2021-02-01 23:57:06 +00:00
|
|
|
integer data_file_PCW, scan_file_PCW;
|
|
|
|
initial begin
|
2021-04-17 03:27:29 +00:00
|
|
|
data_file_PCW = $fopen({`BUSYBEAR_TEST_VECTORS,"parsedPC.txt"}, "r");
|
2021-02-01 23:57:06 +00:00
|
|
|
if (data_file_PCW == 0) begin
|
|
|
|
$display("file couldn't be opened");
|
2021-02-12 17:21:56 +00:00
|
|
|
$stop;
|
2021-02-24 05:28:33 +00:00
|
|
|
end
|
2021-02-01 23:57:06 +00:00
|
|
|
end
|
|
|
|
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
// read register trace file
|
|
|
|
integer data_file_rf, scan_file_rf;
|
|
|
|
initial begin
|
2021-04-17 03:27:29 +00:00
|
|
|
data_file_rf = $fopen({`BUSYBEAR_TEST_VECTORS,"parsedRegs.txt"}, "r");
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
if (data_file_rf == 0) begin
|
|
|
|
$display("file couldn't be opened");
|
2021-02-12 17:21:56 +00:00
|
|
|
$stop;
|
2021-02-24 05:28:33 +00:00
|
|
|
end
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
end
|
|
|
|
|
2021-02-02 03:08:11 +00:00
|
|
|
// read CSR trace file
|
|
|
|
integer data_file_csr, scan_file_csr;
|
|
|
|
initial begin
|
2021-04-23 21:32:24 +00:00
|
|
|
data_file_csr = $fopen({`BUSYBEAR_TEST_VECTORS,"parsedCSRs2.txt"}, "r");
|
2021-02-02 03:08:11 +00:00
|
|
|
if (data_file_csr == 0) begin
|
|
|
|
$display("file couldn't be opened");
|
2021-02-12 17:21:56 +00:00
|
|
|
$stop;
|
2021-02-24 05:28:33 +00:00
|
|
|
end
|
2021-02-02 03:08:11 +00:00
|
|
|
end
|
|
|
|
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
// read memreads trace file
|
2021-01-25 01:43:47 +00:00
|
|
|
integer data_file_memR, scan_file_memR;
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
initial begin
|
2021-04-17 03:27:29 +00:00
|
|
|
data_file_memR = $fopen({`BUSYBEAR_TEST_VECTORS,"parsedMemRead.txt"}, "r");
|
2021-01-25 01:43:47 +00:00
|
|
|
if (data_file_memR == 0) begin
|
|
|
|
$display("file couldn't be opened");
|
2021-02-12 17:21:56 +00:00
|
|
|
$stop;
|
2021-02-24 05:28:33 +00:00
|
|
|
end
|
2021-01-25 01:43:47 +00:00
|
|
|
end
|
2021-02-24 05:28:33 +00:00
|
|
|
|
2021-01-25 01:43:47 +00:00
|
|
|
// read memwrite trace file
|
|
|
|
integer data_file_memW, scan_file_memW;
|
|
|
|
initial begin
|
2021-04-17 03:27:29 +00:00
|
|
|
data_file_memW = $fopen({`BUSYBEAR_TEST_VECTORS,"parsedMemWrite.txt"}, "r");
|
2021-01-25 01:43:47 +00:00
|
|
|
if (data_file_memW == 0) begin
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
$display("file couldn't be opened");
|
2021-02-12 17:21:56 +00:00
|
|
|
$stop;
|
2021-02-24 05:28:33 +00:00
|
|
|
end
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
end
|
2021-02-12 19:56:20 +00:00
|
|
|
|
2021-03-08 19:26:26 +00:00
|
|
|
// initial loading of memories
|
|
|
|
initial begin
|
2021-04-17 03:27:29 +00:00
|
|
|
$readmemh({`BUSYBEAR_TEST_VECTORS,"bootmem.txt"}, dut.uncore.bootdtim.RAM, 'h1000 >> 3);
|
|
|
|
$readmemh({`BUSYBEAR_TEST_VECTORS,"ram.txt"}, dut.uncore.dtim.RAM);
|
2021-03-18 21:37:10 +00:00
|
|
|
$readmemb(`TWO_BIT_PRELOAD, dut.hart.ifu.bpred.Predictor.DirPredictor.PHT.memory);
|
2021-03-15 22:27:27 +00:00
|
|
|
$readmemb(`BTB_PRELOAD, dut.hart.ifu.bpred.TargetPredictor.memory.memory);
|
2021-03-08 19:26:26 +00:00
|
|
|
end
|
|
|
|
|
2021-02-12 17:21:56 +00:00
|
|
|
integer warningCount = 0;
|
2021-03-17 01:42:26 +00:00
|
|
|
integer instrs;
|
2021-02-12 19:56:20 +00:00
|
|
|
|
2021-02-23 18:59:06 +00:00
|
|
|
//logic[63:0] adrTranslation[4:0];
|
|
|
|
//string translationType[4:0] = {"rf", "writeAdr", "PCW", "PC", "readAdr"};
|
|
|
|
//initial begin
|
|
|
|
// for(int i=0; i<5; i++) begin
|
|
|
|
// adrTranslation[i] = 64'b0;
|
|
|
|
// end
|
|
|
|
//end
|
|
|
|
|
|
|
|
//function logic equal(logic[63:0] adr, logic[63:0] adrExpected, integer func);
|
|
|
|
// if (adr[11:0] !== adrExpected[11:0]) begin
|
|
|
|
// equal = 1'b0;
|
|
|
|
// end else begin
|
|
|
|
// equal = 1'b1;
|
|
|
|
// if ((adr+adrTranslation[func]) !== adrExpected) begin
|
|
|
|
// adrTranslation[func] = adrExpected - adr;
|
|
|
|
// $display("warning: probably new address translation %x for %s at instr %0d", adrTranslation[func], translationType[func], instrs);
|
|
|
|
// warningCount += 1;
|
|
|
|
// end
|
|
|
|
// end
|
|
|
|
//endfunction
|
|
|
|
|
|
|
|
// pretty sure this isn't necessary anymore, but keeping this for now since its easier
|
2021-02-12 19:56:20 +00:00
|
|
|
function logic equal(logic[63:0] adr, logic[63:0] adrExpected, integer func);
|
2021-02-23 18:59:06 +00:00
|
|
|
equal = adr === adrExpected;
|
2021-02-12 19:56:20 +00:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
2021-02-12 17:21:56 +00:00
|
|
|
`define ERROR \
|
|
|
|
#10; \
|
|
|
|
$display("processed %0d instructions with %0d warnings", instrs, warningCount); \
|
|
|
|
$stop;
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
|
2021-01-22 20:05:58 +00:00
|
|
|
logic [63:0] pcExpected;
|
2021-02-02 01:27:43 +00:00
|
|
|
logic [63:0] regExpected;
|
|
|
|
integer regNumExpected;
|
2021-03-30 19:25:07 +00:00
|
|
|
logic [`XLEN-1:0] PCW;
|
|
|
|
|
2021-03-31 17:41:40 +00:00
|
|
|
flopenr #(`XLEN) PCWReg(clk, reset, ~dut.hart.ieu.dp.StallW, dut.hart.ifu.PCM, PCW);
|
2021-02-02 01:27:43 +00:00
|
|
|
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
genvar i;
|
|
|
|
generate
|
2021-02-02 01:27:43 +00:00
|
|
|
for(i=1; i<32; i++) begin
|
2021-02-23 18:59:06 +00:00
|
|
|
always @(dut.hart.ieu.dp.regf.rf[i]) begin
|
2021-02-04 19:22:09 +00:00
|
|
|
if ($time == 0) begin
|
|
|
|
scan_file_rf = $fscanf(data_file_rf, "%x\n", regExpected);
|
2021-02-23 18:59:06 +00:00
|
|
|
if (dut.hart.ieu.dp.regf.rf[i] != regExpected) begin
|
2021-02-23 22:01:23 +00:00
|
|
|
$display("%0t ps, instr %0d: rf[%0d] does not equal rf expected: %x, %x", $time, instrs, i, dut.hart.ieu.dp.regf.rf[i], regExpected);
|
2021-02-12 17:21:56 +00:00
|
|
|
`ERROR
|
2021-02-04 19:22:09 +00:00
|
|
|
end
|
|
|
|
end else begin
|
2021-02-02 01:27:43 +00:00
|
|
|
scan_file_rf = $fscanf(data_file_rf, "%d\n", regNumExpected);
|
|
|
|
scan_file_rf = $fscanf(data_file_rf, "%x\n", regExpected);
|
|
|
|
if (i != regNumExpected) begin
|
2021-03-22 22:12:41 +00:00
|
|
|
$display("%0t ps, instr %0d: wrong register changed: %0d, %0d expected to switch to %x from %x", $time, instrs, i, regNumExpected, regExpected, dut.hart.ieu.dp.regf.rf[regNumExpected]);
|
2021-02-12 19:56:20 +00:00
|
|
|
`ERROR
|
2021-02-02 01:27:43 +00:00
|
|
|
end
|
2021-02-23 18:59:06 +00:00
|
|
|
if (~equal(dut.hart.ieu.dp.regf.rf[i],regExpected, 0)) begin
|
2021-02-23 22:01:23 +00:00
|
|
|
$display("%0t ps, instr %0d: rf[%0d] does not equal rf expected: %x, %x", $time, instrs, i, dut.hart.ieu.dp.regf.rf[i], regExpected);
|
2021-02-12 17:21:56 +00:00
|
|
|
`ERROR
|
2021-02-02 01:27:43 +00:00
|
|
|
end
|
2021-03-08 19:48:12 +00:00
|
|
|
//if (dut.hart.ieu.dp.regf.rf[i] !== regExpected) begin
|
|
|
|
// force dut.hart.ieu.dp.regf.rf[i] = regExpected;
|
|
|
|
// release dut.hart.ieu.dp.regf.rf[i];
|
|
|
|
//end
|
2021-02-02 01:27:43 +00:00
|
|
|
end
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
end
|
|
|
|
end
|
2021-02-02 01:27:43 +00:00
|
|
|
endgenerate
|
2021-02-24 05:28:33 +00:00
|
|
|
|
|
|
|
// RAM and bootram are addressed in 64-bit blocks - this logic handles R/W
|
|
|
|
// including subwords. Brief explanation on signals:
|
|
|
|
//
|
|
|
|
// readMask: bitmask of bits to read / write, left-shifted to align with
|
|
|
|
// nearest 64-bit boundary - examples
|
|
|
|
// HSIZE = 0 -> readMask = 11111111
|
|
|
|
// HSIZE = 1 -> readMask = 1111111111111111
|
|
|
|
//
|
|
|
|
// In the linux boot, the processor spends the first ~5 instructions in
|
|
|
|
// bootram, before jr jumps to main RAM
|
|
|
|
|
2021-02-24 01:51:18 +00:00
|
|
|
logic [63:0] readMask;
|
|
|
|
assign readMask = ((1 << (8*(1 << HSIZE))) - 1) << 8 * HADDR[2:0];
|
2021-02-24 19:35:28 +00:00
|
|
|
|
2021-04-23 21:32:24 +00:00
|
|
|
logic [`XLEN-1:0] readAdrExpected, readAdrTranslated;
|
2021-02-24 19:35:28 +00:00
|
|
|
|
2021-04-18 21:48:51 +00:00
|
|
|
import ahbliteState::*;
|
2021-03-22 20:52:22 +00:00
|
|
|
always @(dut.HRDATA) begin
|
2021-03-30 19:25:07 +00:00
|
|
|
#2;
|
2021-04-18 22:18:07 +00:00
|
|
|
if (dut.hart.MemRWM[1]
|
|
|
|
&& (dut.hart.ebu.BusState == MEMREAD || dut.hart.ebu.BusState == ATOMICREAD)
|
|
|
|
&& dut.HRDATA !== {64{1'bx}}) begin
|
2021-03-22 18:54:05 +00:00
|
|
|
//$display("%0t", $time);
|
2021-01-28 18:16:38 +00:00
|
|
|
if($feof(data_file_memR)) begin
|
|
|
|
$display("no more memR data to read");
|
2021-02-12 17:21:56 +00:00
|
|
|
`ERROR
|
2021-01-28 18:16:38 +00:00
|
|
|
end
|
|
|
|
scan_file_memR = $fscanf(data_file_memR, "%x\n", readAdrExpected);
|
2021-01-30 17:38:18 +00:00
|
|
|
scan_file_memR = $fscanf(data_file_memR, "%x\n", HRDATA);
|
2021-04-23 21:32:24 +00:00
|
|
|
assign readAdrTranslated = adrTranslator(readAdrExpected);
|
|
|
|
if (~equal(HADDR,readAdrTranslated,4)) begin
|
|
|
|
$display("%0t ps, instr %0d: HADDR does not equal readAdrExpected: %x, %x", $time, instrs, HADDR, readAdrTranslated);
|
2021-02-23 22:01:23 +00:00
|
|
|
`ERROR
|
|
|
|
end
|
2021-03-22 20:52:22 +00:00
|
|
|
if ((readMask & HRDATA) !== (readMask & dut.HRDATA)) begin
|
2021-03-22 21:28:39 +00:00
|
|
|
if (HADDR inside `BUSYBEAR_FIX_READ) begin
|
2021-03-22 22:12:41 +00:00
|
|
|
//$display("warning %0t ps, instr %0d, adr %0d: forcing HRDATA to expected: %x, %x", $time, instrs, HADDR, HRDATA, dut.HRDATA);
|
2021-03-22 21:28:39 +00:00
|
|
|
force dut.uncore.HRDATA = HRDATA;
|
|
|
|
#9;
|
|
|
|
release dut.uncore.HRDATA;
|
|
|
|
warningCount += 1;
|
|
|
|
end else begin
|
|
|
|
$display("%0t ps, instr %0d: ExpectedHRDATA does not equal dut.HRDATA: %x, %x from address %x, %x", $time, instrs, HRDATA, dut.HRDATA, HADDR, HSIZE);
|
|
|
|
`ERROR
|
|
|
|
end
|
2021-01-28 18:16:38 +00:00
|
|
|
end
|
2021-03-22 18:54:05 +00:00
|
|
|
//end else if(dut.hart.MemRWM[1]) begin
|
2021-03-22 20:52:22 +00:00
|
|
|
// $display("%x, %x, %x, %t", HADDR, dut.PCF, dut.HRDATA, $time);
|
2021-03-22 18:47:43 +00:00
|
|
|
|
2021-01-25 01:43:47 +00:00
|
|
|
end
|
2021-03-22 18:47:43 +00:00
|
|
|
|
2021-01-25 01:43:47 +00:00
|
|
|
end
|
2021-02-24 05:28:33 +00:00
|
|
|
|
2021-04-23 21:32:24 +00:00
|
|
|
logic [`XLEN-1:0] writeDataExpected, writeAdrExpected, writeAdrTranslated;
|
2021-02-24 19:35:28 +00:00
|
|
|
|
2021-01-25 01:43:47 +00:00
|
|
|
// this might need to change
|
2021-03-01 20:56:04 +00:00
|
|
|
//always @(HWDATA or HADDR or HSIZE or HWRITE) begin
|
|
|
|
always @(negedge HWRITE) begin
|
|
|
|
//#1;
|
|
|
|
if ($time != 0) begin
|
2021-01-28 18:16:38 +00:00
|
|
|
if($feof(data_file_memW)) begin
|
|
|
|
$display("no more memW data to read");
|
2021-02-12 17:21:56 +00:00
|
|
|
`ERROR
|
2021-01-28 18:16:38 +00:00
|
|
|
end
|
2021-01-25 01:43:47 +00:00
|
|
|
scan_file_memW = $fscanf(data_file_memW, "%x\n", writeDataExpected);
|
2021-01-26 01:06:13 +00:00
|
|
|
scan_file_memW = $fscanf(data_file_memW, "%x\n", writeAdrExpected);
|
2021-04-23 21:32:24 +00:00
|
|
|
assign writeAdrTranslated = adrTranslator(writeAdrExpected);
|
|
|
|
|
2021-01-30 17:38:18 +00:00
|
|
|
if (writeDataExpected != HWDATA) begin
|
2021-02-23 22:01:23 +00:00
|
|
|
$display("%0t ps, instr %0d: HWDATA does not equal writeDataExpected: %x, %x", $time, instrs, HWDATA, writeDataExpected);
|
2021-02-12 17:21:56 +00:00
|
|
|
`ERROR
|
2021-01-26 01:06:13 +00:00
|
|
|
end
|
2021-04-23 21:32:24 +00:00
|
|
|
if (~equal(writeAdrTranslated,HADDR,1)) begin
|
|
|
|
$display("%0t ps, instr %0d: HADDR does not equal writeAdrExpected: %x, %x", $time, instrs, HADDR, writeAdrTranslated);
|
2021-02-12 17:21:56 +00:00
|
|
|
`ERROR
|
2021-01-25 01:43:47 +00:00
|
|
|
end
|
2021-01-22 20:05:58 +00:00
|
|
|
end
|
change how testbench reads data
we're not sure if this is a good idea, but for now, we broke things up into 3 seperate
files, each read seperately. One for pc and instructions, one for registers, and one for
memory reads. Each is scrolled through essentially independantly: new pc data is read and checked
whenever pc changes, new register data is checked whenever any register changes, and a new mem
read value is gotten whenever DataAdrM or MemRWM changes and MemRWM is not zero. I'm not super
sure about the last one. Currently it looks like things should be working, but it goes wrong after,
like, 3 instructions.
2021-01-23 01:27:01 +00:00
|
|
|
end
|
|
|
|
|
2021-02-04 19:22:09 +00:00
|
|
|
integer totalCSR = 0;
|
|
|
|
logic [99:0] StartCSRexpected[63:0];
|
|
|
|
string StartCSRname[99:0];
|
|
|
|
initial begin
|
|
|
|
while(1) begin
|
2021-02-24 05:28:33 +00:00
|
|
|
scan_file_csr = $fscanf(data_file_csr, "%s\n", StartCSRname[totalCSR]);
|
2021-02-04 19:22:09 +00:00
|
|
|
if(StartCSRname[totalCSR] == "---") begin
|
|
|
|
break;
|
|
|
|
end
|
|
|
|
scan_file_csr = $fscanf(data_file_csr, "%x\n", StartCSRexpected[totalCSR]);
|
|
|
|
totalCSR = totalCSR + 1;
|
|
|
|
end
|
|
|
|
end
|
2021-02-24 05:28:33 +00:00
|
|
|
|
2021-03-17 01:42:26 +00:00
|
|
|
always @(dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW) begin
|
2021-03-30 19:25:07 +00:00
|
|
|
if (dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW == 2 && instrs > 1) begin
|
2021-03-22 22:24:31 +00:00
|
|
|
$display("!!!!!! illegal instruction !!!!!!!!!!");
|
|
|
|
$display("(as a reminder, MCAUSE and MEPC are set by this)");
|
|
|
|
$display("at %0t ps, instr %0d, HADDR %x", $time, instrs, HADDR);
|
|
|
|
`ERROR
|
|
|
|
end
|
2021-03-17 01:42:26 +00:00
|
|
|
if (dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW == 5 && instrs != 0) begin
|
2021-03-22 22:24:31 +00:00
|
|
|
$display("!!!!!! illegal (physical) memory access !!!!!!!!!!");
|
2021-03-17 01:42:26 +00:00
|
|
|
$display("(as a reminder, MCAUSE and MEPC are set by this)");
|
|
|
|
$display("at %0t ps, instr %0d, HADDR %x", $time, instrs, HADDR);
|
|
|
|
`ERROR
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-02-04 20:13:36 +00:00
|
|
|
`define CHECK_CSR2(CSR, PATH) \
|
2021-02-02 06:06:03 +00:00
|
|
|
string CSR; \
|
|
|
|
logic [63:0] expected``CSR``; \
|
|
|
|
//CSR checking \
|
2021-02-04 20:13:36 +00:00
|
|
|
always @(``PATH``.``CSR``_REGW) begin \
|
2021-02-02 06:06:03 +00:00
|
|
|
if ($time > 1) begin \
|
2021-04-14 04:18:44 +00:00
|
|
|
if ("SEPC" == `"CSR`") begin #1; end \
|
|
|
|
if ("SCAUSE" == `"CSR`") begin #2; end \
|
|
|
|
if ("SSTATUS" == `"CSR`") begin #3; end \
|
2021-02-02 06:06:03 +00:00
|
|
|
scan_file_csr = $fscanf(data_file_csr, "%s\n", CSR); \
|
|
|
|
scan_file_csr = $fscanf(data_file_csr, "%x\n", expected``CSR``); \
|
|
|
|
if(CSR.icompare(`"CSR`")) begin \
|
2021-02-23 22:01:23 +00:00
|
|
|
$display("%0t ps, instr %0d: %s changed, expected %s", $time, instrs, `"CSR`", CSR); \
|
2021-02-02 06:06:03 +00:00
|
|
|
end \
|
2021-02-04 20:13:36 +00:00
|
|
|
if(``PATH``.``CSR``_REGW != ``expected``CSR) begin \
|
2021-02-23 22:01:23 +00:00
|
|
|
$display("%0t ps, instr %0d: %s does not equal %s expected: %x, %x", $time, instrs, `"CSR`", CSR, ``PATH``.``CSR``_REGW, ``expected``CSR); \
|
2021-02-12 17:21:56 +00:00
|
|
|
`ERROR \
|
2021-02-04 19:22:09 +00:00
|
|
|
end \
|
|
|
|
end else begin \
|
2021-04-21 20:03:42 +00:00
|
|
|
if (!(`BUILDROOT == 1 && "MSTATUS" == `"CSR`")) begin \
|
|
|
|
for(integer j=0; j<totalCSR; j++) begin \
|
|
|
|
if(!StartCSRname[j].icompare(`"CSR`")) begin \
|
|
|
|
if(``PATH``.``CSR``_REGW != StartCSRexpected[j]) begin \
|
|
|
|
$display("%0t ps, instr %0d: %s does not equal %s expected: %x, %x", $time, instrs, `"CSR`", StartCSRname[j], ``PATH``.``CSR``_REGW, StartCSRexpected[j]); \
|
|
|
|
`ERROR \
|
|
|
|
end \
|
2021-02-04 19:22:09 +00:00
|
|
|
end \
|
|
|
|
end \
|
2021-02-02 06:06:03 +00:00
|
|
|
end \
|
|
|
|
end \
|
|
|
|
end
|
2021-02-04 20:13:36 +00:00
|
|
|
`define CHECK_CSR(CSR) \
|
2021-02-24 05:28:33 +00:00
|
|
|
`CHECK_CSR2(CSR, dut.hart.priv.csr)
|
|
|
|
`define CSRM dut.hart.priv.csr.genblk1.csrm
|
2021-02-23 18:59:06 +00:00
|
|
|
`define CSRS dut.hart.priv.csr.genblk1.csrs.genblk1
|
2021-02-02 06:06:03 +00:00
|
|
|
|
2021-04-05 16:47:11 +00:00
|
|
|
|
2021-02-02 06:06:03 +00:00
|
|
|
//`CHECK_CSR(FCSR)
|
2021-02-17 01:03:24 +00:00
|
|
|
`CHECK_CSR2(MCAUSE, `CSRM)
|
2021-02-02 06:06:03 +00:00
|
|
|
`CHECK_CSR(MCOUNTEREN)
|
|
|
|
`CHECK_CSR(MEDELEG)
|
2021-02-17 01:03:24 +00:00
|
|
|
`CHECK_CSR(MEPC)
|
2021-02-04 20:13:36 +00:00
|
|
|
//`CHECK_CSR(MHARTID)
|
2021-02-17 01:03:24 +00:00
|
|
|
`CHECK_CSR(MIDELEG)
|
2021-02-02 06:06:03 +00:00
|
|
|
`CHECK_CSR(MIE)
|
2021-02-17 01:03:24 +00:00
|
|
|
//`CHECK_CSR(MIP)
|
|
|
|
`CHECK_CSR2(MISA, `CSRM)
|
|
|
|
`CHECK_CSR2(MSCRATCH, `CSRM)
|
|
|
|
`CHECK_CSR(MSTATUS)
|
|
|
|
`CHECK_CSR2(MTVAL, `CSRM)
|
2021-02-02 06:06:03 +00:00
|
|
|
`CHECK_CSR(MTVEC)
|
2021-02-17 01:03:24 +00:00
|
|
|
//`CHECK_CSR2(PMPADDR0, `CSRM)
|
2021-02-28 16:46:53 +00:00
|
|
|
//`CHECK_CSR2(PMdut.PCFG0, `CSRM)
|
2021-03-11 06:45:14 +00:00
|
|
|
`CHECK_CSR(SATP)
|
2021-02-17 01:03:24 +00:00
|
|
|
`CHECK_CSR2(SCAUSE, `CSRS)
|
2021-02-02 06:06:03 +00:00
|
|
|
`CHECK_CSR(SCOUNTEREN)
|
2021-02-17 01:03:24 +00:00
|
|
|
`CHECK_CSR(SEPC)
|
2021-02-12 03:42:58 +00:00
|
|
|
`CHECK_CSR(SIE)
|
2021-02-17 01:03:24 +00:00
|
|
|
`CHECK_CSR2(SSCRATCH, `CSRS)
|
|
|
|
`CHECK_CSR(SSTATUS)
|
|
|
|
`CHECK_CSR2(STVAL, `CSRS)
|
|
|
|
`CHECK_CSR(STVEC)
|
2021-02-02 03:08:11 +00:00
|
|
|
|
2021-04-05 16:47:11 +00:00
|
|
|
//$stop;
|
2021-04-17 18:44:32 +00:00
|
|
|
generate
|
|
|
|
if (`BUSYBEAR == 1) begin
|
|
|
|
initial begin //this is temporary until the bug can be fixed!!!
|
|
|
|
#11130100;
|
|
|
|
force dut.hart.ieu.dp.regf.rf[5] = 64'h0000000080000004;
|
|
|
|
#100;
|
|
|
|
release dut.hart.ieu.dp.regf.rf[5];
|
|
|
|
end
|
|
|
|
end
|
|
|
|
endgenerate
|
2021-03-22 22:12:41 +00:00
|
|
|
|
2021-01-26 00:45:26 +00:00
|
|
|
logic speculative;
|
2021-01-24 00:01:44 +00:00
|
|
|
initial begin
|
|
|
|
speculative = 0;
|
|
|
|
end
|
2021-03-30 19:25:07 +00:00
|
|
|
logic [63:0] lastCheckInstrD, lastPC, lastPC2;
|
2021-02-01 23:57:06 +00:00
|
|
|
|
|
|
|
string PCtextW, PCtext2W;
|
|
|
|
logic [31:0] InstrWExpected;
|
|
|
|
logic [63:0] PCWExpected;
|
2021-03-30 19:25:07 +00:00
|
|
|
always @(PCW or dut.hart.ieu.InstrValidW) begin
|
|
|
|
if(dut.hart.ieu.InstrValidW && PCW != 0) begin
|
2021-02-01 23:57:06 +00:00
|
|
|
if($feof(data_file_PCW)) begin
|
|
|
|
$display("no more PC data to read");
|
2021-02-12 17:21:56 +00:00
|
|
|
`ERROR
|
2021-02-01 23:57:06 +00:00
|
|
|
end
|
|
|
|
scan_file_PCW = $fscanf(data_file_PCW, "%s\n", PCtextW);
|
2021-04-19 07:25:15 +00:00
|
|
|
PCtext2W = "";
|
|
|
|
while (PCtext2W != "***") begin
|
2021-02-01 23:57:06 +00:00
|
|
|
PCtextW = {PCtextW, " ", PCtext2W};
|
2021-04-19 07:25:15 +00:00
|
|
|
scan_file_PC = $fscanf(data_file_PCW, "%s\n", PCtext2W);
|
2021-02-01 23:57:06 +00:00
|
|
|
end
|
|
|
|
scan_file_PCW = $fscanf(data_file_PCW, "%x\n", InstrWExpected);
|
|
|
|
// then expected PC value
|
|
|
|
scan_file_PCW = $fscanf(data_file_PCW, "%x\n", PCWExpected);
|
2021-03-30 19:25:07 +00:00
|
|
|
if(~equal(PCW,PCWExpected,2)) begin
|
|
|
|
$display("%0t ps, instr %0d: PCW does not equal PCW expected: %x, %x", $time, instrs, PCW, PCWExpected);
|
2021-02-12 17:21:56 +00:00
|
|
|
`ERROR
|
2021-02-01 23:57:06 +00:00
|
|
|
end
|
|
|
|
//if(it.InstrW != InstrWExpected) begin
|
2021-02-23 22:01:23 +00:00
|
|
|
// $display("%0t ps, instr %0d: InstrW does not equal InstrW expected: %x, %x", $time, instrs, it.InstrW, InstrWExpected);
|
2021-02-01 23:57:06 +00:00
|
|
|
//end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-26 17:34:12 +00:00
|
|
|
string PCtext, PCtext2;
|
2021-01-25 01:43:47 +00:00
|
|
|
initial begin
|
|
|
|
instrs = 0;
|
2021-02-24 05:28:33 +00:00
|
|
|
end
|
2021-02-26 20:26:54 +00:00
|
|
|
logic [31:0] InstrMask;
|
|
|
|
logic forcedInstr;
|
2021-03-30 19:25:07 +00:00
|
|
|
logic [63:0] lastPCD;
|
|
|
|
always @(dut.hart.ifu.PCD or dut.hart.ifu.InstrRawD or reset or negedge dut.hart.ifu.StallE) begin
|
2021-03-01 20:56:04 +00:00
|
|
|
if(~HWRITE) begin
|
2021-03-30 19:25:07 +00:00
|
|
|
#2;
|
|
|
|
if (~reset && dut.hart.ifu.InstrRawD[15:0] !== {16{1'bx}} && dut.hart.ifu.PCD !== 64'h0 && ~dut.hart.ifu.StallE) begin
|
|
|
|
if (dut.hart.ifu.PCD !== lastPCD) begin
|
|
|
|
lastCheckInstrD = CheckInstrD;
|
|
|
|
lastPC <= dut.hart.ifu.PCD;
|
|
|
|
lastPC2 <= lastPC;
|
|
|
|
if (speculative && (lastPC != pcExpected)) begin
|
|
|
|
speculative = ~equal(dut.hart.ifu.PCD,pcExpected,3);
|
|
|
|
if(dut.hart.ifu.PCD===pcExpected) begin
|
|
|
|
if(dut.hart.ifu.InstrRawD[6:0] == 7'b1010011) begin // for now, NOP out any float instrs
|
|
|
|
force CheckInstrD = 32'b0010011;
|
|
|
|
release CheckInstrD;
|
|
|
|
force dut.hart.ifu.InstrRawD = 32'b0010011;
|
|
|
|
#7;
|
|
|
|
release dut.hart.ifu.InstrRawD;
|
|
|
|
$display("warning: NOPing out %s at PC=%0x, instr %0d, time %0t", PCtext, dut.hart.ifu.PCD, instrs, $time);
|
|
|
|
warningCount += 1;
|
|
|
|
forcedInstr = 1;
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
forcedInstr = 0;
|
|
|
|
end
|
2021-03-08 21:24:19 +00:00
|
|
|
end
|
2021-03-01 19:08:35 +00:00
|
|
|
end
|
2021-03-30 19:25:07 +00:00
|
|
|
else begin
|
|
|
|
if($feof(data_file_PC)) begin
|
|
|
|
$display("no more PC data to read");
|
|
|
|
`ERROR
|
2021-03-01 19:08:35 +00:00
|
|
|
end
|
2021-03-30 19:25:07 +00:00
|
|
|
scan_file_PC = $fscanf(data_file_PC, "%s\n", PCtext);
|
2021-04-19 07:25:15 +00:00
|
|
|
PCtext2 = "";
|
|
|
|
while (PCtext2 != "***") begin
|
2021-03-30 19:25:07 +00:00
|
|
|
PCtext = {PCtext, " ", PCtext2};
|
2021-04-19 07:25:15 +00:00
|
|
|
scan_file_PC = $fscanf(data_file_PC, "%s\n", PCtext2);
|
2021-03-30 19:25:07 +00:00
|
|
|
end
|
|
|
|
scan_file_PC = $fscanf(data_file_PC, "%x\n", CheckInstrD);
|
|
|
|
if(dut.hart.ifu.PCD === pcExpected) begin
|
|
|
|
if(dut.hart.ifu.InstrRawD[6:0] == 7'b1010011) begin // for now, NOP out any float instrs
|
|
|
|
force CheckInstrD = 32'b0010011;
|
|
|
|
release CheckInstrD;
|
|
|
|
force dut.hart.ifu.InstrRawD = 32'b0010011;
|
|
|
|
#7;
|
|
|
|
release dut.hart.ifu.InstrRawD;
|
|
|
|
$display("warning: NOPing out %s at PC=%0x, instr %0d, time %0t", PCtext, dut.hart.ifu.PCD, instrs, $time);
|
|
|
|
warningCount += 1;
|
|
|
|
forcedInstr = 1;
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
forcedInstr = 0;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
// then expected PC value
|
|
|
|
scan_file_PC = $fscanf(data_file_PC, "%x\n", pcExpected);
|
|
|
|
if (instrs <= 10 || (instrs <= 100 && instrs % 10 == 0) ||
|
|
|
|
(instrs <= 1000 && instrs % 100 == 0) || (instrs <= 10000 && instrs % 1000 == 0) ||
|
|
|
|
(instrs <= 100000 && instrs % 10000 == 0) || (instrs <= 1000000 && instrs % 100000 == 0)) begin
|
|
|
|
$display("loaded %0d instructions", instrs);
|
|
|
|
end
|
|
|
|
instrs += 1;
|
|
|
|
// are we at a branch/jump?
|
|
|
|
casex (lastCheckInstrD[31:0])
|
|
|
|
32'b00000000001000000000000001110011, // URET
|
|
|
|
32'b00010000001000000000000001110011, // SRET
|
|
|
|
32'b00110000001000000000000001110011, // MRET
|
|
|
|
32'bXXXXXXXXXXXXXXXXXXXXXXXXX1101111, // JAL
|
|
|
|
32'bXXXXXXXXXXXXXXXXXXXXXXXXX1100111, // JALR
|
|
|
|
32'bXXXXXXXXXXXXXXXXXXXXXXXXX1100011, // B
|
|
|
|
32'bXXXXXXXXXXXXXXXX110XXXXXXXXXXX01, // C.BEQZ
|
|
|
|
32'bXXXXXXXXXXXXXXXX111XXXXXXXXXXX01, // C.BNEZ
|
|
|
|
32'bXXXXXXXXXXXXXXXX101XXXXXXXXXXX01: // C.J
|
|
|
|
speculative = 1;
|
2021-04-14 04:00:27 +00:00
|
|
|
32'bXXXXXXXXXXXXXXXX1001000000000010, // C.EBREAK:
|
|
|
|
32'bXXXXXXXXXXXXXXXXX000XXXXX1110011: // Something that's not CSRR*
|
2021-03-30 19:25:07 +00:00
|
|
|
speculative = 0; // tbh don't really know what should happen here
|
2021-04-14 04:00:27 +00:00
|
|
|
32'b000110000000XXXXXXXXXXXXX1110011, // CSR* SATP, *
|
2021-03-30 19:25:07 +00:00
|
|
|
32'bXXXXXXXXXXXXXXXX1000XXXXX0000010, // C.JR
|
|
|
|
32'bXXXXXXXXXXXXXXXX1001XXXXX0000010: // C.JALR //this is RV64 only so no C.JAL
|
|
|
|
speculative = 1;
|
|
|
|
default:
|
|
|
|
speculative = 0;
|
|
|
|
endcase
|
|
|
|
|
|
|
|
//check things!
|
|
|
|
if ((~speculative) && (~equal(dut.hart.ifu.PCD,pcExpected,3))) begin
|
|
|
|
$display("%0t ps, instr %0d: PC does not equal PC expected: %x, %x", $time, instrs, dut.hart.ifu.PCD, pcExpected);
|
|
|
|
`ERROR
|
|
|
|
end
|
|
|
|
InstrMask = CheckInstrD[1:0] == 2'b11 ? 32'hFFFFFFFF : 32'h0000FFFF;
|
|
|
|
if ((~forcedInstr) && (~speculative) && ((InstrMask & dut.hart.ifu.InstrRawD) !== (InstrMask & CheckInstrD))) begin
|
|
|
|
$display("%0t ps, instr %0d: InstrD does not equal CheckInstrD: %x, %x, PC: %x", $time, instrs, dut.hart.ifu.InstrRawD, CheckInstrD, dut.hart.ifu.PCD);
|
|
|
|
`ERROR
|
2021-03-01 19:08:35 +00:00
|
|
|
end
|
2021-03-01 18:50:42 +00:00
|
|
|
end
|
2021-02-26 20:26:54 +00:00
|
|
|
end
|
2021-03-30 19:25:07 +00:00
|
|
|
lastPCD = dut.hart.ifu.PCD;
|
2021-02-23 22:01:23 +00:00
|
|
|
end
|
2021-03-01 20:56:04 +00:00
|
|
|
end
|
2021-01-22 19:11:17 +00:00
|
|
|
end
|
|
|
|
|
2021-01-23 02:14:45 +00:00
|
|
|
// Track names of instructions
|
|
|
|
string InstrFName, InstrDName, InstrEName, InstrMName, InstrWName;
|
|
|
|
logic [31:0] InstrW;
|
2021-04-26 11:43:16 +00:00
|
|
|
instrTrackerTB it(clk, reset,
|
|
|
|
dut.hart.ifu.icache.controller.FinalInstrRawF,
|
2021-02-23 18:59:06 +00:00
|
|
|
dut.hart.ifu.InstrD, dut.hart.ifu.InstrE,
|
2021-04-26 11:43:16 +00:00
|
|
|
dut.hart.ifu.InstrM, dut.hart.ifu.InstrW,
|
|
|
|
InstrFName, InstrDName, InstrEName, InstrMName, InstrWName);
|
2021-01-21 21:17:34 +00:00
|
|
|
|
|
|
|
// generate clock to sequence tests
|
|
|
|
always
|
|
|
|
begin
|
2021-01-21 22:55:05 +00:00
|
|
|
clk <= 1; # 5; clk <= 0; # 5;
|
2021-01-21 21:17:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
endmodule
|
2021-03-30 19:25:07 +00:00
|
|
|
module instrTrackerTB(
|
2021-04-26 11:43:16 +00:00
|
|
|
input logic clk, reset,
|
|
|
|
input logic [31:0] InstrF,InstrD,InstrE,InstrM,InstrW,
|
|
|
|
output string InstrFName, InstrDName, InstrEName, InstrMName, InstrWName);
|
2021-03-30 19:25:07 +00:00
|
|
|
|
|
|
|
// stage Instr to Writeback for visualization
|
|
|
|
//flopr #(32) InstrWReg(clk, reset, InstrM, InstrW);
|
|
|
|
|
2021-04-26 11:43:16 +00:00
|
|
|
instrNameDecTB fdec(InstrF, InstrFName);
|
2021-03-30 19:25:07 +00:00
|
|
|
instrNameDecTB ddec(InstrD, InstrDName);
|
|
|
|
instrNameDecTB edec(InstrE, InstrEName);
|
|
|
|
instrNameDecTB mdec(InstrM, InstrMName);
|
|
|
|
instrNameDecTB wdec(InstrW, InstrWName);
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
// decode the instruction name, to help the test bench
|
|
|
|
module instrNameDecTB(
|
|
|
|
input logic [31:0] instr,
|
|
|
|
output string name);
|
|
|
|
|
|
|
|
logic [6:0] op;
|
|
|
|
logic [2:0] funct3;
|
|
|
|
logic [6:0] funct7;
|
|
|
|
logic [11:0] imm;
|
|
|
|
|
|
|
|
assign op = instr[6:0];
|
|
|
|
assign funct3 = instr[14:12];
|
|
|
|
assign funct7 = instr[31:25];
|
|
|
|
assign imm = instr[31:20];
|
|
|
|
|
|
|
|
// it would be nice to add the operands to the name
|
|
|
|
// create another variable called decoded
|
|
|
|
|
|
|
|
always_comb
|
|
|
|
casez({op, funct3})
|
|
|
|
10'b0000000_000: name = "BAD";
|
|
|
|
10'b0000011_000: name = "LB";
|
|
|
|
10'b0000011_001: name = "LH";
|
|
|
|
10'b0000011_010: name = "LW";
|
|
|
|
10'b0000011_011: name = "LD";
|
|
|
|
10'b0000011_100: name = "LBU";
|
|
|
|
10'b0000011_101: name = "LHU";
|
|
|
|
10'b0000011_110: name = "LWU";
|
|
|
|
10'b0010011_000: if (instr[31:15] == 0 && instr[11:7] ==0) name = "NOP/FLUSH";
|
|
|
|
else name = "ADDI";
|
|
|
|
10'b0010011_001: if (funct7[6:1] == 6'b000000) name = "SLLI";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0010011_010: name = "SLTI";
|
|
|
|
10'b0010011_011: name = "SLTIU";
|
|
|
|
10'b0010011_100: name = "XORI";
|
|
|
|
10'b0010011_101: if (funct7[6:1] == 6'b000000) name = "SRLI";
|
|
|
|
else if (funct7[6:1] == 6'b010000) name = "SRAI";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0010011_110: name = "ORI";
|
|
|
|
10'b0010011_111: name = "ANDI";
|
|
|
|
10'b0010111_???: name = "AUIPC";
|
|
|
|
10'b0100011_000: name = "SB";
|
|
|
|
10'b0100011_001: name = "SH";
|
|
|
|
10'b0100011_010: name = "SW";
|
|
|
|
10'b0100011_011: name = "SD";
|
|
|
|
10'b0011011_000: name = "ADDIW";
|
|
|
|
10'b0011011_001: name = "SLLIW";
|
|
|
|
10'b0011011_101: if (funct7 == 7'b0000000) name = "SRLIW";
|
|
|
|
else if (funct7 == 7'b0100000) name = "SRAIW";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0111011_000: if (funct7 == 7'b0000000) name = "ADDW";
|
|
|
|
else if (funct7 == 7'b0100000) name = "SUBW";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0111011_001: name = "SLLW";
|
|
|
|
10'b0111011_101: if (funct7 == 7'b0000000) name = "SRLW";
|
|
|
|
else if (funct7 == 7'b0100000) name = "SRAW";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0110011_000: if (funct7 == 7'b0000000) name = "ADD";
|
|
|
|
else if (funct7 == 7'b0000001) name = "MUL";
|
|
|
|
else if (funct7 == 7'b0100000) name = "SUB";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0110011_001: if (funct7 == 7'b0000000) name = "SLL";
|
|
|
|
else if (funct7 == 7'b0000001) name = "MULH";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0110011_010: if (funct7 == 7'b0000000) name = "SLT";
|
|
|
|
else if (funct7 == 7'b0000001) name = "MULHSU";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0110011_011: if (funct7 == 7'b0000000) name = "SLTU";
|
|
|
|
else if (funct7 == 7'b0000001) name = "DIV";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0110011_100: if (funct7 == 7'b0000000) name = "XOR";
|
|
|
|
else if (funct7 == 7'b0000001) name = "MUL";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0110011_101: if (funct7 == 7'b0000000) name = "SRL";
|
|
|
|
else if (funct7 == 7'b0000001) name = "DIVU";
|
|
|
|
else if (funct7 == 7'b0100000) name = "SRA";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0110011_110: if (funct7 == 7'b0000000) name = "OR";
|
|
|
|
else if (funct7 == 7'b0000001) name = "REM";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0110011_111: if (funct7 == 7'b0000000) name = "AND";
|
|
|
|
else if (funct7 == 7'b0000001) name = "REMU";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b0110111_???: name = "LUI";
|
|
|
|
10'b1100011_000: name = "BEQ";
|
|
|
|
10'b1100011_001: name = "BNE";
|
|
|
|
10'b1100011_100: name = "BLT";
|
|
|
|
10'b1100011_101: name = "BGE";
|
|
|
|
10'b1100011_110: name = "BLTU";
|
|
|
|
10'b1100011_111: name = "BGEU";
|
|
|
|
10'b1100111_000: name = "JALR";
|
|
|
|
10'b1101111_???: name = "JAL";
|
|
|
|
10'b1110011_000: if (imm == 0) name = "ECALL";
|
|
|
|
else if (imm == 1) name = "EBREAK";
|
|
|
|
else if (imm == 2) name = "URET";
|
|
|
|
else if (imm == 258) name = "SRET";
|
|
|
|
else if (imm == 770) name = "MRET";
|
|
|
|
else name = "ILLEGAL";
|
|
|
|
10'b1110011_001: name = "CSRRW";
|
|
|
|
10'b1110011_010: name = "CSRRS";
|
|
|
|
10'b1110011_011: name = "CSRRC";
|
|
|
|
10'b1110011_101: name = "CSRRWI";
|
|
|
|
10'b1110011_110: name = "CSRRSI";
|
|
|
|
10'b1110011_111: name = "CSRRCI";
|
|
|
|
10'b0001111_???: name = "FENCE";
|
|
|
|
default: name = "ILLEGAL";
|
|
|
|
endcase
|
|
|
|
endmodule
|
|
|
|
|