WIP: Re-work instruction interface

This commit is contained in:
Florian Zaruba 2017-07-31 16:19:47 +02:00
parent 60bffd35e0
commit ce17852cec
4 changed files with 144 additions and 156 deletions

View file

@ -6,7 +6,7 @@
- Don't use tabs, use spaces.
- Use 4 spaces to open a new indentation level.
- All signal and module names should be lower case with underscores as whitespace replacements (e.g.: `fetch_busy`).
- Instantiation of modules should be postfix with `_i`, e.g.: `prefetcher_i`
- Instantiation of modules should be prefix with `i_`, e.g.: `i_prefetcher`
- For port definitions keep a post-fix direction (`_o`, `_i`).
- For active low signals put an additional (`_no`, `_ni`).
- Denote output of ff with `_q` and the input with `_n`.

View file

@ -25,16 +25,16 @@ module fetch_fifo
input logic rst_ni,
// control signals
input logic flush_i, // clears the contents of the FIFO -> quasi reset
// branch prediction at in_addr_i address, as this is an address and not PC it can be the case
// branch prediction at addr_i address, as this is an address and not PC it can be the case
// that we have two compressed instruction (or one compressed instruction and one unaligned instruction) so we
// only predict on one entry and discard (or keep) the other depending on its position and prediction.
// input port
input branchpredict_sbe branch_predict_i,
input exception ex_i, // fetch exception in
input logic [63:0] in_addr_i,
input logic [63:0] in_rdata_i,
input logic in_valid_i,
output logic in_ready_o,
input logic [63:0] addr_i,
input logic [63:0] rdata_i,
input logic valid_i,
output logic ready_o,
// Dual Port Fetch FIFO
// output port 0
output fetch_entry fetch_entry_0_o,
@ -55,7 +55,7 @@ module fetch_fifo
logic [$clog2(DEPTH)-1:0] write_pointer_n, write_pointer_q;
logic [$clog2(DEPTH)-1:0] status_cnt_n, status_cnt_q; // this integer will be truncated by the synthesis tool
assign in_ready_o = (status_cnt_q < DEPTH-2);
assign ready_o = (status_cnt_q < DEPTH-2);
assign full = (status_cnt_q == DEPTH);
assign empty = (status_cnt_q == '0);
@ -66,11 +66,11 @@ module fetch_fifo
// downsize from 64 bit to 32 bit, simply ignore half of the incoming data
always_comb begin : downsize
// take the upper half
if (in_addr_i[2])
in_rdata = in_rdata_i[63:32];
if (addr_i[2])
in_rdata = rdata_i[63:32];
// take the lower half of the instruction
else
in_rdata = in_rdata_i[31:0];
in_rdata = rdata_i[31:0];
end
always_comb begin : fetch_fifo_logic
@ -84,10 +84,10 @@ module fetch_fifo
// -------------
// Input Port
// -------------
if (in_valid_i) begin
if (valid_i) begin
status_cnt++;
// new input data
mem_n[write_pointer_q] = {in_addr_i, in_rdata, branch_predict_i, ex_i};
mem_n[write_pointer_q] = {addr_i, in_rdata, branch_predict_i, ex_i};
write_pointer++;
end
@ -146,7 +146,7 @@ module fetch_fifo
// Make sure we don't overflow the queue
assert property (@(posedge clk_i) ((full && !flush_i) |-> ##1 !empty)) else $error("Fetch FIFO Overflowed");
assert property (@(posedge clk_i) (flush_i || (status_cnt_q - status_cnt_n) <= 2 || (status_cnt_q - status_cnt_n) >= -2)) else $error("Fetch FIFO over- or underflowed");
assert property (@(posedge clk_i) (in_valid_i |-> !full)) else $error("Got a valid signal, although the queue is not ready to accept a new request");
assert property (@(posedge clk_i) (valid_i |-> !full)) else $error("Got a valid signal, although the queue is not ready to accept a new request");
`endif
`endif
endmodule
endmodule

View file

@ -35,7 +35,7 @@ module if_stage (
input logic instr_gnt_i,
input logic instr_rvalid_i,
input logic [63:0] instr_rdata_i,
input exception instr_ex_i, // Instruction fetch exception, valid if rvalid is one
input exception instr_ex_i, // Instruction fetch exception, valid if rvalid is one
// Output of IF Pipeline stage -> Dual Port Fetch FIFO
// output port 0
output fetch_entry fetch_entry_0_o, // fetch entry containing all relevant data for the ID stage
@ -47,193 +47,181 @@ module if_stage (
input logic fetch_ack_1_i // ID acknowledged this instruction
);
enum logic [1:0] {IDLE, WAIT_GNT, WAIT_RVALID, WAIT_ABORTED } CS, NS;
enum logic [2:0] {IDLE, WAIT_GNT, WAIT_RVALID, WAIT_ABORTED, WAIT_ABORTED_REQUEST } CS, NS;
// define a type where we can store address and branch-prediction information
typedef struct packed {
logic [63:0] address;
branchpredict_sbe branchpredict;
} address_fifo_t;
logic [63:0] fetch_address;
logic addr_valid;
logic [63:0] instr_addr_q;
logic fifo_valid;
logic fifo_ready;
branchpredict_sbe branchpredict_q;
logic [63:0] instr_addr_n, instr_addr_q;
branchpredict_sbe branchpredict_n, branchpredict_q;
// Control signals
address_fifo_t push_data, pop_data;
logic fifo_valid, fifo_ready;
logic pop_empty, empty; // pop the address queue in case of a flush, empty signal
//---------------------------------
// Pre-fetch buffer status
//---------------------------------
// we are busy if we are either waiting for a grant
// or if the FIFO is full
// or if we are waiting for a rvalid and we didn't receive one yet
assign if_busy_o = (CS == WAIT_GNT) || !fifo_ready || (CS == WAIT_RVALID && !instr_rvalid_i);
// we are busy if we are either waiting for a grant or if the FIFO is full
assign if_busy_o = (CS == WAIT_GNT) || !fifo_ready || (CS == WAIT_ABORTED_REQUEST);
assign fetch_address = {fetch_address_i[63:2], 2'b0};
//---------------------------------
// Fetch FIFO
// consumes addresses and rdata
//---------------------------------
fetch_fifo fetch_fifo_i (
.branch_predict_i ( branchpredict_q ),
.ex_i ( instr_ex_i ),
.in_addr_i ( instr_addr_q ),
.in_rdata_i ( instr_rdata_i ),
.in_valid_i ( fifo_valid ),
.in_ready_o ( fifo_ready ),
.*
);
//--------------------------------------------------
// --------------------------------------------------
// Instruction fetch FSM
// deals with instruction memory / instruction cache
//--------------------------------------------------
always_comb begin
// --------------------------------------------------
always_comb begin : instr_fetch_fsm
NS = CS;
instr_req_o = 1'b0;
instr_addr_o = fetch_address;
fifo_valid = 1'b0;
NS = CS;
addr_valid = 1'b0;
push_data = { fetch_address_i, branch_predict_i };
fifo_valid = instr_rvalid_i;
pop_empty = instr_rvalid_i; // only pop the address queue
case(CS)
// default state, not waiting for requested data
// get new data by default
if (!if_busy_o) begin
branchpredict_n = branch_predict_i;
instr_addr_n = fetch_address_i;
// if we are not ready, latch the current data
end else begin
branchpredict_n = branchpredict_q;
instr_addr_n = instr_addr_q;
end
case (CS)
// we are idling, and can accept a new request
IDLE: begin
instr_addr_o = fetch_address;
instr_req_o = 1'b0;
// make a new request
// check if we have space in the FIFO and we want to do a request
if (fifo_ready && fetch_valid_i) begin
instr_req_o = 1'b1;
addr_valid = 1'b1;
if (instr_gnt_i) //~> granted request
// we have one outstanding rvalid: wait for it
if (flush_i)
NS = WAIT_ABORTED;
else
NS = WAIT_RVALID;
else begin //~> got a request but no grant
// if we flush we want to stay in the IDLE state
if (!flush_i)
NS = WAIT_GNT;
end
// did we get a grant?
if (instr_gnt_i)
// Yes, so wait for the rvalid
NS = WAIT_RVALID;
else
// No, so wait for it
NS = WAIT_GNT;
end
end // case: IDLE
end
// we sent a request but did not yet get a grant
WAIT_GNT: begin
instr_addr_o = {instr_addr_q[63:2], 2'b0};
instr_req_o = 1'b1;
if(instr_gnt_i) begin
if (instr_gnt_i) begin
// push the old data
push_data = { instr_addr_q, branchpredict_q };
// we have one outstanding rvalid: wait for it
if (flush_i)
NS = WAIT_ABORTED;
else
NS = WAIT_RVALID;
end else begin
// if we got a flush request we can safely return to IDLE
if (flush_i)
NS = IDLE;
else
NS = WAIT_GNT;
end
end // case: WAIT_GNT
end
// we wait for rvalid, after that we are ready to serve a new request
WAIT_RVALID: begin
instr_addr_o = fetch_address;
// we are waiting for a rvalid and in case we don't receive one, wait in the aborted state if we flushed
if (flush_i)
NS = WAIT_ABORTED;
// prepare for next request
// prepare for next request, we've got one if the fetch_valid_i is high
if (fifo_ready && fetch_valid_i) begin
// wait for the valid signal
if (instr_rvalid_i) begin
instr_req_o = 1'b1;
fifo_valid = 1'b1;
addr_valid = 1'b1;
if (instr_gnt_i) begin
// we have one outstanding rvalid -> wait for it
// if we are receiving a data item during a flush ignore it
if (flush_i)
NS = WAIT_ABORTED;
else
NS = WAIT_RVALID;
end else begin
if (!flush_i)
NS = WAIT_GNT; // lets wait for the grant
else // we didn't get a grant yet so go back to IDLE
NS = IDLE;
end
end
end else begin
// we are requested to abort our current request
// we didn't get an rvalid yet, so wait for it
if (flush_i) begin
NS = WAIT_ABORTED;
end
// just wait for rvalid and go back to IDLE, no new request
if (instr_rvalid_i) begin
// if we are receiving a data item during a flush ignore it
if (flush_i)
fifo_valid = 1'b0;
else
fifo_valid = 1'b1;
// in any case we can go back to IDLE safely
NS = IDLE;
end
end
end // case: WAIT_RVALID
// our last request was aborted, but we didn't yet get a rvalid
WAIT_ABORTED: begin
// we can do a new request here, we won't get a grant until the rvalid came back for the
// request we sent to end in here
instr_addr_o = fetch_address;
addr_valid = 1'b1;
// we are aborting this instruction so don't tell the FIFO it is valid
fifo_valid = 1'b0;
// check if the fetch is valid before making a new request
if (fetch_valid_i) begin
instr_req_o = 1'b1;
instr_req_o = 1'b1;
if (instr_gnt_i) begin
// we have one outstanding rvalid
if (flush_i)
NS = WAIT_ABORTED;
else
NS = WAIT_RVALID;
// we have one outstanding rvalid -> wait for it
NS = WAIT_RVALID;
end else begin
// only if we are not flushing again leave the state
if (!flush_i)
NS = WAIT_GNT;
NS = WAIT_GNT; // lets wait for the grant
end
end else begin
if (instr_rvalid_i) begin
// we didn't make a new request but got the rvalid we where waiting for, go back to IDLE
NS = IDLE;
end
// otherwise wait in this state for the rvalid
// go back to IDLE
NS = IDLE;
end
end
// we save a potential new request here
WAIT_ABORTED: begin
// abort the current rvalid, we don't want it anymore
fifo_valid = 1'b0;
// we've got a new fetch here, the fetch fifo is for sure empty as we just flushed it, but we still need to
// wait for the queue to be emptied
if (fetch_valid_i && empty) begin
instr_req_o = 1'b1;
NS = WAIT_GNT;
end else if (fetch_valid_i) // the fetch is valid but the queue is not empty wait for it
NS = WAIT_ABORTED_REQUEST;
else if (empty) // the fetch is not valid and the queue is empty we are back to normal operation
NS = IDLE;
end
// our last request was aborted, but we didn't yet get a rvalid
WAIT_ABORTED_REQUEST: begin
// abort the current rvalid, we don't want it anymore
fifo_valid = 1'b0;
// Here we wait for the queue to be empty, we do not make any requests
if (empty)
NS = WAIT_GNT;
end
endcase
// -------------
// Flush
// -------------
if (flush_i) begin
// if the address queue is empty this case is simple: just go back to idle
if (empty)
NS = IDLE;
// if it wasn't empty we need to wait for all outstanding rvalids until we can make any further requests
else
NS = WAIT_ABORTED;
end
end
// ---------------------------------
// Address and Branch-predict Queue
// ---------------------------------
fifo #(
.dtype ( address_fifo_t ),
// we can only have two in-flight instructions:
.DEPTH ( 2 )
) i_fifo (
.flush_i ( 1'b0 ), // do not flush, we need to keep track of all outstanding rvalids
.full_o ( ), // This queue should not underflow...
.empty_o ( empty ), // .. or overflow
.single_element_o ( ), // isn't needed here
.data_i ( push_data ),
.push_i ( instr_gnt_i ), // if we got a grant push the address and data
.data_o ( pop_data ),
.pop_i ( fifo_valid || pop_empty ), // pop the data if we say that the fetch is valid
.*
);
// ---------------------------------
// Fetch FIFO
// consumes addresses and rdata
// ---------------------------------
fetch_fifo i_fetch_fifo (
.branch_predict_i ( pop_data.branchpredict ),
.ex_i ( instr_ex_i ),
.addr_i ( pop_data.address ),
.rdata_i ( instr_rdata_i ),
.valid_i ( fifo_valid ),
.ready_o ( fifo_ready ),
.*
);
//-------------
// Registers
//-------------
always_ff @(posedge clk_i, negedge rst_ni) begin
if (~rst_ni) begin
CS <= IDLE;
instr_addr_q <= '0;
branchpredict_q <= '{default: 0};
CS <= IDLE;
instr_addr_q <= '0;
branchpredict_q <= '{default: 0};
end else begin
CS <= NS;
if (addr_valid) begin
instr_addr_q <= fetch_address_i;
branchpredict_q <= branch_predict_i;
end
CS <= NS;
instr_addr_q <= instr_addr_n;
branchpredict_q <= branchpredict_n;
end
end
//-------------

2
tb

@ -1 +1 @@
Subproject commit 4d598a69e20a7dfbd8f9f46f62dcba2bd3e88f16
Subproject commit 36029ea2635e912e67cd069552c4c1fcd1dea027