Skip to content

Axi_burst_splitter: single beat ATOP support #370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Changed
- Add random user signal generation for llc-partition test. #315
- Update `common_verification` from `v0.2.4` to `v0.2.5`.
- Add optional single beat atop support to `axi_burst_splitter`.

## 0.39.6 - 2024-12-04
### Added
Expand Down
147 changes: 83 additions & 64 deletions src/axi_burst_splitter.sv
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,19 @@
///
/// - This module does not support wrapping ([`axi_pkg::BURST_WRAP`](package.axi_pkg)) bursts and
/// responds to such bursts with slave error(s).
/// - This module does not support atomic operations (ATOPs) and responds to ATOPs with a slave
/// error. Place an [`axi_atop_filter`](module.axi_atop_filter) before this module if upstream
/// modules can generate ATOPs.
/// - This module does not support atomic bursts and responds to these with a slave
/// error. It can however support atomic single beat transactions.
module axi_burst_splitter #(
// Maximum number of AXI read bursts outstanding at the same time
parameter int unsigned MaxReadTxns = 32'd0,
// Maximum number of AXI write bursts outstanding at the same time
parameter int unsigned MaxWriteTxns = 32'd0,
// If single-beat ATOPs are supported, will then allow unmatched ids to pass upstream
parameter bit AtopSupport = 0,
// Internal ID queue can work in two bandwidth modes: see id_queue.sv for details
parameter bit FullBW = 0,
// Internal ID queue parameter: if 1, cannot allocate a new ID if full but being popped
parameter bit CutOupPopInpGnt = 0,
// AXI Bus Types
parameter int unsigned AddrWidth = 32'd0,
parameter int unsigned DataWidth = 32'd0,
Expand Down Expand Up @@ -100,10 +103,10 @@ module axi_burst_splitter #(
function bit txn_supported(axi_pkg::atop_t atop, axi_pkg::burst_t burst, axi_pkg::cache_t cache,
axi_pkg::len_t len);
// Single-beat transactions do not need splitting, so all are supported.
if (len == '0) return 1'b1;
if (len == '0 && (AtopSupport || atop[5:4] == axi_pkg::ATOP_NONE)) return 1'b1;
// Wrapping bursts are currently not supported.
if (burst == axi_pkg::BURST_WRAP) return 1'b0;
// ATOPs are not supported.
// ATOP bursts are not supported.
if (atop != '0) return 1'b0;
// The AXI Spec (A3.4.1) only allows splitting non-modifiable transactions ..
if (!axi_pkg::modifiable(cache)) begin
Expand All @@ -123,7 +126,7 @@ module axi_burst_splitter #(
.axi_req_t ( axi_req_t ),
.axi_resp_t ( axi_resp_t ),
.Resp ( axi_pkg::RESP_SLVERR ),
.ATOPs ( 1'b0 ), // The burst splitter does not support ATOPs.
.ATOPs ( 1'b1 ),
.MaxTrans ( 1 ) // Splitting bursts implies a low-performance bus.
) i_err_slv (
.clk_i,
Expand All @@ -139,16 +142,19 @@ module axi_burst_splitter #(
logic w_cnt_dec, w_cnt_req, w_cnt_gnt, w_cnt_err;
axi_pkg::len_t w_cnt_len;
axi_burst_splitter_ax_chan #(
.chan_t ( aw_chan_t ),
.IdWidth ( IdWidth ),
.MaxTxns ( MaxWriteTxns ),
.FullBW ( FullBW )
.chan_t ( aw_chan_t ),
.IdWidth ( IdWidth ),
.MaxTxns ( MaxWriteTxns ),
.FullBW ( FullBW ),
.CutOupPopInpGnt ( CutOupPopInpGnt ),
.AtopSupport ( AtopSupport )
) i_axi_burst_splitter_aw_chan (
.clk_i,
.rst_ni,
.ax_i ( act_req.aw ),
.ax_valid_i ( act_req.aw_valid ),
.ax_ready_o ( act_resp.aw_ready ),
.ax_is_atop_i ( |act_req.aw.atop ),
.ax_o ( mst_req_o.aw ),
.ax_valid_o ( mst_req_o.aw_valid ),
.ax_ready_i ( mst_resp_i.aw_ready ),
Expand Down Expand Up @@ -191,24 +197,22 @@ module axi_burst_splitter #(
BReady: begin
if (mst_resp_i.b_valid) begin
w_cnt_req = 1'b1;
if (w_cnt_gnt) begin
if (w_cnt_len == 8'd0) begin
act_resp.b = mst_resp_i.b;
if (w_cnt_err) begin
act_resp.b.resp = axi_pkg::RESP_SLVERR;
end
act_resp.b_valid = 1'b1;
w_cnt_dec = 1'b1;
if (act_req.b_ready) begin
mst_req_o.b_ready = 1'b1;
end else begin
b_state_d = BWait;
b_err_d = w_cnt_err;
end
end else begin
if (w_cnt_len == 8'd0 || !w_cnt_gnt) begin
act_resp.b = mst_resp_i.b;
if (w_cnt_err && w_cnt_gnt) begin
act_resp.b.resp = axi_pkg::RESP_SLVERR;
end
act_resp.b_valid = 1'b1;
w_cnt_dec = w_cnt_gnt;
if (act_req.b_ready) begin
mst_req_o.b_ready = 1'b1;
w_cnt_dec = 1'b1;
end else begin
b_state_d = BWait;
b_err_d = w_cnt_err;
end
end else begin
mst_req_o.b_ready = 1'b1;
w_cnt_dec = 1'b1;
end
end
end
Expand All @@ -234,16 +238,19 @@ module axi_burst_splitter #(
logic r_cnt_dec, r_cnt_req, r_cnt_gnt;
axi_pkg::len_t r_cnt_len;
axi_burst_splitter_ax_chan #(
.chan_t ( ar_chan_t ),
.IdWidth ( IdWidth ),
.MaxTxns ( MaxReadTxns ),
.FullBW ( FullBW )
.chan_t ( ar_chan_t ),
.IdWidth ( IdWidth ),
.MaxTxns ( MaxReadTxns ),
.FullBW ( FullBW ),
.CutOupPopInpGnt ( CutOupPopInpGnt ),
.AtopSupport ( AtopSupport )
) i_axi_burst_splitter_ar_chan (
.clk_i,
.rst_ni,
.ax_i ( act_req.ar ),
.ax_valid_i ( act_req.ar_valid ),
.ax_ready_o ( act_resp.ar_ready ),
.ax_is_atop_i ( 1'b0 ),
.ax_o ( mst_req_o.ar ),
.ax_valid_o ( mst_req_o.ar_valid ),
.ax_ready_i ( mst_resp_i.ar_ready ),
Expand Down Expand Up @@ -278,20 +285,18 @@ module axi_burst_splitter #(
// that burst, ...
if (mst_resp_i.r_valid) begin
r_cnt_req = 1'b1;
if (r_cnt_gnt) begin
r_last_d = (r_cnt_len == 8'd0);
act_resp.r.last = r_last_d;
// Decrement the counter.
r_cnt_dec = 1'b1;
// Try to forward the beat upstream.
act_resp.r_valid = 1'b1;
if (act_req.r_ready) begin
// Acknowledge downstream.
mst_req_o.r_ready = 1'b1;
end else begin
// Wait for upstream to become ready.
r_state_d = RWait;
end
r_last_d = (r_cnt_len == 8'd0) || !r_cnt_gnt;
act_resp.r.last = r_last_d;
// Decrement the counter if there is one
r_cnt_dec = r_cnt_gnt;
// Try to forward the beat upstream.
act_resp.r_valid = 1'b1;
if (act_req.r_ready) begin
// Acknowledge downstream.
mst_req_o.r_ready = 1'b1;
end else begin
// Wait for upstream to become ready.
r_state_d = RWait;
end
end
end
Expand Down Expand Up @@ -328,10 +333,16 @@ module axi_burst_splitter #(
assume property (@(posedge clk_i) slv_req_i.ar_valid |->
txn_supported('0, slv_req_i.ar.burst, slv_req_i.ar.cache, slv_req_i.ar.len)
) else $warning("Unsupported AR transaction received, returning slave error!");
assume property (@(posedge clk_i) slv_req_i.aw_valid |->
slv_req_i.aw.atop == '0 || slv_req_i.aw.atop[5:4] == axi_pkg::ATOP_ATOMICSTORE
) else $fatal(1, "Unsupported ATOP that gives rise to a R response received,\
cannot respond in protocol-compliant manner!");
if (AtopSupport) begin : gen_atop_support_assertion
assume property (@(posedge clk_i) slv_req_i.aw_valid |->
slv_req_i.aw.atop[5:4] == axi_pkg::ATOP_NONE ||
slv_req_i.aw.len == '0
) else $fatal(1, {"ATOP burst received, which are not supported: returning slave error!"});
end else begin : gen_no_atop_support_assertion
assume property (@(posedge clk_i) slv_req_i.aw_valid |->
slv_req_i.aw.atop[5:4] == axi_pkg::ATOP_NONE
) else $fatal(1, {"ATOP received but not supported: returning slave error!"});
end
// Outputs
assert property (@(posedge clk_i) mst_req_o.aw_valid |-> mst_req_o.aw.len == '0)
else $fatal(1, "AW burst longer than a single beat emitted!");
Expand All @@ -351,6 +362,8 @@ module axi_burst_splitter_ax_chan #(
parameter int unsigned IdWidth = 0,
parameter int unsigned MaxTxns = 0,
parameter bit FullBW = 0,
parameter bit CutOupPopInpGnt = 0,
parameter bit AtopSupport = 0,
parameter type id_t = logic[IdWidth-1:0]
) (
input logic clk_i,
Expand All @@ -359,6 +372,7 @@ module axi_burst_splitter_ax_chan #(
input chan_t ax_i,
input logic ax_valid_i,
output logic ax_ready_o,
input logic ax_is_atop_i,
output chan_t ax_o,
output logic ax_valid_o,
input logic ax_ready_i,
Expand All @@ -375,9 +389,11 @@ module axi_burst_splitter_ax_chan #(

logic cnt_alloc_req, cnt_alloc_gnt;
axi_burst_splitter_counters #(
.MaxTxns ( MaxTxns ),
.FullBW ( FullBW ),
.IdWidth ( IdWidth )
.MaxTxns ( MaxTxns ),
.FullBW ( FullBW ),
.CutOupPopInpGnt ( CutOupPopInpGnt ),
.AtopSupport ( AtopSupport ),
.IdWidth ( IdWidth )
) i_axi_burst_splitter_counters (
.clk_i,
.rst_ni,
Expand Down Expand Up @@ -412,7 +428,7 @@ module axi_burst_splitter_ax_chan #(
ax_valid_o = 1'b1;
// As soon as downstream is ready, allocate a counter and acknowledge upstream.
if (ax_ready_i) begin
cnt_alloc_req = 1'b1;
cnt_alloc_req = AtopSupport ? ~ax_is_atop_i : 1'b1;
ax_ready_o = 1'b1;
end
end else begin // Splitting required.
Expand All @@ -426,9 +442,9 @@ module axi_burst_splitter_ax_chan #(
ax_valid_o = 1'b1;
if (ax_ready_i) begin
// Reduce number of bursts still to be sent by one and increment address.
ax_d.len--;
if (ax_d.burst == axi_pkg::BURST_INCR) begin
ax_d.addr += (1 << ax_d.size);
ax_d.len = ax_i.len - 1;
if (ax_i.burst == axi_pkg::BURST_INCR) begin
ax_d.addr = ax_i.addr + (1 << ax_i.size);
end
end
state_d = Busy;
Expand All @@ -446,9 +462,9 @@ module axi_burst_splitter_ax_chan #(
state_d = Idle;
end else begin
// Otherwise, continue with the next burst.
ax_d.len--;
ax_d.len = ax_q.len - 1;
if (ax_q.burst == axi_pkg::BURST_INCR) begin
ax_d.addr += (1 << ax_q.size);
ax_d.addr = ax_q.addr + (1 << ax_q.size);
end
end
end
Expand All @@ -466,7 +482,9 @@ endmodule
module axi_burst_splitter_counters #(
parameter int unsigned MaxTxns = 0,
parameter bit FullBW = 0,
parameter bit CutOupPopInpGnt = 0,
parameter int unsigned IdWidth = 0,
parameter bit AtopSupport = 0,
parameter type id_t = logic [IdWidth-1:0]
) (
input logic clk_i,
Expand Down Expand Up @@ -522,10 +540,11 @@ module axi_burst_splitter_counters #(
logic idq_inp_req, idq_inp_gnt,
idq_oup_gnt, idq_oup_valid, idq_oup_pop;
id_queue #(
.ID_WIDTH ( $bits(id_t) ),
.CAPACITY ( MaxTxns ),
.data_t ( cnt_idx_t ),
.FULL_BW ( FullBW )
.ID_WIDTH ( $bits(id_t) ),
.CAPACITY ( MaxTxns ),
.data_t ( cnt_idx_t ),
.FULL_BW ( FullBW ),
.CUT_OUP_POP_INP_GNT ( CutOupPopInpGnt )
) i_idq (
.clk_i,
.rst_ni,
Expand All @@ -549,7 +568,7 @@ module axi_burst_splitter_counters #(
assign alloc_gnt_o = idq_inp_gnt & |(cnt_free);
assign cnt_gnt_o = idq_oup_gnt & idq_oup_valid;
logic [8:0] read_len;
assign read_len = cnt_oup[cnt_r_idx] - 1;
assign read_len = cnt_gnt_o ? cnt_oup[cnt_r_idx] - 1 : '0;
assign cnt_len_o = read_len[7:0];

assign idq_oup_pop = cnt_req_i & cnt_gnt_o & cnt_dec_i & (cnt_len_o == 8'd0);
Expand All @@ -563,7 +582,7 @@ module axi_burst_splitter_counters #(
end
always_comb begin
err_d = err_q;
cnt_err_o = err_q[cnt_r_idx];
cnt_err_o = err_q[cnt_r_idx] & cnt_gnt_o;
if (cnt_req_i && cnt_gnt_o && cnt_set_err_i) begin
err_d[cnt_r_idx] = 1'b1;
cnt_err_o = 1'b1;
Expand All @@ -578,7 +597,7 @@ module axi_burst_splitter_counters #(

`ifndef VERILATOR
// pragma translate_off
assume property (@(posedge clk_i) idq_oup_gnt |-> idq_oup_valid)
assume property (@(posedge clk_i) idq_oup_gnt |-> idq_oup_valid || AtopSupport)
else $warning("Invalid output at ID queue, read not granted!");
// pragma translate_on
`endif
Expand Down