//////////////////////////////////////////////////////////////////////////////// // // Filename: axixbar.v // {{{ // Project: WB2AXIPSP: bus bridges and other odds and ends // // Purpose: Create a full crossbar between NM AXI sources (masters), and NS // AXI slaves. Every master can talk to any slave, provided it // isn't already busy. // {{{ // Performance: This core has been designed with the goal of being able to push // one transaction through the interconnect, from any master to // any slave, per clock cycle. This may perhaps be its most unique // feature. While throughput is good, latency is something else. // // The arbiter requires a clock to switch, then another clock to send data // downstream. This creates a minimum two clock latency up front. The // return path suffers another clock of latency as well, placing the // minimum latency at four clocks. The minimum write latency is at // least one clock longer, since the write data must wait for the write // address before proceeeding. // // Note that this arbiter only forwards AxID fields. It does not use // them in arbitration. As a result, only one master may ever make // requests of any given slave at a time. All responses from a slave // will be returned to that known master. This is a known limitation in // this implementation which will be fixed (in time) with funding and // interest. Until that time, in order for a second master to access // a given slave, the first master must receive all of its acknowledgments. // // Usage: To use, you must first set NM and NS to the number of masters // and the number of slaves you wish to connect to. You then need to // adjust the addresses of the slaves, found SLAVE_ADDR array. Those // bits that are relevant in SLAVE_ADDR to then also be set in SLAVE_MASK. // Adjusting the data and address widths go without saying. // // Lower numbered masters are given priority in any "fight". // // Channel grants are given on the condition that 1) they are requested, // 2) no other channel has a grant, 3) all of the responses have been // received from the current channel, and 4) the internal counters are // not overflowing. // // The core limits the number of outstanding transactions on any channel to // 1<1) ? $clog2(OPT_LINGER+1) : 1; // localparam LGNM = (NM>1) ? $clog2(NM) : 1; localparam LGNS = (NS>1) ? $clog2(NS+1) : 1; // // In order to use indexes, and hence fully balanced mux trees, it helps // to make certain that we have a power of two based lookup. NMFULL // is the number of masters in this lookup, with potentially some // unused extra ones. NSFULL is defined similarly. localparam NMFULL = (NM>1) ? (1<1) ? (1< we have to accept more // write data before we can issue BVALID slave_awaccepts[N] = 1'b0; end end // }}} // slave_waccepts // {{{ always @(*) begin slave_waccepts[N] = 1'b1; if (!mwgrant[N]) slave_waccepts[N] = 1'b0; if (!wdata_expected[N] && (!OPT_AWW || !slave_awaccepts[N])) slave_waccepts[N] = 1'b0; if (!wgrant[N][NS]) begin if (!slave_wready[mwindex[N]]) slave_waccepts[N] = 1'b0; end else if (berr_valid[N] && !bskd_ready[N]) slave_waccepts[N] = 1'b0; end // }}} reg r_awvalid; always @(*) begin r_awvalid = dcd_awvalid[N] && !mwfull[N]; wrequest[N]= 0; if (!mwfull[N]) wrequest[N][NS:0] = wdecode; end assign m_awvalid[N] = r_awvalid; // QOS handling via write_qos_lockout // {{{ if (!OPT_QOS || NM == 1) begin : WRITE_NO_QOS // If we aren't using QOS, then never lock any packets // out from arbitration assign write_qos_lockout[N] = 0; end else begin : WRITE_QOS // Lock out a master based upon a second master having // a higher QOS request level // {{{ reg r_write_qos_lockout; initial r_write_qos_lockout = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) r_write_qos_lockout <= 0; else begin r_write_qos_lockout <= 0; for(iN=0; iN 0) begin // Otherwise, we decrement it until it reaches // zero r_linger <= (linger_counter > 1); linger_counter <= linger_counter - 1; end else r_linger <= 0; assign linger = r_linger; end // }}} // leave_channel // {{{ // True of another master is requesting access to this slave, // or if we are requesting access to another slave. If QOS // lockout is enabled, then we also leave the channel if a // request with a higher QOS has arrived always @(*) begin leave_channel = 0; if (!m_awvalid[N] && (!linger || wrequested[NM][mwindex[N]])) // Leave the channel after OPT_LINGER counts // of the channel being idle, or when someone // else asks for the channel leave_channel = 1; if (m_awvalid[N] && !wrequest[N][mwindex[N]]) // Need to leave this channel to connect // to any other channel leave_channel = 1; if (write_qos_lockout[N]) // Need to leave this channel for another higher // priority request leave_channel = 1; end // }}} // WRITE GRANT ALLOCATION // {{{ // Now that we've done our homework, we can switch grants // if necessary initial wgrant[N] = 0; initial mwgrant[N] = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) begin wgrant[N] <= 0; mwgrant[N] <= 0; end else if (!stay_on_channel) begin if (requested_channel_is_available) begin // Switch to a new channel mwgrant[N] <= 1'b1; wgrant[N] <= wrequest[N][NS:0]; end else if (leave_channel) begin // Revoke the given grant mwgrant[N] <= 1'b0; wgrant[N] <= 0; end end // }}} // mwindex (registered) // {{{ always @(wrequest[N]) begin requested_index = 0; for(iM=0; iM<=NS; iM=iM+1) if (wrequest[N][iM]) requested_index= requested_index | iM[LGNS-1:0]; end // Now for mwindex initial r_mwindex = 0; always @(posedge S_AXI_ACLK) if (!stay_on_channel && requested_channel_is_available) r_mwindex <= requested_index; assign mwindex[N] = r_mwindex; // }}} end for (N=NM; N 0) begin r_linger <= (linger_counter > 1); linger_counter <= linger_counter - 1; end else r_linger <= 0; assign linger = r_linger; end // }}} // leave_channel // {{{ // True of another master is requesting access to this slave, // or if we are requesting access to another slave. If QOS // lockout is enabled, then we also leave the channel if a // request with a higher QOS has arrived always @(*) begin leave_channel = 0; if (!m_arvalid[N] && (!linger || rrequested[NM][mrindex[N]])) // Leave the channel after OPT_LINGER counts // of the channel being idle, or when someone // else asks for the channel leave_channel = 1; if (m_arvalid[N] && !rrequest[N][mrindex[N]]) // Need to leave this channel to connect // to any other channel leave_channel = 1; if (read_qos_lockout[N]) leave_channel = 1; end // }}} // READ GRANT ALLOCATION // {{{ initial rgrant[N] = 0; initial mrgrant[N] = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) begin rgrant[N] <= 0; mrgrant[N] <= 0; end else if (!stay_on_channel) begin if (requested_channel_is_available) begin // Switching channels mrgrant[N] <= 1'b1; rgrant[N] <= rrequest[N][NS:0]; end else if (leave_channel) begin mrgrant[N] <= 1'b0; rgrant[N] <= 0; end end // }}} // mrindex (registered) // {{{ always @(rrequest[N]) begin requested_index = 0; for(iM=0; iM<=NS; iM=iM+1) if (rrequest[N][iM]) requested_index = requested_index|iM[LGNS-1:0]; end initial r_mrindex = 0; always @(posedge S_AXI_ACLK) if (!stay_on_channel && requested_channel_is_available) r_mrindex <= requested_index; assign mrindex[N] = r_mrindex; // }}} end for (N=NM; N 1); wpending <= wpending - 1; end 2'b10: begin wpending <= wpending + 1; r_wdata_expected <= 1; end default: begin end endcase assign wdata_expected[N] = r_wdata_expected; assign wlasts_pending[N] = wpending; // }}} // }}} end endgenerate generate for (N=0; N= 1); initial assert(NM >= 1); // }}} //////////////////////////////////////////////////////////////////////// // // Check the arbiter signals for consistency // {{{ generate for(N=0; N