`timescale 1ns / 1ps `default_nettype none `define EOF 32'hFFFF_FFFF `define NULL 0 // SPECIFY TRACE FILE HERE `define TESTBENCH_TRACE "code/timing.trace" `ifdef ICARUS `include "include/bram.v" `include "include/clock_util.v" `include "lc4_pipe.v" `endif module test_pl_timing_v; // Inputs reg CLK; reg RST; wire GWE; wire [15:0] IMEM_OUT; wire [15:0] DMEM_OUT; // Outputs wire [15:0] IMEM_ADDR; wire [15:0] DMEM_ADDR; wire [15:0] DMEM_IN; wire DMEM_WE; wire [15:0] _TEST_W_PC; wire [15:0] _TEST_W_INSN; wire [15:0] _TEST_W_REGFILE_DATA_IN; wire _TEST_W_REGFILE_WE; wire [2:0] _TEST_W_NZP_IN; wire _TEST_W_NZP_WE; wire [15:0] _TEST_W_DMEM_ADDR; wire [15:0] _TEST_W_DMEM_IN; wire _TEST_W_DMEM_WE; wire _TEST_W_STALL; wire [15:0] _TEST_PMC_CYCLE, _TEST_PMC_INSN, _TEST_PMC_LOAD_STALL, _TEST_PMC_BRANCH_STALL; reg [15:0] verify_PC, verify_Insn, verify_WeNZP, verify_DataIn, verify_DmemAddr; reg BPRED_ON, BYPASS_ON; wire [15:0] vout_dummy; // Data and video memory block bram memory (.idclk( CLK ), .iaddr( IMEM_ADDR ), .iout( IMEM_OUT ), .daddr ( DMEM_ADDR ), .dout ( DMEM_OUT ), .din ( DMEM_IN ), .dwe ( DMEM_WE ), .vclk ( 1'b0 ), .vaddr ( 16'h0000 ), .vout ( vout_dummy )); // Instantiate your processor here! lc4_pipe proc (.CLK(CLK), .RST(RST), .GWE( GWE ), .IMEM_ADDR(IMEM_ADDR), .IMEM_OUT(IMEM_OUT), .DMEM_ADDR(DMEM_ADDR), .DMEM_IN(DMEM_IN), .DMEM_OUT(DMEM_OUT), .DMEM_WE(DMEM_WE), .BYPASS_ON(BYPASS_ON), .BPRED_ON(BPRED_ON), // These test signals are from the writeback stage of pipeline ._TEST_W_PC(_TEST_W_PC), ._TEST_W_INSN(_TEST_W_INSN), ._TEST_W_REGFILE_DATA_IN(_TEST_W_REGFILE_DATA_IN), ._TEST_W_REGFILE_WE(_TEST_W_REGFILE_WE), ._TEST_W_NZP_IN(_TEST_W_NZP_IN), ._TEST_W_NZP_WE(_TEST_W_NZP_WE), ._TEST_W_DMEM_WE(_TEST_W_DMEM_WE), ._TEST_W_DMEM_IN(_TEST_W_DMEM_IN), ._TEST_W_DMEM_ADDR(_TEST_W_DMEM_ADDR), // Set this signal high for a stall/flush ._TEST_W_STALL(_TEST_W_STALL), ._TEST_PMC_CYCLE(_TEST_PMC_CYCLE), ._TEST_PMC_INSN(_TEST_PMC_INSN), ._TEST_PMC_LOAD_STALL(_TEST_PMC_LOAD_STALL), ._TEST_PMC_BRANCH_STALL(_TEST_PMC_BRANCH_STALL) ); /* Because the memory needs a rising edge in order to read, we have to slow the processor down to run only on every other cycle. We need 2 rising edges to process a load - 1 for the memory, and 1 for the regfile. */ count #(2) gwe_generator(.clk( CLK ), .out( GWE )); always #5 CLK <= ~CLK; integer file, char, retval, lineno, cntErrors, done; integer cff, cfn, cnf, cnn, iff, ifn, inf, inn, lff, lfn, lnf, lnn, bff, bfn, bnf, bnn; // Can't do the test multiple times because task do_timing_run; begin // Initialize Inputs CLK = 0; RST = 0; verify_PC = 0; verify_Insn = 0; verify_WeNZP = 0; verify_DataIn = 0; lineno = 1; cntErrors = 0; done = 0; // Wait for global reset to finish #20; RST = 1; #40; RST = 0; #40; // open the test inputs file = $fopen(`TESTBENCH_TRACE, "r"); if (file == `NULL) begin $display("Error: unable to open trace file %s", `TESTBENCH_TRACE); $finish; end while (!done) begin retval = $fscanf(file, "%h %h %h %h %h", verify_PC, verify_Insn, verify_WeNZP, verify_DataIn, verify_DmemAddr); if (retval !== 5) begin done = 1; end else begin // Ignore all stall cycles; do not compare them against trace cycles. while ( _TEST_W_STALL == 1'b1) begin #40; // big clock end if ( verify_PC != _TEST_W_PC ) begin $display( "Error at line %d: PC should be %h (but was %h)", lineno, verify_PC, _TEST_W_PC ); cntErrors = cntErrors + 1; $stop; end if ( verify_Insn != _TEST_W_INSN ) begin $display( "Error at line %d: Current insn should be %h (but was %h)", lineno, verify_Insn, _TEST_W_INSN ); cntErrors = cntErrors + 1; $stop; end if ( verify_WeNZP[12] != _TEST_W_REGFILE_WE ) begin $display( "Error at line %d: Regfile WE should be %h (but was %h)", lineno, verify_WeNZP[12], _TEST_W_REGFILE_WE ); cntErrors = cntErrors + 1; $stop; end if ( verify_WeNZP[8] != _TEST_W_DMEM_WE ) begin $display( "Error at line %d: Dmem WE should be %h (but was %h)", lineno, verify_WeNZP[8], _TEST_W_DMEM_WE ); cntErrors = cntErrors + 1; $stop; end if ( verify_Insn[15:12] != 4'h4 && verify_Insn[15:12] != 4'h15 ) begin if ( verify_WeNZP[4] != _TEST_W_NZP_WE ) begin $display( "Error at line %d: NZP WE should be %h (but was %h)", lineno, verify_WeNZP[4], _TEST_W_NZP_WE ); cntErrors = cntErrors + 1; $stop; end if ( verify_WeNZP[4] == 1'b1 && _TEST_W_NZP_IN != verify_WeNZP[2:0] ) begin $display( "Error at line %d: NZP in should be %h (but was %h)", lineno, verify_WeNZP[2:0], _TEST_W_NZP_IN); cntErrors = cntErrors + 1; $stop; end end if ( verify_WeNZP[8] == 1'b1 && _TEST_W_DMEM_IN != verify_DataIn ) begin $display( "Error at line %d: Dmem IN should be %h (but was %h)", lineno, verify_DataIn, _TEST_W_DMEM_IN ); cntErrors = cntErrors + 1; $stop; end if ( verify_WeNZP[12] == 1'b1 && _TEST_W_REGFILE_DATA_IN != verify_DataIn ) begin $display( "Error at line %d: Regfile IN should be %h (but was %h)", lineno, verify_DataIn, _TEST_W_REGFILE_DATA_IN ); cntErrors = cntErrors + 1; $stop; end if ( verify_DmemAddr != 16'h0 && verify_DmemAddr != _TEST_W_DMEM_ADDR ) begin $display( "Error at line %d: Dmem address should be %h (but was %h)", lineno, verify_DmemAddr, _TEST_W_DMEM_ADDR ); cntErrors = cntErrors + 1; $stop; end #40; lineno = lineno + 1; end // end else end // end while $display ( "Summary: %d discrepancies detected.", cntErrors ); $display ("CYCLE: %d INSN: %d LSTALL: %d BSTALL: %d ALL: %d", _TEST_PMC_CYCLE, _TEST_PMC_INSN, _TEST_PMC_LOAD_STALL, _TEST_PMC_BRANCH_STALL, _TEST_PMC_INSN + _TEST_PMC_LOAD_STALL + _TEST_PMC_BRANCH_STALL); $fclose(file); end // end do_one_run endtask initial begin BPRED_ON = 0; BYPASS_ON = 0; $display("Run1: bpred %d, bypass %d", BPRED_ON, BYPASS_ON); do_timing_run; cff = _TEST_PMC_CYCLE; iff = _TEST_PMC_INSN; lff = _TEST_PMC_LOAD_STALL; bff = _TEST_PMC_BRANCH_STALL; $display("Instruction count: %d (%s)", iff, (iff == 6873) ? "passed" : "failed"); BPRED_ON = 0; BYPASS_ON = 1; $display("Run2: bpred %d, bypass %d", BPRED_ON, BYPASS_ON); do_timing_run; cfn = _TEST_PMC_CYCLE; ifn = _TEST_PMC_INSN; lfn = _TEST_PMC_LOAD_STALL; bfn = _TEST_PMC_BRANCH_STALL; $display("Instruction count: %d (%s)", ifn, (ifn == 6873) ? "passed" : "failed"); $display("Branch stall diff: %d (%s)", (bff - bfn), ((bff - bfn) == 0) ? "passed" : "failed"); $display("Load stall diff: %d (%s)", (lff - lfn), ((lff - lfn) == 6460) ? "passed" : "failed"); $display("Cycle count diff: %d (%s)", (cff - cfn), ((cff - cfn) == 6460) ? "passed" : "failed"); BPRED_ON = 1; BYPASS_ON = 0; $display("Run3: bpred %d, bypass %d", BPRED_ON, BYPASS_ON); do_timing_run; cnf = _TEST_PMC_CYCLE; inf = _TEST_PMC_INSN; lnf = _TEST_PMC_LOAD_STALL; bnf = _TEST_PMC_BRANCH_STALL; $display("Instruction count: %d (%s)", inf, (inf == 6873) ? "passed" : "failed"); $display("Branch stall diff: %d (%s)", (bff - bnf), ((bff - bnf) == 1722) ? "passed" : "failed"); $display("Load stalls diff: %d (%s)", (lff - lnf), ((lff - lnf) == -558) ? "passed" : "failed"); $display("Cycle count diff: %d (%s)", (cff - cnf), ((cff - cnf) == (1722 - 558)) ? "passed" : "failed"); BPRED_ON = 1; BYPASS_ON = 1; $display("Run4: bpred %d, bypass %d", BPRED_ON, BYPASS_ON); do_timing_run; cnn = _TEST_PMC_CYCLE; inn = _TEST_PMC_INSN; lnn = _TEST_PMC_LOAD_STALL; bnn = _TEST_PMC_BRANCH_STALL; $display("Instruction count: %d (%s)", inn, (inn == 6873) ? "passed" : "failed"); $display("Branch stall diff: %d (%s)", (bff - bnn), ((bff - bnn) == 1722) ? "passed" : "failed"); $display("Load stalls diff: %d (%s)", (lff - lnn), ((lff - lnn) == 6460) ? "passed" : "failed"); $display("Cycle count diff: %d (%s)", (cff - cnn), ((cff - cnn) == (6460 + 1722)) ? "passed" : "failed"); $stop; end endmodule