Skip to content
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

axi_lite_dw_converter: Enable user size pass from AXI converter #317

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
174 changes: 145 additions & 29 deletions src/axi_lite_dw_converter.sv
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ module axi_lite_dw_converter #(
parameter int unsigned AxiSlvPortDataWidth = 32'd0,
/// AXI4-Lite data width of the master port.
parameter int unsigned AxiMstPortDataWidth = 32'd0,
/// Whether to read a transaction size from the AR user bits
parameter bit UserArSize = 1'b0,
/// Whether to read a transaction size from the AW user bits
parameter bit UserAwSize = 1'b0,
/// Least significant bit (LSB) of size in AR user fields
parameter int unsigned UserArSizeLsb = 32'd0,
/// Least significant bit (LSB) of size in AW user fields
parameter int unsigned UserAwSizeLsb = 32'd0,
/// Assuming AW size in user field, maximum number of inflight reads.
parameter int unsigned UserArMaxTxns = 32'd0,
/// Assuming AW size in user field, maximum number of inflight writes.
parameter int unsigned UserAwMaxTxns = 32'd0,
/// AXI4-Lite AW channel struct type. This is for both ports the same.
parameter type axi_lite_aw_t = logic,
/// AXI4-Lite W channel struct type of the slave port.
Expand Down Expand Up @@ -132,6 +144,7 @@ module axi_lite_dw_converter #(
// Input spill register of the AW channel.
axi_lite_aw_t aw_chan_spill;
logic aw_chan_spill_valid, aw_chan_spill_ready;
logic w_progress, aw_progress, b_progress;

spill_register #(
.T ( axi_lite_aw_t ),
Expand All @@ -147,19 +160,35 @@ module axi_lite_dw_converter #(
.data_o ( aw_chan_spill )
);

sel_t aw_sel_q, aw_sel_d;
sel_t aw_sel_q, aw_sel_d, aw_sel_out;
logic aw_sel_load;
// AW channel output assignment
always_comb begin : proc_aw_chan_oup
mst_req_o.aw = aw_chan_spill;
mst_req_o.aw.addr = out_address(aw_chan_spill.addr, aw_sel_q);
mst_req_o.aw.addr = out_address(aw_chan_spill.addr, aw_sel_out);
end

assign aw_progress = aw_chan_spill_valid & mst_res_i.aw_ready;

