Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

# ===========================
Expand Down Expand Up @@ -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 $@)) \
Expand Down
2 changes: 2 additions & 0 deletions envs/de1-soc/quartus/utoss-risc-v.qsf
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/modules/Instruction_
set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/modules/Instruction_Decode/MemoryLoader.sv
set_global_assignment -name VERILOG_FILE ../../../src/modules/Instruction_Decode/registerFile.v
set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/modules/ControlFSM.sv
set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/ext/c/instruction_reader.sv
set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/ext/c/fetch_compressed.sv
set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/packages/pkg_control_fsm.svh
set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/stages/decode_stage.sv
set_global_assignment -name SYSTEMVERILOG_FILE ../../../src/stages/execute_stage.sv
Expand Down
182 changes: 182 additions & 0 deletions src/ext/c/fetch_compressed.sv
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions src/ext/c/instruction_reader.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
`include "src/timescale.svh"
Comment thread
SiddharthShroff-dotcom marked this conversation as resolved.
`include "src/headers/params.svh"
`include "src/headers/types.svh"

module instruction_reader
( input addr_t pc_i
, input data_t cur_word_i
, input data_t next_word_i
, output instr_t instr_raw_o
, output addr_t instr_pc_o
, output addr_t instr_next_pc_o
, output logic instr_is_compressed_o
, output logic instr_is_split_o
);

wire unused_next_word =
&{1'b0, next_word_i[31:16]};
logic [15:0] selected_halfword;
logic selected_is_32b;
assign selected_halfword =
pc_i[1] ? cur_word_i[31:16] : cur_word_i[15:0];
assign selected_is_32b =
selected_halfword[1:0] == 2'b11;
assign instr_pc_o =
pc_i;
assign instr_is_compressed_o =
!selected_is_32b;
assign instr_is_split_o =
selected_is_32b && pc_i[1];
assign instr_next_pc_o =
pc_i + (instr_is_compressed_o ? 32'd2 : 32'd4);
assign instr_raw_o =
instr_is_compressed_o ? {16'b0, selected_halfword} :
instr_is_split_o ? {next_word_i[15:0], cur_word_i[31:16]} :
cur_word_i;
endmodule
40 changes: 20 additions & 20 deletions src/headers/params.svh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading
Loading