diff --git a/Makefile b/Makefile index 61ccbbf..92da23e 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ RISCOF_CONFIG := $(RISCOF_DIR)/config.ini UTOSS_RISCV_CONFIG ?= RV32I -UTOSS_RISCV_VERILATOR_DEFINES := $(if $(findstring B,$(UTOSS_RISCV_CONFIG)),-DUTOSS_RISCV_ENABLE_B_EXT) +UTOSS_RISCV_VERILATOR_DEFINES := $(if $(findstring B,$(UTOSS_RISCV_CONFIG)),-DUTOSS_RISCV_ENABLE_B_EXT) $(if $(findstring C,$(UTOSS_RISCV_CONFIG)),-DUTOSS_RISCV_ENABLE_C_EXT) UTOSS_RISCV_RISCOF_VERILATOR_DEFINES := -DUTOSS_PIPELINE_LOGGER # =========================== @@ -100,7 +100,7 @@ run_tb: build_tb # Pattern rule for building individual testbenches $(OUT_DIR)/%_tb_sim: $(TB_DIR)/%_tb.sv $(TB_UTILS) $(SRCS) - $(VERILATOR) $(VERILATOR_FLAGS) $(TB_DEFINES) \ + $(VERILATOR) $(VERILATOR_FLAGS) $(TB_DEFINES) $(if $(findstring fetch_rvc_,$*),-DUTOSS_RISCV_ENABLE_C_EXT) \ --top-module $(basename $(notdir $<)) \ --Mdir $(BUILD_DIR)/$(basename $(notdir $@)) \ -o $(basename $(notdir $@)) \ diff --git a/envs/de1-soc/quartus/utoss-risc-v.qsf b/envs/de1-soc/quartus/utoss-risc-v.qsf index 959b58c..0d8a0ac 100755 --- a/envs/de1-soc/quartus/utoss-risc-v.qsf +++ b/envs/de1-soc/quartus/utoss-risc-v.qsf @@ -327,6 +327,8 @@ set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/modules/Instruction_ set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/modules/Instruction_Decode/MemoryLoader.sv set_global_assignment -name VERILOG_FILE ../../../src/modules/Instruction_Decode/registerFile.v set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/modules/ControlFSM.sv +set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/ext/c/instruction_reader.sv +set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/ext/c/fetch_compressed.sv set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/packages/pkg_control_fsm.svh set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/stages/decode_stage.sv set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/stages/execute_stage.sv diff --git a/src/ext/c/fetch_compressed.sv b/src/ext/c/fetch_compressed.sv new file mode 100644 index 0000000..a258f98 --- /dev/null +++ b/src/ext/c/fetch_compressed.sv @@ -0,0 +1,202 @@ +`include "src/timescale.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_compressed + ( output if_to_id_t if_to_id + , input ex_to_if_t ex_to_if + , input wire clk + , input wire reset + , input wire stall_f + , input wire flush_f + , output addr_t imem__address + , input data_t imem__data + ); + + addr_t pc_prev; + addr_t pc_cur; + addr_t pc_next; + + instr_t stalled_instr; + logic stalled_instr_valid; + + instr_t final_instr; + logic final_illegal; + instr_t decompressed_instr; + logic compressed_illegal; + + logic reader_inst_is_compressed; + logic reader_inst_is_split; + instr_t reader_instr_raw; + addr_t reader_instr_pc; + addr_t reader_next_pc; + + logic split_wait_valid; + data_t split_wait_cur_word; + addr_t split_wait_pc; + data_t split_wait_next_word; + logic split_wait_next_word_valid; + + logic need_split_wait; + logic buffer_after_split; + logic hold_pc_for_split_buffer; + + localparam instr_t NOP = 32'h00000013; + + logic buffered_word_valid; + data_t buffered_word; + addr_t buffered_pc; + + logic use_buffer; + logic buffer_next_same_word; + + addr_t reader_pc_i; + data_t reader_cur_word_i; + data_t reader_next_word; + + wire unused = &{ + ex_to_if.pc_old + , ex_to_if.imm_ext + , final_illegal + }; + + always_comb + case (ex_to_if.pc_src) + PC_SRC__INCREMENT: pc_next = {pc_cur[31:2], 2'b00} + 32'd4; + PC_SRC__ALU_RESULT: pc_next = ex_to_if.pc_target; + default: pc_next = 32'hx; + endcase + + always_ff @ (posedge clk) + if (reset) + {stalled_instr, stalled_instr_valid} <= {instr_t'(0), 1'b0}; + else if (flush_f) + {stalled_instr, stalled_instr_valid} <= {stalled_instr, 1'b0}; + else if (stall_f && !stalled_instr_valid) + {stalled_instr, stalled_instr_valid} <= {instr_t'(imem__data), 1'b1}; + else if (!stall_f && stalled_instr_valid) + {stalled_instr, stalled_instr_valid} <= {stalled_instr, 1'b0}; + + assign imem__address = {pc_cur[31:2], 2'b00}; + + instruction_reader u_reader + ( .pc_i (reader_pc_i) + , .cur_word_i (reader_cur_word_i) + , .next_word_i (reader_next_word) + , .instr_raw_o (reader_instr_raw) + , .instr_pc_o (reader_instr_pc) + , .instr_next_pc_o (reader_next_pc) + , .instr_is_compressed_o (reader_inst_is_compressed) + , .instr_is_split_o (reader_inst_is_split) + ); + + assign decompressed_instr = NOP; + assign compressed_illegal = 1'b0; + + always_ff @ (posedge clk) + if (reset) + pc_cur <= addr_t'(0); + else if (!stall_f && !hold_pc_for_split_buffer) + pc_cur <= pc_next; + + always_ff @ (posedge clk) + if (reset) + pc_prev <= addr_t'(0); + else if (!stall_f && !hold_pc_for_split_buffer) + pc_prev <= pc_cur; + + always_ff @ (posedge clk) + if (reset || flush_f) + { split_wait_valid + , split_wait_cur_word + , split_wait_pc + , split_wait_next_word + , split_wait_next_word_valid + } <= {1'b0, data_t'(0), addr_t'(0), data_t'(0), 1'b0}; + + else if (!stall_f && split_wait_valid) + { split_wait_valid + , split_wait_next_word + , split_wait_next_word_valid + } <= {1'b0, data_t'(0), 1'b0}; + + else if (!stall_f && need_split_wait) + { split_wait_valid + , split_wait_cur_word + , split_wait_pc + , split_wait_next_word + , split_wait_next_word_valid + } <= {1'b1, reader_cur_word_i, reader_instr_pc, data_t'(0), 1'b0}; + + else if (stall_f && split_wait_valid && !split_wait_next_word_valid) + {split_wait_next_word, split_wait_next_word_valid} <= {imem__data, 1'b1}; + + always_ff @ (posedge clk) + if (reset || flush_f) + {buffered_word_valid, buffered_word, buffered_pc} + <= {1'b0, data_t'(0), addr_t'(0)}; + + else if (!stall_f && buffer_after_split) + {buffered_word_valid, buffered_word, buffered_pc} + <= {1'b1, reader_next_word, reader_next_pc}; + + else if (!stall_f && use_buffer) + buffered_word_valid <= 1'b0; + + else if (!stall_f && buffer_next_same_word) + {buffered_word_valid, buffered_word, buffered_pc} + <= {1'b1, reader_cur_word_i, reader_next_pc}; + + assign use_buffer = buffered_word_valid; + + assign reader_pc_i = + split_wait_valid ? split_wait_pc : + use_buffer ? buffered_pc : + pc_prev; + + assign reader_cur_word_i = + split_wait_valid ? split_wait_cur_word : + use_buffer ? buffered_word : + stalled_instr_valid ? data_t'(stalled_instr) : + imem__data; + + assign reader_next_word = + split_wait_valid ? + (split_wait_next_word_valid ? split_wait_next_word : imem__data) : + use_buffer ? + imem__data : + data_t'(0); + + assign buffer_next_same_word = + reader_inst_is_compressed && + (reader_next_pc[31:2] == reader_instr_pc[31:2]); + + assign need_split_wait = + reader_inst_is_split && !split_wait_valid; + + assign buffer_after_split = + split_wait_valid && + reader_inst_is_split && + (reader_next_pc[31:2] == (reader_instr_pc[31:2] + 30'd1)); + + assign hold_pc_for_split_buffer = + buffer_after_split; + + assign final_instr = + reader_inst_is_compressed ? decompressed_instr : reader_instr_raw; + + assign final_illegal = + reader_inst_is_compressed && compressed_illegal; + + assign if_to_id.instruction = + need_split_wait ? NOP : final_instr; + + assign if_to_id.pc_cur = + reader_instr_pc; + + assign if_to_id.pc_plus_4 = + reader_next_pc; + +endmodule diff --git a/src/ext/c/instruction_reader.sv b/src/ext/c/instruction_reader.sv new file mode 100644 index 0000000..9a53916 --- /dev/null +++ b/src/ext/c/instruction_reader.sv @@ -0,0 +1,36 @@ +`include "src/timescale.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" + +module instruction_reader + ( input addr_t pc_i + , input data_t cur_word_i + , input data_t next_word_i + , output instr_t instr_raw_o + , output addr_t instr_pc_o + , output addr_t instr_next_pc_o + , output logic instr_is_compressed_o + , output logic instr_is_split_o + ); + + wire unused_next_word = + &{1'b0, next_word_i[31:16]}; + logic [15:0] selected_halfword; + logic selected_is_32b; + assign selected_halfword = + pc_i[1] ? cur_word_i[31:16] : cur_word_i[15:0]; + assign selected_is_32b = + selected_halfword[1:0] == 2'b11; + assign instr_pc_o = + pc_i; + assign instr_is_compressed_o = + !selected_is_32b; + assign instr_is_split_o = + selected_is_32b && pc_i[1]; + assign instr_next_pc_o = + pc_i + (instr_is_compressed_o ? 32'd2 : 32'd4); + assign instr_raw_o = + instr_is_compressed_o ? {16'b0, selected_halfword} : + instr_is_split_o ? {next_word_i[15:0], cur_word_i[31:16]} : + cur_word_i; +endmodule diff --git a/src/headers/params.svh b/src/headers/params.svh index b1a4c51..cd05bbf 100644 --- a/src/headers/params.svh +++ b/src/headers/params.svh @@ -3,30 +3,30 @@ // Opcodes /* verilator lint_off UNUSEDPARAM */ -parameter RType = 7'b0110011; -parameter IType_logic = 7'b0010011; -parameter IType_load = 7'b0000011; -parameter SType = 7'b0100011; -parameter BType = 7'b1100011; -parameter JType = 7'b1101111; -parameter UType_auipc = 7'b0010111; -parameter UType_lui = 7'b0110111; -parameter IType_jalr = 7'b1100111; -parameter FENCE = 7'b0001111; +localparam bit [6:0] RType = 7'b0110011; +localparam bit [6:0] IType_logic = 7'b0010011; +localparam bit [6:0] IType_load = 7'b0000011; +localparam bit [6:0] SType = 7'b0100011; +localparam bit [6:0] BType = 7'b1100011; +localparam bit [6:0] JType = 7'b1101111; +localparam bit [6:0] UType_auipc = 7'b0010111; +localparam bit [6:0] UType_lui = 7'b0110111; +localparam bit [6:0] IType_jalr = 7'b1100111; +localparam bit [6:0] FENCE = 7'b0001111; /* verilator lint_on UNUSEDPARAM */ //ALU Operation Control Codes: Implemented as ENUM in params.svh /* -parameter ALUAdd = 4'b0000; -parameter ALUSub = 4'b0001; -parameter ALUSLL = 4'b0010; -parameter ALUSLT = 4'b0011; -parameter ALUSLTU = 4'b0100; -parameter ALUXOR = 4'b0101; -parameter ALUSRL = 4'b0110; -parameter ALUSRA = 4'b0111; -parameter ALUOR = 4'b1000; -parameter ALUAND = 4'b1001; +localparam ALUAdd = 4'b0000; +localparam ALUSub = 4'b0001; +localparam ALUSLL = 4'b0010; +localparam ALUSLT = 4'b0011; +localparam ALUSLTU = 4'b0100; +localparam ALUXOR = 4'b0101; +localparam ALUSRL = 4'b0110; +localparam ALUSRA = 4'b0111; +localparam ALUOR = 4'b1000; +localparam ALUAND = 4'b1001; */ `endif // PARAMS_VH diff --git a/src/stages/fetch_stage.sv b/src/stages/fetch_stage.sv index 7aaf3d1..4cb6ef2 100644 --- a/src/stages/fetch_stage.sv +++ b/src/stages/fetch_stage.sv @@ -3,19 +3,34 @@ `include "src/headers/types.svh" `include "src/interfaces/if_to_id_if.svh" `include "src/interfaces/ex_to_if_if.svh" - module fetch_stage ( output if_to_id_t if_to_id , input ex_to_if_t ex_to_if - , input wire clk , input wire reset , input wire stall_f , input wire flush_f - - , output addr_t imem__address - , input data_t imem__data +//use these to test + , output addr_t imem__address //output to instruction memory + , input data_t imem__data //get input instruction/data from instruction memory at the address specified by imem__address ); + + + +`ifdef UTOSS_RISCV_ENABLE_C_EXT + //do only if C extension is enabled +fetch_compressed fetch_compressed_i + ( .if_to_id(if_to_id) + , .ex_to_if(ex_to_if) + , .clk(clk) + , .reset(reset) + , .stall_f(stall_f) + , .flush_f(flush_f) + , .imem__address(imem__address) + , .imem__data(imem__data) + ); + +`else // The use of distinct registers for prev, cur and next PC requires some explanation. Below is the // timing diagram of the instruction retireval and subsequenct passing of the relevant data to // decode stage. Importantly, notice the one clock-cycle lag between PC placed on the `imem @@ -38,33 +53,27 @@ module fetch_stage // if->id.pc_cur : | < prev PC > | < cur PC > | < next PC > // if->id.instr : | < prev instr > | < cur instr > | < next instr > // +----------------+ - addr_t pc_prev; addr_t pc_cur; addr_t pc_next; + // RVC/C-extension fetch helpers. always_comb case (ex_to_if.pc_src) - PC_SRC__INCREMENT: pc_next = pc_cur + 32'h4; + // PC_SRC__INCREMENT: pc_next = pc_cur + 32'h4; + PC_SRC__INCREMENT: pc_next = pc_cur + 32'h4; PC_SRC__ALU_RESULT: pc_next = ex_to_if.pc_target; default: pc_next = 32'hx; endcase - - always_ff @ (posedge clk) - if (!stall_f) pc_cur <= reset ? 0 : pc_next; - - always_ff @ (posedge clk) - if (!stall_f) pc_prev <= reset ? 0 : pc_cur; - // With synchronous instruction memory, one in-flight instruction can arrive after stall_f rises. // Keep a one-entry skid copy so decode can consume it once the stall is released; // // NOTE: this takes up extra space, we could have just used the existing space in the IF->ID // register, but that would require breaking the combinational protocol of the stage logic; // revisit if space becomes a problem + instr_t stalled_instr; logic stalled_instr_valid; - always_ff @ (posedge clk) if (reset) {stalled_instr, stalled_instr_valid} <= {instr_t'(0) , 1'b0}; @@ -75,12 +84,22 @@ module fetch_stage else if (!stall_f && stalled_instr_valid) {stalled_instr, stalled_instr_valid} <= {stalled_instr, 1'b0}; - assign imem__address = pc_cur; +assign imem__address = pc_cur; + + + always_ff @ (posedge clk) + if (!stall_f) pc_cur <= reset ? 0 : pc_next; + + always_ff @ (posedge clk) + if (!stall_f) pc_prev <= reset ? 0 : pc_cur; + assign if_to_id.instruction = stalled_instr_valid ? stalled_instr : imem__data; assign if_to_id.pc_cur = pc_prev; - assign if_to_id.pc_plus_4 = pc_prev + 32'h4; // TODO: revisit - - // TODO: probably can just get rid of those altogether + assign if_to_id.pc_plus_4 = pc_prev + 32'h4; wire unused = &{ex_to_if.pc_old, ex_to_if.imm_ext}; + +`endif + + endmodule diff --git a/test/fetch_rvc_01_aligned_32_regression_tb.sv b/test/fetch_rvc_01_aligned_32_regression_tb.sv new file mode 100644 index 0000000..be90bb9 --- /dev/null +++ b/test/fetch_rvc_01_aligned_32_regression_tb.sv @@ -0,0 +1,249 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_01_aligned_32_regression_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = 32'h00A00093; // PC 0x00: addi x1,x0,10 + imem[1] = 32'h00100113; // PC 0x04: addi x2,x0,1 + imem[2] = NOP; + + release_reset(); + + wait_fetch(32'h00000000, 32'h00A00093, "aligned 32-bit at PC 0x00"); + wait_fetch(32'h00000004, 32'h00100113, "aligned 32-bit at PC 0x04"); + wait_fetch(32'h00000008, NOP, "aligned NOP at PC 0x08"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_01_aligned_32_regression_tb) + +endmodule diff --git a/test/fetch_rvc_02_two_compressed_same_word_tb.sv b/test/fetch_rvc_02_two_compressed_same_word_tb.sv new file mode 100644 index 0000000..6a1c142 --- /dev/null +++ b/test/fetch_rvc_02_two_compressed_same_word_tb.sv @@ -0,0 +1,248 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_02_two_compressed_same_word_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = 32'h00010001; // PC 0x00: C.NOP, PC 0x02: C.NOP + imem[1] = NOP; // PC 0x04: normal NOP + + release_reset(); + + wait_fetch(32'h00000000, NOP, "lower C.NOP at PC 0x00"); + wait_fetch(32'h00000002, NOP, "upper C.NOP from same-word buffer at PC 0x02"); + wait_fetch(32'h00000004, NOP, "next aligned word at PC 0x04"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_02_two_compressed_same_word_tb) + +endmodule diff --git a/test/fetch_rvc_03_compressed_then_split_tb.sv b/test/fetch_rvc_03_compressed_then_split_tb.sv new file mode 100644 index 0000000..93e3e31 --- /dev/null +++ b/test/fetch_rvc_03_compressed_then_split_tb.sv @@ -0,0 +1,250 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_03_compressed_then_split_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = 32'h00930001; // PC 0x00: C.NOP, PC 0x02: low half of ADDI + imem[1] = 32'h000100A0; // PC 0x04: high half of ADDI, PC 0x06: C.NOP + imem[2] = NOP; + + release_reset(); + + wait_fetch(32'h00000000, NOP, "C.NOP before split at PC 0x00"); + wait_fetch(32'h00000002, NOP, "bubble for split at PC 0x02"); + wait_fetch(32'h00000002, 32'h00A00093, "real split ADDI at PC 0x02"); + wait_fetch(32'h00000006, NOP, "C.NOP after split at PC 0x06"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_03_compressed_then_split_tb) + +endmodule diff --git a/test/fetch_rvc_04_back_to_back_split_tb.sv b/test/fetch_rvc_04_back_to_back_split_tb.sv new file mode 100644 index 0000000..191ee3d --- /dev/null +++ b/test/fetch_rvc_04_back_to_back_split_tb.sv @@ -0,0 +1,253 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_04_back_to_back_split_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = 32'h00930001; // PC 0x00: C.NOP, PC 0x02: low half A + imem[1] = 32'h011300A0; // PC 0x04: high half A, PC 0x06: low half B + imem[2] = 32'h00010010; // PC 0x08: high half B, PC 0x0A: C.NOP + imem[3] = NOP; + + release_reset(); + + wait_fetch(32'h00000000, NOP, "C.NOP before back-to-back splits"); + wait_fetch(32'h00000002, NOP, "bubble for split A"); + wait_fetch(32'h00000002, 32'h00A00093, "real split A at PC 0x02"); + wait_fetch(32'h00000006, NOP, "bubble for split B"); + wait_fetch(32'h00000006, 32'h00100113, "real split B at PC 0x06"); + wait_fetch(32'h0000000A, NOP, "C.NOP after split B at PC 0x0A"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_04_back_to_back_split_tb) + +endmodule diff --git a/test/fetch_rvc_05_branch_to_split_target_tb.sv b/test/fetch_rvc_05_branch_to_split_target_tb.sv new file mode 100644 index 0000000..3e1d97a --- /dev/null +++ b/test/fetch_rvc_05_branch_to_split_target_tb.sv @@ -0,0 +1,251 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_05_branch_to_split_target_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = 32'h00930001; // target PC 0x02 starts split ADDI + imem[1] = 32'h000100A0; // completes split ADDI, then C.NOP at PC 0x06 + imem[2] = NOP; + + release_reset(); + + redirect_to(32'h00000002); + + wait_fetch(32'h00000002, NOP, "bubble after branch directly to split target"); + wait_fetch(32'h00000002, 32'h00A00093, "real split ADDI after branch target"); + wait_fetch(32'h00000006, NOP, "fallthrough after branch-target split"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_05_branch_to_split_target_tb) + +endmodule diff --git a/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv b/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv new file mode 100644 index 0000000..fa13091 --- /dev/null +++ b/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv @@ -0,0 +1,250 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_06_branch_to_lower_compressed_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = NOP; + imem[1] = 32'h00010001; // PC 0x04: C.NOP, PC 0x06: C.NOP + imem[2] = NOP; + + release_reset(); + + redirect_to(32'h00000004); + + wait_fetch(32'h00000004, NOP, "branch target lower-half C.NOP at PC 0x04"); + wait_fetch(32'h00000006, NOP, "same-word buffered C.NOP at PC 0x06"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_06_branch_to_lower_compressed_tb) + +endmodule diff --git a/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv b/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv new file mode 100644 index 0000000..62b7602 --- /dev/null +++ b/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv @@ -0,0 +1,250 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_07_branch_to_upper_compressed_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = NOP; + imem[1] = 32'h00010013; // PC 0x04: normal low half irrelevant-ish, PC 0x06: C.NOP + imem[2] = NOP; + + release_reset(); + + redirect_to(32'h00000006); + + wait_fetch(32'h00000006, NOP, "branch target upper-half C.NOP at PC 0x06"); + wait_fetch(32'h00000008, NOP, "fallthrough to next word after upper C.NOP"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_07_branch_to_upper_compressed_tb) + +endmodule diff --git a/test/fetch_rvc_08_branch_to_aligned_32_tb.sv b/test/fetch_rvc_08_branch_to_aligned_32_tb.sv new file mode 100644 index 0000000..2195410 --- /dev/null +++ b/test/fetch_rvc_08_branch_to_aligned_32_tb.sv @@ -0,0 +1,251 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_08_branch_to_aligned_32_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = NOP; + imem[1] = NOP; + imem[2] = 32'h00A00093; // PC 0x08: addi x1,x0,10 + imem[3] = 32'h00100113; // PC 0x0C: addi x2,x0,1 + + release_reset(); + + redirect_to(32'h00000008); + + wait_fetch(32'h00000008, 32'h00A00093, "branch target aligned 32-bit at PC 0x08"); + wait_fetch(32'h0000000C, 32'h00100113, "fallthrough aligned 32-bit at PC 0x0C"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_08_branch_to_aligned_32_tb) + +endmodule diff --git a/test/fetch_rvc_09_flush_while_buffer_valid_tb.sv b/test/fetch_rvc_09_flush_while_buffer_valid_tb.sv new file mode 100644 index 0000000..3477a4c --- /dev/null +++ b/test/fetch_rvc_09_flush_while_buffer_valid_tb.sv @@ -0,0 +1,259 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_09_flush_while_buffer_valid_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + localparam bit [15:0] C_NOP = 16'h0001; + localparam instr_t TARGET_INSTR = 32'h00A00093; + + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + wire unused_if_to_id = + &{1'b0, if_to_id.pc_plus_4}; + + fetch_stage uut + ( .if_to_id (if_to_id) + , .ex_to_if (ex_to_if) + , .clk (clk) + , .reset (reset) + , .stall_f (stall_f) + , .flush_f (flush_f) + , .imem__address (imem__address) + , .imem__data (imem__data) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_buffer_valid; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 3; n = n + 1) begin + if (uut.fetch_compressed_i.buffered_word_valid === 1'b1) begin + $display + ( "[pass] %0s" + , label + ); + seen = 1'b1; + n = 3; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s buffered_word_valid never became 1" + , label + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + initial begin + init_common(); + + imem[0] = {C_NOP, C_NOP}; + imem[2] = TARGET_INSTR; + + release_reset(); + + wait_fetch + ( 32'h00000000 + , NOP + , "lower C.NOP creates same-word buffer" + ); + + wait_buffer_valid + ( "same-word buffer becomes valid after lower compressed instruction" + ); + + redirect_to(32'h00000008); + + `assert_equal(uut.fetch_compressed_i.buffered_word_valid, 1'b0) + + wait_target_no_forbidden + ( 32'h00000008 + , TARGET_INSTR + , 32'h00000002 + , NOP + , "flush while buffer valid must kill old buffered PC 0x02" + ); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_09_flush_while_buffer_valid_tb) + +endmodule diff --git a/test/fetch_rvc_10_flush_during_split_wait_tb.sv b/test/fetch_rvc_10_flush_during_split_wait_tb.sv new file mode 100644 index 0000000..d92638d --- /dev/null +++ b/test/fetch_rvc_10_flush_during_split_wait_tb.sv @@ -0,0 +1,262 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_10_flush_during_split_wait_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + localparam instr_t SPLIT_INSTR = 32'h00A00093; + localparam instr_t TARGET_INSTR = 32'h00100113; + + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + wire unused_if_to_id = + &{1'b0, if_to_id.pc_plus_4}; + + fetch_stage uut + ( .if_to_id (if_to_id) + , .ex_to_if (ex_to_if) + , .clk (clk) + , .reset (reset) + , .stall_f (stall_f) + , .flush_f (flush_f) + , .imem__address (imem__address) + , .imem__data (imem__data) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_split_wait_valid; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 3; n = n + 1) begin + if (uut.fetch_compressed_i.split_wait_valid === 1'b1) begin + $display + ( "[pass] %0s" + , label + ); + seen = 1'b1; + n = 3; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s split_wait_valid never became 1" + , label + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + initial begin + init_common(); + + imem[0] = 32'h00930001; + imem[1] = 32'h000100A0; + imem[2] = TARGET_INSTR; + + release_reset(); + + wait_fetch + ( 32'h00000000 + , NOP + , "C.NOP before split" + ); + + wait_split_wait_valid + ( "split_wait becomes valid for split instruction at PC 0x02" + ); + + `assert_equal(uut.fetch_compressed_i.split_wait_pc, 32'h00000002) + + redirect_to(32'h00000008); + + `assert_equal(uut.fetch_compressed_i.split_wait_valid, 1'b0) + + wait_target_no_forbidden + ( 32'h00000008 + , TARGET_INSTR + , 32'h00000002 + , SPLIT_INSTR + , "flush during split wait must kill old real split instruction" + ); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_10_flush_during_split_wait_tb) + +endmodule diff --git a/test/fetch_rvc_11_stall_while_buffer_valid_tb.sv b/test/fetch_rvc_11_stall_while_buffer_valid_tb.sv new file mode 100644 index 0000000..ddaccd0 --- /dev/null +++ b/test/fetch_rvc_11_stall_while_buffer_valid_tb.sv @@ -0,0 +1,194 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_11_stall_while_buffer_valid_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + localparam bit [15:0] C_NOP = 16'h0001; + + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + fetch_stage uut + ( .if_to_id (if_to_id) + , .ex_to_if (ex_to_if) + , .clk (clk) + , .reset (reset) + , .stall_f (stall_f) + , .flush_f (flush_f) + , .imem__address (imem__address) + , .imem__data (imem__data) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_buffer_valid; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 3; n = n + 1) begin + if (uut.fetch_compressed_i.buffered_word_valid === 1'b1) begin + $display + ( "[pass] %0s" + , label + ); + seen = 1'b1; + n = 3; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s buffered_word_valid never became 1" + , label + ); + $fatal(1); + end + end + endtask + + initial begin + init_common(); + + imem[0] = {C_NOP, C_NOP}; + imem[1] = NOP; + + release_reset(); + + wait_fetch + ( 32'h00000000 + , NOP + , "lower C.NOP creates same-word buffer" + ); + + wait_buffer_valid + ( "same-word buffer becomes valid before stall" + ); + + stall_f = 1'b1; + + repeat (3) begin + tick(); + `assert_equal(uut.fetch_compressed_i.buffered_word_valid, 1'b1) + end + + stall_f = 1'b0; + #1; + + `assert_equal(if_to_id.pc_cur, 32'h00000002) + `assert_equal(if_to_id.instruction, NOP) + `assert_equal(if_to_id.pc_plus_4, 32'h00000004) + + tick(); + + `assert_equal(uut.fetch_compressed_i.buffered_word_valid, 1'b0) + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_11_stall_while_buffer_valid_tb) + +endmodule diff --git a/test/fetch_rvc_12_stall_during_split_wait_tb.sv b/test/fetch_rvc_12_stall_during_split_wait_tb.sv new file mode 100644 index 0000000..2f7551f --- /dev/null +++ b/test/fetch_rvc_12_stall_during_split_wait_tb.sv @@ -0,0 +1,213 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_12_stall_during_split_wait_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + localparam data_t SPLIT_CUR_WORD = 32'h00930001; + localparam data_t SPLIT_NEXT_WORD = 32'h000100A0; + localparam instr_t SPLIT_INSTR = 32'h00A00093; + + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + fetch_stage uut + ( .if_to_id (if_to_id) + , .ex_to_if (ex_to_if) + , .clk (clk) + , .reset (reset) + , .stall_f (stall_f) + , .flush_f (flush_f) + , .imem__address (imem__address) + , .imem__data (imem__data) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: + // imem__data returns the word requested by imem__address on the previous + // rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_split_wait_valid; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + + for (n = 0; n < 3; n = n + 1) begin + if (uut.fetch_compressed_i.split_wait_valid === 1'b1) begin + $display + ( "[pass] %0s" + , label + ); + seen = 1'b1; + n = 3; + end + else begin + tick(); + end + end + + if (!seen) begin + $display + ( "[fail] %0s split_wait_valid never became 1" + , label + ); + $fatal(1); + end + end + endtask + + initial begin + init_common(); + + // Word 0: + // PC 0x00 -> C.NOP + // PC 0x02 -> low half of split 32-bit instruction + // + // Word 1: + // PC 0x04 -> high half of split 32-bit instruction + // PC 0x06 -> C.NOP + imem[0] = SPLIT_CUR_WORD; + imem[1] = SPLIT_NEXT_WORD; + imem[2] = NOP; + + release_reset(); + + wait_fetch + ( 32'h00000000 + , NOP + , "C.NOP before split" + ); + + wait_split_wait_valid + ( "split_wait becomes valid for split instruction at PC 0x02" + ); + + `assert_equal(uut.fetch_compressed_i.split_wait_pc, 32'h00000002) + `assert_equal(uut.fetch_compressed_i.split_wait_cur_word, SPLIT_CUR_WORD) + + stall_f = 1'b1; + + repeat (3) begin + tick(); + + `assert_equal(uut.fetch_compressed_i.split_wait_valid, 1'b1) + `assert_equal(uut.fetch_compressed_i.split_wait_pc, 32'h00000002) + `assert_equal(uut.fetch_compressed_i.split_wait_cur_word, SPLIT_CUR_WORD) + end + + stall_f = 1'b0; + #1; + + `assert_equal(if_to_id.pc_cur, 32'h00000002) + `assert_equal(if_to_id.instruction, SPLIT_INSTR) + `assert_equal(if_to_id.pc_plus_4, 32'h00000006) + + tick(); + + `assert_equal(uut.fetch_compressed_i.split_wait_valid, 1'b0) + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_12_stall_during_split_wait_tb) + +endmodule diff --git a/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv b/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv new file mode 100644 index 0000000..1b8df0c --- /dev/null +++ b/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv @@ -0,0 +1,248 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_13_compressed_then_aligned_32_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = 32'h00010001; // PC 0x00: C.NOP, PC 0x02: C.NOP + imem[1] = 32'h00A00093; // PC 0x04: aligned ADDI + + release_reset(); + + wait_fetch(32'h00000000, NOP, "C.NOP at PC 0x00"); + wait_fetch(32'h00000002, NOP, "C.NOP at PC 0x02"); + wait_fetch(32'h00000004, 32'h00A00093, "aligned 32-bit after compressed pair"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_13_compressed_then_aligned_32_tb) + +endmodule diff --git a/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv b/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv new file mode 100644 index 0000000..daea864 --- /dev/null +++ b/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv @@ -0,0 +1,246 @@ +`include "src/timescale.svh" + +`include "test/utils.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +`include "src/interfaces/if_to_id_if.svh" +`include "src/interfaces/ex_to_if_if.svh" + +module fetch_rvc_15_compressed_pc_plus_2_tb; + + reg clk; + reg reset; + reg stall_f; + reg flush_f; + + if_to_id_t if_to_id; + ex_to_if_t ex_to_if; + + addr_t imem__address; + wire unused_imem_address = + &{1'b0, imem__address[31:10], imem__address[1:0]}; + + data_t imem__data; + + data_t imem [0:255]; + + localparam instr_t NOP = 32'h00000013; + + fetch_stage uut + ( .if_to_id ( if_to_id ) + , .ex_to_if ( ex_to_if ) + , .clk ( clk ) + , .reset ( reset ) + , .stall_f ( stall_f ) + , .flush_f ( flush_f ) + , .imem__address ( imem__address ) + , .imem__data ( imem__data ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + // Synchronous instruction memory model: imem__data returns the word + // requested by imem__address on the previous rising edge. + always @ (posedge clk) begin + imem__data <= imem[imem__address[9:2]]; + end + + task tick; + @(posedge clk); #1; + endtask + + task init_common; + integer i; + begin + reset = `TRUE; + stall_f = 1'b0; + flush_f = 1'b0; + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + ex_to_if.pc_old = addr_t'(0); + ex_to_if.imm_ext = data_t'(0); + + imem__data = data_t'(0); + + for (i = 0; i < 256; i = i + 1) begin + imem[i] = NOP; + + end + end + endtask + + task release_reset; + begin + repeat (3) tick(); + reset = `FALSE; + end + endtask + + task redirect_to; + input addr_t target; + begin + ex_to_if.pc_src = PC_SRC__ALU_RESULT; + ex_to_if.pc_target = target; + flush_f = 1'b1; + tick(); + + ex_to_if.pc_src = PC_SRC__INCREMENT; + ex_to_if.pc_target = addr_t'(0); + flush_f = 1'b0; + end + endtask + + task wait_fetch; + input addr_t exp_pc; + input instr_t exp_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.instruction === exp_instr) begin + $display + ( "[pass] %0s pc=0x%08h instr=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.instruction + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h instr=0x%08h, got pc=0x%08h instr=0x%08h" + , label + , exp_pc + , exp_instr + , if_to_id.pc_cur + , if_to_id.instruction + ); + $fatal(1); + end + end + endtask + + task wait_pc_plus; + input addr_t exp_pc; + input addr_t exp_next_pc; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 40; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === exp_pc && + if_to_id.pc_plus_4 === exp_next_pc) begin + $display + ( "[pass] %0s pc=0x%08h next=0x%08h" + , label + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + seen = 1'b1; + n = 40; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s expected pc=0x%08h next=0x%08h, got pc=0x%08h next=0x%08h" + , label + , exp_pc + , exp_next_pc + , if_to_id.pc_cur + , if_to_id.pc_plus_4 + ); + $fatal(1); + end + end + endtask + + task wait_target_no_forbidden; + input addr_t target_pc; + input instr_t target_instr; + input addr_t forbidden_pc; + input instr_t forbidden_instr; + input [8 * 80 - 1:0] label; + integer n; + reg seen; + begin + seen = 1'b0; + for (n = 0; n < 50; n = n + 1) begin + if (!stall_f && !flush_f && + if_to_id.pc_cur === forbidden_pc && + if_to_id.instruction === forbidden_instr) begin + $display + ( "[fail] %0s saw forbidden old-path pc=0x%08h instr=0x%08h" + , label + , forbidden_pc + , forbidden_instr + ); + $fatal(1); + end + + if (!stall_f && !flush_f && + if_to_id.pc_cur === target_pc && + if_to_id.instruction === target_instr) begin + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + seen = 1'b1; + n = 50; + end + + if (!seen) + tick(); + end + + if (!seen) begin + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); + $fatal(1); + end + end + endtask + + + initial begin + + init_common(); + + imem[0] = 32'h00010001; // PC 0x00: C.NOP, PC 0x02: C.NOP + + release_reset(); + + wait_pc_plus(32'h00000000, 32'h00000002, "compressed instruction must report next PC as PC+2"); + wait_pc_plus(32'h00000002, 32'h00000004, "upper compressed instruction must report next PC as PC+2"); + + $finish; + end + + `SETUP_VCD_DUMP(fetch_rvc_15_compressed_pc_plus_2_tb) + +endmodule