[RTL] - Remove timing loop in LSU

- Relates to issue #265
- External signals data_rvalid_i and data_err_i were factored
  into the external data_req_o signal
- To improve timing, these signals are decoupled
- The second part of an unaligned transaction will now be issued
  even if the first received an error response
- The state machine will service the abandoned requests
- pmp_err_q fixed to only update at specific times
This commit is contained in:
Tom Roberts 2019-08-29 17:31:23 +01:00 committed by Tom Roberts
parent e9c2b2ecb3
commit 36db104160
2 changed files with 71 additions and 19 deletions

View file

@ -48,6 +48,10 @@ The LSU is able to handle misaligned memory accesses, meaning accesses that are
However, it does so by performing two separate word-aligned accesses.
This means that at least two cycles are needed for misaligned loads and stores.
If an error response is received for the first transaction, the second transaction will still be issued.
The second transaction will then follow the normal bus protocol, but its response/data will be ignored.
If a new load/store request is received while waiting for an abandoned second part to complete, it will not be serviced until the state machine returns to IDLE.
.. _lsu-protocol:
Protocol
@ -61,6 +65,8 @@ The protocol that is used by the LSU to communicate with a memory works as follo
3. The memory answers with a ``data_rvalid_i`` set high for exactly one cycle to signal the response from the bus or the memory using ``data_err_i`` and ``data_rdata_i`` (during the very same cycle). This may happen one or more cycles after the grant has been received. If ``data_err_i`` is low, the request could successfully be handled at the destination and in the case of a load, ``data_rdata_i`` contains valid data. If ``data_err_i`` is high, an error occurred in the memory system and the core will raise an exception.
4. When multiple granted requests are outstanding, it is assumed that the memory requests will be kept in-order and one ``data_rvalid_i`` will be signalled for each of them, in the order they were issued.
:numref:`timing1`, :numref:`timing2` and :numref:`timing3` show example-timing diagrams of the protocol.
.. wavedrom::

View file

