2021-01-24 22:10:00 +00:00
|
|
|
`include "wally-config.vh"
|
2021-01-21 21:17:34 +00:00
|
|
|
|
2021-01-24 22:10:00 +00:00
|
|
|
module testbench_busybear();
|
2021-01-21 21:17:34 +00:00
|
|
|
|
2021-01-21 22:55:05 +00:00
|
|
|
logic clk, reset;
|
2021-01-24 22:10:00 +00:00
|
|
|
logic [`XLEN-1:0] WriteDataM, DataAdrM;
|
2021-01-21 22:55:05 +00:00
|
|
|
logic [1:0] MemRWM;
|
|
|
|
logic [31:0] GPIOPinsIn;
|
|
|
|
logic [31:0] GPIOPinsOut, GPIOPinsEn;
|
2021-01-21 21:17:34 +00:00
|
|
|
|
|
|
|
// instantiate device to be tested
|
2021-01-24 22:10:00 +00:00
|
|
|
logic [`XLEN-1:0] PCF, ReadDataM;
|
2021-01-21 22:55:05 +00:00
|
|
|
logic [31:0] InstrF;
|
|
|
|
logic [7:0] ByteMaskM;
|
|
|
|
logic InstrAccessFaultF, DataAccessFaultM;
|
2021-01-25 01:43:47 +00:00
|
|
|
logic TimerIntM = 0, SwIntM = 0; // from CLINT
|
2021-01-21 22:55:05 +00:00
|
|
|
logic ExtIntM = 0; // not yet connected
|
2021-01-23 22:52:05 +00:00
|
|
|
|
|
|
|
// for now, seem to need these to be zero until we get a better idea
|
|
|
|
assign InstrAccessFaultF = 0;
|
|
|
|
assign DataAccessFaultM = 0;
|
2021-01-21 22:55:05 +00:00
|
|
|
|
|
|
|
// instantiate processor and memories
|
2021-01-28 06:21:47 +00:00
|
|
|
wallypipelinedhart dut(.*);
|
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-01-22 19:11:17 +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-01-26 01:37:18 +00:00
|
|
|
data_file_PC = $fopen("../busybear-testgen/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");
|
|
|
|
$stop;
|
|
|
|
end
|
|
|
|
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-01-26 01:37:18 +00:00
|
|
|
data_file_rf = $fopen("../busybear-testgen/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");
|
|
|
|
$stop;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
// 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-01-26 01:37:18 +00:00
|
|
|
data_file_memR = $fopen("../busybear-testgen/parsedMemRead.txt", "r");
|
2021-01-25 01:43:47 +00:00
|
|
|
if (data_file_memR == 0) begin
|
|
|
|
$display("file couldn't be opened");
|
|
|
|
$stop;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
// read memwrite trace file
|
|
|
|
integer data_file_memW, scan_file_memW;
|
|
|
|
initial begin
|
2021-01-26 01:37:18 +00:00
|
|
|
data_file_memW = $fopen("../busybear-testgen/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");
|
|
|
|
$stop;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-22 20:05:58 +00:00
|
|
|
logic [63:0] rfExpected[31:1];
|
|
|
|
logic [63:0] pcExpected;
|
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
|
|
|
// I apologize for this hack, I don't have a clue how to properly work with packed arrays
|
|
|
|
logic [64*32:64] rf;
|
|
|
|
genvar i;
|
|
|
|
generate
|
|
|
|
for(i=1; i<32; i++) begin
|
2021-01-27 17:54:09 +00:00
|
|
|
assign rf[i*64+63:i*64] = dut.ieu.dp.regf.rf[i];
|
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
|
|
|
|
endgenerate
|
2021-01-22 19:11:17 +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
|
|
|
always @(rf) begin
|
|
|
|
for(int j=1; j<32; j++) begin
|
2021-01-25 01:43:47 +00:00
|
|
|
if($feof(data_file_rf)) begin
|
|
|
|
$display("no more rf data to read");
|
|
|
|
$stop;
|
|
|
|
end
|
2021-01-28 18:16:38 +00:00
|
|
|
// read 31 integer registers
|
|
|
|
scan_file_rf = $fscanf(data_file_rf, "%x\n", rfExpected[j]);
|
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
|
|
|
// check things!
|
|
|
|
if (rf[j*64+63 -: 64] != rfExpected[j]) begin
|
2021-01-23 21:42:17 +00:00
|
|
|
$display("%t ps: rf[%0d] does not equal rf expected: %x, %x", $time, j, rf[j*64+63 -: 64], rfExpected[j]);
|
|
|
|
// $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
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-28 18:16:38 +00:00
|
|
|
logic [`XLEN-1:0] readAdrExpected;
|
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
|
|
|
// this might need to change
|
2021-01-25 01:43:47 +00:00
|
|
|
always @(MemRWM[1] or DataAdrM) begin
|
|
|
|
if (MemRWM[1]) begin
|
2021-01-28 18:16:38 +00:00
|
|
|
if($feof(data_file_memR)) begin
|
|
|
|
$display("no more memR data to read");
|
|
|
|
$stop;
|
|
|
|
end
|
|
|
|
scan_file_memR = $fscanf(data_file_memR, "%x\n", readAdrExpected);
|
2021-01-25 01:43:47 +00:00
|
|
|
scan_file_memR = $fscanf(data_file_memR, "%x\n", ReadDataM);
|
2021-01-28 18:16:38 +00:00
|
|
|
if (DataAdrM != readAdrExpected) begin
|
|
|
|
$display("%t ps: DataAdrM does not equal readAdrExpected: %x, %x", $time, DataAdrM, readAdrExpected);
|
|
|
|
end
|
2021-01-25 01:43:47 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-26 01:06:13 +00:00
|
|
|
logic [`XLEN-1:0] writeDataExpected, writeAdrExpected;
|
2021-01-25 01:43:47 +00:00
|
|
|
// this might need to change
|
|
|
|
always @(WriteDataM or DataAdrM or ByteMaskM) begin
|
2021-01-26 01:06:13 +00:00
|
|
|
#1;
|
2021-01-25 01:43:47 +00:00
|
|
|
if (MemRWM[0]) begin
|
2021-01-28 18:16:38 +00:00
|
|
|
if($feof(data_file_memW)) begin
|
|
|
|
$display("no more memW data to read");
|
|
|
|
$stop;
|
|
|
|
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-01-28 20:42:47 +00:00
|
|
|
for(int i=0; i<8; i++) begin
|
|
|
|
if (ByteMaskM[i]) begin
|
|
|
|
if (writeDataExpected[i*8+7 -: 8] != WriteDataM[i*8+7 -: 8]) begin
|
|
|
|
$display("%t ps: WriteDataM does not equal writeDataExpected: %x, %x", $time, WriteDataM, writeDataExpected);
|
|
|
|
end
|
|
|
|
end
|
2021-01-26 01:06:13 +00:00
|
|
|
end
|
|
|
|
if (writeAdrExpected != DataAdrM) begin
|
|
|
|
$display("%t ps: DataAdrM does not equal writeAdrExpected: %x, %x", $time, DataAdrM, writeAdrExpected);
|
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-01-26 00:45:26 +00:00
|
|
|
logic speculative;
|
2021-01-24 00:01:44 +00:00
|
|
|
initial begin
|
|
|
|
speculative = 0;
|
2021-01-26 00:45:26 +00:00
|
|
|
speculative = 0;
|
2021-01-24 00:01:44 +00:00
|
|
|
end
|
2021-01-26 00:45:26 +00:00
|
|
|
logic [63:0] lastInstrF, lastPC, lastPC2;
|
2021-01-26 17:34:12 +00:00
|
|
|
|
|
|
|
string PCtext, PCtext2;
|
2021-01-25 01:43:47 +00:00
|
|
|
integer instrs;
|
|
|
|
initial begin
|
|
|
|
instrs = 0;
|
|
|
|
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
|
|
|
always @(PCF) begin
|
2021-01-26 00:45:26 +00:00
|
|
|
lastInstrF = InstrF;
|
|
|
|
lastPC <= PCF;
|
|
|
|
lastPC2 <= lastPC;
|
|
|
|
if (speculative && lastPC != pcExpected) begin
|
|
|
|
speculative = (PCF != pcExpected);
|
2021-01-24 00:01:44 +00:00
|
|
|
end
|
2021-01-26 00:45:26 +00:00
|
|
|
else begin
|
|
|
|
//if (~speculative) begin
|
2021-01-28 18:16:38 +00:00
|
|
|
if($feof(data_file_PC)) begin
|
|
|
|
$display("no more PC data to read");
|
|
|
|
$stop;
|
|
|
|
end
|
2021-01-24 00:01:44 +00:00
|
|
|
// first read instruction
|
2021-01-28 19:47:40 +00:00
|
|
|
scan_file_PC = $fscanf(data_file_PC, "%s\n", PCtext);
|
|
|
|
if (PCtext != "ret") begin
|
|
|
|
scan_file_PC = $fscanf(data_file_PC, "%s\n", PCtext2);
|
|
|
|
PCtext = {PCtext, " ", PCtext2};
|
|
|
|
end
|
2021-01-24 00:01:44 +00:00
|
|
|
scan_file_PC = $fscanf(data_file_PC, "%x\n", InstrF);
|
|
|
|
// then expected PC value
|
|
|
|
scan_file_PC = $fscanf(data_file_PC, "%x\n", pcExpected);
|
2021-01-28 19:40:35 +00:00
|
|
|
if (instrs < 10 || (instrs < 100 && instrs % 10 == 0) ||
|
|
|
|
(instrs < 1000 && instrs % 50 == 0) || instrs > 205) begin
|
2021-01-28 18:33:22 +00:00
|
|
|
$display("loaded %0d instructions", instrs);
|
|
|
|
end
|
2021-01-25 01:43:47 +00:00
|
|
|
instrs += 1;
|
2021-01-24 00:01:44 +00:00
|
|
|
// are we at a branch/jump?
|
2021-01-28 19:40:35 +00:00
|
|
|
casex (lastInstrF[15:0])
|
|
|
|
16'bXXXXXXXXX1101111, // JAL
|
|
|
|
16'bXXXXXXXXX1100111, // JALR
|
|
|
|
16'bXXXXXXXXX1100011, // B
|
2021-01-28 21:35:12 +00:00
|
|
|
16'b110XXXXXXXXXXX01, // C.BEQZ
|
|
|
|
16'b111XXXXXXXXXXX01, // C.BNEZ
|
2021-01-28 19:40:35 +00:00
|
|
|
16'b101XXXXXXXXXXX01: // C.J
|
|
|
|
speculative = 1;
|
|
|
|
16'b1001000000000010: // C.EBREAK:
|
|
|
|
speculative = 0; // tbh don't really know what should happen here
|
|
|
|
16'b1000XXXXX0000010, // C.JR
|
|
|
|
16'b1001XXXXX0000010: // C.JALR //this is RV64 only so no C.JAL
|
2021-01-26 00:45:26 +00:00
|
|
|
speculative = 1;
|
2021-01-24 00:01:44 +00:00
|
|
|
default:
|
2021-01-26 00:45:26 +00:00
|
|
|
speculative = 0;
|
2021-01-24 00:01:44 +00:00
|
|
|
endcase
|
|
|
|
|
|
|
|
//check things!
|
2021-01-26 00:45:26 +00:00
|
|
|
if ((~speculative) && (PCF !== pcExpected)) begin
|
2021-01-24 00:01:44 +00:00
|
|
|
$display("%t ps: PC does not equal PC expected: %x, %x", $time, PCF, pcExpected);
|
|
|
|
// $stop;
|
|
|
|
end
|
2021-01-22 20:05:58 +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;
|
|
|
|
instrNameDecTB dec(InstrF, InstrFName);
|
2021-01-27 17:54:09 +00:00
|
|
|
instrTrackerTB it(clk, reset, dut.ieu.dp.FlushE,
|
2021-01-28 04:42:19 +00:00
|
|
|
dut.ifu.InstrD, dut.ifu.InstrE,
|
|
|
|
dut.ifu.InstrM, InstrW,
|
2021-01-23 02:14:45 +00:00
|
|
|
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
|
|
|
|
|
2021-01-21 22:55:05 +00:00
|
|
|
//// check results
|
|
|
|
//always @(negedge clk)
|
|
|
|
// begin
|
|
|
|
// if(MemWrite) begin
|
|
|
|
// if(DataAdr === 84 & WriteData === 71) begin
|
|
|
|
// $display("Simulation succeeded");
|
|
|
|
// $stop;
|
|
|
|
// end else if (DataAdr !== 80) begin
|
|
|
|
// $display("Simulation failed");
|
|
|
|
// $stop;
|
|
|
|
// end
|
|
|
|
// end
|
|
|
|
// end
|
2021-01-21 21:17:34 +00:00
|
|
|
endmodule
|