//////////////////////////////////////////////////////////////////////////////// // // Filename: axilxbar.v // {{{ // Project: WB2AXIPSP: bus bridges and other odds and ends // // Purpose: Create a full crossbar between NM AXI-lite sources (masters), // and NS AXI-lite 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. // // 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< m_arvalid[N] // {{{ always @(*) begin r_marvalid = dcd_arvalid[N] && !srfull[N]; rrequest[N] = 0; if (!srfull[N]) rrequest[N][NS:0] = rdecode; end assign m_arvalid[N] = r_marvalid; // }}} // slave_raccepts // {{{ always @(*) begin slave_raccepts[N] = 1'b1; if (!srgrant[N]) slave_raccepts[N] = 1'b0; if (srfull[N]) slave_raccepts[N] = 1'b0; // verilator lint_off WIDTH if (!rrequest[N][srindex[N]]) slave_raccepts[N] = 1'b0; // verilator lint_on WIDTH if (!rgrant[N][NS]) begin if (m_axi_arvalid[srindex[N]] && !m_axi_arready[srindex[N]]) slave_raccepts[N] = 1'b0; end else if (S_AXI_RVALID[N] && !S_AXI_RREADY[N]) slave_raccepts[N] = 1'b0; end // }}} // }}} 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; `ifdef FORMAL // {{{ always @(*) assert(linger == (linger_counter != 0)); // }}} `endif // }}} end // leave_channel // {{{ always @(*) begin leave_channel = 0; if (!m_awvalid[N] && (!linger || wrequested[NM][swindex[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][swindex[N]]) // Need to leave this channel to connect // to any other channel leave_channel = 1; end // }}} // wgrant, swgrant // {{{ initial wgrant[N] = 0; initial swgrant[N] = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) begin wgrant[N] <= 0; swgrant[N] <= 0; end else if (!stay_on_channel) begin if (requested_channel_is_available) begin // Switching channels swgrant[N] <= 1'b1; wgrant[N] <= wrequest[N][NS:0]; end else if (leave_channel) begin swgrant[N] <= 1'b0; wgrant[N] <= 0; end end // }}} // requested_index // {{{ 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 swindex // {{{ reg [LGNS-1:0] r_swindex; initial r_swindex = 0; always @(posedge S_AXI_ACLK) if (!stay_on_channel && requested_channel_is_available) r_swindex <= requested_index; assign swindex[N] = r_swindex; // }}} // }}} 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; `ifdef FORMAL // {{{ always @(*) assert(linger == (linger_counter != 0)); // }}} `endif // }}} end // leave_channel // {{{ always @(*) begin leave_channel = 0; if (!m_arvalid[N] && (!linger || rrequested[NM][srindex[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][srindex[N]]) // Need to leave this channel to connect // to any other channel leave_channel = 1; end // }}} // rgrant, srgrant // {{{ initial rgrant[N] = 0; initial srgrant[N] = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) begin rgrant[N] <= 0; srgrant[N] <= 0; end else if (!stay_on_channel) begin if (requested_channel_is_available) begin // Switching channels srgrant[N] <= 1'b1; rgrant[N] <= rrequest[N][NS:0]; end else if (leave_channel) begin srgrant[N] <= 1'b0; rgrant[N] <= 0; end end // }}} // requested_index // {{{ 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 // }}} // Now for srindex // {{{ reg [LGNS-1:0] r_srindex; initial r_srindex = 0; always @(posedge S_AXI_ACLK) if (!stay_on_channel && requested_channel_is_available) r_srindex <= requested_index; assign srindex[N] = r_srindex; // }}} // }}} end for (N=NM; N 1); default: begin end endcase initial rpending = 0; initial srempty[N] = 1; initial srfull[N] = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) begin rpending <= 0; srempty[N]<= 1; srfull[N] <= 0; end else case ({(m_arvalid[N] && slave_raccepts[N]), (S_AXI_RVALID[N] && S_AXI_RREADY[N])}) 2'b01: begin rpending <= rpending - 1; srempty[N] <= (rpending == 1); srfull[N] <= 0; end 2'b10: begin rpending <= rpending + 1; srfull[N] <= &rpending[LGMAXBURST-1:1]; srempty[N] <= 0; end default: begin end endcase assign w_sawpending[N] = awpending; assign w_srpending[N] = rpending; assign wdata_expected[N] = r_wdata_expected; `ifdef FORMAL // {{{ reg [LGMAXBURST-1:0] wpending; reg [LGMAXBURST-1:0] f_missing_wdata; initial wpending = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) wpending <= 0; else case ({(m_wvalid[N] && slave_waccepts[N]), (S_AXI_BVALID[N] && S_AXI_BREADY[N])}) 2'b01: wpending <= wpending - 1; 2'b10: wpending <= wpending + 1; default: begin end endcase assign w_swpending[N] = wpending; always @(*) assert(missing_wdata == awpending - wpending); always @(*) assert(r_wdata_expected == (missing_wdata > 0)); always @(*) assert(awpending >= wpending); // }}} `endif // }}} end endgenerate // Property validation // {{{ initial begin if (NM == 0) begin $display("At least one master must be defined"); $stop; end if (NS == 0) begin $display("At least one slave must be defined"); $stop; end end // }}} //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // Formal properties used to verify this core // {{{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// `ifdef FORMAL // Local declarations // {{{ localparam F_LGDEPTH = LGMAXBURST+1; wire [F_LGDEPTH-1:0] fm_rd_outstanding [0:NM-1]; wire [F_LGDEPTH-1:0] fm_wr_outstanding [0:NM-1]; wire [F_LGDEPTH-1:0] fm_awr_outstanding [0:NM-1]; wire [F_LGDEPTH-1:0] fs_rd_outstanding [0:NS-1]; wire [F_LGDEPTH-1:0] fs_wr_outstanding [0:NS-1]; wire [F_LGDEPTH-1:0] fs_awr_outstanding [0:NS-1]; initial assert(NS >= 1); initial assert(NM >= 1); // }}} `ifdef VERIFIC reg f_past_valid; initial f_past_valid = 0; always @(posedge S_AXI_ACLK) f_past_valid <= 1; //////////////////////////////////////////////////////////////////////// // // Initial value checks // {{{ //////////////////////////////////////////////////////////////////////// // // `ifdef VERIFIC `define INITIAL_CHECK assume `else `define INITIAL_CHECK assert `endif // VERIFIC always @(*) if (!f_past_valid) begin `INITIAL_CHECK(!S_AXI_ARESETN); `INITIAL_CHECK(S_AXI_BVALID == 0); `INITIAL_CHECK(S_AXI_RVALID == 0); `INITIAL_CHECK(swgrant == 0); `INITIAL_CHECK(srgrant == 0); `INITIAL_CHECK(swfull == 0); `INITIAL_CHECK(srfull == 0); `INITIAL_CHECK(&swempty); `INITIAL_CHECK(&srempty); for(iN=0; iN= (S_AXI_AWREADY[N] ? 0:1) +((OPT_BUFFER_DECODER & dcd_awvalid[N]) ? 1:0) + (S_AXI_BVALID[N] ? 1:0)); always @(*) if (S_AXI_ARESETN) assert(fm_wr_outstanding[N] >= (S_AXI_WREADY[N] ? 0:1) + (S_AXI_BVALID[N]? 1:0)); always @(*) if (S_AXI_ARESETN) assert(fm_wr_outstanding[N]-(S_AXI_WREADY[N] ? 0:1) <= fm_awr_outstanding[N]-(S_AXI_AWREADY[N] ? 0:1)); always @(*) if (S_AXI_ARESETN && wgrant[N][NS]) assert(fm_wr_outstanding[N] == (S_AXI_WREADY[N] ? 0:1) + (S_AXI_BVALID[N] ? 1:0)); always @(*) if (S_AXI_ARESETN && !swgrant[N]) begin assert(!S_AXI_BVALID[N]); assert(fm_awr_outstanding[N]==(S_AXI_AWREADY[N] ? 0:1) +((OPT_BUFFER_DECODER & dcd_awvalid[N]) ? 1:0)); assert(fm_wr_outstanding[N] == (S_AXI_WREADY[N] ? 0:1)); assert(w_sawpending[N] == 0); assert(w_swpending[N] == 0); end // // Check read counters // always @(*) if (S_AXI_ARESETN) assert(fm_rd_outstanding[N] >= (S_AXI_ARREADY[N] ? 0:1) +(S_AXI_RVALID[N] ? 1:0)); always @(*) if (S_AXI_ARESETN && (!srgrant[N] || rgrant[N][NS])) assert(fm_rd_outstanding[N] == (S_AXI_ARREADY[N] ? 0:1) +((OPT_BUFFER_DECODER & dcd_arvalid[N]) ? 1:0) +(S_AXI_RVALID[N] ? 1:0)); always @(*) if (S_AXI_ARESETN) assert(fm_rd_outstanding[N] == { 1'b0, w_srpending[N] } +((OPT_BUFFER_DECODER & dcd_arvalid[N]) ? 1:0) + (S_AXI_ARREADY[N] ? 0:1)); always @(*) if (S_AXI_ARESETN && rgrant[N][NS]) assert(fm_rd_outstanding[N] == (S_AXI_ARREADY[N] ? 0:1) +((OPT_BUFFER_DECODER & dcd_arvalid[N]) ? 1:0) +(S_AXI_RVALID[N] ? 1:0)); always @(*) if (S_AXI_ARESETN && !srgrant[N]) begin assert(!S_AXI_RVALID[N]); assert(fm_rd_outstanding[N]== (S_AXI_ARREADY[N] ? 0:1) +((OPT_BUFFER_DECODER && dcd_arvalid[N])? 1:0)); assert(w_srpending[N] == 0); end // // Check full/empty flags // localparam [LGMAXBURST-1:0] NEAR_THRESHOLD = -2; always @(*) begin assert(swfull[N] == &w_sawpending[N]); assert(swempty[N] == (w_sawpending[N] == 0)); end always @(*) begin assert(srfull[N] == &w_srpending[N]); assert(srempty[N] == (w_srpending[N] == 0)); end // }}} end endgenerate generate for(M=0; M= (S_AXI_AWREADY[N] ? 0:1) +((OPT_BUFFER_DECODER && dcd_awvalid[N]) ? 1:0) +(S_AXI_BVALID[N] ? 1:0)); assert(fm_wr_outstanding[N] == (S_AXI_WREADY[N] ? 0:1) +(S_AXI_BVALID[N] ? 1:0)); end always @(*) if (srgrant[N] && (srindex[N] < NS)) begin assert((fm_rd_outstanding[N]//17 - (S_AXI_ARREADY[N] ? 0:1)//1 -((OPT_BUFFER_DECODER && dcd_arvalid[N]) ? 1:0) - (S_AXI_RVALID[N] ? 1:0))//0 == (fs_rd_outstanding[srindex[N]]//16 + (m_axi_arvalid[srindex[N]] ? 1:0)//0 + (m_axi_rready[srindex[N]] ? 0:1)));//0 end // }}} end endgenerate //////////////////////////////////////////////////////////////////////// // // Cover properties // {{{ //////////////////////////////////////////////////////////////////////// // // // Can every master reach every slave? // Can things transition without dropping the request line(s)? generate for(N=0; N 2 && !wgrant[0][NS]) cvr_multi_write_hit <= 1; initial cvr_multi_read_hit = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) cvr_multi_read_hit <= 0; else if (fm_rd_outstanding[0] > 2 && !rgrant[0][NS]) cvr_multi_read_hit <= 1; always @(*) cover(cvr_multi_write_hit); always @(*) cover(cvr_multi_read_hit); always @(*) cover(S_AXI_ARESETN && cvr_multi_write_hit & mwgrant == 0 && M_AXI_BVALID == 0); always @(*) cover(S_AXI_ARESETN && cvr_multi_read_hit & mrgrant == 0 && M_AXI_RVALID == 0); // }}} //////////////////////////////////////////////////////////////////////// // // Negation check // {{{ // Pick a particular value. Assume the value doesn't show up on the // input. Prove it doesn't show up on the output. This will check for // ... // 1. Stuck bits on the output channel // 2. Cross-talk between channels // //////////////////////////////////////////////////////////////////////// // // (* anyconst *) reg [LGNM-1:0] f_const_source; (* anyconst *) reg [AW-1:0] f_const_addr; (* anyconst *) reg [AW-1:0] f_const_addr_n; (* anyconst *) reg [DW-1:0] f_const_data_n; (* anyconst *) reg [DW/8-1:0] f_const_strb_n; (* anyconst *) reg [3-1:0] f_const_prot_n; (* anyconst *) reg [2-1:0] f_const_resp_n; reg [LGNS-1:0] f_const_slave; always @(*) assume(f_const_source < NM); always @(*) begin f_const_slave = NS; for(iM=0; iM