@ -76,11 +76,13 @@ module ibex_load_store_unit (
logic split_misaligned_access;
logic handle_misaligned_q, handle_misaligned_d; // high after receiving grant for first
// part of a misaligned access
logic pmp_err_d;
logic pmp_err_q;
logic data_or_pmp_err;
typedef enum logic [2:0] {
IDLE, WAIT_GNT_MIS, WAIT_RVALID_MIS, WAIT_GNT, WAIT_RVALID
IDLE, WAIT_GNT_MIS, WAIT_RVALID_MIS, WAIT_GNT, WAIT_RVALID,
WAIT_GNT_ERR, WAIT_RVALID_ERR, WAIT_RVALID_DONE
} ls_fsm_e;
ls_fsm_e ls_fsm_cs, ls_fsm_ns;
@ -321,12 +323,14 @@ module ibex_load_store_unit (
addr_incr_req_o = 1'b0;
handle_misaligned_d = handle_misaligned_q;
data_or_pmp_err = 1'b0;
pmp_err_d = pmp_err_q;
unique case (ls_fsm_cs)
IDLE: begin
if (data_req_ex_i) begin
data_req_o = 1'b1;
pmp_err_d = data_pmp_err_i;
if (data_gnt_i) begin
handle_misaligned_d = split_misaligned_access;
ls_fsm_ns = split_misaligned_access ? WAIT_RVALID_MIS : WAIT_RVALID;
@ -338,6 +342,10 @@ module ibex_load_store_unit (
WAIT_GNT_MIS: begin
data_req_o = 1'b1;
// data_pmp_err_i is valid during the address phase of a request. An error will block the
// external request and so a data_gnt_i might never be signalled. The registered version
// pmp_err_q is only updated for new address phases and so can be used in WAIT_GNT* and
// WAIT_RVALID* states
if (data_gnt_i || pmp_err_q) begin
handle_misaligned_d = 1'b1;
ls_fsm_ns = WAIT_RVALID_MIS;
@ -345,32 +353,34 @@ module ibex_load_store_unit (
end
WAIT_RVALID_MIS: begin
// push out second request
data_req_o = 1'b1;
// tell ID/EX stage to update the address
addr_incr_req_o = 1'b1;
// first part rvalid is received, or gets a pmp error
// pmp_err_i will hold stable until the address is updated, and
// therefore pmp_err_q is valid in both WAIT_GNT_MIS and WAIT_RVALID_MIS states
// first part rvalid is received, or gets a PMP error
if (data_rvalid_i || pmp_err_q) begin
// Update the PMP error for the second part
pmp_err_d = data_pmp_err_i;
if (pmp_err_q || data_err_i) begin
// first part created an error, abort transaction
data_valid_o = 1'b1;
data_or_pmp_err = 1'b1;
handle_misaligned_d = 1'b0;
ls_fsm_ns = IDLE;
// If already granted, wait for second rvalid
ls_fsm_ns = data_gnt_i ? WAIT_RVALID_ERR : WAIT_GNT_ERR;
end else begin
// push out second request
data_req_o = 1'b1;
if (data_gnt_i) begin
// second grant is received
ls_fsm_ns = WAIT_RVALID;
end else begin
// second grant is NOT received, but first rvalid
ls_fsm_ns = WAIT_GNT;
end
// No error in first part, proceed with second part
ls_fsm_ns = data_gnt_i ? WAIT_RVALID : WAIT_GNT;
end
end else begin
// first part rvalid is NOT received
ls_fsm_ns = WAIT_RVALID_MIS;
if (data_gnt_i) begin
// second grant is received
ls_fsm_ns = WAIT_RVALID_DONE;
end
end
end
@ -385,8 +395,6 @@ module ibex_load_store_unit (
WAIT_RVALID: begin
data_req_o = 1'b0;
// pmp_err_i will hold stable until the address is updated, and
// therefore pmp_err_q is valid in both WAIT_GNT and WAIT_RVALID states
if (data_rvalid_i || pmp_err_q) begin
data_valid_o = 1'b1;
data_or_pmp_err = data_err_i | pmp_err_q;
@ -397,6 +405,44 @@ module ibex_load_store_unit (
end
end
WAIT_GNT_ERR: begin
// Wait for the grant of the abandoned second access
data_req_o = 1'b1;
// tell ID/EX stage to update the address
addr_incr_req_o = 1'b1;
if (pmp_err_q) begin
// The second part was suppressed by a PMP error
ls_fsm_ns = IDLE;
end else if (data_gnt_i) begin
ls_fsm_ns = WAIT_RVALID_ERR;
end
end
WAIT_RVALID_ERR: begin
// Wait for the rvalid, but do nothing with it
if (data_rvalid_i || pmp_err_q) begin
ls_fsm_ns = IDLE;
end
end
WAIT_RVALID_DONE: begin
// Wait for the first rvalid, second request is already granted
if (data_rvalid_i) begin
// Update the pmp error for the second part
pmp_err_d = data_pmp_err_i;
// The first part cannot see a PMP error in this state
if (data_err_i) begin
// first part created an error, abort transaction and wait for second rvalid
data_valid_o = 1'b1;
data_or_pmp_err = 1'b1;
handle_misaligned_d = 1'b0;
ls_fsm_ns = WAIT_RVALID_ERR;
end else begin
ls_fsm_ns = WAIT_RVALID;
end
end
end
default: begin
ls_fsm_ns = ls_fsm_e'(1'bX);
end
@ -424,7 +470,7 @@ module ibex_load_store_unit (
ls_fsm_cs <= ls_fsm_ns;
addr_last_q <= addr_last_d;
handle_misaligned_q <= handle_misaligned_d;
pmp_err_q <= data_pmp_err_i;
pmp_err_q <= pmp_err_d;
end
end
@ -451,7 +497,7 @@ module ibex_load_store_unit (
assign load_err_o = data_or_pmp_err & ~data_we_q;
assign store_err_o = data_or_pmp_err & data_we_q;
assign busy_o = (ls_fsm_cs == WAIT_RVALID) | (data_req_o == 1'b1);
assign busy_o = (ls_fsm_cs != IDLE);
////////////////
// Assertions //