mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 21:57:11 -04:00
WIP: Re-work instruction interface
This commit is contained in:
parent
60bffd35e0
commit
ce17852cec
4 changed files with 144 additions and 156 deletions
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
|
|
270
src/if_stage.sv
270
src/if_stage.sv
|
@ -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
2
tb
|
@ -1 +1 @@
|
|||
Subproject commit 4d598a69e20a7dfbd8f9f46f62dcba2bd3e88f16
|
||||
Subproject commit 36029ea2635e912e67cd069552c4c1fcd1dea027
|
Loading…
Add table
Add a link
Reference in a new issue