From 75c8c8ef1eac4311fe126534ef61dda2b8eb328c Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 02:37:48 -0400 Subject: [PATCH 01/17] C-extended fetch stage and instruction reader module Implemented initial RVC fetch support with compressed instruction handling, decompression path integration, same-word buffering, and split instruction handling. Main aligned, compressed, branch-target, and split-at-PC-0x02 cases are passing locally. Stall/flush interaction and later split-boundary cases still need follow-up. Requires C decompressor module from Andrew. --- src/modules/instruction_reader.sv | 84 +++++++++++ src/stages/fetch_stage.sv | 238 ++++++++++++++++++++++++++++-- 2 files changed, 307 insertions(+), 15 deletions(-) create mode 100644 src/modules/instruction_reader.sv diff --git a/src/modules/instruction_reader.sv b/src/modules/instruction_reader.sv new file mode 100644 index 0000000..c79c35f --- /dev/null +++ b/src/modules/instruction_reader.sv @@ -0,0 +1,84 @@ +`include "src/timescale.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" +//instantiate inside fetch stage + +// Purpose: + +// This module should sit between fetch_stage and instruction memory. +// It should output one complete instruction at a time to the decode stage. +// +// This module reads one complete instruction from synchronous instruction memory. +// +// Cases handled: +// 1. 16-bit compressed instruction starting in lower or upper halfword. +// 2. 32-bit instruction starting at lower halfword, fully inside current word. +// 3. 32-bit instruction starting at upper halfword, split across two words. +// +// Notes: +// - instr_o is always 32 bits for pipeline compatibility. +// - If compressed, instr_o = {16'b0, raw_16_bit_instruction} for now. +// - Decompression happens later in a separate module. + + + +module instruction_reader + ( input addr_t pc_i //current fetch PC from fetch_stage + , input data_t cur_word_i //current 32-bit word read from instruction memory at the aligned PC address + , input data_t next_word_i //next 32-bit word read from instruction memory at the next aligned PC address + , 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 + ); + 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; + +always_comb begin + + instr_raw_o = instr_t'(0); + instr_pc_o = pc_i; + instr_next_pc_o = pc_i; + instr_is_compressed_o = 1'b0; + instr_is_split_o = 1'b0; + +// 1. 16-bit compressed instruction starting in lower or upper halfword. +if(selected_is_32b==1'b0) begin +instr_raw_o = {16'b0, selected_halfword}; // output the 16-bit instruction in the lower half of instr_raw_o, upper half is zero-padded +instr_pc_o = pc_i; +instr_next_pc_o = pc_i + 32'd2; // next PC is current PC + 2 +instr_is_compressed_o = 1'b1; +instr_is_split_o = 1'b0; +end + +// 2. 32-bit instruction starting at lower halfword, fully inside current word. +else if(pc_i[1] == 1'b0) begin +instr_raw_o = cur_word_i; // the whole 32-bit instruction is in the current word +instr_pc_o = pc_i; +instr_next_pc_o = pc_i + 32'd4; // next PC is current PC + 4 +instr_is_compressed_o = 1'b0; +instr_is_split_o = 1'b0; +end + + +// 3. 32-bit instruction starting at upper halfword, split across two words. +else begin +instr_raw_o = {next_word_i[15:0], cur_word_i[31:16]}; // combine the upper half of the current word and the lower half of the next word +instr_pc_o = pc_i; +instr_next_pc_o = pc_i + 32'd4; // next PC is current PC + 4 +instr_is_compressed_o = 1'b0; +instr_is_split_o = 1'b1; // this instruction is split across two words +end + +end + + +endmodule + + + + diff --git a/src/stages/fetch_stage.sv b/src/stages/fetch_stage.sv index 7aaf3d1..49c8192 100644 --- a/src/stages/fetch_stage.sv +++ b/src/stages/fetch_stage.sv @@ -13,8 +13,9 @@ module fetch_stage , 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 ); // 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 @@ -43,25 +44,25 @@ module fetch_stage 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[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 (!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; @@ -75,12 +76,219 @@ 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; + +assign imem__address = {pc_cur[31:2], 2'b00}; + + +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; + +//state bit to indicate : currently waiting to finish a split instruction +logic split_wait_valid; +data_t split_wait_cur_word; +addr_t split_wait_pc; + +logic need_split_wait; +logic buffer_after_split; +logic hold_pc_for_split_buffer; + +//a safety instruction that doesnt do anything +//outputted when we need to wait an extra cycle to fetch a split 32-bit instruction +localparam instr_t NOP = 32'h00000013; + +//for buffer for when two compressed instructions are back to back, +// and the second one is in the same 32-bit word as the first one, +//so we can just buffer the whole word and then read the second instruction +//from it without needing to fetch again from memory. +//This is an optimization to avoid unnecessary memory accesses for +//consecutive compressed instructions that happen to be in the same word. +logic buffered_word_valid; +data_t buffered_word; +addr_t buffered_pc; +// buffered_pc tells the reader which halfword inside buffered_word to use. + +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 +}; + + +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) + ); + + +Compressed_Decode u_compressdecode ( + .instr_c(reader_instr_raw[15:0]), + .instr_out(decompressed_instr), + .is_illegal(compressed_illegal) + ); + + +always_ff @ (posedge clk) begin + if (reset || flush_f) begin + split_wait_valid <= 1'b0; + split_wait_cur_word <= data_t'(0); + split_wait_pc <= addr_t'(0); + end + + else if (!stall_f) begin + // If we are currently waiting for the second half of a split instruction, + //then it must have arrived in this cycle, so we can stop waiting. + if (split_wait_valid) begin + split_wait_valid <= 1'b0; + end + // If we just fetched the first half of a split instruction, + //we need to start waiting for the second half in the next cycle. + //we must also save the current word and PC to be used for the split instruction + else if (need_split_wait) begin + split_wait_valid <= 1'b1; + split_wait_cur_word <= reader_cur_word_i; + split_wait_pc <= reader_instr_pc; + end + end +end + + +always_ff @ (posedge clk) begin + if (reset) + pc_cur <= addr_t'(0); + else if (!stall_f && !hold_pc_for_split_buffer) + pc_cur <= pc_next; +end + +always_ff @ (posedge clk) begin + if (reset) + pc_prev <= addr_t'(0); + else if (!stall_f && !hold_pc_for_split_buffer) + pc_prev <= pc_cur; +end + +always_ff @ (posedge clk) begin + // On reset or flush, clear the buffer. + if (reset || flush_f) begin + buffered_word_valid <= 1'b0; + buffered_word <= data_t'(0); + buffered_pc <= addr_t'(0); + end + //If fetch is stalled, we should not consume or overwrite the buffer. + else if (!stall_f) begin + + //This handles the case when the 32 bit word retrieved last after a + //split 32-bit instruction retrieval contains another instruction in + //its upper half that we will need to fetch next. + if (buffer_after_split) begin + buffered_word_valid <= 1'b1; + buffered_word <= imem__data; + buffered_pc <= reader_next_pc; + end + // the buffer is currently being used but we don't need to keep it for the next instruction, invalidate it. + else if (use_buffer) begin + buffered_word_valid <= 1'b0; + end + + // If the next instruction we need to fetch is in the same word + //as the current instruction, buffer it for the next cycle. + //this case happens when we have a compressed instruction followed by another + //instruction (compressed or the start of a split 32-bit instruction) that starts in + //the same 32-bit word + else if (buffer_next_same_word) begin + buffered_word_valid <= 1'b1; + buffered_word <= reader_cur_word_i; + buffered_pc <= reader_next_pc; + end + end +end + + +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 || 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]); + + +//Only insert the wait bubble the FIRST time you discover the split. +//Do not insert another bubble when you are already completing it. +assign need_split_wait = + reader_inst_is_split && !split_wait_valid; + + +//I just completed a split instruction, and the next instruction starts inside +//the next word that just arrived. Save that word for the next cycle. +assign buffer_after_split = + split_wait_valid && + reader_inst_is_split && + (reader_next_pc[31:2] == (reader_instr_pc[31:2] + 30'd1)); + +//prevents the memory fetch PC from advancing too far while you +//consume the buffered instruction +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; + +//final output to decode stage +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; + - 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 - wire unused = &{ex_to_if.pc_old, ex_to_if.imm_ext}; endmodule + + + + + + + From 20f20bf563e56f5ecc0f5fb174a4242d8ef1c8df Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 02:53:47 -0400 Subject: [PATCH 02/17] Temporary placeholder for C decompressor module --- src/stages/fetch_stage.sv | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/stages/fetch_stage.sv b/src/stages/fetch_stage.sv index 49c8192..539af79 100644 --- a/src/stages/fetch_stage.sv +++ b/src/stages/fetch_stage.sv @@ -143,12 +143,10 @@ instruction_reader u_reader , .instr_is_split_o (reader_inst_is_split) ); - -Compressed_Decode u_compressdecode ( - .instr_c(reader_instr_raw[15:0]), - .instr_out(decompressed_instr), - .is_illegal(compressed_illegal) - ); +// TODO: Replace with real compressed decoder once C decompressor module is merged. +// For now, compressed instructions are converted to NOP so fetch-stage integration builds. +assign decompressed_instr = NOP; +assign compressed_illegal = 1'b0; always_ff @ (posedge clk) begin From 1d949fbdd8b77ef15be86f8e609b790e9ef8b4b4 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 03:14:41 -0400 Subject: [PATCH 03/17] formatting changes and unused wires --- src/modules/instruction_reader.sv | 27 ++++++----- src/stages/fetch_stage.sv | 76 +++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 30 deletions(-) diff --git a/src/modules/instruction_reader.sv b/src/modules/instruction_reader.sv index c79c35f..42b4fa9 100644 --- a/src/modules/instruction_reader.sv +++ b/src/modules/instruction_reader.sv @@ -1,7 +1,7 @@ `include "src/timescale.svh" `include "src/headers/params.svh" `include "src/headers/types.svh" -//instantiate inside fetch stage +//instantiate inside fetch stage // Purpose: @@ -25,7 +25,7 @@ module instruction_reader ( input addr_t pc_i //current fetch PC from fetch_stage , input data_t cur_word_i //current 32-bit word read from instruction memory at the aligned PC address - , input data_t next_word_i //next 32-bit word read from instruction memory at the next aligned PC address + , input data_t next_word_i //next 32-bit word read from instruction memory at the next aligned PC address , output instr_t instr_raw_o , output addr_t instr_pc_o , output addr_t instr_next_pc_o @@ -35,10 +35,10 @@ module instruction_reader 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 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; -always_comb begin +always_comb begin instr_raw_o = instr_t'(0); instr_pc_o = pc_i; @@ -47,12 +47,12 @@ always_comb begin instr_is_split_o = 1'b0; // 1. 16-bit compressed instruction starting in lower or upper halfword. -if(selected_is_32b==1'b0) begin +if(selected_is_32b==1'b0) begin instr_raw_o = {16'b0, selected_halfword}; // output the 16-bit instruction in the lower half of instr_raw_o, upper half is zero-padded -instr_pc_o = pc_i; +instr_pc_o = pc_i; instr_next_pc_o = pc_i + 32'd2; // next PC is current PC + 2 -instr_is_compressed_o = 1'b1; -instr_is_split_o = 1'b0; +instr_is_compressed_o = 1'b1; +instr_is_split_o = 1'b0; end // 2. 32-bit instruction starting at lower halfword, fully inside current word. @@ -62,12 +62,12 @@ instr_pc_o = pc_i; instr_next_pc_o = pc_i + 32'd4; // next PC is current PC + 4 instr_is_compressed_o = 1'b0; instr_is_split_o = 1'b0; -end +end // 3. 32-bit instruction starting at upper halfword, split across two words. else begin -instr_raw_o = {next_word_i[15:0], cur_word_i[31:16]}; // combine the upper half of the current word and the lower half of the next word +instr_raw_o = {next_word_i[15:0], cur_word_i[31:16]}; // combine the upper half of the current word and the lower half of the next word instr_pc_o = pc_i; instr_next_pc_o = pc_i + 32'd4; // next PC is current PC + 4 instr_is_compressed_o = 1'b0; @@ -77,6 +77,11 @@ end end +wire unused = &{ + next_word_i[31:16] +}; + + endmodule diff --git a/src/stages/fetch_stage.sv b/src/stages/fetch_stage.sv index 539af79..6330a74 100644 --- a/src/stages/fetch_stage.sv +++ b/src/stages/fetch_stage.sv @@ -107,12 +107,12 @@ logic hold_pc_for_split_buffer; localparam instr_t NOP = 32'h00000013; //for buffer for when two compressed instructions are back to back, -// and the second one is in the same 32-bit word as the first one, -//so we can just buffer the whole word and then read the second instruction -//from it without needing to fetch again from memory. -//This is an optimization to avoid unnecessary memory accesses for +// and the second one is in the same 32-bit word as the first one, +//so we can just buffer the whole word and then read the second instruction +//from it without needing to fetch again from memory. +//This is an optimization to avoid unnecessary memory accesses for //consecutive compressed instructions that happen to be in the same word. -logic buffered_word_valid; +logic buffered_word_valid; data_t buffered_word; addr_t buffered_pc; // buffered_pc tells the reader which halfword inside buffered_word to use. @@ -124,11 +124,10 @@ 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 + ex_to_if.pc_old +, ex_to_if.imm_ext +, final_illegal }; @@ -149,6 +148,9 @@ assign decompressed_instr = NOP; assign compressed_illegal = 1'b0; + +/* + always_ff @ (posedge clk) begin if (reset || flush_f) begin split_wait_valid <= 1'b0; @@ -157,12 +159,12 @@ always_ff @ (posedge clk) begin end else if (!stall_f) begin - // If we are currently waiting for the second half of a split instruction, + // If we are currently waiting for the second half of a split instruction, //then it must have arrived in this cycle, so we can stop waiting. if (split_wait_valid) begin split_wait_valid <= 1'b0; end - // If we just fetched the first half of a split instruction, + // If we just fetched the first half of a split instruction, //we need to start waiting for the second half in the next cycle. //we must also save the current word and PC to be used for the split instruction else if (need_split_wait) begin @@ -172,6 +174,11 @@ always_ff @ (posedge clk) begin end end end +*/ + + + + always_ff @ (posedge clk) begin @@ -188,8 +195,37 @@ always_ff @ (posedge clk) begin pc_prev <= pc_cur; end + +always_ff @ (posedge clk) + if (reset || flush_f) + {split_wait_valid, split_wait_cur_word, split_wait_pc} + <= {1'b0, data_t'(0), addr_t'(0)}; + else if (!stall_f) + if (split_wait_valid) + split_wait_valid <= 1'b0; + else if (need_split_wait) + {split_wait_valid, split_wait_cur_word, split_wait_pc} + <= {1'b1, reader_cur_word_i, reader_instr_pc}; + + +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) + if (buffer_after_split) + {buffered_word_valid, buffered_word, buffered_pc} + <= {1'b1, imem__data, reader_next_pc}; + else if (use_buffer) + buffered_word_valid <= 1'b0; + else if (buffer_next_same_word) + {buffered_word_valid, buffered_word, buffered_pc} + <= {1'b1, reader_cur_word_i, reader_next_pc}; + + +/* always_ff @ (posedge clk) begin - // On reset or flush, clear the buffer. + // On reset or flush, clear the buffer. if (reset || flush_f) begin buffered_word_valid <= 1'b0; buffered_word <= data_t'(0); @@ -198,8 +234,8 @@ always_ff @ (posedge clk) begin //If fetch is stalled, we should not consume or overwrite the buffer. else if (!stall_f) begin - //This handles the case when the 32 bit word retrieved last after a - //split 32-bit instruction retrieval contains another instruction in + //This handles the case when the 32 bit word retrieved last after a + //split 32-bit instruction retrieval contains another instruction in //its upper half that we will need to fetch next. if (buffer_after_split) begin buffered_word_valid <= 1'b1; @@ -211,10 +247,10 @@ always_ff @ (posedge clk) begin buffered_word_valid <= 1'b0; end - // If the next instruction we need to fetch is in the same word + // If the next instruction we need to fetch is in the same word //as the current instruction, buffer it for the next cycle. - //this case happens when we have a compressed instruction followed by another - //instruction (compressed or the start of a split 32-bit instruction) that starts in + //this case happens when we have a compressed instruction followed by another + //instruction (compressed or the start of a split 32-bit instruction) that starts in //the same 32-bit word else if (buffer_next_same_word) begin buffered_word_valid <= 1'b1; @@ -224,6 +260,8 @@ always_ff @ (posedge clk) begin end end +*/ + assign use_buffer = buffered_word_valid; @@ -255,14 +293,14 @@ assign need_split_wait = reader_inst_is_split && !split_wait_valid; -//I just completed a split instruction, and the next instruction starts inside +//I just completed a split instruction, and the next instruction starts inside //the next word that just arrived. Save that word for the next cycle. assign buffer_after_split = split_wait_valid && reader_inst_is_split && (reader_next_pc[31:2] == (reader_instr_pc[31:2] + 30'd1)); -//prevents the memory fetch PC from advancing too far while you +//prevents the memory fetch PC from advancing too far while you //consume the buffered instruction assign hold_pc_for_split_buffer = buffer_after_split; From 47f253c1fc149a7100f52fd3d82000a8a7fe4ef8 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 13:39:43 -0400 Subject: [PATCH 04/17] Fix svlint style issues --- src/stages/fetch_stage.sv | 68 ++------------------------------------- 1 file changed, 3 insertions(+), 65 deletions(-) diff --git a/src/stages/fetch_stage.sv b/src/stages/fetch_stage.sv index 6330a74..4d26c95 100644 --- a/src/stages/fetch_stage.sv +++ b/src/stages/fetch_stage.sv @@ -149,51 +149,18 @@ assign compressed_illegal = 1'b0; -/* - -always_ff @ (posedge clk) begin - if (reset || flush_f) begin - split_wait_valid <= 1'b0; - split_wait_cur_word <= data_t'(0); - split_wait_pc <= addr_t'(0); - end - - else if (!stall_f) begin - // If we are currently waiting for the second half of a split instruction, - //then it must have arrived in this cycle, so we can stop waiting. - if (split_wait_valid) begin - split_wait_valid <= 1'b0; - end - // If we just fetched the first half of a split instruction, - //we need to start waiting for the second half in the next cycle. - //we must also save the current word and PC to be used for the split instruction - else if (need_split_wait) begin - split_wait_valid <= 1'b1; - split_wait_cur_word <= reader_cur_word_i; - split_wait_pc <= reader_instr_pc; - end - end -end -*/ - - - - - -always_ff @ (posedge clk) begin +always_ff @ (posedge clk) if (reset) pc_cur <= addr_t'(0); else if (!stall_f && !hold_pc_for_split_buffer) pc_cur <= pc_next; -end -always_ff @ (posedge clk) begin +always_ff @ (posedge clk) if (reset) pc_prev <= addr_t'(0); else if (!stall_f && !hold_pc_for_split_buffer) pc_prev <= pc_cur; -end always_ff @ (posedge clk) @@ -222,43 +189,14 @@ always_ff @ (posedge clk) {buffered_word_valid, buffered_word, buffered_pc} <= {1'b1, reader_cur_word_i, reader_next_pc}; - + /* -always_ff @ (posedge clk) begin - // On reset or flush, clear the buffer. - if (reset || flush_f) begin - buffered_word_valid <= 1'b0; - buffered_word <= data_t'(0); - buffered_pc <= addr_t'(0); - end - //If fetch is stalled, we should not consume or overwrite the buffer. - else if (!stall_f) begin - - //This handles the case when the 32 bit word retrieved last after a - //split 32-bit instruction retrieval contains another instruction in - //its upper half that we will need to fetch next. - if (buffer_after_split) begin - buffered_word_valid <= 1'b1; - buffered_word <= imem__data; - buffered_pc <= reader_next_pc; - end - // the buffer is currently being used but we don't need to keep it for the next instruction, invalidate it. - else if (use_buffer) begin - buffered_word_valid <= 1'b0; - end // If the next instruction we need to fetch is in the same word //as the current instruction, buffer it for the next cycle. //this case happens when we have a compressed instruction followed by another //instruction (compressed or the start of a split 32-bit instruction) that starts in //the same 32-bit word - else if (buffer_next_same_word) begin - buffered_word_valid <= 1'b1; - buffered_word <= reader_cur_word_i; - buffered_pc <= reader_next_pc; - end - end -end */ From 4006a107395425d85092accca4f9ac38e6092215 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 14:35:45 -0400 Subject: [PATCH 05/17] Add instruction reader to DE1-SoC synthesis --- envs/de1-soc/quartus/utoss-risc-v.qsf | 1 + 1 file changed, 1 insertion(+) diff --git a/envs/de1-soc/quartus/utoss-risc-v.qsf b/envs/de1-soc/quartus/utoss-risc-v.qsf index 959b58c..21350f8 100755 --- a/envs/de1-soc/quartus/utoss-risc-v.qsf +++ b/envs/de1-soc/quartus/utoss-risc-v.qsf @@ -327,6 +327,7 @@ 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/modules/instruction_reader.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 From 5a4c63d09a64e7bca89798d1c57c6be499c1425c Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 14:36:44 -0400 Subject: [PATCH 06/17] fixed formatting of instruction reader to pass checks --- src/modules/instruction_reader.sv | 53 +++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/modules/instruction_reader.sv b/src/modules/instruction_reader.sv index 42b4fa9..7cde2eb 100644 --- a/src/modules/instruction_reader.sv +++ b/src/modules/instruction_reader.sv @@ -1,6 +1,53 @@ `include "src/timescale.svh" `include "src/headers/params.svh" `include "src/headers/types.svh" + +/* verilator lint_off UNUSEDSIGNAL */ +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 + ); +/* verilator lint_on UNUSEDSIGNAL */ + + 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 + + +/*`include "src/timescale.svh" +`include "src/headers/params.svh" +`include "src/headers/types.svh" //instantiate inside fetch stage // Purpose: @@ -38,7 +85,7 @@ module instruction_reader 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; -always_comb begin + instr_raw_o = instr_t'(0); instr_pc_o = pc_i; @@ -74,7 +121,7 @@ instr_is_compressed_o = 1'b0; instr_is_split_o = 1'b1; // this instruction is split across two words end -end + wire unused = &{ @@ -85,5 +132,5 @@ wire unused = &{ endmodule - +*/ From 478ac6c4ba0675a47f5fe42311e43f0e9a8f972f Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 15:39:24 -0400 Subject: [PATCH 07/17] Clean final svlint formatting issues --- src/modules/instruction_reader.sv | 93 +------------------------------ 1 file changed, 1 insertion(+), 92 deletions(-) diff --git a/src/modules/instruction_reader.sv b/src/modules/instruction_reader.sv index 7cde2eb..5b2b30a 100644 --- a/src/modules/instruction_reader.sv +++ b/src/modules/instruction_reader.sv @@ -42,95 +42,4 @@ module instruction_reader instr_is_split_o ? {next_word_i[15:0], cur_word_i[31:16]} : cur_word_i; -endmodule - - -/*`include "src/timescale.svh" -`include "src/headers/params.svh" -`include "src/headers/types.svh" -//instantiate inside fetch stage - -// Purpose: - -// This module should sit between fetch_stage and instruction memory. -// It should output one complete instruction at a time to the decode stage. -// -// This module reads one complete instruction from synchronous instruction memory. -// -// Cases handled: -// 1. 16-bit compressed instruction starting in lower or upper halfword. -// 2. 32-bit instruction starting at lower halfword, fully inside current word. -// 3. 32-bit instruction starting at upper halfword, split across two words. -// -// Notes: -// - instr_o is always 32 bits for pipeline compatibility. -// - If compressed, instr_o = {16'b0, raw_16_bit_instruction} for now. -// - Decompression happens later in a separate module. - - - -module instruction_reader - ( input addr_t pc_i //current fetch PC from fetch_stage - , input data_t cur_word_i //current 32-bit word read from instruction memory at the aligned PC address - , input data_t next_word_i //next 32-bit word read from instruction memory at the next aligned PC address - , 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 - ); - 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; - - - - instr_raw_o = instr_t'(0); - instr_pc_o = pc_i; - instr_next_pc_o = pc_i; - instr_is_compressed_o = 1'b0; - instr_is_split_o = 1'b0; - -// 1. 16-bit compressed instruction starting in lower or upper halfword. -if(selected_is_32b==1'b0) begin -instr_raw_o = {16'b0, selected_halfword}; // output the 16-bit instruction in the lower half of instr_raw_o, upper half is zero-padded -instr_pc_o = pc_i; -instr_next_pc_o = pc_i + 32'd2; // next PC is current PC + 2 -instr_is_compressed_o = 1'b1; -instr_is_split_o = 1'b0; -end - -// 2. 32-bit instruction starting at lower halfword, fully inside current word. -else if(pc_i[1] == 1'b0) begin -instr_raw_o = cur_word_i; // the whole 32-bit instruction is in the current word -instr_pc_o = pc_i; -instr_next_pc_o = pc_i + 32'd4; // next PC is current PC + 4 -instr_is_compressed_o = 1'b0; -instr_is_split_o = 1'b0; -end - - -// 3. 32-bit instruction starting at upper halfword, split across two words. -else begin -instr_raw_o = {next_word_i[15:0], cur_word_i[31:16]}; // combine the upper half of the current word and the lower half of the next word -instr_pc_o = pc_i; -instr_next_pc_o = pc_i + 32'd4; // next PC is current PC + 4 -instr_is_compressed_o = 1'b0; -instr_is_split_o = 1'b1; // this instruction is split across two words -end - - - - -wire unused = &{ - next_word_i[31:16] -}; - - -endmodule - - -*/ - +endmodule \ No newline at end of file From 5c83bc40033dcfa49f2c7b569832fa7babde8857 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 15:45:20 -0400 Subject: [PATCH 08/17] Update instruction_reader.sv --- src/modules/instruction_reader.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/instruction_reader.sv b/src/modules/instruction_reader.sv index 5b2b30a..8bb6ab6 100644 --- a/src/modules/instruction_reader.sv +++ b/src/modules/instruction_reader.sv @@ -42,4 +42,4 @@ module instruction_reader instr_is_split_o ? {next_word_i[15:0], cur_word_i[31:16]} : cur_word_i; -endmodule \ No newline at end of file +endmodule From 166b3d16a15f63fabd17fecda67598db63019199 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 15:56:23 -0400 Subject: [PATCH 09/17] Fix final lint whitespace issues --- src/modules/instruction_reader.sv | 11 ----- src/stages/fetch_stage.sv | 68 ------------------------------- 2 files changed, 79 deletions(-) diff --git a/src/modules/instruction_reader.sv b/src/modules/instruction_reader.sv index 8bb6ab6..e618751 100644 --- a/src/modules/instruction_reader.sv +++ b/src/modules/instruction_reader.sv @@ -1,13 +1,11 @@ `include "src/timescale.svh" `include "src/headers/params.svh" `include "src/headers/types.svh" - /* verilator lint_off UNUSEDSIGNAL */ 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 @@ -15,31 +13,22 @@ module instruction_reader , output logic instr_is_split_o ); /* verilator lint_on UNUSEDSIGNAL */ - 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/stages/fetch_stage.sv b/src/stages/fetch_stage.sv index 4d26c95..785b42a 100644 --- a/src/stages/fetch_stage.sv +++ b/src/stages/fetch_stage.sv @@ -3,16 +3,13 @@ `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 - //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 @@ -39,14 +36,10 @@ 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; @@ -54,18 +47,14 @@ module fetch_stage PC_SRC__ALU_RESULT: pc_next = ex_to_if.pc_target; default: pc_next = 32'hx; endcase - // 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,37 +64,27 @@ module fetch_stage {stalled_instr, stalled_instr_valid} <= {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; - assign imem__address = {pc_cur[31:2], 2'b00}; - - 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; - //state bit to indicate : currently waiting to finish a split instruction logic split_wait_valid; data_t split_wait_cur_word; addr_t split_wait_pc; - logic need_split_wait; logic buffer_after_split; logic hold_pc_for_split_buffer; - //a safety instruction that doesnt do anything //outputted when we need to wait an extra cycle to fetch a split 32-bit instruction localparam instr_t NOP = 32'h00000013; - //for buffer for when two compressed instructions are back to back, // and the second one is in the same 32-bit word as the first one, //so we can just buffer the whole word and then read the second instruction @@ -116,21 +95,16 @@ logic buffered_word_valid; data_t buffered_word; addr_t buffered_pc; // buffered_pc tells the reader which halfword inside buffered_word to use. - 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 }; - - instruction_reader u_reader ( .pc_i (reader_pc_i) , .cur_word_i (reader_cur_word_i) @@ -141,28 +115,20 @@ instruction_reader u_reader , .instr_is_compressed_o (reader_inst_is_compressed) , .instr_is_split_o (reader_inst_is_split) ); - // TODO: Replace with real compressed decoder once C decompressor module is merged. // For now, compressed instructions are converted to NOP so fetch-stage integration builds. 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} @@ -173,8 +139,6 @@ always_ff @ (posedge clk) else if (need_split_wait) {split_wait_valid, split_wait_cur_word, split_wait_pc} <= {1'b1, reader_cur_word_i, reader_instr_pc}; - - always_ff @ (posedge clk) if (reset || flush_f) {buffered_word_valid, buffered_word, buffered_pc} @@ -188,81 +152,49 @@ always_ff @ (posedge clk) else if (buffer_next_same_word) {buffered_word_valid, buffered_word, buffered_pc} <= {1'b1, reader_cur_word_i, reader_next_pc}; - - /* - // If the next instruction we need to fetch is in the same word //as the current instruction, buffer it for the next cycle. //this case happens when we have a compressed instruction followed by another //instruction (compressed or the start of a split 32-bit instruction) that starts in //the same 32-bit word - */ - - 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 || 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]); - - //Only insert the wait bubble the FIRST time you discover the split. //Do not insert another bubble when you are already completing it. assign need_split_wait = reader_inst_is_split && !split_wait_valid; - - //I just completed a split instruction, and the next instruction starts inside //the next word that just arrived. Save that word for the next cycle. assign buffer_after_split = split_wait_valid && reader_inst_is_split && (reader_next_pc[31:2] == (reader_instr_pc[31:2] + 30'd1)); - //prevents the memory fetch PC from advancing too far while you //consume the buffered instruction 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; - //final output to decode stage 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 - - - - - - - From 4d9b0cf1a1918d25ab1a97c9649b71217e234d1b Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 16:06:27 -0400 Subject: [PATCH 10/17] Fix final fetch stage indentation --- src/stages/fetch_stage.sv | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stages/fetch_stage.sv b/src/stages/fetch_stage.sv index 785b42a..a61bc0f 100644 --- a/src/stages/fetch_stage.sv +++ b/src/stages/fetch_stage.sv @@ -39,7 +39,8 @@ module fetch_stage addr_t pc_prev; addr_t pc_cur; addr_t pc_next; - // RVC/C-extension fetch helpers. + // RVC/C-extension fetch helpers. + always_comb case (ex_to_if.pc_src) // PC_SRC__INCREMENT: pc_next = pc_cur + 32'h4; @@ -163,7 +164,7 @@ assign use_buffer = buffered_word_valid; assign reader_pc_i = split_wait_valid ? split_wait_pc : use_buffer ? buffered_pc : - pc_prev; + pc_prev; assign reader_cur_word_i = split_wait_valid ? split_wait_cur_word : use_buffer ? buffered_word : From f26e3b1a55978018c68a0f3eef9e8d9957a848ae Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sat, 20 Jun 2026 16:25:42 -0400 Subject: [PATCH 11/17] Add passing RVC fetch testbenches --- test/fetch_rvc_01_aligned_32_regression_tb.sv | 212 +++++++++++++++++ ...etch_rvc_02_two_compressed_same_word_tb.sv | 211 +++++++++++++++++ test/fetch_rvc_03_compressed_then_split_tb.sv | 213 +++++++++++++++++ test/fetch_rvc_04_back_to_back_split_tb.sv | 216 ++++++++++++++++++ .../fetch_rvc_05_branch_to_split_target_tb.sv | 214 +++++++++++++++++ ...ch_rvc_06_branch_to_lower_compressed_tb.sv | 213 +++++++++++++++++ ...ch_rvc_07_branch_to_upper_compressed_tb.sv | 213 +++++++++++++++++ test/fetch_rvc_08_branch_to_aligned_32_tb.sv | 214 +++++++++++++++++ ...ch_rvc_13_compressed_then_aligned_32_tb.sv | 211 +++++++++++++++++ test/fetch_rvc_15_compressed_pc_plus_2_tb.sv | 209 +++++++++++++++++ 10 files changed, 2126 insertions(+) create mode 100644 test/fetch_rvc_01_aligned_32_regression_tb.sv create mode 100644 test/fetch_rvc_02_two_compressed_same_word_tb.sv create mode 100644 test/fetch_rvc_03_compressed_then_split_tb.sv create mode 100644 test/fetch_rvc_04_back_to_back_split_tb.sv create mode 100644 test/fetch_rvc_05_branch_to_split_target_tb.sv create mode 100644 test/fetch_rvc_06_branch_to_lower_compressed_tb.sv create mode 100644 test/fetch_rvc_07_branch_to_upper_compressed_tb.sv create mode 100644 test/fetch_rvc_08_branch_to_aligned_32_tb.sv create mode 100644 test/fetch_rvc_13_compressed_then_aligned_32_tb.sv create mode 100644 test/fetch_rvc_15_compressed_pc_plus_2_tb.sv 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..f954c2b --- /dev/null +++ b/test/fetch_rvc_01_aligned_32_regression_tb.sv @@ -0,0 +1,212 @@ +`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; + 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) + imem[i] = NOP; + 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..0037420 --- /dev/null +++ b/test/fetch_rvc_02_two_compressed_same_word_tb.sv @@ -0,0 +1,211 @@ +`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; + 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) + imem[i] = NOP; + 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..32b1bee --- /dev/null +++ b/test/fetch_rvc_03_compressed_then_split_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_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; + 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) + imem[i] = NOP; + 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..a065a63 --- /dev/null +++ b/test/fetch_rvc_04_back_to_back_split_tb.sv @@ -0,0 +1,216 @@ +`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; + 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) + imem[i] = NOP; + 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..de525a9 --- /dev/null +++ b/test/fetch_rvc_05_branch_to_split_target_tb.sv @@ -0,0 +1,214 @@ +`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; + 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) + imem[i] = NOP; + 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..fac9c95 --- /dev/null +++ b/test/fetch_rvc_06_branch_to_lower_compressed_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_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; + 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) + imem[i] = NOP; + 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..3c38552 --- /dev/null +++ b/test/fetch_rvc_07_branch_to_upper_compressed_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_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; + 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) + imem[i] = NOP; + 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..77204e1 --- /dev/null +++ b/test/fetch_rvc_08_branch_to_aligned_32_tb.sv @@ -0,0 +1,214 @@ +`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; + 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) + imem[i] = NOP; + 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_13_compressed_then_aligned_32_tb.sv b/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv new file mode 100644 index 0000000..60a457e --- /dev/null +++ b/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv @@ -0,0 +1,211 @@ +`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; + 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) + imem[i] = NOP; + 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..7f20dc8 --- /dev/null +++ b/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv @@ -0,0 +1,209 @@ +`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; + 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) + imem[i] = NOP; + 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 From 4aec683d6a6be03f62beee2a6991d5d5bc57e1a6 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sun, 21 Jun 2026 02:55:02 -0400 Subject: [PATCH 12/17] Style-fix RVC fetch testbenches --- src/headers/params.svh | 40 ++++----- test/fetch_rvc_01_aligned_32_regression_tb.sv | 72 +++++++++++----- ...etch_rvc_02_two_compressed_same_word_tb.sv | 70 ++++++++++++---- test/fetch_rvc_03_compressed_then_split_tb.sv | 78 +++++++++++++----- test/fetch_rvc_04_back_to_back_split_tb.sv | 82 +++++++++++++------ .../fetch_rvc_05_branch_to_split_target_tb.sv | 76 ++++++++++++----- ...ch_rvc_06_branch_to_lower_compressed_tb.sv | 70 ++++++++++++---- ...ch_rvc_07_branch_to_upper_compressed_tb.sv | 70 ++++++++++++---- test/fetch_rvc_08_branch_to_aligned_32_tb.sv | 70 ++++++++++++---- ...ch_rvc_13_compressed_then_aligned_32_tb.sv | 76 ++++++++++++----- test/fetch_rvc_15_compressed_pc_plus_2_tb.sv | 70 ++++++++++++---- 11 files changed, 557 insertions(+), 217 deletions(-) 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/test/fetch_rvc_01_aligned_32_regression_tb.sv b/test/fetch_rvc_01_aligned_32_regression_tb.sv index f954c2b..92911f4 100644 --- a/test/fetch_rvc_01_aligned_32_regression_tb.sv +++ b/test/fetch_rvc_01_aligned_32_regression_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_01_aligned_32_regression_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_01_aligned_32_regression_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_01_aligned_32_regression_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_01_aligned_32_regression_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_01_aligned_32_regression_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_01_aligned_32_regression_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_01_aligned_32_regression_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_01_aligned_32_regression_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_01_aligned_32_regression_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_01_aligned_32_regression_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end @@ -202,7 +236,7 @@ module fetch_rvc_01_aligned_32_regression_tb; 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"); + wait_fetch(32'h00000008, NOP, "aligned NOP at PC 0x08"); $finish; end diff --git a/test/fetch_rvc_02_two_compressed_same_word_tb.sv b/test/fetch_rvc_02_two_compressed_same_word_tb.sv index 0037420..55455b9 100644 --- a/test/fetch_rvc_02_two_compressed_same_word_tb.sv +++ b/test/fetch_rvc_02_two_compressed_same_word_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_02_two_compressed_same_word_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_02_two_compressed_same_word_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_02_two_compressed_same_word_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_02_two_compressed_same_word_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_02_two_compressed_same_word_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_02_two_compressed_same_word_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_02_two_compressed_same_word_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_02_two_compressed_same_word_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_02_two_compressed_same_word_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_02_two_compressed_same_word_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end diff --git a/test/fetch_rvc_03_compressed_then_split_tb.sv b/test/fetch_rvc_03_compressed_then_split_tb.sv index 32b1bee..97a9ab4 100644 --- a/test/fetch_rvc_03_compressed_then_split_tb.sv +++ b/test/fetch_rvc_03_compressed_then_split_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_03_compressed_then_split_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_03_compressed_then_split_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_03_compressed_then_split_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_03_compressed_then_split_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_03_compressed_then_split_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_03_compressed_then_split_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_03_compressed_then_split_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_03_compressed_then_split_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_03_compressed_then_split_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_03_compressed_then_split_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end @@ -200,10 +234,10 @@ module fetch_rvc_03_compressed_then_split_tb; 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"); + 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 diff --git a/test/fetch_rvc_04_back_to_back_split_tb.sv b/test/fetch_rvc_04_back_to_back_split_tb.sv index a065a63..118244d 100644 --- a/test/fetch_rvc_04_back_to_back_split_tb.sv +++ b/test/fetch_rvc_04_back_to_back_split_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_04_back_to_back_split_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_04_back_to_back_split_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_04_back_to_back_split_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_04_back_to_back_split_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_04_back_to_back_split_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_04_back_to_back_split_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_04_back_to_back_split_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_04_back_to_back_split_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_04_back_to_back_split_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_04_back_to_back_split_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end @@ -201,12 +235,12 @@ module fetch_rvc_04_back_to_back_split_tb; 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"); + 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 diff --git a/test/fetch_rvc_05_branch_to_split_target_tb.sv b/test/fetch_rvc_05_branch_to_split_target_tb.sv index de525a9..bc68975 100644 --- a/test/fetch_rvc_05_branch_to_split_target_tb.sv +++ b/test/fetch_rvc_05_branch_to_split_target_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_05_branch_to_split_target_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_05_branch_to_split_target_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_05_branch_to_split_target_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_05_branch_to_split_target_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_05_branch_to_split_target_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_05_branch_to_split_target_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_05_branch_to_split_target_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_05_branch_to_split_target_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_05_branch_to_split_target_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_05_branch_to_split_target_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end @@ -202,9 +236,9 @@ module fetch_rvc_05_branch_to_split_target_tb; 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"); + 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 diff --git a/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv b/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv index fac9c95..d76a688 100644 --- a/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv +++ b/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end diff --git a/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv b/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv index 3c38552..dff6124 100644 --- a/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv +++ b/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end diff --git a/test/fetch_rvc_08_branch_to_aligned_32_tb.sv b/test/fetch_rvc_08_branch_to_aligned_32_tb.sv index 77204e1..d193869 100644 --- a/test/fetch_rvc_08_branch_to_aligned_32_tb.sv +++ b/test/fetch_rvc_08_branch_to_aligned_32_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_08_branch_to_aligned_32_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_08_branch_to_aligned_32_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_08_branch_to_aligned_32_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_08_branch_to_aligned_32_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_08_branch_to_aligned_32_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_08_branch_to_aligned_32_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_08_branch_to_aligned_32_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_08_branch_to_aligned_32_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_08_branch_to_aligned_32_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_08_branch_to_aligned_32_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end diff --git a/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv b/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv index 60a457e..9f55f5f 100644 --- a/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv +++ b/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end @@ -199,9 +233,9 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; 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"); + 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 diff --git a/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv b/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv index 7f20dc8..0fe0c34 100644 --- a/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv +++ b/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv @@ -63,8 +63,10 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; imem__data = data_t'(0); - for (i = 0; i < 256; i = i + 1) + for (i = 0; i < 256; i = i + 1) begin imem[i] = NOP; + + end end endtask @@ -92,7 +94,7 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; task wait_fetch; input addr_t exp_pc; input instr_t exp_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -101,8 +103,12 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; 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); + $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 @@ -112,8 +118,14 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; 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); + $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 @@ -122,7 +134,7 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; task wait_pc_plus; input addr_t exp_pc; input addr_t exp_next_pc; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -131,8 +143,12 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; 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); + $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 @@ -142,8 +158,14 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; 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); + $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 @@ -154,7 +176,7 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; input instr_t target_instr; input addr_t forbidden_pc; input instr_t forbidden_instr; - input [8*80-1:0] label; + input [8 * 80 - 1:0] label; integer n; reg seen; begin @@ -163,16 +185,24 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; 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); + $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); + $display + ( "[pass] %0s target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); seen = 1'b1; n = 50; end @@ -182,8 +212,12 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; end if (!seen) begin - $display("[fail] %0s did not reach target pc=0x%08h instr=0x%08h", - label, target_pc, target_instr); + $display + ( "[fail] %0s did not reach target pc=0x%08h instr=0x%08h" + , label + , target_pc + , target_instr + ); $fatal(1); end end From fc6db13066357abbee125dd2719f7f86cbcfcec3 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sun, 21 Jun 2026 03:04:23 -0400 Subject: [PATCH 13/17] Fix RVC fetch testbench lint warnings --- test/fetch_rvc_01_aligned_32_regression_tb.sv | 3 +++ test/fetch_rvc_02_two_compressed_same_word_tb.sv | 3 +++ test/fetch_rvc_03_compressed_then_split_tb.sv | 3 +++ test/fetch_rvc_04_back_to_back_split_tb.sv | 3 +++ test/fetch_rvc_05_branch_to_split_target_tb.sv | 3 +++ test/fetch_rvc_06_branch_to_lower_compressed_tb.sv | 3 +++ test/fetch_rvc_07_branch_to_upper_compressed_tb.sv | 3 +++ test/fetch_rvc_08_branch_to_aligned_32_tb.sv | 3 +++ test/fetch_rvc_13_compressed_then_aligned_32_tb.sv | 3 +++ test/fetch_rvc_15_compressed_pc_plus_2_tb.sv | 3 +++ 10 files changed, 30 insertions(+) diff --git a/test/fetch_rvc_01_aligned_32_regression_tb.sv b/test/fetch_rvc_01_aligned_32_regression_tb.sv index 92911f4..be90bb9 100644 --- a/test/fetch_rvc_01_aligned_32_regression_tb.sv +++ b/test/fetch_rvc_01_aligned_32_regression_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_01_aligned_32_regression_tb; 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]; diff --git a/test/fetch_rvc_02_two_compressed_same_word_tb.sv b/test/fetch_rvc_02_two_compressed_same_word_tb.sv index 55455b9..6a1c142 100644 --- a/test/fetch_rvc_02_two_compressed_same_word_tb.sv +++ b/test/fetch_rvc_02_two_compressed_same_word_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_02_two_compressed_same_word_tb; 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]; diff --git a/test/fetch_rvc_03_compressed_then_split_tb.sv b/test/fetch_rvc_03_compressed_then_split_tb.sv index 97a9ab4..93e3e31 100644 --- a/test/fetch_rvc_03_compressed_then_split_tb.sv +++ b/test/fetch_rvc_03_compressed_then_split_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_03_compressed_then_split_tb; 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]; diff --git a/test/fetch_rvc_04_back_to_back_split_tb.sv b/test/fetch_rvc_04_back_to_back_split_tb.sv index 118244d..191ee3d 100644 --- a/test/fetch_rvc_04_back_to_back_split_tb.sv +++ b/test/fetch_rvc_04_back_to_back_split_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_04_back_to_back_split_tb; 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]; diff --git a/test/fetch_rvc_05_branch_to_split_target_tb.sv b/test/fetch_rvc_05_branch_to_split_target_tb.sv index bc68975..3e1d97a 100644 --- a/test/fetch_rvc_05_branch_to_split_target_tb.sv +++ b/test/fetch_rvc_05_branch_to_split_target_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_05_branch_to_split_target_tb; 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]; diff --git a/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv b/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv index d76a688..fa13091 100644 --- a/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv +++ b/test/fetch_rvc_06_branch_to_lower_compressed_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_06_branch_to_lower_compressed_tb; 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]; diff --git a/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv b/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv index dff6124..62b7602 100644 --- a/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv +++ b/test/fetch_rvc_07_branch_to_upper_compressed_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_07_branch_to_upper_compressed_tb; 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]; diff --git a/test/fetch_rvc_08_branch_to_aligned_32_tb.sv b/test/fetch_rvc_08_branch_to_aligned_32_tb.sv index d193869..2195410 100644 --- a/test/fetch_rvc_08_branch_to_aligned_32_tb.sv +++ b/test/fetch_rvc_08_branch_to_aligned_32_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_08_branch_to_aligned_32_tb; 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]; diff --git a/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv b/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv index 9f55f5f..1b8df0c 100644 --- a/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv +++ b/test/fetch_rvc_13_compressed_then_aligned_32_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_13_compressed_then_aligned_32_tb; 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]; diff --git a/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv b/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv index 0fe0c34..daea864 100644 --- a/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv +++ b/test/fetch_rvc_15_compressed_pc_plus_2_tb.sv @@ -17,6 +17,9 @@ module fetch_rvc_15_compressed_pc_plus_2_tb; 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]; From 958a4f1cde49b1f16ba45a4f91e664419bf3cf77 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Sun, 21 Jun 2026 14:56:10 -0400 Subject: [PATCH 14/17] Move instruction reader to ext flder and remove lint UNUSED SIGNAL comment --- envs/de1-soc/quartus/utoss-risc-v.qsf | 2 +- src/{modules => ext/c}/instruction_reader.sv | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) rename src/{modules => ext/c}/instruction_reader.sv (93%) diff --git a/envs/de1-soc/quartus/utoss-risc-v.qsf b/envs/de1-soc/quartus/utoss-risc-v.qsf index 21350f8..cc275bc 100755 --- a/envs/de1-soc/quartus/utoss-risc-v.qsf +++ b/envs/de1-soc/quartus/utoss-risc-v.qsf @@ -327,7 +327,7 @@ 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/modules/instruction_reader.sv +set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/ext/c/instruction_reader.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/modules/instruction_reader.sv b/src/ext/c/instruction_reader.sv similarity index 93% rename from src/modules/instruction_reader.sv rename to src/ext/c/instruction_reader.sv index e618751..9a53916 100644 --- a/src/modules/instruction_reader.sv +++ b/src/ext/c/instruction_reader.sv @@ -1,7 +1,7 @@ `include "src/timescale.svh" `include "src/headers/params.svh" `include "src/headers/types.svh" -/* verilator lint_off UNUSEDSIGNAL */ + module instruction_reader ( input addr_t pc_i , input data_t cur_word_i @@ -12,7 +12,9 @@ module instruction_reader , output logic instr_is_compressed_o , output logic instr_is_split_o ); -/* verilator lint_on UNUSEDSIGNAL */ + + wire unused_next_word = + &{1'b0, next_word_i[31:16]}; logic [15:0] selected_halfword; logic selected_is_32b; assign selected_halfword = From 6003888cdd9b27d0c41876ad6ebbf1f7a12ec1b5 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Mon, 22 Jun 2026 02:45:34 -0400 Subject: [PATCH 15/17] Gate compressed fetch behind C extension --- Makefile | 2 +- envs/de1-soc/quartus/utoss-risc-v.qsf | 1 + src/ext/c/fetch_compressed.sv | 182 ++++++++++++++++++++++++++ src/stages/fetch_stage.sv | 172 ++++++------------------ 4 files changed, 222 insertions(+), 135 deletions(-) create mode 100644 src/ext/c/fetch_compressed.sv diff --git a/Makefile b/Makefile index 61ccbbf..3a47c06 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 # =========================== diff --git a/envs/de1-soc/quartus/utoss-risc-v.qsf b/envs/de1-soc/quartus/utoss-risc-v.qsf index cc275bc..0d8a0ac 100755 --- a/envs/de1-soc/quartus/utoss-risc-v.qsf +++ b/envs/de1-soc/quartus/utoss-risc-v.qsf @@ -328,6 +328,7 @@ set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/modules/Instruction_ 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..e970afc --- /dev/null +++ b/src/ext/c/fetch_compressed.sv @@ -0,0 +1,182 @@ +`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; + // RVC/C-extension fetch helpers. + + 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; +//state bit to indicate : currently waiting to finish a split instruction +logic split_wait_valid; +data_t split_wait_cur_word; +addr_t split_wait_pc; +logic need_split_wait; +logic buffer_after_split; +logic hold_pc_for_split_buffer; +//a safety instruction that doesnt do anything +//outputted when we need to wait an extra cycle to fetch a split 32-bit instruction +localparam instr_t NOP = 32'h00000013; +//for buffer for when two compressed instructions are back to back, +// and the second one is in the same 32-bit word as the first one, +//so we can just buffer the whole word and then read the second instruction +//from it without needing to fetch again from memory. +//This is an optimization to avoid unnecessary memory accesses for +//consecutive compressed instructions that happen to be in the same word. +logic buffered_word_valid; +data_t buffered_word; +addr_t buffered_pc; +// buffered_pc tells the reader which halfword inside buffered_word to use. +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 + 32'h4; + 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} <= {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; +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) + ); +// TODO: Replace with real compressed decoder once C decompressor module is merged. +// For now, compressed instructions are converted to NOP so fetch-stage integration builds. +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} + <= {1'b0, data_t'(0), addr_t'(0)}; + else if (!stall_f) + if (split_wait_valid) + split_wait_valid <= 1'b0; + else if (need_split_wait) + {split_wait_valid, split_wait_cur_word, split_wait_pc} + <= {1'b1, reader_cur_word_i, reader_instr_pc}; +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) + if (buffer_after_split) + {buffered_word_valid, buffered_word, buffered_pc} + <= {1'b1, imem__data, reader_next_pc}; + else if (use_buffer) + buffered_word_valid <= 1'b0; + else if (buffer_next_same_word) + {buffered_word_valid, buffered_word, buffered_pc} + <= {1'b1, reader_cur_word_i, reader_next_pc}; +/* + // If the next instruction we need to fetch is in the same word + //as the current instruction, buffer it for the next cycle. + //this case happens when we have a compressed instruction followed by another + //instruction (compressed or the start of a split 32-bit instruction) that starts in + //the same 32-bit word +*/ +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 || 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]); +//Only insert the wait bubble the FIRST time you discover the split. +//Do not insert another bubble when you are already completing it. +assign need_split_wait = + reader_inst_is_split && !split_wait_valid; +//I just completed a split instruction, and the next instruction starts inside +//the next word that just arrived. Save that word for the next cycle. +assign buffer_after_split = + split_wait_valid && + reader_inst_is_split && + (reader_next_pc[31:2] == (reader_instr_pc[31:2] + 30'd1)); +//prevents the memory fetch PC from advancing too far while you +//consume the buffered instruction +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; +//final output to decode stage +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/stages/fetch_stage.sv b/src/stages/fetch_stage.sv index a61bc0f..4cb6ef2 100644 --- a/src/stages/fetch_stage.sv +++ b/src/stages/fetch_stage.sv @@ -14,6 +14,23 @@ module fetch_stage , 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 @@ -44,7 +61,7 @@ module fetch_stage always_comb case (ex_to_if.pc_src) // PC_SRC__INCREMENT: pc_next = pc_cur + 32'h4; - PC_SRC__INCREMENT: pc_next = {pc_cur[31:2], 2'b00} + 32'd4; + 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 @@ -54,6 +71,7 @@ module fetch_stage // 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) @@ -65,137 +83,23 @@ module fetch_stage {stalled_instr, stalled_instr_valid} <= {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; -assign imem__address = {pc_cur[31:2], 2'b00}; -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; -//state bit to indicate : currently waiting to finish a split instruction -logic split_wait_valid; -data_t split_wait_cur_word; -addr_t split_wait_pc; -logic need_split_wait; -logic buffer_after_split; -logic hold_pc_for_split_buffer; -//a safety instruction that doesnt do anything -//outputted when we need to wait an extra cycle to fetch a split 32-bit instruction -localparam instr_t NOP = 32'h00000013; -//for buffer for when two compressed instructions are back to back, -// and the second one is in the same 32-bit word as the first one, -//so we can just buffer the whole word and then read the second instruction -//from it without needing to fetch again from memory. -//This is an optimization to avoid unnecessary memory accesses for -//consecutive compressed instructions that happen to be in the same word. -logic buffered_word_valid; -data_t buffered_word; -addr_t buffered_pc; -// buffered_pc tells the reader which halfword inside buffered_word to use. -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 -}; -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) - ); -// TODO: Replace with real compressed decoder once C decompressor module is merged. -// For now, compressed instructions are converted to NOP so fetch-stage integration builds. -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} - <= {1'b0, data_t'(0), addr_t'(0)}; - else if (!stall_f) - if (split_wait_valid) - split_wait_valid <= 1'b0; - else if (need_split_wait) - {split_wait_valid, split_wait_cur_word, split_wait_pc} - <= {1'b1, reader_cur_word_i, reader_instr_pc}; -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) - if (buffer_after_split) - {buffered_word_valid, buffered_word, buffered_pc} - <= {1'b1, imem__data, reader_next_pc}; - else if (use_buffer) - buffered_word_valid <= 1'b0; - else if (buffer_next_same_word) - {buffered_word_valid, buffered_word, buffered_pc} - <= {1'b1, reader_cur_word_i, reader_next_pc}; -/* - // If the next instruction we need to fetch is in the same word - //as the current instruction, buffer it for the next cycle. - //this case happens when we have a compressed instruction followed by another - //instruction (compressed or the start of a split 32-bit instruction) that starts in - //the same 32-bit word -*/ -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 || 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]); -//Only insert the wait bubble the FIRST time you discover the split. -//Do not insert another bubble when you are already completing it. -assign need_split_wait = - reader_inst_is_split && !split_wait_valid; -//I just completed a split instruction, and the next instruction starts inside -//the next word that just arrived. Save that word for the next cycle. -assign buffer_after_split = - split_wait_valid && - reader_inst_is_split && - (reader_next_pc[31:2] == (reader_instr_pc[31:2] + 30'd1)); -//prevents the memory fetch PC from advancing too far while you -//consume the buffered instruction -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; -//final output to decode stage -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; + +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; + wire unused = &{ex_to_if.pc_old, ex_to_if.imm_ext}; + +`endif + + endmodule From 089bec821616b32ac142f0091d30f4d20b48204d Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Mon, 22 Jun 2026 03:06:57 -0400 Subject: [PATCH 16/17] Enable C extension for RVC fetch tests --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3a47c06..92da23e 100644 --- a/Makefile +++ b/Makefile @@ -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 $@)) \ From 0ea56af918fb6567c41f5e3e9e725485f352eb04 Mon Sep 17 00:00:00 2001 From: SiddharthShroff Date: Mon, 22 Jun 2026 16:31:19 -0400 Subject: [PATCH 17/17] Fix RVC split fetch handling under stall and flush --- src/ext/c/fetch_compressed.sv | 290 ++++++++++-------- ...etch_rvc_09_flush_while_buffer_valid_tb.sv | 259 ++++++++++++++++ ...fetch_rvc_10_flush_during_split_wait_tb.sv | 262 ++++++++++++++++ ...etch_rvc_11_stall_while_buffer_valid_tb.sv | 194 ++++++++++++ ...fetch_rvc_12_stall_during_split_wait_tb.sv | 213 +++++++++++++ 5 files changed, 1083 insertions(+), 135 deletions(-) create mode 100644 test/fetch_rvc_09_flush_while_buffer_valid_tb.sv create mode 100644 test/fetch_rvc_10_flush_during_split_wait_tb.sv create mode 100644 test/fetch_rvc_11_stall_while_buffer_valid_tb.sv create mode 100644 test/fetch_rvc_12_stall_during_split_wait_tb.sv diff --git a/src/ext/c/fetch_compressed.sv b/src/ext/c/fetch_compressed.sv index e970afc..a258f98 100644 --- a/src/ext/c/fetch_compressed.sv +++ b/src/ext/c/fetch_compressed.sv @@ -3,6 +3,7 @@ `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 @@ -14,58 +15,55 @@ module fetch_compressed , input data_t imem__data ); -addr_t pc_prev; + addr_t pc_prev; addr_t pc_cur; addr_t pc_next; - // RVC/C-extension fetch helpers. 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; -//state bit to indicate : currently waiting to finish a split instruction -logic split_wait_valid; -data_t split_wait_cur_word; -addr_t split_wait_pc; -logic need_split_wait; -logic buffer_after_split; -logic hold_pc_for_split_buffer; -//a safety instruction that doesnt do anything -//outputted when we need to wait an extra cycle to fetch a split 32-bit instruction -localparam instr_t NOP = 32'h00000013; -//for buffer for when two compressed instructions are back to back, -// and the second one is in the same 32-bit word as the first one, -//so we can just buffer the whole word and then read the second instruction -//from it without needing to fetch again from memory. -//This is an optimization to avoid unnecessary memory accesses for -//consecutive compressed instructions that happen to be in the same word. -logic buffered_word_valid; -data_t buffered_word; -addr_t buffered_pc; -// buffered_pc tells the reader which halfword inside buffered_word to use. -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 -}; + 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 + 32'h4; 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; @@ -73,110 +71,132 @@ wire unused = &{ always_ff @ (posedge clk) if (reset) - {stalled_instr, stalled_instr_valid} <= {instr_t'(0) , 1'b0}; + {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} <= {imem__data , 1'b1}; + {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}; - //assign imem__address = pc_cur; -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) + ); -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) - ); -// TODO: Replace with real compressed decoder once C decompressor module is merged. -// For now, compressed instructions are converted to NOP so fetch-stage integration builds. -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} - <= {1'b0, data_t'(0), addr_t'(0)}; - else if (!stall_f) - if (split_wait_valid) - split_wait_valid <= 1'b0; - else if (need_split_wait) - {split_wait_valid, split_wait_cur_word, split_wait_pc} - <= {1'b1, reader_cur_word_i, reader_instr_pc}; -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) - if (buffer_after_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, imem__data, reader_next_pc}; - else if (use_buffer) + <= {1'b1, reader_next_word, reader_next_pc}; + + else if (!stall_f && use_buffer) buffered_word_valid <= 1'b0; - else if (buffer_next_same_word) + + else if (!stall_f && buffer_next_same_word) {buffered_word_valid, buffered_word, buffered_pc} <= {1'b1, reader_cur_word_i, reader_next_pc}; -/* - // If the next instruction we need to fetch is in the same word - //as the current instruction, buffer it for the next cycle. - //this case happens when we have a compressed instruction followed by another - //instruction (compressed or the start of a split 32-bit instruction) that starts in - //the same 32-bit word -*/ -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 || 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]); -//Only insert the wait bubble the FIRST time you discover the split. -//Do not insert another bubble when you are already completing it. -assign need_split_wait = - reader_inst_is_split && !split_wait_valid; -//I just completed a split instruction, and the next instruction starts inside -//the next word that just arrived. Save that word for the next cycle. -assign buffer_after_split = - split_wait_valid && - reader_inst_is_split && - (reader_next_pc[31:2] == (reader_instr_pc[31:2] + 30'd1)); -//prevents the memory fetch PC from advancing too far while you -//consume the buffered instruction -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; -//final output to decode stage -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; + 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/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