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

RoB-less chimneys #9

Merged
merged 13 commits into from
Oct 4, 2023
1 change: 1 addition & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ sources:
- src/floo_wormhole_arbiter.sv
- src/floo_simple_rob.sv
- src/floo_rob.sv
- src/floo_rob_wrapper.sv
- src/floo_meta_buffer.sv
# Level 2
- src/floo_axi_chimney.sv
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

- Table based routing support in `narrow_wide_chimney`
- Support for different number of inputs and outputs in `narrow_wide_router`
- Add wrapper for different types of Reorder Buffers in chimneys
- Support for simple RoB-less chimneys with ID counters

### Fixed

Expand Down
244 changes: 85 additions & 159 deletions src/floo_axi_chimney.sv

Large diffs are not rendered by default.

229 changes: 151 additions & 78 deletions src/floo_meta_buffer.sv
Original file line number Diff line number Diff line change
Expand Up @@ -17,119 +17,192 @@ module floo_meta_buffer #(
parameter bit AtopSupport = 1'b1,
/// Number of outstanding atomic requests
parameter int MaxAtomicTxns = 32'd1,
/// External Atomic ID
parameter bit ExtAtomicId = 1'b0,
/// Information to be buffered for responses
parameter type buf_t = logic,
/// ID type for outgoing requests
parameter type id_t = logic,
/// ID width of incoming requests
parameter int IdInWidth = 32'd4,
/// ID width of outgoing requests
parameter int IdOutWidth = 32'd2,
/// AXI request channel
parameter type axi_req_t = logic,
/// AXI response channel
parameter type axi_rsp_t = logic,
/// ID type for incoming requests
localparam type id_in_t = logic[IdInWidth-1:0],
/// ID type for outgoing responses
localparam type id_out_t = logic[IdOutWidth-1:0],
/// Constant ID for non-atomic requests
localparam id_t NonAtomicId = '1
localparam id_out_t NonAtomicId = '1
) (
input logic clk_i,
input logic rst_ni,
input logic test_enable_i,
input logic req_push_i,
input logic req_valid_i,
input buf_t req_buf_i,
input logic req_is_atop_i,
input id_t req_atop_id_i,
output logic req_full_o,
output id_t req_id_o,
input logic rsp_pop_i,
input id_t rsp_id_i,
output buf_t rsp_buf_o
input axi_req_t axi_req_i,
output axi_rsp_t axi_rsp_o,
output axi_req_t axi_req_o,
input axi_rsp_t axi_rsp_i,
input buf_t aw_buf_i,
input buf_t ar_buf_i,
output buf_t r_buf_o,
output buf_t b_buf_o
);

buf_t no_atop_buf_out;
logic no_atop_buf_full;
logic rsp_is_atop;
logic ar_no_atop_buf_full, aw_no_atop_buf_full;
logic ar_no_atop_push, aw_no_atop_push;
logic ar_no_atop_pop, aw_no_atop_pop;
logic is_atop_r_rsp, is_atop_b_rsp;
logic is_atop_aw, atop_has_r_rsp;

assign rsp_is_atop = AtopSupport && (rsp_id_i != NonAtomicId);
buf_t no_atop_r_buf, no_atop_b_buf;
buf_t [MaxAtomicTxns-1:0] atop_r_buf, atop_b_buf;

fifo_v3 #(
.FALL_THROUGH ( 1'b0 ),
.DEPTH ( MaxTxns ),
.dtype ( buf_t )
) i_no_atop_fifo (
) i_ar_no_atop_fifo (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( test_enable_i ),
.full_o ( no_atop_buf_full ),
.empty_o ( ),
.usage_o ( ),
.data_i ( req_buf_i ),
.push_i ( req_push_i && !req_is_atop_i ),
.data_o ( no_atop_buf_out ),
.pop_i ( rsp_pop_i && !rsp_is_atop )
.flush_i ( 1'b0 ),
.testmode_i ( test_enable_i ),
.full_o ( ar_no_atop_buf_full ),
.empty_o ( ),
.usage_o ( ),
.data_i ( ar_buf_i ),
.push_i ( ar_no_atop_push ),
.data_o ( no_atop_r_buf ),
.pop_i ( ar_no_atop_pop )
);

fifo_v3 #(
.FALL_THROUGH ( 1'b0 ),
.DEPTH ( MaxTxns ),
.dtype ( buf_t )
) i_aw_no_atop_fifo (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( test_enable_i ),
.full_o ( aw_no_atop_buf_full ),
.empty_o ( ),
.usage_o ( ),
.data_i ( aw_buf_i ),
.push_i ( aw_no_atop_push ),
.data_o ( no_atop_b_buf ),
.pop_i ( aw_no_atop_pop )
);

// Non-atomic AR's
assign ar_no_atop_push = axi_req_o.ar_valid && axi_rsp_i.ar_ready;
assign ar_no_atop_pop = axi_rsp_o.r_valid && axi_req_i.r_ready && axi_rsp_o.r.last &&
!is_atop_r_rsp;
// Non-atomic AW's
assign is_atop_aw = axi_req_i.aw_valid && axi_req_i.aw.atop[5:4] != axi_pkg::ATOP_NONE;
assign aw_no_atop_push = axi_req_o.aw_valid && axi_rsp_i.aw_ready && !is_atop_aw;
assign aw_no_atop_pop = axi_rsp_o.b_valid && axi_req_i.b_ready && !is_atop_b_rsp;

assign is_atop_r_rsp = axi_rsp_i.r_valid && axi_rsp_i.r.id != NonAtomicId;
assign is_atop_b_rsp = axi_rsp_i.b_valid && axi_rsp_i.b.id != NonAtomicId;
`ASSERT(NoAtopSupport, !(!AtopSupport && is_atop_aw),
"Atomics not supported, but atomic request received!")

assign r_buf_o = (is_atop_r_rsp && AtopSupport)? atop_r_buf[axi_rsp_i.r.id] : no_atop_r_buf;
assign b_buf_o = (is_atop_b_rsp && AtopSupport)? atop_b_buf[axi_rsp_i.b.id] : no_atop_b_buf;

if (AtopSupport) begin : gen_atop_support

logic [MaxAtomicTxns-1:0] atop_req_out_push;
logic [MaxAtomicTxns-1:0] atop_req_out_pop;
logic [MaxAtomicTxns-1:0] atop_req_out_full;
logic [MaxAtomicTxns-1:0] atop_req_out_empty;
buf_t [MaxAtomicTxns-1:0] atop_data_out;
logic [MaxAtomicTxns-1:0] ar_atop_reg_full, aw_atop_reg_full;
logic [MaxAtomicTxns-1:0] ar_atop_reg_empty, aw_atop_reg_empty;
logic [MaxAtomicTxns-1:0] ar_atop_reg_push, aw_atop_reg_push;
logic [MaxAtomicTxns-1:0] ar_atop_reg_pop, aw_atop_reg_pop;
logic [MaxAtomicTxns-1:0] available_atop_ids;
logic no_atop_id_available;

id_t req_atop_id;
assign atop_has_r_rsp = axi_req_i.aw.atop[axi_pkg::ATOP_R_RESP];
assign available_atop_ids = ar_atop_reg_empty & aw_atop_reg_empty;
assign no_atop_id_available = (available_atop_ids == '0);

stream_register #(
.T(buf_t)
) i_ar_atop_regs [MaxAtomicTxns-1:0] (
.clk_i,
.rst_ni,
.clr_i ( '0 ),
.testmode_i ( test_enable_i ),
.valid_i ( ar_atop_reg_push ),
.ready_o ( ar_atop_reg_empty ),
.data_i ( ar_buf_i ),
.valid_o ( ar_atop_reg_full ),
.ready_i ( ar_atop_reg_pop ),
.data_o ( atop_r_buf )
);

stream_register #(
.T(buf_t)
) i_atop_regs [MaxAtomicTxns-1:0] (
) i_aw_atop_regs [MaxAtomicTxns-1:0] (
.clk_i,
.rst_ni,
.clr_i ( '0 ),
.testmode_i ( test_enable_i ),
.valid_i ( atop_req_out_push ),
.ready_o ( atop_req_out_empty ),
.data_i ( req_buf_i ),
.valid_o ( atop_req_out_full ),
.ready_i ( atop_req_out_pop ),
.data_o ( atop_data_out )
.clr_i ( '0 ),
.testmode_i ( test_enable_i ),
.valid_i ( aw_atop_reg_push ),
.ready_o ( aw_atop_reg_empty ),
.data_i ( aw_buf_i ),
.valid_o ( aw_atop_reg_full ),
.ready_i ( aw_atop_reg_pop ),
.data_o ( atop_b_buf )
);

if (ExtAtomicId) begin : gen_ext_atop_id
// Atomics need to register an r response with the same ID
// as the B response. The ID is given by the AW buffer from externally.
assign req_atop_id = req_atop_id_i;
end else begin : gen_atop_id
typedef logic [cf_math_pkg::idx_width(MaxAtomicTxns)-1:0] lzc_cnt_t;
lzc_cnt_t lzc_cnt, lzc_cnt_q;
logic [MaxAtomicTxns-1:0] next_free_slots;
assign next_free_slots = ~(atop_req_out_full | atop_req_out_push) | atop_req_out_pop;
lzc #(
.WIDTH (MaxAtomicTxns)
) i_lzc (
.in_i ( next_free_slots ),
.cnt_o ( lzc_cnt ),
.empty_o ( )
);
// Next free slot needs to be registered to have a stable ID at the AXI interface
assign req_atop_id = lzc_cnt_q;
`FFL(lzc_cnt_q, lzc_cnt, req_push_i && req_is_atop_i || !req_valid_i, '0, clk_i, rst_ni)
end
typedef logic [cf_math_pkg::idx_width(MaxAtomicTxns)-1:0] atop_req_id_t;
atop_req_id_t lzc_cnt_q, lzc_cnt_d;
atop_req_id_t atop_req_id;
logic atop_req_pending_q, atop_req_pending_d;

lzc #(
.WIDTH (MaxAtomicTxns)
) i_lzc (
.in_i ( available_atop_ids ),
.cnt_o ( lzc_cnt_d ),
.empty_o ( )
);

assign atop_req_id = (atop_req_pending_q)? lzc_cnt_q : lzc_cnt_d;
assign atop_req_pending_d = is_atop_aw && axi_req_o.aw_valid && !axi_rsp_i.aw_ready;

`FF(atop_req_pending_q, atop_req_pending_d, '0)
`FFL(lzc_cnt_q, lzc_cnt_d, !atop_req_pending_q, '0)

always_comb begin
atop_req_out_push = '0;
atop_req_out_pop = '0;
atop_req_out_push[req_atop_id] = req_push_i && req_is_atop_i;
atop_req_out_pop[rsp_id_i] = rsp_pop_i && rsp_is_atop;
ar_atop_reg_push = '0;
aw_atop_reg_push = '0;
ar_atop_reg_pop = '0;
aw_atop_reg_pop = '0;
ar_atop_reg_push[atop_req_id] = is_atop_aw && atop_has_r_rsp &&
axi_req_o.aw_valid && axi_rsp_i.aw_ready;
aw_atop_reg_push[atop_req_id] = is_atop_aw && axi_req_o.aw_valid && axi_rsp_i.aw_ready;
ar_atop_reg_pop[axi_rsp_i.r.id] = is_atop_r_rsp &&
axi_rsp_o.r_valid && axi_req_i.r_ready && axi_rsp_o.r.last;
aw_atop_reg_pop[axi_rsp_i.b.id] = is_atop_b_rsp && axi_rsp_o.b_valid && axi_req_i.b_ready;
end

// Atomics: The ID is the first empty slot in the buffer
// Non-atomics: The ID is the constant `NonAtomicId`
assign req_id_o = (req_is_atop_i)? req_atop_id : NonAtomicId;
assign rsp_buf_o = (rsp_is_atop)? atop_data_out[rsp_id_i] : no_atop_buf_out;
assign req_full_o = (req_is_atop_i)? &atop_req_out_full : no_atop_buf_full;
end else begin : gen_no_atop_support
assign req_id_o = NonAtomicId;
assign rsp_buf_o = no_atop_buf_out;
assign req_full_o = no_atop_buf_full;
always_comb begin
axi_req_o = axi_req_i;
axi_rsp_o = axi_rsp_i;
// Use fixed ID for non-atomic requests and unique ID for atomic requests
axi_req_o.ar.id = NonAtomicId;
axi_req_o.aw.id = (is_atop_aw && AtopSupport)? atop_req_id : NonAtomicId;
// Use original, buffered ID again for responses
axi_rsp_o.r.id = (is_atop_r_rsp && AtopSupport)?
atop_r_buf[axi_rsp_i.r.id] : no_atop_r_buf.id;
axi_rsp_o.b.id = (is_atop_b_rsp && AtopSupport)?
atop_b_buf[axi_rsp_i.b.id] : no_atop_b_buf.id;
axi_req_o.ar_valid = axi_req_i.ar_valid && !ar_no_atop_buf_full;
axi_rsp_o.ar_ready = axi_rsp_i.ar_ready && !ar_no_atop_buf_full;
axi_req_o.aw_valid = axi_req_i.aw_valid && ((is_atop_aw && AtopSupport)?
!no_atop_id_available : !aw_no_atop_buf_full);
axi_rsp_o.aw_ready = axi_rsp_i.aw_ready && ((is_atop_aw && AtopSupport)?
!no_atop_id_available : !aw_no_atop_buf_full);
end
end

`ASSERT(NoAtopSupport, !(!AtopSupport && (req_push_i && req_is_atop_i)))

endmodule
Loading
Loading