From 3038edc09a2eb15762f2e58533f429489107520b Mon Sep 17 00:00:00 2001 From: Alejandro Soto Date: Wed, 6 Mar 2024 02:38:24 -0600 Subject: rtl/wb2axip: add to version control --- rtl/wb2axip/axilxbar.v | 2421 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2421 insertions(+) create mode 100644 rtl/wb2axip/axilxbar.v (limited to 'rtl/wb2axip/axilxbar.v') diff --git a/rtl/wb2axip/axilxbar.v b/rtl/wb2axip/axilxbar.v new file mode 100644 index 0000000..4a1ed43 --- /dev/null +++ b/rtl/wb2axip/axilxbar.v @@ -0,0 +1,2421 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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