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_XBAR pipeline #116

Closed
wants to merge 9 commits into from
Closed
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `axi_test::rand_axi_lite_slave` and `axi_test::rand_axi_lite_master`: Change type of address and
data width parameters (`AW` and `DW`) from `int` to `int unsigned`. Same rationale as for
`AXI_BUS` (et al.) above.
- `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents
AWs from being issued to one master port while Ws from another burst are ongoing to another
master port. This is required to prevents deadlocks due to circular waits downstream.
- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts`
in the crossed connections in the xbar between the demuxes and muxes.
- `axi_pkg`: Add documentation to `xbar_cfg_t`.

### Fixed
- `axi_demux`: Break combinatorial simulation loop.
Expand Down
23 changes: 23 additions & 0 deletions scripts/run_vsim.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,29 @@ exec_test() {
done
done
;;
axi_xbar)
for GEN_ATOP in 0 1; do
for NUM_MST in 1 6; do
for NUM_SLV in 2 9; do
for MST_ID_USE in 3 5; do
MST_ID=5
for DATA_WIDTH in 64 256; do
for PIPE in 0 1; do
call_vsim tb_axi_xbar -t 1ns -voptargs="+acc" \
-gTbNumMasters=$NUM_MST \
-gTbNumSlaves=$NUM_SLV \
-gTbAxiIdWidthMasters=$MST_ID \
-gTbAxiIdUsed=$MST_ID_USE \
-gTbAxiDataWidth=$DATA_WIDTH \
-gTbPipeline=$PIPE \
-gTbEnAtop=$GEN_ATOP
done
done
done
done
done
done
;;
*)
call_vsim tb_$1 -t 1ns -coverage -voptargs="+acc +cover=bcesfx"
;;
Expand Down
89 changes: 50 additions & 39 deletions src/axi_demux.sv
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ module axi_demux #(
parameter bit SpillAr = 1'b1,
parameter bit SpillR = 1'b0,
// Dependent parameters, DO NOT OVERRIDE!
parameter int unsigned SelectWidth = (NoMstPorts > 32'd1) ? $clog2(NoMstPorts) : 32'd1,
parameter int unsigned SelectWidth = cf_math_pkg::idx_width(NoMstPorts),
parameter type select_t = logic [SelectWidth-1:0]
) (
input logic clk_i,
Expand All @@ -49,7 +49,8 @@ module axi_demux #(
input resp_t [NoMstPorts-1:0] mst_resps_i
);

localparam int unsigned IdCounterWidth = MaxTrans > 1 ? $clog2(MaxTrans) : 1;
localparam int unsigned IdCounterWidth = cf_math_pkg::idx_width(MaxTrans);
typedef logic [IdCounterWidth-1:0] id_cnt_t;

//--------------------------------------
// Typedefs for the FIFOs / Queues
Expand Down Expand Up @@ -87,14 +88,14 @@ module axi_demux #(
// AW ID counter
select_t lookup_aw_select;
logic aw_select_occupied, aw_id_cnt_full;
logic aw_push;
// Upon an ATOP load, inject IDs from the AW into the AR channel
logic atop_inject;

// W FIFO: stores the decision to which master W beats should go
logic w_fifo_pop;
logic w_fifo_full, w_fifo_empty;
select_t w_select;
// W select counter: stores the decision to which master W beats should go
select_t w_select, w_select_q;
logic w_select_valid;
id_cnt_t w_open;
logic w_cnt_up, w_cnt_down;

