From 4e98cb8bfeff6ea58855949e07a949490a006112 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 22 May 2024 17:52:57 +0200 Subject: [PATCH 1/4] Rewrite simpler axi_id_serializer Removes demux and mux inside --- src/axi_id_serialize.sv | 316 ++++++++++++++-------------------------- 1 file changed, 112 insertions(+), 204 deletions(-) diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index 9a787dd25..0d1553b99 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -12,6 +12,7 @@ // Authors: // - Andreas Kurth // - Paul Scheffler +// - Michael Rogenmoser `include "axi/assign.svh" `include "axi/typedef.svh" @@ -29,9 +30,6 @@ module axi_id_serialize #( /// ID width of the AXI4+ATOP slave port parameter int unsigned AxiSlvPortIdWidth = 32'd0, - /// Maximum number of transactions that can be in flight at the slave port. Reads and writes are - /// counted separately (except for ATOPs, which count as both read and write). - parameter int unsigned AxiSlvPortMaxTxns = 32'd0, /// ID width of the AXI4+ATOP master port parameter int unsigned AxiMstPortIdWidth = 32'd0, /// Maximum number of different IDs that can be in flight at the master port. Reads and writes @@ -41,14 +39,6 @@ module axi_id_serialize #( parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, /// Maximum number of in-flight transactions with the same ID at the master port. parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, - /// Address width of both AXI4+ATOP ports - parameter int unsigned AxiAddrWidth = 32'd0, - /// Data width of both AXI4+ATOP ports - parameter int unsigned AxiDataWidth = 32'd0, - /// User width of both AXI4+ATOP ports - parameter int unsigned AxiUserWidth = 32'd0, - /// Enable support for AXI4+ATOP atomics - parameter bit AtopSupport = 1'b1, /// Request struct type of the AXI4+ATOP slave port parameter type slv_req_t = logic, /// Response struct type of the AXI4+ATOP slave port @@ -66,7 +56,13 @@ module axi_id_serialize #( /// Number of Entries in the explicit ID map (default: None) parameter int unsigned IdMapNumEntries = 32'd0, /// Explicit ID map; index [0] in each entry is the input ID to match, index [1] the output ID. - parameter int unsigned IdMap [IdMapNumEntries-1:0][0:1] = '{default: {32'b0, 32'b0}} + parameter int unsigned IdMap [IdMapNumEntries-1:0][0:1] = '{default: {32'b0, 32'b0}}, + // unused parameters, no longer needed, left for backwards-compatibility + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, // unused + parameter int unsigned AxiAddrWidth = 32'd0, + parameter int unsigned AxiDataWidth = 32'd0, + parameter int unsigned AxiUserWidth = 32'd0, + parameter bit AtopSupport = 1'b1 ) ( /// Rising-edge clock of both ports input logic clk_i, @@ -87,73 +83,9 @@ module axi_id_serialize #( /// Slice of slave port IDs that determines the master port ID typedef logic [SelectWidth-1:0] select_t; - /// ID width after the multiplexer - localparam int unsigned MuxIdWidth = (AxiMstPortMaxUniqIds > 32'd1) ? SelectWidth + 32'd1 : 32'd1; - - /// ID after serializer (i.e., with a constant value of zero) - typedef logic [0:0] ser_id_t; - /// ID after the multiplexer - typedef logic [MuxIdWidth-1:0] mux_id_t; - /// ID at the slave port - typedef logic [AxiSlvPortIdWidth-1:0] slv_id_t; /// ID at the master port typedef logic [AxiMstPortIdWidth-1:0] mst_id_t; - /// Address in any AXI channel - typedef logic [AxiAddrWidth-1:0] addr_t; - /// Data in any AXI channel - typedef logic [AxiDataWidth-1:0] data_t; - /// Strobe in any AXI channel - typedef logic [AxiDataWidth/8-1:0] strb_t; - /// User signal in any AXI channel - typedef logic [AxiUserWidth-1:0] user_t; - - /// W channel at any interface - `AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t) - - /// AW channel at slave port - `AXI_TYPEDEF_AW_CHAN_T(slv_aw_t, addr_t, slv_id_t, user_t) - /// B channel at slave port - `AXI_TYPEDEF_B_CHAN_T(slv_b_t, slv_id_t, user_t) - /// AR channel at slave port - `AXI_TYPEDEF_AR_CHAN_T(slv_ar_t, addr_t, slv_id_t, user_t) - /// R channel at slave port - `AXI_TYPEDEF_R_CHAN_T(slv_r_t, data_t, slv_id_t, user_t) - - /// AW channel after serializer - `AXI_TYPEDEF_AW_CHAN_T(ser_aw_t, addr_t, ser_id_t, user_t) - /// B channel after serializer - `AXI_TYPEDEF_B_CHAN_T(ser_b_t, ser_id_t, user_t) - /// AR channel after serializer - `AXI_TYPEDEF_AR_CHAN_T(ser_ar_t, addr_t, ser_id_t, user_t) - /// R channel after serializer - `AXI_TYPEDEF_R_CHAN_T(ser_r_t, data_t, ser_id_t, user_t) - /// AXI Requests from serializer - `AXI_TYPEDEF_REQ_T(ser_req_t, ser_aw_t, w_t, ser_ar_t) - /// AXI responses to serializer - `AXI_TYPEDEF_RESP_T(ser_resp_t, ser_b_t, ser_r_t) - - /// AW channel after the multiplexer - `AXI_TYPEDEF_AW_CHAN_T(mux_aw_t, addr_t, mux_id_t, user_t) - /// B channel after the multiplexer - `AXI_TYPEDEF_B_CHAN_T(mux_b_t, mux_id_t, user_t) - /// AR channel after the multiplexer - `AXI_TYPEDEF_AR_CHAN_T(mux_ar_t, addr_t, mux_id_t, user_t) - /// R channel after the multiplexer - `AXI_TYPEDEF_R_CHAN_T(mux_r_t, data_t, mux_id_t, user_t) - /// AXI requests from the multiplexer - `AXI_TYPEDEF_REQ_T(mux_req_t, mux_aw_t, w_t, mux_ar_t) - /// AXI responses to the multiplexer - `AXI_TYPEDEF_RESP_T(mux_resp_t, mux_b_t, mux_r_t) - - /// AW channel at master port - `AXI_TYPEDEF_AW_CHAN_T(mst_aw_t, addr_t, mst_id_t, user_t) - /// B channel at master port - `AXI_TYPEDEF_B_CHAN_T(mst_b_t, mst_id_t, user_t) - /// AR channel at master port - `AXI_TYPEDEF_AR_CHAN_T(mst_ar_t, addr_t, mst_id_t, user_t) - /// R channel at master port - `AXI_TYPEDEF_R_CHAN_T(mst_r_t, data_t, mst_id_t, user_t) - + /// Type for slave ID map typedef mst_id_t [2**AxiSlvPortIdWidth-1:0] slv_id_map_t; @@ -172,49 +104,70 @@ module axi_id_serialize #( /// Input-to-output ID map used localparam slv_id_map_t SlvIdMap = map_slv_ids(); - select_t slv_aw_select, slv_ar_select; + select_t slv_aw_select, slv_ar_select, slv_b_select, slv_r_select; assign slv_aw_select = select_t'(SlvIdMap[slv_req_i.aw.id]); assign slv_ar_select = select_t'(SlvIdMap[slv_req_i.ar.id]); slv_req_t [AxiMstPortMaxUniqIds-1:0] to_serializer_reqs; slv_resp_t [AxiMstPortMaxUniqIds-1:0] to_serializer_resps; - axi_demux #( - .AxiIdWidth ( AxiSlvPortIdWidth ), - .aw_chan_t ( slv_aw_t ), - .w_chan_t ( w_t ), - .b_chan_t ( slv_b_t ), - .ar_chan_t ( slv_ar_t ), - .r_chan_t ( slv_r_t ), - .axi_req_t ( slv_req_t ), - .axi_resp_t ( slv_resp_t ), - .NoMstPorts ( AxiMstPortMaxUniqIds ), - .MaxTrans ( AxiSlvPortMaxTxns ), - .AxiLookBits ( AxiSlvPortIdWidth ), - .AtopSupport ( AtopSupport ), - .SpillAw ( 1'b1 ), - .SpillW ( 1'b0 ), - .SpillB ( 1'b0 ), - .SpillAr ( 1'b1 ), - .SpillR ( 1'b0 ) - ) i_axi_demux ( - .clk_i, - .rst_ni, - .test_i ( 1'b0 ), - .slv_req_i ( slv_req_i ), - .slv_aw_select_i ( slv_aw_select ), - .slv_ar_select_i ( slv_ar_select ), - .slv_resp_o ( slv_resp_o ), - .mst_reqs_o ( to_serializer_reqs ), - .mst_resps_i ( to_serializer_resps ) + logic [AxiMstPortMaxUniqIds-1:0] to_serializer_resps_b_valid, + to_serializer_resps_r_valid; + + onehot_to_bin #( + .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) + ) i_slv_b_select ( + .onehot(to_serializer_resps_b_valid), + .bin (slv_b_select) ); + onehot_to_bin #( + .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) + ) i_slv_r_select ( + .onehot(to_serializer_resps_r_valid), + .bin (slv_r_select) + ); + + for (genvar i = 0; i < AxiMstPortMaxUniqIds; i++) begin + assign to_serializer_resps_b_valid[i] = to_serializer_resps[i].b_valid; + assign to_serializer_resps_r_valid[i] = to_serializer_resps[i].r_valid; + end + + // Due to static ID mapping, ID consistency checking is not needed. + always_comb begin + // AW, W, AR + for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin + to_serializer_reqs[i] = slv_req_i; // .aw, .w, .ar + to_serializer_reqs[i].aw_valid = '0; + to_serializer_reqs[i].w_valid = '0; + to_serializer_reqs[i].ar_valid = '0; + end + to_serializer_reqs[slv_aw_select].aw_valid = slv_req_i.aw_valid; + slv_resp_o.aw_ready = to_serializer_resps[slv_aw_select].aw_ready; + + slv_resp_o.w_ready = mst_resp_i.w_ready; + + to_serializer_reqs[slv_ar_select].ar_valid = slv_req_i.ar_valid; + slv_resp_o.ar_ready = to_serializer_resps[slv_ar_select].ar_ready; + + // B, R (these are passed through or both gated inside the serializer) + slv_resp_o.b_valid = |to_serializer_resps_b_valid; + slv_resp_o.b = to_serializer_resps[slv_b_select].b; + slv_resp_o.r_valid = |to_serializer_resps_r_valid; + slv_resp_o.r = to_serializer_resps[slv_r_select].r; + for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin + to_serializer_reqs[i].b_ready = slv_req_i.b_ready; + to_serializer_reqs[i].r_ready = slv_req_i.r_ready; + end + end + slv_req_t [AxiMstPortMaxUniqIds-1:0] tmp_serializer_reqs; slv_resp_t [AxiMstPortMaxUniqIds-1:0] tmp_serializer_resps; - ser_req_t [AxiMstPortMaxUniqIds-1:0] from_serializer_reqs; - ser_resp_t [AxiMstPortMaxUniqIds-1:0] from_serializer_resps; + mst_req_t [AxiMstPortMaxUniqIds-1:0] from_serializer_reqs; + mst_resp_t [AxiMstPortMaxUniqIds-1:0] from_serializer_resps; for (genvar i = 0; i < AxiMstPortMaxUniqIds; i++) begin : gen_serializers + // serializer takes care to ensure unique IDs for ATOPs axi_serializer #( .MaxReadTxns ( AxiMstPortMaxTxnsPerId ), .MaxWriteTxns ( AxiMstPortMaxTxnsPerId ), @@ -231,110 +184,65 @@ module axi_id_serialize #( ); always_comb begin `AXI_SET_REQ_STRUCT(from_serializer_reqs[i], tmp_serializer_reqs[i]) - // Truncate to ID width 1 as all requests have ID '0. - from_serializer_reqs[i].aw.id = tmp_serializer_reqs[i].aw.id[0]; - from_serializer_reqs[i].ar.id = tmp_serializer_reqs[i].ar.id[0]; + from_serializer_reqs[i].aw.id = i; + from_serializer_reqs[i].ar.id = i; `AXI_SET_RESP_STRUCT(tmp_serializer_resps[i], from_serializer_resps[i]) // Zero-extend response IDs. - tmp_serializer_resps[i].b.id = {{AxiSlvPortIdWidth-1{1'b0}}, from_serializer_resps[i].b.id}; - tmp_serializer_resps[i].r.id = {{AxiSlvPortIdWidth-1{1'b0}}, from_serializer_resps[i].r.id}; + tmp_serializer_resps[i].b.id = '0; + tmp_serializer_resps[i].r.id = '0; end end - mux_req_t axi_mux_req; - mux_resp_t axi_mux_resp; - - axi_mux #( - .SlvAxiIDWidth ( 32'd1 ), - .slv_aw_chan_t ( ser_aw_t ), - .mst_aw_chan_t ( mux_aw_t ), - .w_chan_t ( w_t ), - .slv_b_chan_t ( ser_b_t ), - .mst_b_chan_t ( mux_b_t ), - .slv_ar_chan_t ( ser_ar_t ), - .mst_ar_chan_t ( mux_ar_t ), - .slv_r_chan_t ( ser_r_t ), - .mst_r_chan_t ( mux_r_t ), - .slv_req_t ( ser_req_t ), - .slv_resp_t ( ser_resp_t ), - .mst_req_t ( mux_req_t ), - .mst_resp_t ( mux_resp_t ), - .NoSlvPorts ( AxiMstPortMaxUniqIds ), - .MaxWTrans ( AxiMstPortMaxTxnsPerId ), - .FallThrough ( 1'b0 ), - .SpillAw ( 1'b1 ), - .SpillW ( 1'b0 ), - .SpillB ( 1'b0 ), - .SpillAr ( 1'b1 ), - .SpillR ( 1'b0 ) - ) i_axi_mux ( - .clk_i, - .rst_ni, - .test_i ( 1'b0 ), - .slv_reqs_i ( from_serializer_reqs ), - .slv_resps_o ( from_serializer_resps ), - .mst_req_o ( axi_mux_req ), - .mst_resp_i ( axi_mux_resp ) + logic [AxiMstPortMaxUniqIds-1:0] from_serializer_reqs_aw_valid, + from_serializer_reqs_ar_valid, + from_serializer_reqs_b_ready, + from_serializer_reqs_r_ready; + + select_t mst_aw_select, mst_ar_select; + + onehot_to_bin #( + .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) + ) i_mst_aw_select ( + .onehot(from_serializer_reqs_aw_valid), + .bin (mst_aw_select) ); - // Shift the ID one down if needed, as mux prepends IDs - if (MuxIdWidth > 32'd1) begin : gen_id_shift - always_comb begin - `AXI_SET_REQ_STRUCT(mst_req_o, axi_mux_req) - mst_req_o.aw.id = mst_id_t'(axi_mux_req.aw.id >> 32'd1); - mst_req_o.ar.id = mst_id_t'(axi_mux_req.ar.id >> 32'd1); - `AXI_SET_RESP_STRUCT(axi_mux_resp, mst_resp_i) - axi_mux_resp.b.id = mux_id_t'(mst_resp_i.b.id << 32'd1); - axi_mux_resp.r.id = mux_id_t'(mst_resp_i.r.id << 32'd1); + onehot_to_bin #( + .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) + ) i_mst_ar_select ( + .onehot(from_serializer_reqs_ar_valid), + .bin (mst_ar_select) + ); + + for (genvar i = 0; i < AxiMstPortMaxUniqIds; i++) begin + assign from_serializer_reqs_aw_valid[i] = from_serializer_reqs[i].aw_valid; + assign from_serializer_reqs_ar_valid[i] = from_serializer_reqs[i].ar_valid; + end + + always_comb begin + mst_req_o.aw_valid = |from_serializer_reqs_aw_valid; + mst_req_o.aw = from_serializer_reqs[mst_aw_select].aw; + mst_req_o.w_valid = slv_req_i.w_valid; + mst_req_o.w = slv_req_i.w; + mst_req_o.ar_valid = |from_serializer_reqs_ar_valid; + mst_req_o.ar = from_serializer_reqs[mst_ar_select].ar; + for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin + from_serializer_resps[i].aw_ready = mst_resp_i.aw_ready; + from_serializer_resps[i].ar_ready = mst_resp_i.ar_ready; end - end else begin : gen_no_id_shift - axi_id_prepend #( - .NoBus ( 32'd1 ), - .AxiIdWidthSlvPort ( MuxIdWidth ), - .AxiIdWidthMstPort ( AxiMstPortIdWidth ), - .slv_aw_chan_t ( mux_aw_t ), - .slv_w_chan_t ( w_t ), - .slv_b_chan_t ( mux_b_t ), - .slv_ar_chan_t ( mux_ar_t ), - .slv_r_chan_t ( mux_r_t ), - .mst_aw_chan_t ( mst_aw_t ), - .mst_w_chan_t ( w_t ), - .mst_b_chan_t ( mst_b_t ), - .mst_ar_chan_t ( mst_ar_t ), - .mst_r_chan_t ( mst_r_t ) - ) i_axi_id_prepend ( - .pre_id_i ( '0 ), - .slv_aw_chans_i ( axi_mux_req.aw ), - .slv_aw_valids_i ( axi_mux_req.aw_valid ), - .slv_aw_readies_o ( axi_mux_resp.aw_ready ), - .slv_w_chans_i ( axi_mux_req.w ), - .slv_w_valids_i ( axi_mux_req.w_valid ), - .slv_w_readies_o ( axi_mux_resp.w_ready ), - .slv_b_chans_o ( axi_mux_resp.b ), - .slv_b_valids_o ( axi_mux_resp.b_valid ), - .slv_b_readies_i ( axi_mux_req.b_ready ), - .slv_ar_chans_i ( axi_mux_req.ar ), - .slv_ar_valids_i ( axi_mux_req.ar_valid ), - .slv_ar_readies_o ( axi_mux_resp.ar_ready ), - .slv_r_chans_o ( axi_mux_resp.r ), - .slv_r_valids_o ( axi_mux_resp.r_valid ), - .slv_r_readies_i ( axi_mux_req.r_ready ), - .mst_aw_chans_o ( mst_req_o.aw ), - .mst_aw_valids_o ( mst_req_o.aw_valid ), - .mst_aw_readies_i ( mst_resp_i.aw_ready ), - .mst_w_chans_o ( mst_req_o.w ), - .mst_w_valids_o ( mst_req_o.w_valid ), - .mst_w_readies_i ( mst_resp_i.w_ready ), - .mst_b_chans_i ( mst_resp_i.b ), - .mst_b_valids_i ( mst_resp_i.b_valid ), - .mst_b_readies_o ( mst_req_o.b_ready ), - .mst_ar_chans_o ( mst_req_o.ar ), - .mst_ar_valids_o ( mst_req_o.ar_valid ), - .mst_ar_readies_i ( mst_resp_i.ar_ready ), - .mst_r_chans_i ( mst_resp_i.r ), - .mst_r_valids_i ( mst_resp_i.r_valid ), - .mst_r_readies_o ( mst_req_o.r_ready ) - ); + + for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin + from_serializer_reqs_b_ready[i] = from_serializer_reqs[i].b_ready; + from_serializer_reqs_r_ready[i] = from_serializer_reqs[i].r_ready; + from_serializer_resps[i].b_valid = '0; + from_serializer_resps[i].r_valid = '0; + from_serializer_resps[i].b = mst_resp_i.b; + from_serializer_resps[i].r = mst_resp_i.r; + end + from_serializer_resps[mst_resp_i.b.id].b_valid = mst_resp_i.b_valid; + mst_req_o.b_ready = from_serializer_reqs[mst_resp_i.b.id].b_ready; + from_serializer_resps[mst_resp_i.r.id].r_valid = mst_resp_i.r_valid; + mst_req_o.r_ready = from_serializer_reqs[mst_resp_i.r.id].r_ready; end // pragma translate_off From 04911ef2f24648c7b202f0ccb0d0a03145db7b29 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Mon, 27 May 2024 17:32:05 +0200 Subject: [PATCH 2/4] Add spill for AR and AW --- src/axi_id_serialize.sv | 75 ++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index 0d1553b99..24898f65e 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -39,6 +39,10 @@ module axi_id_serialize #( parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, /// Maximum number of in-flight transactions with the same ID at the master port. parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, + /// Address width of both AXI4+ATOP ports + parameter int unsigned AxiAddrWidth = 32'd0, + /// User width of both AXI4+ATOP ports + parameter int unsigned AxiUserWidth = 32'd0, /// Request struct type of the AXI4+ATOP slave port parameter type slv_req_t = logic, /// Response struct type of the AXI4+ATOP slave port @@ -57,11 +61,11 @@ module axi_id_serialize #( parameter int unsigned IdMapNumEntries = 32'd0, /// Explicit ID map; index [0] in each entry is the input ID to match, index [1] the output ID. parameter int unsigned IdMap [IdMapNumEntries-1:0][0:1] = '{default: {32'b0, 32'b0}}, + /// Spill AR and AW + parameter bit SpillAx = 1'b1, // unused parameters, no longer needed, left for backwards-compatibility parameter int unsigned AxiSlvPortMaxTxns = 32'd0, // unused - parameter int unsigned AxiAddrWidth = 32'd0, parameter int unsigned AxiDataWidth = 32'd0, - parameter int unsigned AxiUserWidth = 32'd0, parameter bit AtopSupport = 1'b1 ) ( /// Rising-edge clock of both ports @@ -85,7 +89,16 @@ module axi_id_serialize #( /// ID at the master port typedef logic [AxiMstPortIdWidth-1:0] mst_id_t; - + /// Address in any AXI channel + typedef logic [AxiAddrWidth-1:0] addr_t; + /// User signal in any AXI channel + typedef logic [AxiUserWidth-1:0] user_t; + + /// AW channel at master port + `AXI_TYPEDEF_AW_CHAN_T(mst_aw_t, addr_t, mst_id_t, user_t) + /// AR channel at master port + `AXI_TYPEDEF_AR_CHAN_T(mst_ar_t, addr_t, mst_id_t, user_t) + /// Type for slave ID map typedef mst_id_t [2**AxiSlvPortIdWidth-1:0] slv_id_map_t; @@ -219,16 +232,19 @@ module axi_id_serialize #( assign from_serializer_reqs_ar_valid[i] = from_serializer_reqs[i].ar_valid; end + mst_req_t mst_req; + logic mst_aw_ready, mst_ar_ready; + always_comb begin - mst_req_o.aw_valid = |from_serializer_reqs_aw_valid; - mst_req_o.aw = from_serializer_reqs[mst_aw_select].aw; - mst_req_o.w_valid = slv_req_i.w_valid; - mst_req_o.w = slv_req_i.w; - mst_req_o.ar_valid = |from_serializer_reqs_ar_valid; - mst_req_o.ar = from_serializer_reqs[mst_ar_select].ar; + mst_req.aw_valid = |from_serializer_reqs_aw_valid; + mst_req.aw = from_serializer_reqs[mst_aw_select].aw; + mst_req.w_valid = slv_req_i.w_valid; + mst_req.w = slv_req_i.w; + mst_req.ar_valid = |from_serializer_reqs_ar_valid; + mst_req.ar = from_serializer_reqs[mst_ar_select].ar; for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin - from_serializer_resps[i].aw_ready = mst_resp_i.aw_ready; - from_serializer_resps[i].ar_ready = mst_resp_i.ar_ready; + from_serializer_resps[i].aw_ready = mst_aw_ready; + from_serializer_resps[i].ar_ready = mst_ar_ready; end for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin @@ -240,11 +256,44 @@ module axi_id_serialize #( from_serializer_resps[i].r = mst_resp_i.r; end from_serializer_resps[mst_resp_i.b.id].b_valid = mst_resp_i.b_valid; - mst_req_o.b_ready = from_serializer_reqs[mst_resp_i.b.id].b_ready; + mst_req.b_ready = from_serializer_reqs[mst_resp_i.b.id].b_ready; from_serializer_resps[mst_resp_i.r.id].r_valid = mst_resp_i.r_valid; - mst_req_o.r_ready = from_serializer_reqs[mst_resp_i.r.id].r_ready; + mst_req.r_ready = from_serializer_reqs[mst_resp_i.r.id].r_ready; end + spill_register #( + .T ( mst_aw_t ), + .Bypass( ~SpillAx ) + ) i_aw_spill_reg ( + .clk_i, + .rst_ni, + .valid_i( mst_req.aw_valid ), + .ready_o( mst_aw_ready ), + .data_i ( mst_req.aw), + .valid_o( mst_req_o.aw_valid ), + .ready_i( mst_resp_i.aw_ready ), + .data_o ( mst_req_o.aw ) + ); + + spill_register #( + .T ( mst_ar_t ), + .Bypass( ~SpillAx ) + ) i_ar_spill_reg ( + .clk_i, + .rst_ni, + .valid_i( mst_req.ar_valid ), + .ready_o( mst_ar_ready ), + .data_i ( mst_req.ar ), + .valid_o( mst_req_o.ar_valid ), + .ready_i( mst_resp_i.ar_ready ), + .data_o ( mst_req_o.ar ) + ); + + `AXI_ASSIGN_W_STRUCT(mst_req_o.w, mst_req.w) + assign mst_req_o.w_valid = mst_req.w_valid; + assign mst_req_o.b_ready = mst_req.b_ready; + assign mst_req_o.r_ready = mst_req.r_ready; + // pragma translate_off `ifndef VERILATOR initial begin : p_assert From 0015266aab74e8994d755591cf0a05aa1aa33023 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 25 Jul 2024 11:15:24 +0200 Subject: [PATCH 3/4] axi_id_serializer: Add comments, update intf parameters --- src/axi_id_serialize.sv | 42 ++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index 24898f65e..4aa068683 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -64,7 +64,7 @@ module axi_id_serialize #( /// Spill AR and AW parameter bit SpillAx = 1'b1, // unused parameters, no longer needed, left for backwards-compatibility - parameter int unsigned AxiSlvPortMaxTxns = 32'd0, // unused + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, parameter int unsigned AxiDataWidth = 32'd0, parameter bit AtopSupport = 1'b1 ) ( @@ -127,6 +127,7 @@ module axi_id_serialize #( logic [AxiMstPortMaxUniqIds-1:0] to_serializer_resps_b_valid, to_serializer_resps_r_valid; + // Convert B response valid to index for selection onehot_to_bin #( .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) ) i_slv_b_select ( @@ -134,6 +135,7 @@ module axi_id_serialize #( .bin (slv_b_select) ); + // Convert R response valid to index for selection onehot_to_bin #( .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) ) i_slv_r_select ( @@ -150,19 +152,24 @@ module axi_id_serialize #( always_comb begin // AW, W, AR for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin - to_serializer_reqs[i] = slv_req_i; // .aw, .w, .ar + to_serializer_reqs[i] = slv_req_i; // assigns.aw, .w, .ar to_serializer_reqs[i].aw_valid = '0; to_serializer_reqs[i].w_valid = '0; to_serializer_reqs[i].ar_valid = '0; end + + // Only pass AW to the selected serializer to_serializer_reqs[slv_aw_select].aw_valid = slv_req_i.aw_valid; slv_resp_o.aw_ready = to_serializer_resps[slv_aw_select].aw_ready; + // W is always in order (i.e., serialized) and does not have ID field, pass through slv_resp_o.w_ready = mst_resp_i.w_ready; + // Only pass AR to the selected serializer to_serializer_reqs[slv_ar_select].ar_valid = slv_req_i.ar_valid; slv_resp_o.ar_ready = to_serializer_resps[slv_ar_select].ar_ready; + // Recombine responses, only one will be active at a time // B, R (these are passed through or both gated inside the serializer) slv_resp_o.b_valid = |to_serializer_resps_b_valid; slv_resp_o.b = to_serializer_resps[slv_b_select].b; @@ -179,6 +186,7 @@ module axi_id_serialize #( mst_req_t [AxiMstPortMaxUniqIds-1:0] from_serializer_reqs; mst_resp_t [AxiMstPortMaxUniqIds-1:0] from_serializer_resps; + // One serializer per desired ouput ID for (genvar i = 0; i < AxiMstPortMaxUniqIds; i++) begin : gen_serializers // serializer takes care to ensure unique IDs for ATOPs axi_serializer #( @@ -195,6 +203,7 @@ module axi_id_serialize #( .mst_req_o ( tmp_serializer_reqs[i] ), .mst_resp_i ( tmp_serializer_resps[i] ) ); + // Assign desired output ID always_comb begin `AXI_SET_REQ_STRUCT(from_serializer_reqs[i], tmp_serializer_reqs[i]) from_serializer_reqs[i].aw.id = i; @@ -213,6 +222,7 @@ module axi_id_serialize #( select_t mst_aw_select, mst_ar_select; + // Convert AW request valid to index for selection onehot_to_bin #( .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) ) i_mst_aw_select ( @@ -220,6 +230,7 @@ module axi_id_serialize #( .bin (mst_aw_select) ); + // Convert AR request valid to index for selection onehot_to_bin #( .ONEHOT_WIDTH( AxiMstPortMaxUniqIds ) ) i_mst_ar_select ( @@ -236,17 +247,25 @@ module axi_id_serialize #( logic mst_aw_ready, mst_ar_ready; always_comb begin + // Recombine AW requests, only one active at a time mst_req.aw_valid = |from_serializer_reqs_aw_valid; mst_req.aw = from_serializer_reqs[mst_aw_select].aw; + + // W is always in order (i.e., serialized) and does not have ID field, pass through mst_req.w_valid = slv_req_i.w_valid; mst_req.w = slv_req_i.w; + + // Recombine AR requests, only one active at a time mst_req.ar_valid = |from_serializer_reqs_ar_valid; mst_req.ar = from_serializer_reqs[mst_ar_select].ar; + + // AW/AR ready distributed, only one request active at a time for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin from_serializer_resps[i].aw_ready = mst_aw_ready; from_serializer_resps[i].ar_ready = mst_ar_ready; end + // Distribute B and R responses to corresponding serializers for (int unsigned i = 0; i < AxiMstPortMaxUniqIds; i++) begin from_serializer_reqs_b_ready[i] = from_serializer_reqs[i].b_ready; from_serializer_reqs_r_ready[i] = from_serializer_reqs[i].r_ready; @@ -261,6 +280,7 @@ module axi_id_serialize #( mst_req.r_ready = from_serializer_reqs[mst_resp_i.r.id].r_ready; end + // Optional spill register to help prevent timing loops spill_register #( .T ( mst_aw_t ), .Bypass( ~SpillAx ) @@ -275,6 +295,7 @@ module axi_id_serialize #( .data_o ( mst_req_o.aw ) ); + // Optional spill register to help prevent timing loops spill_register #( .T ( mst_ar_t ), .Bypass( ~SpillAx ) @@ -326,13 +347,18 @@ endmodule /// See the documentation of the main module for the definition of ports and parameters. module axi_id_serialize_intf #( parameter int unsigned AXI_SLV_PORT_ID_WIDTH = 32'd0, - parameter int unsigned AXI_SLV_PORT_MAX_TXNS = 32'd0, parameter int unsigned AXI_MST_PORT_ID_WIDTH = 32'd0, parameter int unsigned AXI_MST_PORT_MAX_UNIQ_IDS = 32'd0, parameter int unsigned AXI_MST_PORT_MAX_TXNS_PER_ID = 32'd0, parameter int unsigned AXI_ADDR_WIDTH = 32'd0, parameter int unsigned AXI_DATA_WIDTH = 32'd0, - parameter int unsigned AXI_USER_WIDTH = 32'd0 + parameter int unsigned AXI_USER_WIDTH = 32'd0, + parameter int unsigned MST_ID_BASE_OFFSET = 32'd0, + parameter int unsigned ID_MAP_NUM_ENTRIES = 32'd0, + parameter int unsigned ID_MAP [ID_MAP_NUM_ENTRIES-1:0][0:1] = '{default: {32'b0, 32'b0}}, + parameter bit SPILL_AX = 1'b1, + // Now unused, kept for backward compatibility + parameter int unsigned AXI_SLV_PORT_MAX_TXNS = 32'd0 ) ( input logic clk_i, input logic rst_ni, @@ -374,17 +400,19 @@ module axi_id_serialize_intf #( axi_id_serialize #( .AxiSlvPortIdWidth ( AXI_SLV_PORT_ID_WIDTH ), - .AxiSlvPortMaxTxns ( AXI_SLV_PORT_MAX_TXNS ), .AxiMstPortIdWidth ( AXI_MST_PORT_ID_WIDTH ), .AxiMstPortMaxUniqIds ( AXI_MST_PORT_MAX_UNIQ_IDS ), .AxiMstPortMaxTxnsPerId ( AXI_MST_PORT_MAX_TXNS_PER_ID ), .AxiAddrWidth ( AXI_ADDR_WIDTH ), - .AxiDataWidth ( AXI_DATA_WIDTH ), .AxiUserWidth ( AXI_USER_WIDTH ), .slv_req_t ( slv_req_t ), .slv_resp_t ( slv_resp_t ), .mst_req_t ( mst_req_t ), - .mst_resp_t ( mst_resp_t ) + .mst_resp_t ( mst_resp_t ), + .MstIdBaseOffset ( MST_ID_BASE_OFFSET ), + .IdMapNumEntries ( ID_MAP_NUM_ENTRIES ), + .IdMap ( ID_MAP ), + .SpillAx ( SPILL_AX ) ) i_axi_id_serialize ( .clk_i, .rst_ni, From bea405979f60c86e34ab8d38cad99a2dc875223f Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 25 Jul 2024 11:19:40 +0200 Subject: [PATCH 4/4] update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c00e5b7b..b240e23a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `axi_dw_downsizer`: Fix `i_forward_b_beats_queue` underflow. - `axi_test`: Ensure random requests do not cross 4KiB page boundaries. +### Changed +- `axi_id_serializer`: Change internal design (and behavior) for simpler code, less hardware, and + less stalling. + ## 0.39.3 - 2024-05-08 ### Added - `axi_sim_mem`: Allow response data for uninitialized region to have configurable defined value.