diff --git a/CHANGELOG.md b/CHANGELOG.md index af25fb49c..73639c142 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index b9ab4f4bb..90fabe1ed 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -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" ;; diff --git a/src/axi_demux.sv b/src/axi_demux.sv index b5ce581bf..126ffc530 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -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, @@ -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 @@ -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; @@ -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; @@ -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; @@ -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 //-------------------------------------- @@ -442,8 +450,8 @@ 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! @@ -451,7 +459,7 @@ module axi_demux #( // 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 @@ -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 @@ -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 diff --git a/src/axi_pkg.sv b/src/axi_pkg.sv index f5e6835f4..67339e263 100644 --- a/src/axi_pkg.sv +++ b/src/axi_pkg.sv @@ -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; diff --git a/src/axi_xbar.sv b/src/axi_xbar.sv index d285f63c9..0d3aeac55 100644 --- a/src/axi_xbar.sv +++ b/src/axi_xbar.sv @@ -13,38 +13,89 @@ // axi_xbar: Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. // See `doc/axi_xbar.md` for the documentation, including the definition of parameters and ports. module axi_xbar #( - parameter axi_pkg::xbar_cfg_t Cfg = '0, - parameter bit ATOPs = 1'b1, - parameter type slv_aw_chan_t = logic, - parameter type mst_aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type slv_b_chan_t = logic, - parameter type mst_b_chan_t = logic, - parameter type slv_ar_chan_t = logic, - parameter type mst_ar_chan_t = logic, - parameter type slv_r_chan_t = logic, - parameter type mst_r_chan_t = logic, - parameter type slv_req_t = logic, - parameter type slv_resp_t = logic, - parameter type mst_req_t = logic, - parameter type mst_resp_t = logic, - parameter type rule_t = axi_pkg::xbar_rule_64_t + /// Configuration struct for the crossbar see `axi_pkg` for fields and definitions. + parameter axi_pkg::xbar_cfg_t Cfg = '0, + /// Enable atomic operations support. + parameter bit ATOPs = 1'b1, + /// AXI4+ATOP AW channel struct type for the slave ports. + parameter type slv_aw_chan_t = logic, + /// AXI4+ATOP AW channel struct type for the master ports. + parameter type mst_aw_chan_t = logic, + /// AXI4+ATOP W channel struct type for all ports. + parameter type w_chan_t = logic, + /// AXI4+ATOP B channel struct type for the slave ports. + parameter type slv_b_chan_t = logic, + /// AXI4+ATOP B channel struct type for the master ports. + parameter type mst_b_chan_t = logic, + /// AXI4+ATOP AR channel struct type for the slave ports. + parameter type slv_ar_chan_t = logic, + /// AXI4+ATOP AR channel struct type for the master ports. + parameter type mst_ar_chan_t = logic, + /// AXI4+ATOP R channel struct type for the slave ports. + parameter type slv_r_chan_t = logic, + /// AXI4+ATOP R channel struct type for the master ports. + parameter type mst_r_chan_t = logic, + /// AXI4+ATOP request struct type for the slave ports. + parameter type slv_req_t = logic, + /// AXI4+ATOP response struct type for the slave ports. + parameter type slv_resp_t = logic, + /// AXI4+ATOP request struct type for the master ports. + parameter type mst_req_t = logic, + /// AXI4+ATOP response struct type for the master ports. + parameter type mst_resp_t = logic, + /// Address rule type for the address decoders from `common_cells:addr_decode`. + /// + /// Example types are provided in `axi_pkg`. + /// + /// Required struct fields: + /// + /// typedef struct packed { + /// int unsigned idx; + /// axi_addr_t start_addr; + /// axi_addr_t end_addr; + /// } rule_t; + parameter type rule_t = axi_pkg::xbar_rule_64_t, + /// Dependent parameter, do **not** override! + /// Width of the index specifying a master port. + parameter int unsigned DefaultIdxWidth = cf_math_pkg::idx_width(Cfg.NoMstPorts), + /// Dependent parameter, do **not** override! + /// Type of index for a default master port. + parameter type default_idx_t = logic [DefaultIdxWidth-1:0] ) ( - input logic clk_i, - input logic rst_ni, - input logic test_i, - input slv_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, - output slv_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, - output mst_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, - input mst_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, - input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, - input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, - input logic [Cfg.NoSlvPorts-1:0][$clog2(Cfg.NoMstPorts)-1:0] default_mst_port_i + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Testmode enable, active high. + input logic test_i, + /// AXI4+ATOP requests to the slave ports. + input slv_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, + /// AXI4+ATOP responses of the slave ports. + output slv_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, + /// AXI4+ATOP requests of the master ports. + output mst_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, + /// AXI4+ATOP responses to the master ports. + input mst_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, + /// Address map array input for the crossbar. This map is global for the whole module. + /// It is used for routing the transactions to the respective master ports. + /// Each master port can have multiple different rules. + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + /// Enables a default master port for each slave port. When this is enabled unmapped + /// transactions get issued at the master port given by `default_mst_port_i`. + /// + /// When not used, tie to `'0`. + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, + /// For each slave port the default index where the transaction should be routed, if + /// for this slave port the default index functionality is enabled by setting the + /// bit `en_default_mst_port_i[slave_port_idx]` to `'1`. + /// + /// When not used, tie to `'0`. + input default_idx_t [Cfg.NoSlvPorts-1:0] default_mst_port_i ); typedef logic [Cfg.AxiAddrWidth-1:0] addr_t; // to account for the decoding error slave - typedef logic [$clog2(Cfg.NoMstPorts + 1)-1:0] mst_port_idx_t; + typedef logic [cf_math_pkg::idx_width(Cfg.NoMstPorts + 1)-1:0] mst_port_idx_t; // signals from the axi_demuxes, one index more for decode error slv_req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs; @@ -178,8 +229,25 @@ module axi_xbar #( // cross all channels for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_xbar_slv_cross for (genvar j = 0; j < Cfg.NoMstPorts; j++) begin : gen_xbar_mst_cross - assign mst_reqs[j][i] = slv_reqs[i][j]; - assign slv_resps[i][j] = mst_resps[j][i]; + // slv_req_t pipe_req; + // slv_resp_t pipe_resp; + axi_multicut #( + .NoCuts ( Cfg.PipelineStages ), + .aw_chan_t ( slv_aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( slv_b_chan_t ), + .ar_chan_t ( slv_ar_chan_t ), + .r_chan_t ( slv_r_chan_t ), + .req_t ( slv_req_t ), + .resp_t ( slv_resp_t ) + ) i_axi_multicut_xbar_pipeline ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_reqs[i][j] ), + .slv_resp_o ( slv_resps[i][j] ), + .mst_req_o ( mst_reqs[j][i] ), + .mst_resp_i ( mst_resps[j][i] ) + ); end end diff --git a/test/tb_axi_xbar.sv b/test/tb_axi_xbar.sv index b5c034687..0d033ed0c 100644 --- a/test/tb_axi_xbar.sv +++ b/test/tb_axi_xbar.sv @@ -20,49 +20,62 @@ `include "axi/typedef.svh" `include "axi/assign.svh" -module tb_axi_xbar; - // Dut parameters - localparam int unsigned NoMasters = 6; // How many Axi Masters there are - localparam int unsigned NoSlaves = 8; // How many Axi Slaves there are - // Random master no Transactions - localparam int unsigned NoWrites = 1000; // How many writes per master - localparam int unsigned NoReads = 1000; // How many reads per master - // Random Master Atomics - localparam bit EnAtop = 1'b1; - // timing parameters +/// Testbench for the module `axi_xbar`. +module tb_axi_xbar #( + /// Number of AXI masters connected to the xbar. (Number of slave ports) + parameter int unsigned TbNumMasters = 32'd6, + /// Number of AXI slaves connected to the xbar. (Number of master ports) + parameter int unsigned TbNumSlaves = 32'd8, + /// Number of write transactions per master. + parameter int unsigned TbNumWrites = 32'd100, + /// Number of read transactions per master. + parameter int unsigned TbNumReads = 32'd100, + /// AXI4+ATOP ID wisth of the masters connected to the slave ports of the DUT. + /// The ID width of the salves is calulated depending on the xbar configuration. + parameter int unsigned TbAxiIdWidthMasters = 32'd5, + /// The used ID width of the DUT. + /// Has to be `TbAxiIdWidthMasters >= TbAxiIdUsed`. + parameter int unsigned TbAxiIdUsed = 32'd3, + /// Data width of the AXI channels. + parameter int unsigned TbAxiDataWidth = 32'd64, + /// Pipeline stages in the xbar itself. (Between Demux and mux) + parameter int unsigned TbPipeline = 32'd1, + /// Eanable ATOP generation + parameter bit TbEnAtop = 1'b1 +); + + // TB timing parameters localparam time CyclTime = 10ns; localparam time ApplTime = 2ns; localparam time TestTime = 8ns; - // axi configuration - localparam int unsigned AxiIdWidthMasters = 4; - localparam int unsigned AxiIdUsed = 3; // Has to be <= AxiIdWidthMasters - localparam int unsigned AxiIdWidthSlaves = AxiIdWidthMasters + $clog2(NoMasters); - localparam int unsigned AxiAddrWidth = 32; // Axi Address Width - localparam int unsigned AxiDataWidth = 64; // Axi Data Width - localparam int unsigned AxiStrbWidth = AxiDataWidth / 8; - localparam int unsigned AxiUserWidth = 5; - // in the bench can change this variables which are set here freely + // AXI configuration which is automatically derived. + localparam int unsigned TbAxiIdWidthSlaves = TbAxiIdWidthMasters + $clog2(TbNumMasters); + localparam int unsigned TbAxiAddrWidth = 32'd32; + localparam int unsigned TbAxiStrbWidth = TbAxiDataWidth / 8; + localparam int unsigned TbAxiUserWidth = 5; + // In the bench can change this variables which are set here freely, localparam axi_pkg::xbar_cfg_t xbar_cfg = '{ - NoSlvPorts: NoMasters, - NoMstPorts: NoSlaves, + NoSlvPorts: TbNumMasters, + NoMstPorts: TbNumSlaves, MaxMstTrans: 10, MaxSlvTrans: 6, FallThrough: 1'b0, LatencyMode: axi_pkg::CUT_ALL_AX, - AxiIdWidthSlvPorts: AxiIdWidthMasters, - AxiIdUsedSlvPorts: AxiIdUsed, - AxiAddrWidth: AxiAddrWidth, - AxiDataWidth: AxiDataWidth, - NoAddrRules: 8 + PipelineStages: TbPipeline, + AxiIdWidthSlvPorts: TbAxiIdWidthMasters, + AxiIdUsedSlvPorts: TbAxiIdUsed, + AxiAddrWidth: TbAxiAddrWidth, + AxiDataWidth: TbAxiDataWidth, + NoAddrRules: TbNumSlaves }; - typedef logic [AxiIdWidthMasters-1:0] id_mst_t; - typedef logic [AxiIdWidthSlaves-1:0] id_slv_t; - typedef logic [AxiAddrWidth-1:0] addr_t; - typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr - typedef logic [AxiDataWidth-1:0] data_t; - typedef logic [AxiStrbWidth-1:0] strb_t; - typedef logic [AxiUserWidth-1:0] user_t; + typedef logic [TbAxiIdWidthMasters-1:0] id_mst_t; + typedef logic [TbAxiIdWidthSlaves-1:0] id_slv_t; + typedef logic [TbAxiAddrWidth-1:0] addr_t; + typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr + typedef logic [TbAxiDataWidth-1:0] data_t; + typedef logic [TbAxiStrbWidth-1:0] strb_t; + typedef logic [TbAxiUserWidth-1:0] user_t; `AXI_TYPEDEF_AW_CHAN_T(aw_chan_mst_t, addr_t, id_mst_t, user_t) `AXI_TYPEDEF_AW_CHAN_T(aw_chan_slv_t, addr_t, id_slv_t, user_t) @@ -80,40 +93,43 @@ module tb_axi_xbar; `AXI_TYPEDEF_REQ_T(slv_req_t, aw_chan_slv_t, w_chan_t, ar_chan_slv_t) `AXI_TYPEDEF_RESP_T(slv_resp_t, b_chan_slv_t, r_chan_slv_t) - localparam rule_t [xbar_cfg.NoAddrRules-1:0] AddrMap = '{ - '{idx: 32'd7, start_addr: 32'h0001_0000, end_addr: 32'h0001_1000}, - '{idx: 32'd6, start_addr: 32'h0000_9000, end_addr: 32'h0001_0000}, - '{idx: 32'd5, start_addr: 32'h0000_8000, end_addr: 32'h0000_9000}, - '{idx: 32'd4, start_addr: 32'h0000_7000, end_addr: 32'h0000_8000}, - '{idx: 32'd3, start_addr: 32'h0000_6300, end_addr: 32'h0000_7000}, - '{idx: 32'd2, start_addr: 32'h0000_4000, end_addr: 32'h0000_6300}, - '{idx: 32'd1, start_addr: 32'h0000_3000, end_addr: 32'h0000_4000}, - '{idx: 32'd0, start_addr: 32'h0000_0000, end_addr: 32'h0000_3000} - }; + // Each slave has its own address range: + localparam rule_t [xbar_cfg.NoAddrRules-1:0] AddrMap = addr_map_gen(); + + function rule_t [xbar_cfg.NoAddrRules-1:0] addr_map_gen (); + for (int unsigned i = 0; i < xbar_cfg.NoAddrRules; i++) begin + addr_map_gen[i] = rule_t'{ + idx: unsigned'(i), + start_addr: i * 32'h0000_2000, + end_addr: (i+1) * 32'h0000_2000, + default: '0 + }; + end + endfunction typedef axi_test::axi_rand_master #( // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidthMasters ), - .UW ( AxiUserWidth ), + .AW ( TbAxiAddrWidth ), + .DW ( TbAxiDataWidth ), + .IW ( TbAxiIdWidthMasters ), + .UW ( TbAxiUserWidth ), // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), + .TA ( ApplTime ), + .TT ( TestTime ), // Maximum number of read and write transactions in flight - .MAX_READ_TXNS ( 20 ), - .MAX_WRITE_TXNS ( 20 ), - .AXI_ATOPS ( EnAtop ) + .MAX_READ_TXNS ( 20 ), + .MAX_WRITE_TXNS ( 20 ), + .AXI_ATOPS ( TbEnAtop ) ) axi_rand_master_t; typedef axi_test::axi_rand_slave #( // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidthSlaves ), - .UW ( AxiUserWidth ), + .AW ( TbAxiAddrWidth ), + .DW ( TbAxiDataWidth ), + .IW ( TbAxiIdWidthSlaves ), + .UW ( TbAxiUserWidth ), // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ) + .TA ( ApplTime ), + .TT ( TestTime ) ) axi_rand_slave_t; // ------------- @@ -122,62 +138,62 @@ module tb_axi_xbar; logic clk; // DUT signals logic rst_n; - logic [NoMasters-1:0] end_of_sim; + logic [TbNumMasters-1:0] end_of_sim; // master structs - mst_req_t [NoMasters-1:0] masters_req; - mst_resp_t [NoMasters-1:0] masters_resp; + mst_req_t [TbNumMasters-1:0] masters_req; + mst_resp_t [TbNumMasters-1:0] masters_resp; // slave structs - slv_req_t [NoSlaves-1:0] slaves_req; - slv_resp_t [NoSlaves-1:0] slaves_resp; + slv_req_t [TbNumSlaves-1:0] slaves_req; + slv_resp_t [TbNumSlaves-1:0] slaves_resp; // ------------------------------- // AXI Interfaces // ------------------------------- AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master [NoMasters-1:0] (); + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthMasters ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) master [TbNumMasters-1:0] (); AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_dv [NoMasters-1:0] (clk); + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthMasters ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) master_dv [TbNumMasters-1:0] (clk); AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_monitor_dv [NoMasters-1:0] (clk); - for (genvar i = 0; i < NoMasters; i++) begin : gen_conn_dv_masters + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthMasters ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) master_monitor_dv [TbNumMasters-1:0] (clk); + for (genvar i = 0; i < TbNumMasters; i++) begin : gen_conn_dv_masters `AXI_ASSIGN (master[i], master_dv[i]) `AXI_ASSIGN_TO_REQ(masters_req[i], master[i]) `AXI_ASSIGN_FROM_RESP(master[i], masters_resp[i]) end AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave [NoSlaves-1:0] (); + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthSlaves ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) slave [TbNumSlaves-1:0] (); AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_dv [NoSlaves-1:0](clk); + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthSlaves ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) slave_dv [TbNumSlaves-1:0](clk); AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_monitor_dv [NoSlaves-1:0](clk); - for (genvar i = 0; i < NoSlaves; i++) begin : gen_conn_dv_slaves + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthSlaves ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) slave_monitor_dv [TbNumSlaves-1:0](clk); + for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_conn_dv_slaves `AXI_ASSIGN(slave_dv[i], slave[i]) `AXI_ASSIGN_FROM_REQ(slave[i], slaves_req[i]) `AXI_ASSIGN_TO_RESP(slaves_resp[i], slave[i]) @@ -186,23 +202,23 @@ module tb_axi_xbar; // AXI Rand Masters and Slaves // ------------------------------- // Masters control simulation run time - for (genvar i = 0; i < NoMasters; i++) begin : gen_rand_master - static axi_rand_master_t axi_rand_master = new ( master_dv[i] ); + for (genvar i = 0; i < TbNumMasters; i++) begin : gen_rand_master initial begin + static axi_rand_master_t axi_rand_master = new ( master_dv[i] ); end_of_sim[i] <= 1'b0; axi_rand_master.add_memory_region(AddrMap[0].start_addr, AddrMap[xbar_cfg.NoAddrRules-1].end_addr, axi_pkg::DEVICE_NONBUFFERABLE); axi_rand_master.reset(); @(posedge rst_n); - axi_rand_master.run(NoReads, NoWrites); + axi_rand_master.run(TbNumReads, TbNumWrites); end_of_sim[i] <= 1'b1; end end - for (genvar i = 0; i < NoSlaves; i++) begin : gen_rand_slave - static axi_rand_slave_t axi_rand_slave = new( slave_dv[i] ); + for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_rand_slave initial begin + static axi_rand_slave_t axi_rand_slave = new( slave_dv[i] ); axi_rand_slave.reset(); @(posedge rst_n); axi_rand_slave.run(); @@ -211,13 +227,13 @@ module tb_axi_xbar; initial begin : proc_monitor static tb_axi_xbar_pkg::axi_xbar_monitor #( - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidthMasters ( AxiIdWidthMasters ), - .AxiIdWidthSlaves ( AxiIdWidthSlaves ), - .AxiUserWidth ( AxiUserWidth ), - .NoMasters ( NoMasters ), - .NoSlaves ( NoSlaves ), + .AxiAddrWidth ( TbAxiAddrWidth ), + .AxiDataWidth ( TbAxiDataWidth ), + .AxiIdWidthMasters ( TbAxiIdWidthMasters ), + .AxiIdWidthSlaves ( TbAxiIdWidthSlaves ), + .AxiUserWidth ( TbAxiUserWidth ), + .NoMasters ( TbNumMasters ), + .NoSlaves ( TbNumSlaves ), .NoAddrRules ( xbar_cfg.NoAddrRules ), .rule_t ( rule_t ), .AddrMap ( AddrMap ), @@ -251,36 +267,36 @@ module tb_axi_xbar; // DUT //----------------------------------- axi_xbar #( - .Cfg ( xbar_cfg ), + .Cfg ( xbar_cfg ), .slv_aw_chan_t( aw_chan_mst_t ), .mst_aw_chan_t( aw_chan_slv_t ), - .w_chan_t ( w_chan_t ), - .slv_b_chan_t ( b_chan_mst_t ), - .mst_b_chan_t ( b_chan_slv_t ), + .w_chan_t ( w_chan_t ), + .slv_b_chan_t ( b_chan_mst_t ), + .mst_b_chan_t ( b_chan_slv_t ), .slv_ar_chan_t( ar_chan_mst_t ), .mst_ar_chan_t( ar_chan_slv_t ), - .slv_r_chan_t ( r_chan_mst_t ), - .mst_r_chan_t ( r_chan_slv_t ), + .slv_r_chan_t ( r_chan_mst_t ), + .mst_r_chan_t ( r_chan_slv_t ), .slv_req_t ( mst_req_t ), .slv_resp_t ( mst_resp_t ), .mst_req_t ( slv_req_t ), .mst_resp_t ( slv_resp_t ), - .rule_t (rule_t ) + .rule_t ( rule_t ) ) i_xbar_dut ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .test_i ( 1'b0 ), - .slv_ports_req_i ( masters_req ), - .slv_ports_resp_o ( masters_resp ), - .mst_ports_req_o ( slaves_req ), - .mst_ports_resp_i ( slaves_resp ), - .addr_map_i ( AddrMap ), - .en_default_mst_port_i ( '0 ), - .default_mst_port_i ( '0 ) + .clk_i ( clk ), + .rst_ni ( rst_n ), + .test_i ( 1'b0 ), + .slv_ports_req_i ( masters_req ), + .slv_ports_resp_o ( masters_resp ), + .mst_ports_req_o ( slaves_req ), + .mst_ports_resp_i ( slaves_resp ), + .addr_map_i ( AddrMap ), + .en_default_mst_port_i ( '0 ), + .default_mst_port_i ( '0 ) ); // logger for master modules - for (genvar i = 0; i < NoMasters; i++) begin : gen_master_logger + for (genvar i = 0; i < TbNumMasters; i++) begin : gen_master_logger axi_chan_logger #( .TestTime ( TestTime ), // Time after clock, where sampling happens .LoggerName( $sformatf("axi_logger_master_%0d", i)), @@ -316,7 +332,7 @@ module tb_axi_xbar; ); end // logger for slave modules - for (genvar i = 0; i < NoSlaves; i++) begin : gen_slave_logger + for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_slave_logger axi_chan_logger #( .TestTime ( TestTime ), // Time after clock, where sampling happens .LoggerName( $sformatf("axi_logger_slave_%0d",i)), @@ -353,7 +369,7 @@ module tb_axi_xbar; end - for (genvar i = 0; i < NoMasters; i++) begin : gen_connect_master_monitor + for (genvar i = 0; i < TbNumMasters; i++) begin : gen_connect_master_monitor assign master_monitor_dv[i].aw_id = master[i].aw_id ; assign master_monitor_dv[i].aw_addr = master[i].aw_addr ; assign master_monitor_dv[i].aw_len = master[i].aw_len ; @@ -400,7 +416,7 @@ module tb_axi_xbar; assign master_monitor_dv[i].r_valid = master[i].r_valid ; assign master_monitor_dv[i].r_ready = master[i].r_ready ; end - for (genvar i = 0; i < NoSlaves; i++) begin : gen_connect_slave_monitor + for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_connect_slave_monitor assign slave_monitor_dv[i].aw_id = slave[i].aw_id ; assign slave_monitor_dv[i].aw_addr = slave[i].aw_addr ; assign slave_monitor_dv[i].aw_len = slave[i].aw_len ;