Support multiple outstanding stores (#1474)

This commit is contained in:
Luca Colagrande 2023-10-19 22:03:54 +02:00 committed by GitHub
parent 9b55204283
commit 74675b400c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 204 additions and 98 deletions

View file

@ -54,6 +54,10 @@ module axi_adapter #(
) > 0) ? $clog2(
DATA_WIDTH / CVA6Cfg.AxiDataWidth
) : 1;
localparam MAX_OUTSTANDING_AW = CVA6Cfg.MaxOutstandingStores;
localparam MAX_OUTSTANDING_AW_CNT_WIDTH = $clog2(MAX_OUTSTANDING_AW + 1) > 0 ? $clog2(MAX_OUTSTANDING_AW + 1) : 1;
typedef logic [MAX_OUTSTANDING_AW_CNT_WIDTH-1:0] outstanding_aw_cnt_t;
enum logic [3:0] {
IDLE,
@ -80,6 +84,11 @@ module axi_adapter #(
// save the atomic operation and size
ariane_pkg::amo_t amo_d, amo_q;
logic [1:0] size_d, size_q;
// outstanding write transactions counter
outstanding_aw_cnt_t outstanding_aw_cnt_q, outstanding_aw_cnt_d;
logic any_outstanding_aw;
assign any_outstanding_aw = outstanding_aw_cnt_q != '0;
always_comb begin : axi_fsm
// Default assignments
@ -143,6 +152,8 @@ module axi_adapter #(
size_d = size_q;
index = '0;
outstanding_aw_cnt_d = outstanding_aw_cnt_q;
case (state_q)
IDLE: begin
@ -152,72 +163,81 @@ module axi_adapter #(
// is this a read or write?
// write
if (we_i) begin
// the data is valid
axi_req_o.aw_valid = 1'b1;
axi_req_o.w_valid = 1'b1;
// store-conditional requires exclusive access
axi_req_o.aw.lock = amo_i == ariane_pkg::AMO_SC;
// its a single write
if (type_i == ariane_pkg::SINGLE_REQ) begin
// only a single write so the data is already the last one
axi_req_o.w.last = 1'b1;
// single req can be granted here
gnt_o = axi_resp_i.aw_ready & axi_resp_i.w_ready;
case ({
axi_resp_i.aw_ready, axi_resp_i.w_ready
})
2'b11: state_d = WAIT_B_VALID;
2'b01: state_d = WAIT_AW_READY;
2'b10: state_d = WAIT_LAST_W_READY;
default: state_d = IDLE;
endcase
// multiple outstanding write transactions are only
// allowed if they are guaranteed not to be reordered
// i.e. same ID
if (!any_outstanding_aw || ((id_i == id_q) && (amo_i == ariane_pkg::AMO_NONE))) begin
// the data is valid
axi_req_o.aw_valid = 1'b1;
axi_req_o.w_valid = 1'b1;
// store-conditional requires exclusive access
axi_req_o.aw.lock = amo_i == ariane_pkg::AMO_SC;
// its a single write
if (type_i == ariane_pkg::SINGLE_REQ) begin
// only a single write so the data is already the last one
axi_req_o.w.last = 1'b1;
// single req can be granted here
gnt_o = axi_resp_i.aw_ready & axi_resp_i.w_ready;
case ({
axi_resp_i.aw_ready, axi_resp_i.w_ready
})
2'b11: state_d = WAIT_B_VALID;
2'b01: state_d = WAIT_AW_READY;
2'b10: state_d = WAIT_LAST_W_READY;
default: state_d = IDLE;
endcase
if (axi_resp_i.aw_ready) begin
amo_d = amo_i;
size_d = size_i;
if (axi_resp_i.aw_ready) begin
id_d = id_i;
amo_d = amo_i;
size_d = size_i;
end
// its a request for the whole cache line
end else begin
// bursts of AMOs unsupported
assert (amo_i == ariane_pkg::AMO_NONE)
else $fatal("Bursts of atomic operations are not supported");
axi_req_o.aw.len = BURST_SIZE[7:0]; // number of bursts to do
axi_req_o.w.data = wdata_i[0];
axi_req_o.w.strb = be_i[0];
if (axi_resp_i.w_ready) cnt_d = BURST_SIZE[ADDR_INDEX-1:0] - 1;
else cnt_d = BURST_SIZE[ADDR_INDEX-1:0];
case ({
axi_resp_i.aw_ready, axi_resp_i.w_ready
})
2'b11: state_d = WAIT_LAST_W_READY;
2'b01: state_d = WAIT_LAST_W_READY_AW_READY;
2'b10: state_d = WAIT_LAST_W_READY;
default: ;
endcase
end
// its a request for the whole cache line
end else begin
// bursts of AMOs unsupported
assert (amo_i == ariane_pkg::AMO_NONE)
else $fatal("Bursts of atomic operations are not supported");
axi_req_o.aw.len = BURST_SIZE[7:0]; // number of bursts to do
axi_req_o.w.data = wdata_i[0];
axi_req_o.w.strb = be_i[0];
if (axi_resp_i.w_ready) cnt_d = BURST_SIZE[ADDR_INDEX-1:0] - 1;
else cnt_d = BURST_SIZE[ADDR_INDEX-1:0];
case ({
axi_resp_i.aw_ready, axi_resp_i.w_ready
})
2'b11: state_d = WAIT_LAST_W_READY;
2'b01: state_d = WAIT_LAST_W_READY_AW_READY;
2'b10: state_d = WAIT_LAST_W_READY;
default: ;
endcase
end
// read
end else begin
// only multiple outstanding write transactions are allowed
if (!any_outstanding_aw) begin
axi_req_o.ar_valid = 1'b1;
// load-reserved requires exclusive access
axi_req_o.ar.lock = amo_i == ariane_pkg::AMO_LR;
axi_req_o.ar_valid = 1'b1;
// load-reserved requires exclusive access
axi_req_o.ar.lock = amo_i == ariane_pkg::AMO_LR;
gnt_o = axi_resp_i.ar_ready;
if (type_i != ariane_pkg::SINGLE_REQ) begin
assert (amo_i == ariane_pkg::AMO_NONE)
else $fatal("Bursts of atomic operations are not supported");
gnt_o = axi_resp_i.ar_ready;
if (type_i != ariane_pkg::SINGLE_REQ) begin
assert (amo_i == ariane_pkg::AMO_NONE)
else $fatal("Bursts of atomic operations are not supported");
axi_req_o.ar.len = BURST_SIZE[7:0];
cnt_d = BURST_SIZE[ADDR_INDEX-1:0];
end
axi_req_o.ar.len = BURST_SIZE[7:0];
cnt_d = BURST_SIZE[ADDR_INDEX-1:0];
end
if (axi_resp_i.ar_ready) begin
state_d = (type_i == ariane_pkg::SINGLE_REQ) ? WAIT_R_VALID : WAIT_R_VALID_MULTIPLE;
addr_offset_d = addr_i[ADDR_INDEX-1+3:3];
if (axi_resp_i.ar_ready) begin
state_d = (type_i == ariane_pkg::SINGLE_REQ) ? WAIT_R_VALID : WAIT_R_VALID_MULTIPLE;
addr_offset_d = addr_i[ADDR_INDEX-1+3:3];
end
end
end
end
@ -230,6 +250,7 @@ module axi_adapter #(
if (axi_resp_i.aw_ready) begin
gnt_o = 1'b1;
state_d = WAIT_B_VALID;
id_d = id_i;
amo_d = amo_i;
size_d = size_i;
end
@ -314,7 +335,7 @@ module axi_adapter #(
id_o = axi_resp_i.b.id;
// Write is valid
if (axi_resp_i.b_valid) begin
if (axi_resp_i.b_valid && !any_outstanding_aw) begin
axi_req_o.b_ready = 1'b1;
// some atomics must wait for read data
@ -348,6 +369,13 @@ module axi_adapter #(
end
end
end
// if the request was not an atomic we can possibly issue
// other requests while waiting for the response
end else begin
if ((amo_q == ariane_pkg::AMO_NONE) && (outstanding_aw_cnt_q != MAX_OUTSTANDING_AW)) begin
state_d = IDLE;
outstanding_aw_cnt_d = outstanding_aw_cnt_q + 1;
end
end
end
@ -410,6 +438,16 @@ module axi_adapter #(
default: state_d = IDLE;
endcase
// This process handles B responses when accepting
// multiple outstanding write transactions
if (any_outstanding_aw && axi_resp_i.b_valid) begin
axi_req_o.b_ready = 1'b1;
valid_o = 1'b1;
// Right hand side contains non-registered signal as we want
// to preserve a possible increment from the WAIT_B_VALID state
outstanding_aw_cnt_d = outstanding_aw_cnt_d - 1;
end
end
// ----------------
@ -418,21 +456,23 @@ module axi_adapter #(
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
// start in flushing state and initialize the memory
state_q <= IDLE;
cnt_q <= '0;
cache_line_q <= '0;
addr_offset_q <= '0;
id_q <= '0;
amo_q <= ariane_pkg::AMO_NONE;
size_q <= '0;
state_q <= IDLE;
cnt_q <= '0;
cache_line_q <= '0;
addr_offset_q <= '0;
id_q <= '0;
amo_q <= ariane_pkg::AMO_NONE;
size_q <= '0;
outstanding_aw_cnt_q <= '0;
end else begin
state_q <= state_d;
cnt_q <= cnt_d;
cache_line_q <= cache_line_d;
addr_offset_q <= addr_offset_d;
id_q <= id_d;
amo_q <= amo_d;
size_q <= size_d;
state_q <= state_d;
cnt_q <= cnt_d;
cache_line_q <= cache_line_d;
addr_offset_q <= addr_offset_d;
id_q <= id_d;
amo_q <= amo_d;
size_q <= size_d;
outstanding_aw_cnt_q <= outstanding_aw_cnt_d;
end
end

View file

@ -360,8 +360,13 @@ module cache_ctrl
// got a grant so go to valid
if (bypass_gnt_i) begin
state_d = WAIT_REFILL_VALID;
// if this was a write we still need to give a grant to the store unit
if (mem_req_q.we) req_port_o.data_gnt = 1'b1;
// if this was a write we still need to give a grant to the store unit.
// We can also avoid waiting for the response valid, this signal is
// currently not used by the store unit
if (mem_req_q.we) begin
req_port_o.data_gnt = 1'b1;
state_d = IDLE;
end
end
if (miss_gnt_i && !mem_req_q.we) state_d = WAIT_CRITICAL_WORD;

View file

@ -550,9 +550,10 @@ module miss_handler
// Arbitrate bypass ports
// ----------------------
axi_adapter_arbiter #(
.NR_PORTS(NR_BYPASS_PORTS),
.req_t (bypass_req_t),
.rsp_t (bypass_rsp_t)
.NR_PORTS (NR_BYPASS_PORTS),
.MAX_OUTSTANDING_REQ(CVA6Cfg.MaxOutstandingStores),
.req_t (bypass_req_t),
.rsp_t (bypass_rsp_t)
) i_bypass_arbiter (
.clk_i (clk_i),
.rst_ni(rst_ni),
@ -674,6 +675,7 @@ endmodule
//
module axi_adapter_arbiter #(
parameter NR_PORTS = 4,
parameter MAX_OUTSTANDING_REQ = 0,
parameter type req_t = std_cache_pkg::bypass_req_t,
parameter type rsp_t = std_cache_pkg::bypass_rsp_t
) (
@ -687,6 +689,10 @@ module axi_adapter_arbiter #(
input rsp_t rsp_i
);
localparam MAX_OUTSTANDING_CNT_WIDTH = $clog2(MAX_OUTSTANDING_REQ + 1) > 0 ? $clog2(MAX_OUTSTANDING_REQ + 1) : 1;
typedef logic [MAX_OUTSTANDING_CNT_WIDTH-1:0] outstanding_cnt_t;
enum logic {
IDLE,
SERVING
@ -695,9 +701,20 @@ module axi_adapter_arbiter #(
req_t req_d, req_q;
logic [NR_PORTS-1:0] sel_d, sel_q;
outstanding_cnt_t outstanding_cnt_d, outstanding_cnt_q;
logic [NR_PORTS-1:0] req_flat;
logic any_unselected_port_valid;
for (genvar i = 0; i < NR_PORTS; i++) begin : gen_req_flat
assign req_flat[i] = req_i[i].req;
end
assign any_unselected_port_valid = |(req_flat & ~(1 << sel_q));
always_comb begin
sel_d = sel_q;
outstanding_cnt_d = outstanding_cnt_q;
state_d = state_q;
req_d = req_q;
@ -706,6 +723,7 @@ module axi_adapter_arbiter #(
rsp_o = '0;
rsp_o[sel_q].rdata = rsp_i.rdata;
rsp_o[sel_q].valid = rsp_i.valid;
case (state_q)
@ -722,12 +740,38 @@ module axi_adapter_arbiter #(
req_d = req_i[sel_d];
req_o = req_i[sel_d];
rsp_o[sel_d].gnt = req_i[sel_d].req;
// Count outstanding transactions, i.e. requests which have been
// granted but response hasn't arrived yet
if (req_o.req && rsp_i.gnt) begin
req_d.req = 1'b0;
outstanding_cnt_d += 1;
end
end
SERVING: begin
// Count outstanding transactions, i.e. requests which have been
// granted but response hasn't arrived yet
if (req_o.req && rsp_i.gnt) begin
req_d.req = 1'b0;
outstanding_cnt_d += 1;
end
if (rsp_i.valid) begin
outstanding_cnt_d -= 1;
rsp_o[sel_q].valid = 1'b1;
state_d = IDLE;
if ((outstanding_cnt_d == 0) && (!req_o.req || rsp_i.gnt)) state_d = IDLE;
end
// We can accept multiple outstanding transactions from same port.
// To ensure fairness, we allow this only if all other ports are idle
if ((!req_o.req || rsp_i.gnt) && !any_unselected_port_valid &&
(outstanding_cnt_d != MAX_OUTSTANDING_REQ)) begin
if (req_i[sel_q].req) begin
req_d = req_i[sel_q];
rsp_o[sel_q].gnt = 1'b1;
state_d = SERVING;
end
end
end
@ -737,13 +781,15 @@ module axi_adapter_arbiter #(
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
state_q <= IDLE;
sel_q <= '0;
req_q <= '0;
state_q <= IDLE;
sel_q <= '0;
req_q <= '0;
outstanding_cnt_q <= '0;
end else begin
state_q <= state_d;
sel_q <= sel_d;
req_q <= req_d;
state_q <= state_d;
sel_q <= sel_d;
req_q <= req_d;
outstanding_cnt_q <= outstanding_cnt_d;
end
end
// ------------
@ -758,7 +804,7 @@ module axi_adapter_arbiter #(
$error("There was a grant without a rvalid");
$stop();
end
// assert that there is no grant without a request
// assert that there is no grant without a request or outstanding transactions
assert property (@(negedge clk_i) rsp_i.gnt |-> req_o.req)
else begin
$error("There was a grant without a request.");

View file

@ -210,7 +210,8 @@ module cva6
CVA6Cfg.ExecuteRegionLength,
CVA6Cfg.NrCachedRegionRules,
CVA6Cfg.CachedRegionAddrBase,
CVA6Cfg.CachedRegionLength
CVA6Cfg.CachedRegionLength,
CVA6Cfg.MaxOutstandingStores
};

