mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-24 14:09:08 -04:00
First version of optimized new IF stage, simplified a lot
This commit is contained in:
parent
384f160f2b
commit
08b2be2b76
4 changed files with 256 additions and 310 deletions
|
@ -129,6 +129,7 @@ module controller
|
|||
// Jump target calcuation done decision
|
||||
input logic [1:0] jump_in_ex_i, // jump is being calculated in ALU
|
||||
output logic [1:0] jump_in_id_o, // jump is being calculated in ALU
|
||||
input logic branch_decision_i,
|
||||
|
||||
output logic stall_if_o, // Stall IF stage (deassert requests)
|
||||
output logic stall_id_o, // Stall ID stage (and instr and data memory interface) ( ID_STAGE )
|
||||
|
@ -137,7 +138,8 @@ module controller
|
|||
);
|
||||
|
||||
// FSM state encoding
|
||||
enum logic [2:0] { RESET, IDLE, FIRST_FETCH, DECODE, DBG_FLUSH_EX, DBG_FLUSH_WB, DBG_SIGNAL, DBG_WAIT } ctrl_fsm_cs, ctrl_fsm_ns;
|
||||
enum logic [3:0] { RESET, IDLE, FIRST_FETCH, DECODE, BRANCH, BRANCH_DELAY,
|
||||
DBG_FLUSH_EX, DBG_FLUSH_WB, DBG_SIGNAL, DBG_WAIT } ctrl_fsm_cs, ctrl_fsm_ns;
|
||||
|
||||
logic reg_d_ex_is_reg_a_id;
|
||||
logic reg_d_ex_is_reg_b_id;
|
||||
|
@ -164,7 +166,6 @@ module controller
|
|||
logic jr_stall;
|
||||
logic trap_stall;
|
||||
|
||||
logic [2:0] pc_mux_sel;
|
||||
logic set_npc;
|
||||
`ifdef BRANCH_PREDICTION
|
||||
logic wrong_branch_taken;
|
||||
|
@ -186,8 +187,7 @@ module controller
|
|||
always_comb
|
||||
begin
|
||||
// Default values
|
||||
pc_mux_sel = `PC_INCR;
|
||||
jump_in_id_o = 2'b00;
|
||||
jump_in_id_o = `BRANCH_NONE;
|
||||
|
||||
alu_operator = `ALU_NOP;
|
||||
extend_immediate_o = 1'b0;
|
||||
|
@ -254,8 +254,7 @@ module controller
|
|||
`OPCODE_JAL: begin // Jump and Link
|
||||
if (instr_rdata_i ==? `INSTR_JAL) begin
|
||||
// Insert bubbles
|
||||
pc_mux_sel = `PC_NO_INCR;
|
||||
jump_in_id_o = 2'b01;
|
||||
jump_in_id_o = `BRANCH_JAL;
|
||||
// Calculate and store PC+4
|
||||
alu_op_a_mux_sel_o = `OP_A_CURRPC;
|
||||
alu_op_b_mux_sel_o = `OP_B_IMM;
|
||||
|
@ -272,8 +271,7 @@ module controller
|
|||
`OPCODE_JALR: begin // Jump and Link Register
|
||||
if (instr_rdata_i ==? `INSTR_JALR) begin
|
||||
// Insert bubbles
|
||||
pc_mux_sel = `PC_NO_INCR;
|
||||
jump_in_id_o = 2'b01;
|
||||
jump_in_id_o = `BRANCH_JALR;
|
||||
// Calculate and store PC+4
|
||||
alu_op_a_mux_sel_o = `OP_A_CURRPC;
|
||||
alu_op_b_mux_sel_o = `OP_B_IMM;
|
||||
|
@ -289,8 +287,7 @@ module controller
|
|||
end
|
||||
|
||||
`OPCODE_BRANCH: begin // Branch
|
||||
pc_mux_sel = `PC_NO_INCR;
|
||||
jump_in_id_o = 2'b10;
|
||||
jump_in_id_o = `BRANCH_COND;
|
||||
alu_op_c_mux_sel_o = `OP_C_JT;
|
||||
rega_used = 1'b1;
|
||||
regb_used = 1'b1;
|
||||
|
@ -863,7 +860,8 @@ module controller
|
|||
trap_insn_o = 1'b1;
|
||||
end
|
||||
`INSTR_ERET: begin
|
||||
pc_mux_sel = `PC_ERET;
|
||||
// TODO: Handle in controller
|
||||
//pc_mux_sel = `PC_ERET;
|
||||
clear_isr_running_o = 1'b1;
|
||||
end
|
||||
`INSTR_WFI: begin
|
||||
|
@ -976,9 +974,8 @@ module controller
|
|||
|
||||
default: begin
|
||||
illegal_insn_o = 1'b1;
|
||||
pc_mux_sel = `PC_NO_INCR;
|
||||
end
|
||||
endcase; // case (instr_rdata_i[6:0])
|
||||
endcase
|
||||
|
||||
// synopsys translate_off
|
||||
if (illegal_insn_o == 1'b1) begin
|
||||
|
@ -1023,7 +1020,6 @@ module controller
|
|||
instr_req_o = 1'b1;
|
||||
|
||||
pc_mux_sel_o = `PC_INCR;
|
||||
pc_mux_boot_o = 1'b0;
|
||||
|
||||
ctrl_fsm_ns = ctrl_fsm_cs;
|
||||
|
||||
|
@ -1042,13 +1038,12 @@ module controller
|
|||
begin
|
||||
// We were just reset and have to copy the boot address from
|
||||
// outside to our PC
|
||||
// We do not yet start fetching instructions as the next_pc is invalid!
|
||||
core_busy_o = 1'b0;
|
||||
instr_req_o = 1'b0;
|
||||
pc_mux_boot_o = 1'b1;
|
||||
instr_req_o = fetch_enable_i;
|
||||
pc_mux_sel_o = `PC_BOOT;
|
||||
|
||||
if (fetch_enable_i == 1'b1)
|
||||
ctrl_fsm_ns = IDLE;
|
||||
ctrl_fsm_ns = FIRST_FETCH;
|
||||
end
|
||||
|
||||
IDLE:
|
||||
|
@ -1057,7 +1052,6 @@ module controller
|
|||
// interrupt has arrived
|
||||
core_busy_o = 1'b0;
|
||||
instr_req_o = fetch_enable_i || irq_present_i;
|
||||
pc_mux_sel_o = `PC_NO_INCR;
|
||||
|
||||
if (fetch_enable_i || irq_present_i)
|
||||
begin
|
||||
|
@ -1081,7 +1075,11 @@ module controller
|
|||
|
||||
DECODE:
|
||||
begin
|
||||
pc_mux_sel_o = pc_mux_sel;
|
||||
if (jump_in_id_o != `BRANCH_NONE) begin
|
||||
// handle branch if decision is availble in next cycle
|
||||
if (~stall_id_o)
|
||||
ctrl_fsm_ns = BRANCH;
|
||||
end
|
||||
|
||||
// synopsys translate_off
|
||||
if (illegal_insn_o == 1'b1) begin
|
||||
|
@ -1090,10 +1088,6 @@ module controller
|
|||
end
|
||||
// synopsys translate_on
|
||||
|
||||
`ifdef BRANCH_PREDICTION
|
||||
if (wrong_branch_taken)
|
||||
pc_mux_sel_o = `PC_BRANCH_PRED;
|
||||
`endif
|
||||
// the pipeline is flushed and we are requested to go to sleep
|
||||
if ((pipe_flushed_i == 1'b1) && (fetch_enable_i == 1'b0))
|
||||
ctrl_fsm_ns = IDLE;
|
||||
|
@ -1109,6 +1103,26 @@ module controller
|
|||
end
|
||||
end
|
||||
|
||||
BRANCH:
|
||||
begin
|
||||
// assume branch instruction is in EX
|
||||
if (jump_in_ex_i == `BRANCH_COND && ~branch_decision_i) begin
|
||||
// not taken
|
||||
pc_mux_sel_o = `PC_INCR;
|
||||
ctrl_fsm_ns = DECODE;
|
||||
end else begin
|
||||
// branch taken or jump
|
||||
pc_mux_sel_o = `PC_JUMP;
|
||||
ctrl_fsm_ns = BRANCH_DELAY;
|
||||
end
|
||||
end
|
||||
|
||||
BRANCH_DELAY:
|
||||
begin
|
||||
if (~stall_id_o)
|
||||
ctrl_fsm_ns = DECODE;
|
||||
end
|
||||
|
||||
DBG_FLUSH_EX:
|
||||
begin
|
||||
dbg_halt = 1'b1;
|
||||
|
@ -1203,10 +1217,7 @@ module controller
|
|||
assign data_req_o = (deassert_we) ? 1'b0 : data_req;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Jump and Branch handling //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//assign force_nop_o = (jump_in_id_o != 2'b00 || jump_in_ex_i != 2'b00)? 1'b1 : 1'b0;
|
||||
// TODO: Remove? Can be replaced with stall.
|
||||
assign force_nop_o = 1'b0;
|
||||
|
||||
|
||||
|
@ -1218,7 +1229,7 @@ module controller
|
|||
// we unstall the if_stage if the debug unit wants to set a new
|
||||
// pc, so that the new value gets written into current_pc_if and is
|
||||
// used by the instr_core_interface
|
||||
stall_if_o = /*instr_ack_stall |*/ load_stall | jr_stall | lsu_stall | misalign_stall | dbg_halt | dbg_stall_i | (~pc_valid_i);
|
||||
stall_if_o = instr_ack_stall | load_stall | jr_stall | lsu_stall | misalign_stall | dbg_halt | dbg_stall_i | (~pc_valid_i);
|
||||
stall_id_o = instr_ack_stall | load_stall | jr_stall | lsu_stall | misalign_stall | dbg_halt | dbg_stall_i;
|
||||
stall_ex_o = instr_ack_stall | lsu_stall | dbg_stall_i;
|
||||
stall_wb_o = lsu_stall | dbg_stall_i;
|
||||
|
|
|
@ -50,6 +50,7 @@ module id_stage
|
|||
// Jumps and branches
|
||||
output logic [1:0] jump_in_id_o,
|
||||
output logic [1:0] jump_in_ex_o,
|
||||
input logic branch_decision_i,
|
||||
|
||||
// IF and ID stage signals
|
||||
output logic compressed_instr_o,
|
||||
|
@ -629,6 +630,8 @@ module id_stage
|
|||
// To controller (TODO: Remove when control/decode separated and moved)
|
||||
.jump_in_ex_i ( jump_in_ex_o ),
|
||||
|
||||
.branch_decision_i ( branch_decision_i ),
|
||||
|
||||
// To exception controller and EX: Jump/Branch indication
|
||||
.jump_in_id_o ( jump_in_id_o ),
|
||||
|
||||
|
|
491
if_stage.sv
491
if_stage.sv
|
@ -43,7 +43,7 @@ module if_stage
|
|||
|
||||
// instruction request control
|
||||
input logic req_i,
|
||||
output logic ack_o,
|
||||
output logic valid_o,
|
||||
input logic drop_request_i,
|
||||
|
||||
// instruction cache interface
|
||||
|
@ -81,292 +81,78 @@ module if_stage
|
|||
);
|
||||
|
||||
|
||||
logic [31:0] next_pc;
|
||||
logic [31:0] exc_pc;
|
||||
|
||||
// next PC mux inputs
|
||||
logic [31:0] incr_pc; // increased PC
|
||||
logic [31:0] exc_pc; // PC from exception
|
||||
|
||||
|
||||
logic sample_addr;
|
||||
logic [1:0] is_compressed;
|
||||
|
||||
|
||||
logic [31:0] last_fetch_addr;
|
||||
|
||||
logic unaligned, unaligned_Q;
|
||||
logic unaligned_jump;
|
||||
logic branch_taken;
|
||||
|
||||
logic force_nop_int;
|
||||
logic [31:0] instr_rdata_int;
|
||||
|
||||
logic [31:0] current_pc_id_int;
|
||||
|
||||
// instr_core_interface
|
||||
logic req_int;
|
||||
logic [31:0] rdata_int;
|
||||
logic fetch_ack;
|
||||
logic fetch_req;
|
||||
logic [31:0] fetch_rdata;
|
||||
logic fetch_valid;
|
||||
logic [31:0] fetch_addr, fetch_addr_n;
|
||||
|
||||
// stall ack logic
|
||||
logic ack_stalled, ack_stalled_n;
|
||||
logic ack_int;
|
||||
logic [31:0] fetch_addr_Q;
|
||||
|
||||
// local cache
|
||||
logic [31:0] data_tag;
|
||||
logic [1:0][31:0] data_arr;
|
||||
|
||||
|
||||
logic bypass_data_reg; // use data from instr_core_if, not from data_arr
|
||||
logic cross_line, cross_line_n;
|
||||
logic pc_if_offset, pc_if_offset_n;
|
||||
|
||||
|
||||
enum logic[3:0] {IDLE, START_REQ, WAIT_REQ, WAIT_ACK, FETCH_NEXT, HANDLE_BRANCH} fetch_fsm_cs, fetch_fsm_ns;
|
||||
|
||||
always_ff @(posedge clk, negedge rst_n)
|
||||
begin
|
||||
if (rst_n == 1'b0) begin
|
||||
fetch_fsm_cs <= IDLE;
|
||||
end else begin
|
||||
fetch_fsm_cs <= fetch_fsm_ns;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
always_comb
|
||||
begin
|
||||
if (pc_if_offset) begin
|
||||
incr_pc = fetch_addr + (is_compressed[1] ? 32'd4 : 32'd6);
|
||||
end else begin
|
||||
incr_pc = fetch_addr + (is_compressed[0] ? 32'd2 : 32'd4);
|
||||
end
|
||||
end
|
||||
//assign incr_pc = current_pc_if_o + (instr_rdata_int[1:0] != 2'b11 ? 32'd2 : 32'd4);
|
||||
logic [15:0] data_arr;
|
||||
|
||||
|
||||
// store instr_core_if data in local cache
|
||||
always_ff @(posedge clk, negedge rst_n)
|
||||
begin
|
||||
if (rst_n == 1'b0) begin
|
||||
data_tag <= '0;
|
||||
data_arr <= 32'h0003;
|
||||
data_arr <= 16'b0;
|
||||
end else begin
|
||||
if (fetch_ack) begin
|
||||
data_tag <= last_fetch_addr;
|
||||
data_arr[1] <= data_arr[0];
|
||||
data_arr[0] <= rdata_int;
|
||||
if (fetch_valid) begin
|
||||
data_arr <= fetch_rdata[31:16];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// cache data output muxes
|
||||
|
||||
always_comb
|
||||
begin
|
||||
if (bypass_data_reg) begin
|
||||
// bypass cache and get data directly from instr_core_if
|
||||
if (pc_if_offset && (cross_line || cross_line_n))
|
||||
instr_rdata_int = {rdata_int[15:0], data_arr[0][31:16]};
|
||||
else if (pc_if_offset)
|
||||
instr_rdata_int = {16'b0, rdata_int[31:16]};
|
||||
else
|
||||
instr_rdata_int = rdata_int;
|
||||
end else begin
|
||||
// serve data from cache
|
||||
if (pc_if_offset && cross_line)
|
||||
instr_rdata_int = {data_arr[0][15:0], data_arr[1][31:16]};
|
||||
else if (pc_if_offset)
|
||||
instr_rdata_int = {16'b0, data_arr[0][31:16]};
|
||||
else
|
||||
instr_rdata_int = data_arr[0];
|
||||
// default values for regular aligned access
|
||||
instr_rdata_int = fetch_rdata;
|
||||
current_pc_id_int = {fetch_addr[31:2], 2'b00};
|
||||
|
||||
if (unaligned) begin
|
||||
if (unaligned_Q) begin
|
||||
// cross-word access
|
||||
instr_rdata_int = {fetch_rdata[15:0], data_arr};
|
||||
current_pc_id_int = {fetch_addr_Q[31:2], 2'b10};
|
||||
end else begin
|
||||
// unaligned compressed instruction
|
||||
// don't care about upper half-word, insert good value for
|
||||
// optimization
|
||||
instr_rdata_int = {fetch_rdata[31:16], fetch_rdata[31:16]};
|
||||
current_pc_id_int = {fetch_addr[31:2], 2'b10};
|
||||
end
|
||||
end
|
||||
|
||||
// insert NOPs for branches
|
||||
if (force_nop_int)
|
||||
instr_rdata_int = {25'b0, `OPCODE_OPIMM};
|
||||
end
|
||||
|
||||
always_comb
|
||||
begin
|
||||
if (fetch_fsm_cs == WAIT_ACK) begin
|
||||
is_compressed[0] = rdata_int[1:0] != 2'b11;
|
||||
is_compressed[1] = rdata_int[17:16] != 2'b11;
|
||||
end else begin
|
||||
is_compressed[0] = data_arr[0][1:0] != 2'b11;
|
||||
is_compressed[1] = data_arr[0][17:16] != 2'b11;
|
||||
end
|
||||
end
|
||||
|
||||
assign current_pc_if_o = last_fetch_addr + (pc_if_offset? 32'd2 : 32'd0);
|
||||
assign current_pc_if_o = current_pc_id_int;
|
||||
|
||||
|
||||
always_ff @(posedge clk, negedge rst_n)
|
||||
begin
|
||||
if (rst_n == 1'b0) begin
|
||||
ack_stalled <= 1'b0;
|
||||
cross_line <= 1'b0;
|
||||
end else begin
|
||||
ack_stalled <= ack_stalled_n;
|
||||
cross_line <= cross_line_n;
|
||||
end
|
||||
end
|
||||
|
||||
// handshake to stall logic: keep ack asserted as long as ID is stalled
|
||||
// (do not accept new requests while IF/ID stalled)
|
||||
always_comb
|
||||
begin
|
||||
ack_stalled_n = ack_stalled;
|
||||
cross_line_n = cross_line;
|
||||
ack_o = ack_int;
|
||||
bypass_data_reg = 1'b1;
|
||||
|
||||
if (ack_stalled) begin
|
||||
ack_o = 1'b1;
|
||||
bypass_data_reg = 1'b0;
|
||||
if (stall_id_i == 1'b0) begin
|
||||
ack_stalled_n = 1'b0;
|
||||
cross_line_n = 1'b0;
|
||||
end
|
||||
end else begin
|
||||
cross_line_n = (fetch_fsm_cs == FETCH_NEXT);
|
||||
if (ack_int && stall_id_i && jump_in_id_i == `BRANCH_NONE)
|
||||
ack_stalled_n = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
always_comb
|
||||
begin
|
||||
fetch_fsm_ns = fetch_fsm_cs;
|
||||
pc_if_offset_n = pc_if_offset;
|
||||
|
||||
force_nop_int = 1'b0;
|
||||
sample_addr = 1'b0;
|
||||
ack_int = 1'b0;
|
||||
req_int = 1'b0;
|
||||
fetch_addr_n = {next_pc[31:2], 2'b0};
|
||||
|
||||
case (fetch_fsm_cs)
|
||||
IDLE:
|
||||
begin
|
||||
sample_addr = 1'b0;
|
||||
if (req_i)
|
||||
fetch_fsm_ns = START_REQ;
|
||||
end
|
||||
|
||||
// fetch word at fetch addr and start serving requests
|
||||
START_REQ: begin
|
||||
req_int = 1'b1;
|
||||
sample_addr = 1'b1;
|
||||
fetch_addr_n = fetch_addr + 32'd4;
|
||||
fetch_fsm_ns = WAIT_ACK;
|
||||
end
|
||||
|
||||
FETCH_NEXT: begin
|
||||
if (fetch_ack) begin
|
||||
ack_int = 1'b1; // ack even in presence of stalls
|
||||
fetch_fsm_ns = WAIT_REQ;
|
||||
end
|
||||
end
|
||||
|
||||
WAIT_REQ,
|
||||
WAIT_ACK:
|
||||
begin
|
||||
if (fetch_fsm_cs == WAIT_ACK) begin
|
||||
if (fetch_ack) begin
|
||||
if (pc_if_offset == 1'b0 || is_compressed[1])
|
||||
ack_int = 1'b1; // ack even in presence of stalls
|
||||
fetch_fsm_ns = WAIT_REQ;
|
||||
end
|
||||
end
|
||||
|
||||
if ((fetch_fsm_cs == WAIT_REQ || fetch_ack) && (stall_if_i == 1'b0 || (cross_line == 1'b1 && cross_line_n == 1'b0) || fetch_fsm_cs == WAIT_REQ) && req_i) begin
|
||||
if (pc_if_offset) begin
|
||||
if (is_compressed[1]) begin
|
||||
// serve second part of fetched instruction and request next
|
||||
sample_addr = 1'b1;
|
||||
req_int = 1'b1;
|
||||
ack_int = 1'b1;
|
||||
pc_if_offset_n = 1'b0;
|
||||
fetch_fsm_ns = WAIT_ACK;
|
||||
end else begin
|
||||
// cross line access
|
||||
// .. need to fetch next word here and delay everything till then
|
||||
sample_addr = 1'b1;
|
||||
req_int = 1'b1;
|
||||
ack_int = 1'b0;
|
||||
fetch_fsm_ns = FETCH_NEXT;
|
||||
end
|
||||
end else begin
|
||||
if (is_compressed[0]) begin
|
||||
// compressed instruction, only increase PC by two bytes
|
||||
sample_addr = 1'b0;
|
||||
req_int = 1'b0;
|
||||
ack_int = 1'b1;
|
||||
pc_if_offset_n = 1'b1;
|
||||
fetch_fsm_ns = WAIT_REQ;
|
||||
end else begin
|
||||
// aligned 32 bit instruction
|
||||
sample_addr = 1'b1;
|
||||
req_int = 1'b1;
|
||||
ack_int = 1'b1;
|
||||
fetch_fsm_ns = WAIT_ACK;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// handle jumps and branches (note: if jump_in_ex is signaling
|
||||
// a branch in this state, it is because the IF/ID stage was stalled
|
||||
// when the state changed from HANDLE_BRANCH to WAIT_REQ/-ACK)
|
||||
if (jump_in_id_i != `BRANCH_NONE) begin
|
||||
// restore all state changes
|
||||
pc_if_offset_n = pc_if_offset;
|
||||
sample_addr = 1'b0;
|
||||
req_int = 1'b0;
|
||||
// insert NOPs and wait for valid target
|
||||
force_nop_int = 1'b1;
|
||||
ack_int = 1'b1;
|
||||
fetch_fsm_ns = HANDLE_BRANCH;
|
||||
end
|
||||
end
|
||||
|
||||
HANDLE_BRANCH:
|
||||
begin
|
||||
if (jump_in_ex_i == `BRANCH_NONE) begin
|
||||
// insert NOPs until branch arrives in EX
|
||||
// required in case of stalls (latches NOP in IF/ID rdata register)
|
||||
force_nop_int = 1'b1;
|
||||
ack_int = req_i;
|
||||
end else begin
|
||||
if (jump_in_ex_i == `BRANCH_COND && branch_decision_i == 1'b0) begin
|
||||
// branch not taken, continue as before
|
||||
// TODO: Already serve request here?
|
||||
fetch_fsm_ns = WAIT_REQ;
|
||||
end else begin
|
||||
// set PC
|
||||
sample_addr = 1'b1;
|
||||
fetch_addr_n = {jump_target_i[31:2], 2'b0};
|
||||
if (jump_target_i[1:0] == 2'b0) begin
|
||||
// regular fetch
|
||||
pc_if_offset_n = 1'b0;
|
||||
end else begin
|
||||
// unaligned access
|
||||
pc_if_offset_n = 1'b1;
|
||||
end
|
||||
fetch_fsm_ns = START_REQ;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
default:
|
||||
begin
|
||||
fetch_fsm_ns = IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
// compressed instruction detection
|
||||
assign is_compressed[0] = fetch_rdata[1:0] != 2'b11;
|
||||
assign is_compressed[1] = fetch_rdata[17:16] != 2'b11;
|
||||
|
||||
|
||||
// exception PC selection mux
|
||||
always_comb
|
||||
begin : EXC_PC_MUX
|
||||
unique case (exc_pc_mux_i)
|
||||
`EXC_PC_NO_INCR: begin exc_pc = current_pc_if_o; end
|
||||
`EXC_PC_ILLINSN: begin exc_pc = {boot_addr_i[31:5], `EXC_OFF_ILLINSN }; end
|
||||
`EXC_PC_IRQ: begin exc_pc = {boot_addr_i[31:5], `EXC_OFF_IRQ }; end
|
||||
`EXC_PC_IRQ_NM: begin exc_pc = {boot_addr_i[31:5], `EXC_OFF_IRQ_NM }; end
|
||||
|
@ -377,15 +163,15 @@ module if_stage
|
|||
always_comb
|
||||
begin
|
||||
unique case (pc_mux_sel_i)
|
||||
`PC_BOOT: next_pc = {boot_addr_i[31:5], `EXC_OFF_RST};
|
||||
`PC_JUMP: next_pc = jump_target_i;
|
||||
`PC_INCR: next_pc = incr_pc; // incremented PC
|
||||
`PC_EXCEPTION: next_pc = exc_pc; // set PC to exception handler
|
||||
`PC_ERET: next_pc = exception_pc_reg_i; // PC is restored when returning from IRQ/exception
|
||||
`PC_HWLOOP: next_pc = pc_from_hwloop_i; // PC is taken from hwloop start addr
|
||||
`PC_BOOT: fetch_addr_n = {boot_addr_i[31:5], `EXC_OFF_RST};
|
||||
`PC_JUMP: fetch_addr_n = {jump_target_i[31:2], 2'b0};
|
||||
`PC_INCR: fetch_addr_n = fetch_addr + 32'd4; // incremented PC
|
||||
`PC_EXCEPTION: fetch_addr_n = exc_pc; // set PC to exception handler
|
||||
`PC_ERET: fetch_addr_n = exception_pc_reg_i; // PC is restored when returning from IRQ/exception
|
||||
`PC_HWLOOP: fetch_addr_n = pc_from_hwloop_i; // PC is taken from hwloop start addr
|
||||
default:
|
||||
begin
|
||||
next_pc = {boot_addr_i[31:5], `EXC_OFF_RST};
|
||||
fetch_addr_n = {boot_addr_i[31:5], `EXC_OFF_RST};
|
||||
// synopsys translate_off
|
||||
$display("%t: Illegal pc_mux_sel value (%0d)!", $time, pc_mux_sel_i);
|
||||
// synopsys translate_on
|
||||
|
@ -400,11 +186,11 @@ module if_stage
|
|||
.clk ( clk ),
|
||||
.rst_n ( rst_n ),
|
||||
|
||||
.req_i ( req_int ),
|
||||
.valid_o ( fetch_ack ),
|
||||
.addr_i ( fetch_addr ),
|
||||
.rdata_o ( rdata_int ),
|
||||
.last_addr_o ( last_fetch_addr ),
|
||||
.req_i ( fetch_req ),
|
||||
.valid_o ( fetch_valid ),
|
||||
.addr_i ( fetch_addr_n ),
|
||||
.rdata_o ( fetch_rdata ),
|
||||
.last_addr_o ( fetch_addr ),
|
||||
|
||||
.instr_req_o ( instr_req_o ),
|
||||
.instr_addr_o ( instr_addr_o ),
|
||||
|
@ -412,13 +198,167 @@ module if_stage
|
|||
.instr_rvalid_i ( instr_rvalid_i ),
|
||||
.instr_rdata_i ( instr_rdata_i ),
|
||||
|
||||
.stall_if_i ( 1'b0 ),
|
||||
.drop_request_i ( 1'b0 ) // TODO: Remove?
|
||||
.stall_if_i ( 1'b0 ),
|
||||
.drop_request_i ( 1'b0 ) // TODO: Remove?
|
||||
);
|
||||
|
||||
|
||||
// offset FSM
|
||||
enum logic[3:0] {IDLE, WAIT_ALIGNED, WAIT_UNALIGNED, VALID_ALIGNED, VALID_UNALIGNED,
|
||||
HANDLE_BRANCH, FETCH_UNALIGNED} offset_fsm_cs, offset_fsm_ns;
|
||||
|
||||
always_ff @(posedge clk, negedge rst_n)
|
||||
begin
|
||||
if (rst_n == 1'b0) begin
|
||||
offset_fsm_cs <= IDLE;
|
||||
end else begin
|
||||
offset_fsm_cs <= offset_fsm_ns;
|
||||
end
|
||||
end
|
||||
|
||||
// offset FSM state logic
|
||||
always_comb
|
||||
begin
|
||||
offset_fsm_ns = offset_fsm_cs;
|
||||
|
||||
fetch_req = 1'b0;
|
||||
valid_o = 1'b0;
|
||||
|
||||
unaligned = 1'b0;
|
||||
force_nop_int = 1'b0;
|
||||
branch_taken = 1'b0;
|
||||
|
||||
unique case (offset_fsm_cs)
|
||||
// no valid instruction data for ID stage
|
||||
// assume aligned
|
||||
IDLE: begin
|
||||
if (req_i) begin
|
||||
fetch_req = 1'b1;
|
||||
offset_fsm_ns = WAIT_ALIGNED;
|
||||
end
|
||||
end
|
||||
|
||||
VALID_ALIGNED,
|
||||
WAIT_ALIGNED: begin
|
||||
if (fetch_valid || offset_fsm_cs == VALID_ALIGNED) begin
|
||||
valid_o = 1'b1;
|
||||
offset_fsm_ns = VALID_ALIGNED;
|
||||
|
||||
if (req_i && ~stall_if_i) begin
|
||||
if (jump_in_id_i == `BRANCH_NONE) begin
|
||||
// ----------------------------------------------------------------------
|
||||
// no branch in ID, do regular fetch
|
||||
// ----------------------------------------------------------------------
|
||||
if (is_compressed[0]) begin
|
||||
// compressed instruction
|
||||
if (is_compressed[1]) begin
|
||||
// upper half contains compressed instruction and is available
|
||||
// from register
|
||||
offset_fsm_ns = VALID_UNALIGNED;
|
||||
end else begin
|
||||
// cross-word access, upper half is regular instruction
|
||||
fetch_req = 1'b1;
|
||||
offset_fsm_ns = WAIT_UNALIGNED;
|
||||
end
|
||||
end else begin
|
||||
// regular instruction
|
||||
fetch_req = 1'b1;
|
||||
offset_fsm_ns = WAIT_ALIGNED;
|
||||
end
|
||||
|
||||
end else begin
|
||||
// ----------------------------------------------------------------------
|
||||
// need to handle branch
|
||||
// ----------------------------------------------------------------------
|
||||
offset_fsm_ns = HANDLE_BRANCH;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
WAIT_UNALIGNED,
|
||||
VALID_UNALIGNED: begin
|
||||
unaligned = 1'b1;
|
||||
|
||||
if (fetch_valid || offset_fsm_cs == VALID_UNALIGNED) begin
|
||||
valid_o = 1'b1;
|
||||
|
||||
if (req_i && ~stall_if_i) begin
|
||||
if (jump_in_id_i == `BRANCH_NONE) begin
|
||||
// ----------------------------------------------------------------------
|
||||
// no branch in ID, do regular fetch
|
||||
// ----------------------------------------------------------------------
|
||||
fetch_req = 1'b1;
|
||||
|
||||
if (is_compressed[1]) begin
|
||||
// compressed instruction, next instruction will be aligned
|
||||
offset_fsm_ns = WAIT_ALIGNED;
|
||||
end else begin
|
||||
// regular instruction, fetch following instruction
|
||||
offset_fsm_ns = WAIT_UNALIGNED;
|
||||
end
|
||||
end else begin
|
||||
// ----------------------------------------------------------------------
|
||||
// need to handle branch
|
||||
// ----------------------------------------------------------------------
|
||||
offset_fsm_ns = HANDLE_BRANCH;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
HANDLE_BRANCH: begin
|
||||
// assume jump/branch instruction is in EX stage
|
||||
if (jump_in_ex_i == `BRANCH_COND && ~branch_decision_i) begin
|
||||
// TODO: Optimize this, already send request & valid
|
||||
if (unaligned_Q)
|
||||
offset_fsm_ns = VALID_UNALIGNED;
|
||||
else
|
||||
offset_fsm_ns = VALID_ALIGNED;
|
||||
end else begin
|
||||
// branch/jump taken
|
||||
branch_taken = 1'b1;
|
||||
fetch_req = 1'b1;
|
||||
if (unaligned_jump) begin
|
||||
unaligned = 1'b1;
|
||||
// if the target address is unaligned, we need to fetch the lower
|
||||
// word first
|
||||
offset_fsm_ns = FETCH_UNALIGNED;
|
||||
end else begin
|
||||
offset_fsm_ns = WAIT_ALIGNED;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
FETCH_UNALIGNED: begin
|
||||
unaligned = 1'b1;
|
||||
|
||||
if (fetch_valid) begin
|
||||
if (is_compressed[1]) begin
|
||||
// no cross-word access
|
||||
valid_o = 1'b1;
|
||||
offset_fsm_ns = WAIT_ALIGNED;
|
||||
end else begin
|
||||
// cross-word access, fetch next word
|
||||
fetch_req = 1'b1;
|
||||
offset_fsm_ns = WAIT_UNALIGNED;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
|
||||
|
||||
always_comb
|
||||
begin
|
||||
|
||||
unaligned_jump = 1'b0;
|
||||
|
||||
case (pc_mux_sel_i)
|
||||
`PC_JUMP: unaligned_jump = jump_target_i[1];
|
||||
`PC_ERET: unaligned_jump = exception_pc_reg_i[1];
|
||||
`PC_HWLOOP: unaligned_jump = pc_from_hwloop_i[1];
|
||||
endcase
|
||||
end
|
||||
|
||||
|
||||
|
@ -427,20 +367,14 @@ module if_stage
|
|||
begin : IF_PIPELINE
|
||||
if (rst_n == 1'b0)
|
||||
begin
|
||||
fetch_addr <= '0;
|
||||
pc_if_offset <= '0;
|
||||
unaligned_Q <= 1'b0;
|
||||
fetch_addr_Q <= 32'b0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (dbg_set_npc == 1'b1) begin
|
||||
// get PC from debug unit
|
||||
fetch_addr <= {dbg_pc_from_npc[31:2], 2'b0};
|
||||
pc_if_offset <= (dbg_pc_from_npc[1:0] != 2'b0);
|
||||
end else begin
|
||||
// update PC
|
||||
pc_if_offset <= pc_if_offset_n;
|
||||
if (sample_addr)
|
||||
fetch_addr <= fetch_addr_n;
|
||||
if (fetch_valid) begin
|
||||
unaligned_Q <= unaligned;
|
||||
fetch_addr_Q <= fetch_addr;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -458,10 +392,7 @@ module if_stage
|
|||
if (stall_id_i == 1'b0)
|
||||
begin : ENABLED_PIPE
|
||||
instr_rdata_id_o <= instr_rdata_int;
|
||||
if (pc_if_offset)
|
||||
current_pc_id_o <= last_fetch_addr + ((fetch_fsm_cs != FETCH_NEXT)? 32'd2 : -2); // TODO: Cleanup
|
||||
else
|
||||
current_pc_id_o <= last_fetch_addr;
|
||||
current_pc_id_o <= current_pc_id_int;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -252,7 +252,7 @@ module riscv_core
|
|||
|
||||
// instruction request control
|
||||
.req_i ( instr_req_int ),
|
||||
.ack_o ( instr_ack_int ),
|
||||
.valid_o ( instr_ack_int ),
|
||||
.drop_request_i ( 1'b0 ),
|
||||
|
||||
// instruction cache interface
|
||||
|
@ -308,6 +308,7 @@ module riscv_core
|
|||
|
||||
.jump_in_id_o ( jump_in_id ),
|
||||
.jump_in_ex_o ( jump_in_ex ),
|
||||
.branch_decision_i ( branch_decision ),
|
||||
|
||||
.core_busy_o ( core_busy_o ),
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue