Skip to content

Commit

Permalink
RoB-less chimneys (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
fischeti authored Oct 4, 2023
1 parent dc9ef7d commit 2cd3458
Show file tree
Hide file tree
Showing 13 changed files with 688 additions and 619 deletions.
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

0 comments on commit 2cd3458

Please sign in to comment.