Add control FSM to IF stage

Veeery messy, but works. Not the final form, needs a lot of cleanup.
This commit is contained in:
Sven Stucki 2015-08-05 03:19:29 +02:00
parent 1f1ea049f4
commit 8e26797549
3 changed files with 223 additions and 82 deletions

View file

@ -1207,7 +1207,8 @@ module controller
////////////////////////////////////////////////////////////////////////////////////////////
// Jump and Branch handling //
////////////////////////////////////////////////////////////////////////////////////////////
assign force_nop_o = (jump_in_id_o != 2'b00 || jump_in_ex_i != 2'b00)? 1'b1 : 1'b0;
//assign force_nop_o = (jump_in_id_o != 2'b00 || jump_in_ex_i != 2'b00)? 1'b1 : 1'b0;
assign force_nop_o = 1'b0;
////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -17,13 +17,15 @@
// Language: SystemVerilog //
// //
// Description: Instruction fetch unit: Selection of the next PC, and //
// buffering (Sampling) of the read instruction //
// buffering (sampling) of the read instruction //
// Revision: //
// Revision v0.1 - File Created //
// Revision v0.2 - (August 6th 2014) Changed port and signal names, addedd //
// comments //
// Revision v0.3 - (December 1th 2014) Merged debug unit and added more //
// exceptions //
// Revision v0.4 - (July 30th 2015) Removed instr_core_interface, handling //
// the cache interface in IF now //
// //
// //
////////////////////////////////////////////////////////////////////////////////
@ -81,33 +83,46 @@ module if_stage
);
////////////////////////////////////
// Instruction Fetch (IF) signals //
////////////////////////////////////
logic [31:0] next_pc; // Next PC (directly sent to I$)
logic [31:0] incr_pc; // Increased PC
logic [31:0] exc_pc; // Exception PC
logic [31:0] instr_rdata_int; // The instruction read from instr memory/cache is forwarded to ID stage, and the controller can force this forwarding to a nop (BUBBLE)
logic [31:0] next_pc;
// next PC mux inputs
logic [31:0] incr_pc; // increased PC
logic [31:0] exc_pc; // PC from exception
// Address to fetch the instruction
assign instr_addr_o = next_pc;
enum logic [2:0] {IDLE, WAIT_BRANCH, FETCH_STALLED, FETCH_UNSTALLED, WAIT_IF_STALL} CS, NS;
// PC generation FSM
enum logic [0:0] {REGULAR, HANDLE_BRANCH} CS_PCGEN, NS_PCGEN;
// instruction cache interface signals
//enum logic [1:0] {IDLE, WAIT_GNT, WAIT_RVALID, WAIT_IF_STALL} CS_FETCH, NS_FETCH;
logic [31:0] fetch_addr, fetch_addr_n;
logic [31:0] fetch_data, fetch_data_n;
logic [31:0] instr_addr_o_n;
logic [31:0] last_fetch_addr;
// instr_core_interface
logic req_int;
logic ack_int;
logic [31:0] addr_int;
logic [31:0] rdata_int;
logic force_nop_int;
logic [31:0] instr_rdata_int;
assign instr_rdata_int = (force_nop_int == 1'b0)? rdata_int : {25'b0, `OPCODE_OPIMM};
// Exception PC selection
always_comb
begin : EXC_PC_MUX
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
endcase //~case (exc_pc_mux_i)
end
// increased PC calculation
always_comb
begin
if (instr_rdata_i[1:0] != 2'b11) begin
if (rdata_int[1:0] != 2'b11) begin
// compressed instruction
incr_pc = current_pc_if_o + 32'd2;
end else begin
@ -115,36 +130,35 @@ module if_stage
end
end
// PC selection and force NOP logic
// 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
endcase
end
// next PC selection
always_comb
begin
next_pc = current_pc_if_o;
instr_rdata_int = instr_rdata_i;
unique case (pc_mux_sel_i)
`PC_INCR: next_pc = incr_pc; // PC is incremented and points the next instruction
`PC_NO_INCR: next_pc = current_pc_if_o; // PC is not incremented
`PC_EXCEPTION: next_pc = exc_pc; // PC that points to the exception
`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
default:
begin
next_pc = current_pc_if_o;
// synopsys translate_off
$display("%t: Illegal pc_mux_sel value (%0d)!", $time, pc_mux_sel_i);
// synopsys translate_on
end
endcase // unique case (pc_mux_sel_i)
// if force NOP, do not increase PC
if (force_nop_i == 1'b1) begin
instr_rdata_int = { 25'b0, `OPCODE_OPIMM }; // addi x0 = x0 + 0
end
// freeze PC if jump/branch in pipeline
if (jump_in_id_i != 2'b00) begin
next_pc = current_pc_if_o;
end
`PC_NO_INCR: next_pc = current_pc_if_o; // PC is not incremented
`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
default:
begin
next_pc = current_pc_if_o;
// synopsys translate_off
$display("%t: Illegal pc_mux_sel value (%0d)!", $time, pc_mux_sel_i);
// synopsys translate_on
end
endcase
if (jump_in_ex_i == 2'b01) begin
// jump handling
@ -154,59 +168,178 @@ module if_stage
if (branch_decision_i == 1'b1)
next_pc = jump_target_i;
else
next_pc = current_pc_if_o;
next_pc = incr_pc;
//next_pc = current_pc_if_o;
end
if (pc_mux_boot_i)
next_pc = {boot_addr_i[31:5], `EXC_OFF_RST};
end
always_comb
begin
NS = CS;
req_int = 1'b0;
addr_int = '0;
ack_o = 1'b0;
force_nop_int = 1'b0;
unique case (CS)
IDLE:
begin
if (req_i) begin
if (jump_in_id_i == 2'b0) begin
if (force_nop_i) begin
force_nop_int = 1'b1;
ack_o = 1'b1;
end else begin
// no branch, do normal fetch
req_int = 1'b1;
addr_int = next_pc;
if (stall_if_i) begin
NS = FETCH_STALLED;
end else begin
NS = FETCH_UNSTALLED;
end
end
end
else
begin
// wait for branch decision / jump target
force_nop_int = 1'b1;
ack_o = 1'b1;
NS = WAIT_BRANCH;
end
end
end
WAIT_BRANCH:
begin
if (jump_in_ex_i != 2'b0) begin
req_int = 1'b1;
addr_int = next_pc;
if (stall_if_i) begin
NS = FETCH_STALLED;
end else begin
NS = FETCH_UNSTALLED;
end
end
end
FETCH_STALLED,
FETCH_UNSTALLED:
begin
if (ack_int) begin
NS = IDLE;
ack_o = 1'b1;
if (stall_if_i) begin
if (CS == FETCH_STALLED) begin
// already fetched instruction for after stall
NS = WAIT_IF_STALL;
end
end
end
end
WAIT_IF_STALL:
begin
NS = IDLE;
ack_o = 1'b1;
if (stall_if_i)
NS = WAIT_IF_STALL;
end
default:
begin
$display("%t: invalid state", $time);
end
endcase
end
always_ff @(posedge clk, negedge rst_n)
begin
if (rst_n == 1'b0)
begin
CS <= IDLE;
end
else
begin
CS <= NS;
end
end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IF PC register //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cache fetch interface
instr_core_interface instr_core_if_i
(
.clk ( clk ),
.rst_n ( rst_n ),
.req_i ( req_int ),
.ack_o ( ack_int ),
.addr_i ( addr_int ),
.rdata_o ( rdata_int ),
.instr_req_o ( instr_req_o ),
.instr_addr_o ( instr_addr_o ),
.instr_gnt_i ( instr_gnt_i ),
.instr_rvalid_i ( instr_rvalid_i ),
.instr_rdata_i ( instr_rdata_i ),
.last_addr_o ( last_fetch_addr ),
.stall_if_i ( stall_if_i ),
.drop_request_i ( drop_request_i )
);
// IF PC register
always_ff @(posedge clk, negedge rst_n)
begin : IF_PIPELINE
if (rst_n == 1'b0)
begin : ASSERT_RESET
begin
current_pc_if_o <= 32'h0;
end
else
begin : DEASSERT_RESET
if ( pc_mux_boot_i == 1'b1 )
begin
// set PC to reset vector
current_pc_if_o <= {boot_addr_i[31:5], `EXC_OFF_RST};
end
else if ( dbg_set_npc == 1'b1 )
begin
// debug units sets NPC, PC_MUX_SEL holds this value
current_pc_if_o <= dbg_pc_from_npc;
end
else if ( stall_if_i == 1'b0 )
begin : ENABLED_PIPE
current_pc_if_o <= next_pc;
end
else if ( jump_in_ex_i == 2'b01 )
begin
current_pc_if_o <= next_pc;
end
begin
if (pc_mux_boot_i == 1'b1)
begin
// set PC to reset vector
current_pc_if_o <= {boot_addr_i[31:5], `EXC_OFF_RST};
end
else if (dbg_set_npc == 1'b1)
begin
// debug units sets NPC, PC_MUX_SEL holds this value
current_pc_if_o <= dbg_pc_from_npc;
end
else if (stall_if_i == 1'b0)
begin
current_pc_if_o <= last_fetch_addr;
end
end
end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IF-ID PIPE: Pipeline that is frozen when the ID stage is stalled //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IF-ID pipeline registers, frozen when the ID stage is stalled
always_ff @(posedge clk, negedge rst_n)
begin : IF_ID_PIPE_REGISTERS
if (rst_n == 1'b0)
begin : ASSERT_RESET
begin
instr_rdata_id_o <= '0;
current_pc_id_o <= '0;
end
else
begin : DEASSERT_RESET
begin
if (stall_id_i == 1'b0)
begin : ENABLED_PIPE
instr_rdata_id_o <= instr_rdata_int;
current_pc_id_o <= current_pc_if_o;
//current_pc_id_o <= current_pc_if_o;
current_pc_id_o <= last_fetch_addr;
end
end
end

View file

@ -40,12 +40,13 @@ module instr_core_interface
input logic instr_rvalid_i,
input logic [31:0] instr_rdata_i,
output logic [31:0] last_addr_o,
input logic stall_if_i,
input logic drop_request_i
);
enum logic [2:0] {IDLE, PENDING, WAIT_RVALID, WAIT_IF_STALL, WAIT_GNT, ABORT} CS, NS;
logic save_rdata;
@ -66,15 +67,21 @@ module instr_core_interface
begin
CS <= NS;
if(wait_gnt)
if (instr_req_o && instr_gnt_i)
addr_Q <= instr_addr_o;
if (wait_gnt)
addr_Q <= addr_i;
if(save_rdata)
if (save_rdata)
rdata_Q <= instr_rdata_i;
end
end
assign last_addr_o = addr_Q;
always_comb
begin
instr_req_o = 1'b0;