if (UserAwSize) begin : gen_user_aw
// Couple AW, W, and B FIFO and make requests selectively
sel_t aw_sel_end, aw_sel_base;
assign aw_sel_end = UserAwSize ?
(sel_t'(1) << aw_chan_spill.user[UserAwSizeLsb:+axi_pkg::SizeWidth]) >> SelOffset : '0;
assign aw_sel_base = aw_chan_spill.aw.addr[SelOffset+:SelWidth] & sel_t'(aw_sel_end -1);
assign aw_sel_out = aw_sel_q + aw_sel_base;
assign mst_req_o.aw_valid = w_progress & b_progress & aw_chan_spill_valid;
assign aw_chan_spill_ready = w_progress & b_progress & mst_res_i.aw_ready &
((&aw_sel_q) | (aw_sel_d == aw_sel_end));
end else begin : gen_no_user_aw
// AW, W, and B are uncoupled
assign aw_sel_out = aw_sel_q;
assign mst_req_o.aw_valid = aw_chan_spill_valid;
assign aw_chan_spill_ready = mst_res_i.aw_ready & (&aw_sel_q);
end
// Slave port aw is valid, if there is something in the spill register.
assign mst_req_o.aw_valid = aw_chan_spill_valid;
assign aw_chan_spill_ready = mst_res_i.aw_ready & (&aw_sel_q);

assign aw_sel_load = mst_req_o.aw_valid & mst_res_i.aw_ready;
assign aw_sel_d = sel_t'(aw_sel_q + 1'b1);
assign aw_sel_d = sel_t'(aw_sel_load ? '0 : aw_sel_q + 1'b1);
`FFLARN(aw_sel_q, aw_sel_d, aw_sel_load, '0, clk_i, rst_ni)

// Input spill register of the W channel.
Expand All @@ -180,41 +209,82 @@ module axi_lite_dw_converter #(
);

// Data multiplexer on the W channel
sel_t w_sel_q, w_sel_d;
logic w_sel_load;
sel_t w_sel;
// W channel output assignment
assign mst_req_o.w = axi_lite_mst_w_t'{
data: w_chan_spill.data[w_sel_q*AxiMstPortDataWidth+:AxiMstPortDataWidth],
strb: w_chan_spill.strb[w_sel_q*AxiMstPortStrbWidth+:AxiMstPortStrbWidth],
data: w_chan_spill.data[w_sel*AxiMstPortDataWidth+:AxiMstPortDataWidth],
strb: w_chan_spill.strb[w_sel*AxiMstPortStrbWidth+:AxiMstPortStrbWidth],
default: '0
};
assign mst_req_o.w_valid = w_chan_spill_valid;
assign w_chan_spill_ready = mst_res_i.w_ready & (&w_sel_q);

assign w_sel_load = mst_req_o.w_valid & mst_res_i.w_ready;
assign w_sel_d = sel_t'(w_sel_q + 1'b1);
`FFLARN(w_sel_q, w_sel_d, w_sel_load, '0, clk_i, rst_ni)
assign w_progress = w_chan_spill_valid & mst_res_i.w_ready;

if (UserAwSize) begin : gen_user_aw_w
// We must couple the AW, W, and B FIFO; adopt AW channel counts here
assign mst_req_o.w_valid = aw_progress & b_progress & w_chan_spill_valid;
assign w_chan_spill_ready = aw_progress & b_progress & mst_res_i.w_ready;
assign w_sel = aw_sel_out;
end else begin : gen_no_user_aw_w
// The W channel can operate uncoupled
sel_t w_sel_q, w_sel_d;
logic w_sel_load;
assign w_sel_load = mst_req_o.w_valid & mst_res_i.w_ready;
assign w_sel_d = sel_t'(w_sel_q + 1'b1);
`FFLARN(w_sel_q, w_sel_d, w_sel_load, '0, clk_i, rst_ni)
assign mst_req_o.w_valid = w_chan_spill_valid;
assign w_chan_spill_ready = mst_res_i.w_ready & (&w_sel_q);
assign w_sel = w_sel_q;
end

// B response aggregation
// Slave port B output is the aggregated error of the last few B responses.
sel_t b_sel_q, b_sel_d;
axi_pkg::resp_t b_resp_q, b_resp_d;
logic b_resp_load;
logic b_end;

assign slv_res_o.b = axi_lite_b_t'{
resp: b_resp_q | mst_res_i.b.resp,
default: '0
};

if (UserAwSize) begin : gen_user_aw_b
// When an upstream AW/W pair completes, store the expected downstream B count
sel_t b_out;
stream_fifo #(
.FALL_THROUGH ( 1'b0 ),
.DEPTH ( UserAwMaxTxns ),
.T ( sel_t ),
) i_b_count_fifo (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.usage_o ( ),
.data_i ( aw_sel_out ),
.valid_i ( mst_req_o.aw_valid & mst_resp_i.aw_ready ),
.ready_o ( b_progress ),
.data_o ( b_out ),
.valid_o ( ), // TODO: Assert true when B comes in (`b_resp_load`)
.ready_i ( b_end )
);
assign b_end = (&b_sel_q) | (b_sel_d == b_out);
end else begin : gen_no_user_aw_b
// Simply count payloads as for AW and W
assign b_end = (&b_sel_q);
assign b_progress = 1'b1;
end

// Output is valid, if it is the last b response for the wide W, we have something
// in the B FIFO and the B response is valid from the master port.
assign slv_res_o.b_valid = mst_res_i.b_valid & (&b_sel_q);
assign slv_res_o.b_valid = mst_res_i.b_valid & b_end;

// Assign the b_channel ready output. The master port is ready if something is in the
// B FIFO. Except, if it is the last one which should do a response on the slave port.
assign mst_req_o.b_ready = (&b_sel_q) ? slv_req_i.b_ready : 1'b1;
assign mst_req_o.b_ready = b_end ? slv_req_i.b_ready : 1'b1;
// B channel error response retention FF
assign b_sel_d = sel_t'(b_sel_q + 1'b1);
assign b_resp_d = (&b_sel_q) ? axi_pkg::RESP_OKAY : (b_resp_q | mst_res_i.b.resp);
assign b_sel_d = sel_t'(b_end ? '0 : b_sel_q + 1'b1);
assign b_resp_d = b_end ? axi_pkg::RESP_OKAY : (b_resp_q | mst_res_i.b.resp);
assign b_resp_load = mst_res_i.b_valid & mst_req_o.b_ready;
`FFLARN(b_sel_q, b_sel_d, b_resp_load, '0, clk_i, rst_ni)
`FFLARN(b_resp_q, b_resp_d, b_resp_load, axi_pkg::RESP_OKAY, clk_i, rst_ni)
Expand All @@ -223,6 +293,7 @@ module axi_lite_dw_converter #(
// Input spill register of the AW channel.
axi_lite_ar_t ar_chan_spill;
logic ar_chan_spill_valid, ar_chan_spill_ready;
logic ar_progress, r_progress,

spill_register #(
.T ( axi_lite_ar_t ),
Expand All @@ -238,32 +309,77 @@ module axi_lite_dw_converter #(
.data_o ( ar_chan_spill )
);

sel_t ar_sel_q, ar_sel_d;
sel_t ar_sel_q, ar_sel_d, ar_sel_out;
logic ar_sel_load;
// AR channel output assignment
always_comb begin : proc_ar_chan_oup
mst_req_o.ar = ar_chan_spill;
mst_req_o.ar.addr = out_address(ar_chan_spill.addr, ar_sel_q);
mst_req_o.ar.addr = out_address(ar_chan_spill.addr, ar_sel_out);
end

assign ar_progress = ar_chan_spill_valid & mst_res_i.ar_ready;

if (UserAwSize) begin : gen_user_ar
// Couple AR and R FIFO
sel_t ar_sel_end, ar_sel_base;
assign ar_sel_end = UserAwSize ?
(sel_t'(1) << ar_chan_spill.user[UserArSizeLsb:+axi_pkg::SizeWidth]) >> SelOffset : '0;
assign ar_sel_base = ar_chan_spill.ar.addr[SelOffset+:SelWidth] & sel_t'(ar_sel_end -1);
assign ar_sel_out = ar_sel_q + ar_sel_base;
assign mst_req_o.aw_valid = r_progress & ar_chan_spill_valid;
assign aw_chan_spill_ready = r_progress & mst_res_i.ar_ready &
((&ar_sel_q) | (ar_sel_d == ar_sel_end));
end else begin : gen_no_user_ar
// AR and R are uncoupled
assign ar_sel_out = ar_sel_q;
assign mst_req_o.ar_valid = ar_chan_spill_valid;
assign ar_chan_spill_ready = mst_res_i.ar_ready & (&ar_sel_q);
end
// Slave port aw is valid, if there is something in the spill register.
assign mst_req_o.ar_valid = ar_chan_spill_valid;
assign ar_chan_spill_ready = mst_res_i.ar_ready & (&ar_sel_q);

assign ar_sel_load = mst_req_o.ar_valid & mst_res_i.ar_ready;
assign ar_sel_d = sel_t'(ar_sel_q + 1'b1);
assign ar_sel_d = sel_t'(ar_sel_load ? '0 : ar_sel_q + 1'b1);
`FFLARN(ar_sel_q, ar_sel_d, ar_sel_load, '0, clk_i, rst_ni)

// Responses have to be aggregated, one FF less, as the last data is feed directly through.
sel_t r_sel_q, r_sel_d;
logic r_sel_load;
axi_lite_mst_r_t [DownsizeFactor-2:0] r_chan_mst_q;
logic [DownsizeFactor-2:0] r_chan_mst_load;
logic r_end;

if (UserArSize) begin : gen_user_ar_r
// When an upstream AR completes, store the expected downstream R count
sel_t r_out;
stream_fifo #(
.FALL_THROUGH ( 1'b0 ),
.DEPTH ( UserArMaxTxns ),
.T ( sel_t ),
) i_r_count_fifo (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.usage_o ( ),
.data_i ( ar_sel_out ),
.valid_i ( mst_req_o.ar_valid & mst_resp_i.ar_ready ),
.ready_o ( r_progress ),
.data_o ( r_out ),
.valid_o ( ), // TODO: Assert true when R comes in (`r_sel_load`)
.ready_i ( r_end )
);
assign r_end = (&r_sel_q) | (r_sel_d == r_out);
end else begin : gen_no_user_ar_r
// Simply count payloads as for AW and W
assign r_end = (&r_sel_q);
assign r_progress = 1'b1;
end

for (genvar i = 0; unsigned'(i) < (DownsizeFactor-1); i++) begin : gen_r_chan_ff
assign r_chan_mst_load[i] = (sel_t'(i) == r_sel_q) & mst_res_i.r_valid & mst_req_o.r_ready;
`FFLARN(r_chan_mst_q[i], mst_res_i.r, r_chan_mst_load[i], axi_lite_mst_r_t'{default: '0}, clk_i, rst_ni)
end
assign r_sel_load = mst_res_i.r_valid & mst_req_o.r_ready;
assign r_sel_d = sel_t'(r_sel_q + 1'b1);
assign r_sel_d = sel_t'(r_sel_load ? '0 : r_sel_q + 1'b1);
`FFLARN(r_sel_q, r_sel_d, r_sel_load, '0, clk_i, rst_ni)

always_comb begin : proc_r_chan_oup
Expand All @@ -273,16 +389,16 @@ module axi_lite_dw_converter #(
};
// Response is the OR of all responses
for (int unsigned i = 0; i < (DownsizeFactor-1); i++) begin
slv_res_o.r.resp = slv_res_o.r.resp | r_chan_mst_q[i].resp;
slv_res_o.r.resp = slv_res_o.r.resp | (r_sel_q >= sel_t'(i) ? r_chan_mst_q[i].resp : '0);
slv_res_o.r.data[i*AxiMstPortDataWidth+:AxiMstPortDataWidth] = r_chan_mst_q[i].data;
end
// The highest bits of the data can be directly the master port.
slv_res_o.r.data[(DownsizeFactor-1)*AxiMstPortDataWidth+:AxiMstPortDataWidth] =
mst_res_i.r.data;
end

assign slv_res_o.r_valid = (&r_sel_q) ? mst_res_i.r_valid : 1'b0;
assign mst_req_o.r_ready = (&r_sel_q) ? slv_req_i.r_ready : 1'b1;
assign slv_res_o.r_valid = r_end ? mst_res_i.r_valid : 1'b0;
assign mst_req_o.r_ready = r_end ? slv_req_i.r_ready : 1'b1;

end else if (AxiMstPortDataWidth > AxiSlvPortDataWidth) begin : gen_upsizer
// The upsize factor determines the amount of replication.
Expand Down
23 changes: 21 additions & 2 deletions src/axi_to_axi_lite.sv
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@ module axi_to_axi_lite #(
parameter int unsigned AxiUserWidth = 32'd0,
parameter int unsigned AxiMaxWriteTxns = 32'd0,
parameter int unsigned AxiMaxReadTxns = 32'd0,
parameter bit FallThrough = 1'b1, // FIFOs in Fall through mode in ID reflect
/// FIFOs in Fall through mode in ID reflect
parameter bit FallThrough = 1'b1,
/// Whether to include the lost size field on Ax user bits
parameter bit UserArSize = 1'b0,
parameter bit UserAwSize = 1'b0,
/// Least significant bit (LSB) of size in Ax user fields
parameter int unsigned UserArSizeLsb = 32'd0,
parameter int unsigned UserAwSizeLsb = 32'd0,
parameter type full_req_t = logic,
parameter type full_resp_t = logic,
parameter type lite_req_t = logic,
Expand All @@ -42,6 +49,9 @@ module axi_to_axi_lite #(
full_req_t filtered_req, splitted_req;
full_resp_t filtered_resp, splitted_resp;

// lite bus declarations
lite_req_t reflected_req;

// atomics adapter so that atomics can be resolved
axi_atop_filter #(
.AxiIdWidth ( AxiIdWidth ),
Expand Down Expand Up @@ -92,10 +102,19 @@ module axi_to_axi_lite #(
.test_i ( test_i ),
.slv_req_i ( splitted_req ),
.slv_resp_o ( splitted_resp ),
.mst_req_o ( mst_req_o ),
.mst_req_o ( reflected_req ),
.mst_resp_i ( mst_resp_i )
);

// Inject AR and AW size from full bus on user bits if requested
always_comb begin
mst_req_o = reflected_req;
if (UserArSize)
mst_req_o.ar.user[UserArSizeLsb+:axi_pkg::SizeWidth] = splitted_req.ar.size;
of (UserAwSize)
mst_req_o.aw.user[UserAwSizeLsb+:axi_pkg::SizeWidth] = splitted_req.ar.size;
end

// Assertions, check params
// pragma translate_off
`ifndef VERILATOR
Expand Down
Loading