// Register which locks the AW valid signal
logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock;
Expand Down Expand Up @@ -177,9 +178,9 @@ module axi_demux #(
lock_aw_valid_d = lock_aw_valid_q;
load_aw_lock = 1'b0;
// AW ID counter and W FIFO
aw_push = 1'b0;
w_cnt_up = 1'b0;
// ATOP injection into ar counter
atop_inject = 1'b0;
atop_inject = 1'b0;
// we had an arbitration decision, the valid is locked, wait for the transaction
if (lock_aw_valid_q) begin
aw_valid = 1'b1;
Expand All @@ -193,14 +194,18 @@ module axi_demux #(
end else begin
// Process can start handling a transaction if its `i_aw_id_counter` and `w_fifo` have
// space in them. Further check if we could inject something on the AR channel.
if (!aw_id_cnt_full && !w_fifo_full && !ar_id_cnt_full) begin
// there is a valid AW vector make the id lookup and go further, if it passes
if (slv_aw_valid && (!aw_select_occupied ||
(slv_aw_chan_select.aw_select == lookup_aw_select))) begin
if (!aw_id_cnt_full && (w_open != {IdCounterWidth{1'b1}}) && !ar_id_cnt_full) begin
// There is a valid AW vector make the id lookup and go further, if it passes.
// Also stall if previous transmitted AWs still have active W's in flight.
// This prevents deadlocking of the W channel. The counters are there for the
// Handling of the B responses.
if (slv_aw_valid &&
((w_open == '0) || (w_select == slv_aw_chan_select.aw_select)) &&
(!aw_select_occupied || (slv_aw_chan_select.aw_select == lookup_aw_select))) begin
// connect the handshake
aw_valid = 1'b1;
// push arbitration to the W FIFO regardless, do not wait for the AW transaction
aw_push = 1'b1;
w_cnt_up = 1'b1;
// on AW transaction
if (aw_ready) begin
slv_aw_ready = 1'b1;
Expand Down Expand Up @@ -234,31 +239,34 @@ module axi_demux #(
.inject_i ( 1'b0 ),
.push_axi_id_i ( slv_aw_chan_select.aw_chan.id[0+:AxiLookBits] ),
.push_mst_select_i ( slv_aw_chan_select.aw_select ),
.push_i ( aw_push ),
.push_i ( w_cnt_up ),
.pop_axi_id_i ( slv_b_chan.id[0+:AxiLookBits] ),
.pop_i ( slv_b_valid & slv_b_ready )
);
// pop from ID counter on outward transaction

// FIFO to save W selection
fifo_v3 #(
.FALL_THROUGH ( FallThrough ),
.DEPTH ( MaxTrans ),
.dtype ( select_t )
) i_w_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i( test_i ),
.full_o ( w_fifo_full ),
.empty_o ( w_fifo_empty ),
.usage_o ( ),
.data_i ( slv_aw_chan_select.aw_select ),
.push_i ( aw_push ), // controlled from proc_aw_chan
.data_o ( w_select ), // where the w beat should go
.pop_i ( w_fifo_pop ) // controlled from proc_w_chan

// This counter steers the demultiplexer of the W channel.
// `w_select` determines, which handshaking is connected.
// AWs are only forwarded, if the counter is empty, or `w_select_q` is the same as
// `slv_aw_chan_select.aw_select`.
counter #(
.WIDTH ( IdCounterWidth ),
.STICKY_OVERFLOW ( 1'b0 )
) i_counter_open_w (
.clk_i,
.rst_ni,
.clear_i ( 1'b0 ),
.en_i ( w_cnt_up ^ w_cnt_down ),
.load_i ( 1'b0 ),
.down_i ( w_cnt_down ),
.d_i ( '0 ),
.q_o ( w_open ),
.overflow_o ( /*not used*/ )
);

`FFLARN(w_select_q, slv_aw_chan_select.aw_select, w_cnt_up, select_t'(0), clk_i, rst_ni)
assign w_select = (|w_open) ? w_select_q : slv_aw_chan_select.aw_select;
assign w_select_valid = w_cnt_up | (|w_open);

//--------------------------------------
// W Channel
//--------------------------------------
Expand Down Expand Up @@ -442,16 +450,16 @@ module axi_demux #(
.idx_o ( )
);

assign ar_ready = ar_valid & mst_resps_i[slv_ar_chan_select.ar_select].ar_ready;
assign aw_ready = aw_valid & mst_resps_i[slv_aw_chan_select.aw_select].aw_ready;
assign ar_ready = ar_valid & mst_resps_i[slv_ar_chan_select.ar_select].ar_ready;
assign aw_ready = aw_valid & mst_resps_i[slv_aw_chan_select.aw_select].aw_ready;

// process that defines the individual demuxes and assignments for the arbitration
// as mst_reqs_o has to be drivem from the same always comb block!
always_comb begin
// default assignments
mst_reqs_o = '0;
slv_w_ready = 1'b0;
w_fifo_pop = 1'b0;
w_cnt_down = 1'b0;

for (int unsigned i = 0; i < NoMstPorts; i++) begin
// AW channel
Expand All @@ -464,10 +472,10 @@ module axi_demux #(
// W channel
mst_reqs_o[i].w = slv_w_chan;
mst_reqs_o[i].w_valid = 1'b0;
if (!w_fifo_empty && (w_select == i)) begin
if (w_select_valid && (w_select == select_t'(i))) begin
mst_reqs_o[i].w_valid = slv_w_valid;
slv_w_ready = mst_resps_i[i].w_ready;
w_fifo_pop = slv_w_valid & mst_resps_i[i].w_ready & slv_w_chan.last;
w_cnt_down = slv_w_valid & mst_resps_i[i].w_ready & slv_w_chan.last;
end

// B channel
Expand Down Expand Up @@ -529,6 +537,9 @@ module axi_demux #(
internal_aw_select: assert property( @(posedge clk_i)
(aw_valid |-> slv_aw_chan_select.aw_select < NoMstPorts))
else $fatal(1, "slv_aw_chan_select.aw_select illegal while aw_valid.");
w_underflow: assert property( @(posedge clk_i)
((w_open == '0) && (w_cnt_up ^ w_cnt_down) |-> !w_cnt_down)) else
$fatal(1, "W counter underflowed!");
`endif
`endif
// pragma translate_on
Expand Down
26 changes: 26 additions & 0 deletions src/axi_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -389,16 +389,42 @@ package axi_pkg;

/// Configuration for `axi_xbar`.
typedef struct packed {
/// Number of slave ports of the crossbar.
/// This many master modules are connected to it.
int unsigned NoSlvPorts;
/// Number of master ports of the crossbar.
/// This many slave modules are connected to it.
int unsigned NoMstPorts;
/// Maximum number of open transactions each master connected to the crossbar can have in
/// flight at the same time.
int unsigned MaxMstTrans;
/// Maximum number of open transactions each slave connected to the crossbar can have in
/// flight at the same time.
int unsigned MaxSlvTrans;
/// Determine if the internal FIFOs of the crossbar are instantiated in fallthrough mode.
/// 0: No fallthrough
/// 1: Fallthrough
bit FallThrough;
/// The Latency mode of the xbar. This determines if the channels on the ports have
/// a spill register instantiated.
/// Example configurations are provided with the enum `xbar_latency_e`.
xbar_latency_e LatencyMode;
/// This is the number of `axi_multicut` stages instantiated in the line cross of the channels.
/// Having multiple stages can potentially add a large number of FFs!
int unsigned PipelineStages;
/// AXI ID width of the salve ports. The ID width of the master ports is determined
/// Automatically. See `axi_mux` for details.
int unsigned AxiIdWidthSlvPorts;
/// The used ID portion to determine if a different salve is used for the same ID.
/// See `axi_demux` for details.
int unsigned AxiIdUsedSlvPorts;
/// AXI4+ATOP address field width.
int unsigned AxiAddrWidth;
/// AXI4+ATOP data field width.
int unsigned AxiDataWidth;
/// The number of address rules defined for routing of the transactions.
/// Each master port can have multiple rules, should have however at least one.
/// If a transaction can not be routed the xbar will answer with an `axi_pkg::RESP_DECERR`.
int unsigned NoAddrRules;
} xbar_cfg_t;

Expand Down
Loading