View file

@ -107,6 +107,8 @@ package config_pkg;
logic [NrMaxRules-1:0][63:0] CachedRegionAddrBase;
/// Bit mask which bits to consider when matching the rule.
logic [NrMaxRules-1:0][63:0] CachedRegionLength;
/// Maximum number of outstanding stores.
int unsigned MaxOutstandingStores;
} cva6_cfg_t;

View file

@ -134,7 +134,8 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -133,7 +133,8 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -134,7 +134,8 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -134,6 +134,7 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -134,7 +134,8 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -134,7 +134,8 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -133,7 +133,8 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -134,7 +134,8 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -134,7 +134,8 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -134,7 +134,8 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -133,6 +133,7 @@ package cva6_config_pkg;
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
CachedRegionLength: 1024'({64'h40000000}),
MaxOutstandingStores: unsigned'(7)
};
endpackage

View file

@ -205,7 +205,8 @@ localparam config_pkg::cva6_cfg_t CVA6Cfg = '{
// cached region
NrCachedRegionRules: unsigned'(1),
CachedRegionAddrBase: 1024'({ariane_soc::DRAMBase}),
CachedRegionLength: 1024'({ariane_soc::DRAMLength})
CachedRegionLength: 1024'({ariane_soc::DRAMLength}),
MaxOutstandingStores: unsigned'(7)
};
localparam type rvfi_instr_t = logic;