diff options
| author | Alejandro Soto <alejandro@34project.org> | 2024-03-06 02:38:24 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2024-03-06 02:38:24 -0600 |
| commit | 3038edc09a2eb15762f2e58533f429489107520b (patch) | |
| tree | f7a45e424d39e6fef0d59e329c1bf6ea206e2886 /rtl/wb2axip/axi2axilsub.v | |
| parent | 3b62399f92e9faa2602ac30865e5fc3c7c4e12b8 (diff) | |
rtl/wb2axip: add to version control
Diffstat (limited to 'rtl/wb2axip/axi2axilsub.v')
| -rw-r--r-- | rtl/wb2axip/axi2axilsub.v | 2157 |
1 files changed, 2157 insertions, 0 deletions
diff --git a/rtl/wb2axip/axi2axilsub.v b/rtl/wb2axip/axi2axilsub.v new file mode 100644 index 0000000..694dc58 --- /dev/null +++ b/rtl/wb2axip/axi2axilsub.v @@ -0,0 +1,2157 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Filename: axi2axilsub.v +// {{{ +// Project: WB2AXIPSP: bus bridges and other odds and ends +// +// Purpose: Convert from AXI to AXI-lite with no performance loss, and to +// a potentially smaller bus width. +// +// Performance: The goal of this converter is to convert from AXI to an AXI-lite +// bus of a smaller width, while still maintaining the one-clock +// per transaction speed of AXI. It currently achieves this goal. The +// design needs very little configuration to be useful, but you might +// wish to resize the FIFOs within depending upon the length of your +// slave's data path. The current FIFO length, LGFIFO=4, is sufficient to +// maintain full speed. If the slave, however, can maintain full speed but +// requires a longer processing cycle, then you may need longer FIFOs. +// +// The AXI specification does require an additional 2 clocks per +// transaction when using this core, so your latency will go up. +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2019-2024, Gisselquist Technology, LLC +// {{{ +// This digital logic component is the proprietary property of Gisselquist +// Technology, LLC. It may only be distributed and/or re-distributed by the +// express permission of Gisselquist Technology. +// +// Permission has been granted to the Patreon sponsors of the ZipCPU blog +// to use this logic component as they see fit, but not to redistribute it +// beyond their individual personal or commercial use. +// +// This component is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +// +// Please feel free to contact me should you have any questions, or even if you +// just want to ask about what you find within here. +// +// Yours, +// +// Dan Gisselquist +// Owner +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// +`default_nettype none +// +`ifdef FORMAL +`ifdef BMC +`define BMC_ASSERT assert +`else +`define BMC_ASSERT assume +`endif +`endif +// +// }}} +module axi2axilsub #( + // {{{ + parameter integer C_AXI_ID_WIDTH = 2, + parameter integer C_S_AXI_DATA_WIDTH = 64, + parameter integer C_M_AXI_DATA_WIDTH = 32, + parameter integer C_AXI_ADDR_WIDTH = 6, + parameter [0:0] OPT_LOWPOWER = 1, + parameter [0:0] OPT_WRITES = 1, + parameter [0:0] OPT_READS = 1, + parameter SLVSZ = $clog2(C_S_AXI_DATA_WIDTH/8), + parameter MSTSZ = $clog2(C_M_AXI_DATA_WIDTH/8), + // Log (based two) of the maximum number of outstanding AXI + // (not AXI-lite) transactions. If you multiply 2^LGFIFO * 256, + // you'll get the maximum number of outstanding AXI-lite + // transactions + parameter LGFIFO = 4 + // }}} + ) ( + // {{{ + input wire S_AXI_ACLK, + input wire S_AXI_ARESETN, + // AXI4 slave interface + // {{{ + // Write address channel + // {{{ + input wire S_AXI_AWVALID, + output wire S_AXI_AWREADY, + input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID, + input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR, + input wire [7:0] S_AXI_AWLEN, + input wire [2:0] S_AXI_AWSIZE, + input wire [1:0] S_AXI_AWBURST, + input wire S_AXI_AWLOCK, + input wire [3:0] S_AXI_AWCACHE, + input wire [2:0] S_AXI_AWPROT, + input wire [3:0] S_AXI_AWQOS, + // }}} + // Write data channel + // {{{ + input wire S_AXI_WVALID, + output wire S_AXI_WREADY, + input wire [C_S_AXI_DATA_WIDTH-1:0] S_AXI_WDATA, + input wire [(C_S_AXI_DATA_WIDTH/8)-1:0] S_AXI_WSTRB, + input wire S_AXI_WLAST, + // }}} + // Write return channel + // {{{ + output wire S_AXI_BVALID, + input wire S_AXI_BREADY, + output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID, + output wire [1:0] S_AXI_BRESP, + // }}} + // Read address channel + // {{{ + input wire S_AXI_ARVALID, + output wire S_AXI_ARREADY, + input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID, + input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR, + input wire [7:0] S_AXI_ARLEN, + input wire [2:0] S_AXI_ARSIZE, + input wire [1:0] S_AXI_ARBURST, + input wire S_AXI_ARLOCK, + input wire [3:0] S_AXI_ARCACHE, + input wire [2:0] S_AXI_ARPROT, + input wire [3:0] S_AXI_ARQOS, + // }}} + // Read data channel + // {{{ + output wire S_AXI_RVALID, + input wire S_AXI_RREADY, + output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID, + output wire [C_S_AXI_DATA_WIDTH-1:0] S_AXI_RDATA, + output wire [1:0] S_AXI_RRESP, + output wire S_AXI_RLAST, + // }}} + // }}} + // AXI-lite master interface + // {{{ + // AXI-lite Write interface + // {{{ + output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR, + output wire [2 : 0] M_AXI_AWPROT, + output wire M_AXI_AWVALID, + input wire M_AXI_AWREADY, + output wire [C_M_AXI_DATA_WIDTH-1:0] M_AXI_WDATA, + output wire [(C_M_AXI_DATA_WIDTH/8)-1:0] M_AXI_WSTRB, + output wire M_AXI_WVALID, + input wire M_AXI_WREADY, + input wire [1 : 0] M_AXI_BRESP, + input wire M_AXI_BVALID, + output wire M_AXI_BREADY, + // }}} + // AXI-lite read interface + // {{{ + output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR, + output wire [2:0] M_AXI_ARPROT, + output wire M_AXI_ARVALID, + input wire M_AXI_ARREADY, + // + input wire M_AXI_RVALID, + output wire M_AXI_RREADY, + input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA, + input wire [1 : 0] M_AXI_RRESP + // }}} + // }}} + // }}} + ); + + // Local parameters, register, and net declarations + // {{{ + // Verilator lint_off UNUSED + localparam [1:0] OKAY = 2'b00, + EXOKAY = 2'b01, + SLVERR = 2'b10; + // localparam [1:0] DECERR = 2'b10; + + // Verilator lint_on UNUSED + localparam AW = C_AXI_ADDR_WIDTH; + localparam SLVDW = C_S_AXI_DATA_WIDTH; + localparam MSTDW = C_M_AXI_DATA_WIDTH; + localparam IW = C_AXI_ID_WIDTH; + // localparam LSB = $clog2(C_AXI_DATA_WIDTH)-3; + // }}} + // Register declarations + // {{{ + // + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Write logic + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + generate if (OPT_WRITES && SLVDW == MSTDW) + begin : IMPLEMENT_AXI2AXILITE_WRITES + // {{{ + + // (Unused) signal declarations + // {{{ + // Verilator lint_off UNUSED + wire ign_arready, ign_rvalid, + ign_rlast, + ign_arvalid, ign_rready; + wire [IW-1:0] ign_rid; + wire [SLVDW-1:0] ign_rdata; + wire [1:0] ign_rresp; + wire [AW-1:0] ign_araddr; + wire [2:0] ign_arprot; + // Verilator lint_on UNUSED + // }}} + + axi2axilite #( + // {{{ + .C_AXI_ID_WIDTH(IW), + .C_AXI_DATA_WIDTH(SLVDW), + .C_AXI_ADDR_WIDTH(AW), + .OPT_WRITES(1), + .OPT_READS(0), + .LGFIFO(LGFIFO) + // }}} + ) axilwrite ( + // {{{ + .S_AXI_ACLK(S_AXI_ACLK), + .S_AXI_ARESETN(S_AXI_ARESETN), + // AXI4 slave interface + // {{{ + // Write address channel + // {{{ + .S_AXI_AWVALID(S_AXI_AWVALID), + .S_AXI_AWREADY(S_AXI_AWREADY), + .S_AXI_AWID( S_AXI_AWID), + .S_AXI_AWADDR( S_AXI_AWADDR), + .S_AXI_AWLEN( S_AXI_AWLEN), + .S_AXI_AWSIZE( S_AXI_AWSIZE), + .S_AXI_AWBURST(S_AXI_AWBURST), + .S_AXI_AWLOCK( S_AXI_AWLOCK), + .S_AXI_AWCACHE(S_AXI_AWCACHE), + .S_AXI_AWPROT( S_AXI_AWPROT), + .S_AXI_AWQOS( S_AXI_AWQOS), + // }}} + // Write data channel + // {{{ + .S_AXI_WVALID(S_AXI_WVALID), + .S_AXI_WREADY(S_AXI_WREADY), + .S_AXI_WDATA( S_AXI_WDATA), + .S_AXI_WSTRB( S_AXI_WSTRB), + .S_AXI_WLAST( S_AXI_WLAST), + // }}} + // Write return channel + // {{{ + .S_AXI_BVALID(S_AXI_BVALID), + .S_AXI_BREADY(S_AXI_BREADY), + .S_AXI_BID( S_AXI_BID), + .S_AXI_BRESP( S_AXI_BRESP), + // }}} + // Read address channel + // {{{ + .S_AXI_ARVALID(1'b0), + .S_AXI_ARREADY(ign_arready), + .S_AXI_ARID( {(IW){1'b0}}), + .S_AXI_ARADDR( {(AW){1'b0}}), + .S_AXI_ARLEN( 8'h0), + .S_AXI_ARSIZE( 3'h0), + .S_AXI_ARBURST(2'h0), + .S_AXI_ARLOCK( 1'b0), + .S_AXI_ARCACHE(4'h0), + .S_AXI_ARPROT( 3'h0), + .S_AXI_ARQOS( 4'h0), + // }}} + // Read data channel + // {{{ + .S_AXI_RVALID(ign_rvalid), + .S_AXI_RREADY(1'b1), + .S_AXI_RID( ign_rid), + .S_AXI_RDATA( ign_rdata), + .S_AXI_RRESP( ign_rresp), + .S_AXI_RLAST( ign_rlast), + // }}} + // }}} + // AXI-lite master interface + // {{{ + // AXI-lite Write interface + // {{{ + .M_AXI_AWVALID(M_AXI_AWVALID), + .M_AXI_AWREADY(M_AXI_AWREADY), + .M_AXI_AWADDR(M_AXI_AWADDR), + .M_AXI_AWPROT(M_AXI_AWPROT), + // + .M_AXI_WVALID(M_AXI_WVALID), + .M_AXI_WREADY(M_AXI_WREADY), + .M_AXI_WDATA(M_AXI_WDATA), + .M_AXI_WSTRB(M_AXI_WSTRB), + // + .M_AXI_BVALID(M_AXI_BVALID), + .M_AXI_BREADY(M_AXI_BREADY), + .M_AXI_BRESP(M_AXI_BRESP), + // }}} + // AXI-lite read interface + // {{{ + .M_AXI_ARVALID(ign_arvalid), + .M_AXI_ARREADY(1'b1), + .M_AXI_ARADDR(ign_araddr), + .M_AXI_ARPROT(ign_arprot), + // + .M_AXI_RVALID(1'b0), + .M_AXI_RREADY(ign_rready), + .M_AXI_RDATA({(MSTDW){1'b0}}), + .M_AXI_RRESP(2'b00) + // }}} + // }}} + // }}} + ); + + // }}} + end else begin : WDN + if (OPT_WRITES) + begin : IMPLEMENT_WRITES + // {{{ + + // Register declarations + // {{{ + localparam BIDFIFOBW = IW+1+(SLVSZ-MSTSZ+1); + + // + // S_AXI_AW* skid buffer + wire skids_awvalid; + wire skids_awready; + wire [IW-1:0] skids_awid; + wire [AW-1:0] skids_awaddr; + wire [7:0] skids_awlen; + wire [2:0] skids_awsize; + wire [1:0] skids_awburst; + wire [2:0] skids_awprot; + // + // S_AXI_W* skid buffer + wire skids_wvalid, skids_wready; + wire [SLVDW-1:0] skids_wdata; + wire [SLVDW/8-1:0] skids_wstrb; + + wire slv_awready, slv_wready; + reg [SLVDW-1:0] slv_wdata; + reg [SLVDW/8-1:0] slv_wstrb; + + reg [IW-1:0] slv_awid; + reg [AW-1:0] slv_awaddr; + wire [AW-1:0] slv_next_awaddr; + reg [2:0] slv_awsize; + reg [1:0] slv_awburst; + reg [7:0] slv_awlen; + reg [8:0] slv_wlen; + reg slv_awlast; + + // Write registers + reg m_awvalid; + reg bfifo_write; + wire wfifo_wlast; + reg [IW+1+SLVSZ-MSTSZ:0] bfifo_wdata; + wire [LGFIFO:0] wfifo_count; + wire wfifo_full; + wire wfifo_empty; + wire [SLVSZ-MSTSZ:0] wfifo_subcount; + wire [IW-1:0] wfifo_bid; + reg [SLVSZ-MSTSZ:0] bcounts; + reg [IW-1:0] s_axi_bid, bid; + reg blast; + reg [1:0] s_axi_bresp, bresp; + reg s_axi_bvalid; + reg b_return_stall; + wire read_from_wrfifo; + // + // S_AXI_B* skid buffer isn't needed + // + // M_AXI_AW* skid buffer isn't needed + // + // M_AXI_W* skid buffer isn't needed + // + // M_AXI_B* skid buffer + wire skidm_bvalid, skidm_bready; + wire [1:0] skidm_bresp; + + reg m_wvalid; + reg [AW-1:0] mst_awaddr; + reg [2:0] mst_awprot; + reg [SLVSZ-MSTSZ:0] mst_awbeats, + mst_wbeats, next_slv_beats; + // }}} + //////////////////////////////////////////////////////////////// + // + // Incoming write address / data handling + // {{{ + //////////////////////////////////////////////////////////////// + // + // + + //////////////////////////////////////// + // + // Clock #1: The skid buffer + // {{{ + + // The write address channel's skid buffer + // {{{ + skidbuffer #( + // {{{ + .DW(IW+AW+8+3+2+3), + .OPT_LOWPOWER(OPT_LOWPOWER), + .OPT_OUTREG(0) + // }}} + ) awskid( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY), + .i_data({ S_AXI_AWID, S_AXI_AWADDR, S_AXI_AWLEN, + S_AXI_AWSIZE, S_AXI_AWBURST, S_AXI_AWPROT }), + .o_valid(skids_awvalid), .i_ready(skids_awready), + .o_data({ skids_awid, skids_awaddr, skids_awlen, + skids_awsize, skids_awburst, skids_awprot }) + // }}} + ); + // }}} + // + // The write data channel's skid buffer (S_AXI_W*) + // {{{ + skidbuffer #( + // {{{ + .DW(SLVDW+SLVDW/8), + .OPT_LOWPOWER(OPT_LOWPOWER), + .OPT_OUTREG(0) + // }}} + ) wskid( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY), + .i_data({ S_AXI_WDATA, S_AXI_WSTRB }), + .o_valid(skids_wvalid), .i_ready(skids_wready), + .o_data({ skids_wdata, skids_wstrb }) + // }}} + ); + // }}} + + assign skids_awready = skids_wvalid && skids_wready + && slv_awlast; + + assign skids_wready = slv_wready; + // }}} + //////////////////////////////////////// + // + // Clock 2: slv_* clock + // {{{ + + // When are we ready for a next SLAVE beat? + assign slv_awready = (skids_wvalid && skids_wready); + + // slv_aw* + // {{{ + // slv_awaddr is the address of the value *CURRENTLY* in the + // buffer, *NOT* the next address. + initial slv_awlast = 1; + always @(posedge S_AXI_ACLK) + if (skids_awvalid && skids_awready) + begin + slv_awid <= skids_awid; + slv_awaddr <= skids_awaddr; + slv_awlen <= skids_awlen; + slv_awsize <= skids_awsize; + slv_awburst <= skids_awburst; + end else if (slv_awready && slv_wlen > 0) + begin + // Step forward a full beat -- but only when we have + // the data to do so + slv_awaddr <= slv_next_awaddr; + end + // }}} + + // slv_awlast + // {{{ + initial slv_awlast = 1; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + slv_awlast <= 1; + else if (skids_awvalid && skids_awready) + slv_awlast <= (skids_awlen == 0); + else if (skids_wvalid && skids_wready) + slv_awlast <= (slv_wlen <= 2); + +`ifdef FORMAL + always @(*) + assert(slv_awlast == (slv_wlen <= 1)); +`endif + // }}} + + // slv_wlen = Number of beats remaining + // {{{ + // ... in the slave's data space, to include the one we've + // just ingested. Therefore, this is a 1-up counter. If + // slv_wlen == 0, then we are idle. + initial slv_wlen = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + slv_wlen <= 0; + else if (skids_awvalid && skids_awready) + begin + slv_wlen <= skids_awlen + 1; + end else if (skids_wvalid && skids_wready) // (slv_awready && slv_wlen > 0) + begin + slv_wlen <= slv_wlen - 1; +`ifdef FORMAL + assert(slv_wlen > 0); +`endif + end else if (slv_wlen == 1 && slv_wready) + slv_wlen <= 0; + // }}} + + // next_slv_beats + // {{{ + always @(*) + begin + // Verilator lint_off WIDTH + next_slv_beats = (1<<(skids_awsize-MSTSZ[2:0])) + - (skids_awaddr[SLVSZ-1:0] >> skids_awsize); + if (skids_awsize <= MSTSZ[2:0]) + next_slv_beats = 1; + // Verilator lint_on WIDTH + end + // }}} + + // slv_next_awaddr + // {{{ + axi_addr #( + // {{{ + .AW(AW), + .DW(SLVDW) + // }}} + ) get_next_slave_addr ( + // {{{ + .i_last_addr(slv_awaddr), + .i_size(slv_awsize), + .i_burst(slv_awburst), + .i_len(slv_awlen), + .o_next_addr(slv_next_awaddr) + // }}} + ); + // }}} + + assign slv_wready = (mst_awbeats <= (M_AXI_AWREADY ? 1:0)) + && (mst_wbeats <= (M_AXI_WREADY ? 1:0)) + && (!bfifo_write || !wfifo_full); + + // slv_wstrb, slv_wdata + // {{{ + always @(posedge S_AXI_ACLK) + if (OPT_LOWPOWER && !S_AXI_ARESETN) + { slv_wstrb, slv_wdata } <= 0; + else if (skids_awready) + begin + slv_wstrb <= skids_wstrb >> (skids_awaddr[SLVSZ-1:MSTSZ]*MSTDW/8); + slv_wdata <= skids_wdata >> (skids_awaddr[SLVSZ-1:MSTSZ]*MSTDW); + if (OPT_LOWPOWER && !skids_awvalid) + begin + slv_wstrb <= 0; + slv_wdata <= 0; + end + end else if (skids_wready) + begin + slv_wstrb <= skids_wstrb >> (slv_next_awaddr[SLVSZ-1:MSTSZ]*MSTDW/8); + slv_wdata <= skids_wdata >> (slv_next_awaddr[SLVSZ-1:MSTSZ]*MSTDW); + if (OPT_LOWPOWER && !skids_wvalid) + begin + slv_wstrb <= 0; + slv_wdata <= 0; + end + end else if (M_AXI_WVALID && M_AXI_WREADY) + begin + slv_wstrb <= slv_wstrb >> (MSTDW/8); + slv_wdata <= slv_wdata >> (MSTDW); + end + // }}} + + // }}} + //////////////////////////////////////// + // + // Clock 2 (continued): mst_* = M_AXI_* + // {{{ + + // mst_awaddr, mst_awprot, mst_awbeats + // {{{ + initial mst_awaddr = 0; + initial mst_awprot = 0; + always @(posedge S_AXI_ACLK) + begin + if (!M_AXI_AWVALID || M_AXI_AWREADY) + begin + if (skids_awvalid && skids_awready) + begin + mst_awaddr <= skids_awaddr; + mst_awprot <= skids_awprot; + mst_awbeats<= next_slv_beats; + end else if (skids_wvalid && skids_wready) + begin + mst_awaddr <= slv_next_awaddr; + mst_awbeats<= 1; + if (slv_awsize >= MSTSZ[2:0]) + mst_awbeats <= (1<<(slv_awsize - MSTSZ[2:0])); + end else begin + if (mst_awbeats > 1) + begin + mst_awaddr <= mst_awaddr + (1<<MSTSZ); + mst_awaddr[MSTSZ-1:0] <= 0; + end else if (OPT_LOWPOWER) + begin + mst_awaddr <= 0; + end + + if (mst_awbeats > 0) + mst_awbeats <= mst_awbeats - 1; + end + end + + if (!S_AXI_ARESETN) + begin + // {{{ + mst_awbeats <= 0; + if (OPT_LOWPOWER) + begin + mst_awaddr <= 0; + mst_awprot <= 0; + end + // }}} + end + end +`ifdef FORMAL + always @(*) + if(S_AXI_ARESETN && OPT_LOWPOWER && mst_awbeats == 0) + begin + assert(mst_awaddr == 0); + end + + always @(*) + if (S_AXI_ARESETN) + assert(m_axi_awvalid == (mst_awbeats > 0)); +`endif + // }}} + + // m_awvalid + // {{{ + initial m_axi_awvalid = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + m_awvalid <= 0; + else if (!M_AXI_AWVALID || M_AXI_AWREADY) + begin + if (skids_wvalid && skids_wready) + m_awvalid <= 1'b1; + else if (mst_awbeats == 1) + m_awvalid <= 1'b0; + end + // }}} + + assign M_AXI_AWVALID = m_awvalid; + assign M_AXI_AWADDR = mst_awaddr; + assign M_AXI_AWPROT = mst_awprot; + + // M_AXI_WVALID, mst_wbeats + // {{{ + initial m_wvalid = 0; + initial mst_wbeats = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + begin + m_wvalid <= 0; + mst_wbeats <= 0; + end else if (!M_AXI_WVALID || M_AXI_WREADY) + begin + if (skids_wvalid && skids_wready) + begin + m_wvalid <= 1'b1; + if (skids_awvalid && skids_awready) + mst_wbeats <= next_slv_beats; + else if (slv_awsize <= MSTSZ[2:0]) + mst_wbeats <= 1; + else + mst_wbeats <= (1<<(slv_awsize - MSTSZ[2:0])); + end else begin + if (mst_wbeats <= 1) + m_wvalid <= 1'b0; + if (mst_wbeats > 0) + mst_wbeats <= mst_wbeats - 1; + end + end +`ifdef FORMAL + always @(*) + if (S_AXI_ARESETN) + assert(M_AXI_WVALID == (mst_wbeats > 0)); +`endif + // }}} + + // M_AXI_W* + // {{{ + assign M_AXI_WVALID = m_wvalid; + assign M_AXI_WDATA = slv_wdata[MSTDW-1:0]; + assign M_AXI_WSTRB = slv_wstrb[MSTDW/8-1:0]; + // }}} + // }}} + //////////////////////////////////////// + // + // Clock 3: The B FIFO + // {{{ + + // bfifo_write + // {{{ + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + bfifo_write <= 0; + else if (!bfifo_write || !wfifo_full) + bfifo_write <= skids_wvalid && skids_wready; + // }}} + + // bfifo_wdata + // {{{ + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + bfifo_wdata <= 0; + else if (skids_awvalid && skids_awready) + begin + bfifo_wdata[SLVSZ-MSTSZ:0] <= next_slv_beats; + bfifo_wdata[SLVSZ-MSTSZ+1] <= (skids_awlen == 0); + bfifo_wdata[SLVSZ-MSTSZ+2 +: IW] <= skids_awid; +`ifdef FORMAL + assert(skids_wvalid && skids_wready); +`endif + end else if (skids_wvalid && skids_wready) + begin + bfifo_wdata[SLVSZ-MSTSZ+2 +: IW] <= slv_awid; + bfifo_wdata[SLVSZ-MSTSZ+1] <= (slv_wlen <= 2) + ? 1'b1 : 1'b0; + + if (slv_awsize <= MSTSZ[2:0]) + bfifo_wdata[SLVSZ-MSTSZ:0] <= 1; + else + bfifo_wdata[SLVSZ-MSTSZ:0] + <= (1<<(slv_awsize - MSTSZ[2:0])); + end + // }}} + + // BFIFO + // {{{ + sfifo #( + // {{{ + .BW(BIDFIFOBW), .LGFLEN(LGFIFO) + // }}} + ) bidlnfifo( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_wr(bfifo_write), .i_data(bfifo_wdata), + .o_full(wfifo_full), .o_fill(wfifo_count), + .i_rd(read_from_wrfifo), + .o_data({ wfifo_bid, wfifo_wlast,wfifo_subcount }), + .o_empty(wfifo_empty) + // }}} + ); + // }}} + + // read_from_wrfifo + // {{{ + assign read_from_wrfifo = (bcounts <= 1)&&(!wfifo_empty) + &&(skidm_bvalid && skidm_bready); + // }}} + // }}} + //////////////////////////////////////// + // + // Return clock 1: the skid buffer + // {{{ + + // + // The downstream AXI-lite response (M_AXI_B*) skid buffer + skidbuffer #( + // {{{ + .DW(2), + .OPT_LOWPOWER(OPT_LOWPOWER), + .OPT_OUTREG(0) + // }}} + ) bskid( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(M_AXI_BVALID), .o_ready(M_AXI_BREADY), + .i_data({ M_AXI_BRESP }), + .o_valid(skidm_bvalid), .i_ready(skidm_bready), + .o_data({ skidm_bresp }) + // }}} + ); + + assign skidm_bready = ((bcounts > 0)||(!wfifo_empty)) + && !b_return_stall; + // }}} + //////////////////////////////////////// + // + // Return staging: Counting returns + // {{{ + + // bcounts + // {{{ + // Return counts + initial bcounts = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + bcounts <= 0; + else if (skidm_bvalid && skidm_bready) + begin + if (read_from_wrfifo) + begin + /* + if (bcounts == 0 && wfifo_subcount <= 1 + && wfifo_wlast) + // Go straight to S_AXI_BVALID, no + // more bursts to count here + bcounts <= 0; + else + */ + bcounts <= wfifo_subcount + bcounts - 1; + end else + bcounts <= bcounts - 1; + end + // }}} + + // blast + // {{{ + always @(posedge S_AXI_ACLK) + if (read_from_wrfifo) + blast <= wfifo_wlast; + // }}} + + // bid + // {{{ + always @(posedge S_AXI_ACLK) + if (read_from_wrfifo) + bid <= wfifo_bid; + // }}} + + // bresp + // {{{ + initial bresp = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + bresp <= OKAY; + else if (!S_AXI_BVALID || S_AXI_BREADY) + bresp <= OKAY; + else if (skidm_bvalid && skidm_bready) + begin + // Let SLVERR take priority over DECERR + casez({ bresp, skidm_bresp }) + 4'b??0?: bresp <= bresp; + 4'b0?1?: bresp <= skidm_bresp; + 4'b1?10: bresp <= SLVERR; + 4'b1011: bresp <= SLVERR; + 4'b1111: bresp <= skidm_bresp; + endcase + + if (blast) + bresp <= OKAY; + end + // }}} + // }}} + //////////////////////////////////////// + // + // Return clock 2: S_AXI_* returns + // {{{ + + // s_axi_bid + // {{{ + always @(posedge S_AXI_ACLK) + if (!S_AXI_BVALID || S_AXI_BREADY) + begin + if (bcounts > 0 && blast) + s_axi_bid <= bid; + else if (read_from_wrfifo) + s_axi_bid <= wfifo_bid; + else + s_axi_bid <= bid; + end + // }}} + + // s_axi_bvalid, b_return_stall + // {{{ + always @(*) + begin + // Force simulation evaluation on reset + if (!S_AXI_ARESETN) + b_return_stall = 0; + + // Default: stalled if the output is stalled + b_return_stall = S_AXI_BVALID && !S_AXI_BREADY; + + if (bcounts > 1) + b_return_stall = 0; + else if (bcounts == 1 && !blast) + b_return_stall = 0; + else if (bcounts == 0 + && (wfifo_subcount > 1 || !wfifo_wlast)) + b_return_stall = 0; + end + + initial s_axi_bvalid = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + s_axi_bvalid <= 0; + else if (!S_AXI_BVALID || S_AXI_BREADY) + begin + s_axi_bvalid <= 0; + if (skidm_bvalid && skidm_bready) + s_axi_bvalid <= ((bcounts == 1)&& blast) + ||((bcounts == 0) && (!wfifo_empty) + && (wfifo_subcount <= 1)&& wfifo_wlast); + end + // }}} + + // s_axi_bresp + // {{{ + initial s_axi_bresp = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + s_axi_bresp <= OKAY; + else if (!S_AXI_BVALID || S_AXI_BREADY) + begin + if (skidm_bvalid && skidm_bready) + begin + // Let SLVERR take priority over DECERR + casez({ bresp, skidm_bresp[1] }) + 3'b??0: s_axi_bresp <= s_axi_bresp; + 3'b0?1: s_axi_bresp <= skidm_bresp; + 3'b101: s_axi_bresp <= SLVERR; + 3'b111: s_axi_bresp <= skidm_bresp; + endcase + end else + s_axi_bresp <= bresp; + end + // }}} + + // S_AXI_B* assignments + // {{{ + assign S_AXI_BID = s_axi_bid; + assign S_AXI_BRESP = s_axi_bresp; + assign S_AXI_BVALID = s_axi_bvalid; + // }}} + // }}} + // }}} + // Make Verilator happy + // {{{ + // Verilator lint_off UNUSED + wire unused_write; + assign unused_write = &{ 1'b0, wfifo_count, S_AXI_WLAST }; + // Verilator lint_on UNUSED + // }}} + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + // + // Formal properties, write half + // {{{ + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// +`ifdef FORMAL + // These are only a subset of the formal properties used to + // verify this design. While I will warrant that the full + // property set will work, I don't really expect the below + // properties to be sufficient or for that matter even + // syntactically correct. + // + // Register declarations + // {{{ + localparam F_LGDEPTH = LGFIFO+1+8; + + wire [F_LGDEPTH-1:0] faxi_awr_nbursts; + wire [9-1:0] faxi_wr_pending; + wire [F_LGDEPTH-1:0] faxi_rd_nbursts; + wire [F_LGDEPTH-1:0] faxi_rd_outstanding; + + // + // ... + // + + localparam F_AXIL_LGDEPTH = F_LGDEPTH; + wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding, + faxil_wr_outstanding, + faxil_awr_outstanding; + // }}} + //////////////////////////////////////////////////////////////// + // + // AXI channel properties + // {{{ + //////////////////////////////////////////////////////////////// + // + // + faxi_slave #( + // {{{ + .C_AXI_ID_WIDTH(IW), + .C_AXI_DATA_WIDTH(SLVDW), + .C_AXI_ADDR_WIDTH(AW), + .F_LGDEPTH(F_AXIL_LGDEPTH), + .OPT_EXCLUSIVE(0) + // ... + // }}} + ) faxi( + // {{{ + .i_clk(S_AXI_ACLK), + .i_axi_reset_n(S_AXI_ARESETN), + // Write address + // {{{ + .i_axi_awvalid(skids_awvalid), + .i_axi_awready(skids_awready), + .i_axi_awid( skids_awid), + .i_axi_awaddr( skids_awaddr), + .i_axi_awlen( skids_awlen), + .i_axi_awsize( skids_awsize), + .i_axi_awburst(skids_awburst), + .i_axi_awlock( 0), + .i_axi_awcache(0), + .i_axi_awprot( skids_awprot), + .i_axi_awqos( 0), + // }}} + // Write data + // {{{ + .i_axi_wvalid( skids_wvalid), + .i_axi_wready( skids_wready), + .i_axi_wdata( skids_wdata), + .i_axi_wstrb( skids_wstrb), + .i_axi_wlast( S_AXI_WLAST), + // }}} + // Write return response + // {{{ + .i_axi_bvalid( S_AXI_BVALID), + .i_axi_bready( S_AXI_BREADY), + .i_axi_bid( S_AXI_BID), + .i_axi_bresp( S_AXI_BRESP), + // }}} + // Read address + // {{{ + .i_axi_arvalid(1'b0), + .i_axi_arready(1'b0), + .i_axi_arid( skids_awid), + .i_axi_araddr( skids_awaddr), + .i_axi_arlen( skids_awlen), + .i_axi_arsize( skids_awsize), + .i_axi_arburst(skids_awburst), + .i_axi_arlock( 0), + .i_axi_arcache(0), + .i_axi_arprot( 0), + .i_axi_arqos( 0), + // }}} + // Read response + // {{{ + .i_axi_rvalid( 1'b0), + .i_axi_rready( 1'b0), + .i_axi_rid( S_AXI_RID), + .i_axi_rdata( S_AXI_RDATA), + .i_axi_rlast( S_AXI_RLAST), + .i_axi_rresp( S_AXI_RRESP), + // }}} + // Formal property data + // {{{ + .f_axi_awr_nbursts( faxi_awr_nbursts), + .f_axi_wr_pending( faxi_wr_pending), + .f_axi_rd_nbursts( faxi_rd_nbursts), + .f_axi_rd_outstanding(faxi_rd_outstanding) + // + // ... + // + // }}} + // }}} + ); + + always @(*) + begin + // ... + assert(faxi_rd_nbursts == 0); + end + + // }}} + //////////////////////////////////////////////////////////////// + // + // AXI-lite properties + // {{{ + //////////////////////////////////////////////////////////////// + // + // + + faxil_master #( + // {{{ + .C_AXI_DATA_WIDTH(MSTDW), + .C_AXI_ADDR_WIDTH(AW), + .F_OPT_NO_RESET(1), + .F_AXI_MAXWAIT(5), + .F_AXI_MAXDELAY(4), + .F_AXI_MAXRSTALL(0), + .F_OPT_WRITE_ONLY(1), + .F_OPT_READ_ONLY(1'b0), + .F_LGDEPTH(F_AXIL_LGDEPTH) + // }}} + ) faxil( + // {{{ + .i_clk(S_AXI_ACLK), + .i_axi_reset_n(S_AXI_ARESETN), + // Write address channel + // {{{ + .i_axi_awvalid(M_AXI_AWVALID), + .i_axi_awready(M_AXI_AWREADY), + .i_axi_awaddr( M_AXI_AWADDR), + .i_axi_awprot( M_AXI_AWPROT), + // }}} + // Write data + // {{{ + .i_axi_wvalid( M_AXI_WVALID), + .i_axi_wready( M_AXI_WREADY), + .i_axi_wdata( M_AXI_WDATA), + .i_axi_wstrb( M_AXI_WSTRB), + // }}} + // Write response + // {{{ + .i_axi_bvalid( skidm_bvalid), + .i_axi_bready( skidm_bready), + .i_axi_bresp( skidm_bresp), + // }}} + // Read address + // {{{ + .i_axi_arvalid(M_AXI_ARVALID), + .i_axi_arready(M_AXI_ARREADY), + .i_axi_araddr( M_AXI_ARADDR), + .i_axi_arprot( M_AXI_ARPROT), + // }}} + // Read data return + // {{{ + .i_axi_rvalid( 1'b0), + .i_axi_rready( 1'b1), + .i_axi_rdata( {(C_M_AXI_DATA_WIDTH){1'b0}}), + .i_axi_rresp( 2'b00), + // }}} + // Formal check variables + // {{{ + .f_axi_rd_outstanding(faxil_rd_outstanding), + .f_axi_wr_outstanding(faxil_wr_outstanding), + .f_axi_awr_outstanding(faxil_awr_outstanding) + // }}} + // }}} + ); + + always @(*) + if (S_AXI_ARESETN) + begin + assert(faxil_rd_outstanding == 0); + + assert(faxil_awr_outstanding + + ((mst_awbeats > 0) ? mst_awbeats : 0) + == f_wfifo_beats + + (bfifo_write ? bfifo_wdata[SLVSZ-MSTSZ:0] : 0) + + bcounts); + + assert(faxil_wr_outstanding + + ((mst_wbeats > 0) ? mst_wbeats : 0) + == f_wfifo_beats + + (bfifo_write ? bfifo_wdata[SLVSZ-MSTSZ:0] : 0) + + bcounts); + end + + + always @(*) + assert(faxil_awr_outstanding <= { 1'b1, {(LGFIFO+8){1'b0}} } + bcounts); + always @(*) + assert(faxil_wr_outstanding <= { 1'b1, {(LGFIFO+8){1'b0}} } + bcounts); + + // }}} + //////////////////////////////////////////////////////////////// + // + // BFIFO property checking + // {{{ + //////////////////////////////////////////////////////////////// + // + // + + // + // ... + // + + always @(posedge S_AXI_ACLK) + if (S_AXI_ARESETN) + begin + assert(faxi_awr_nbursts == f_bfifo_packets + + (slv_awvalid ? 1:0)); + assert(f_bfifo_packets <= wfifo_count); + + // ... + end + + // + // ... + // + + // }}} +`endif + // }}} + // }}} + end else begin : NO_WRITE_SUPPORT + // {{{ + assign S_AXI_AWREADY = 0; + assign S_AXI_WREADY = 0; + assign S_AXI_BID = 0; + assign S_AXI_BRESP = 2'b11; + assign S_AXI_BVALID = 0; + assign S_AXI_BID = 0; + + // + assign M_AXI_AWVALID = 0; + assign M_AXI_AWADDR = 0; + assign M_AXI_AWPROT = 0; + // + assign M_AXI_WVALID = 0; + assign M_AXI_WDATA = 0; + assign M_AXI_WSTRB = 0; + // + assign M_AXI_BREADY = 0; + // }}} + end end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Read logic + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + generate if (OPT_READS && SLVDW == MSTDW) + begin : IMPLEMENT_AXI2AXILITE_READS + // {{{ + + // (Unused) signal declarations + // {{{ + // Verilator lint_off UNUSED + wire ign_awready, ign_wready, + ign_bvalid; + wire [IW-1:0] ign_bid; + wire [1:0] ign_bresp; + + wire ign_awvalid, ign_wvalid, + ign_bready; + wire [AW-1:0] ign_awaddr; + wire [2:0] ign_awprot; + wire [MSTDW-1:0] ign_wdata; + wire [MSTDW/8-1:0] ign_wstrb; + // Verilator lint_on UNUSED + // }}} + + axi2axilite #( + // {{{ + .C_AXI_ID_WIDTH(IW), + .C_AXI_DATA_WIDTH(SLVDW), + .C_AXI_ADDR_WIDTH(AW), + .OPT_WRITES(0), + .OPT_READS(1), + .LGFIFO(LGFIFO) + // }}} + ) axilread ( + // {{{ + .S_AXI_ACLK(S_AXI_ACLK), + .S_AXI_ARESETN(S_AXI_ARESETN), + // AXI4 slave interface + // {{{ + // Write address channel + // {{{ + .S_AXI_AWVALID(1'b0), + .S_AXI_AWREADY(ign_awready), + .S_AXI_AWID( {(IW){1'b0}} ), + .S_AXI_AWADDR( {(AW){1'b0}} ), + .S_AXI_AWLEN( 8'h0), + .S_AXI_AWSIZE( 3'h0), + .S_AXI_AWBURST(2'h0), + .S_AXI_AWLOCK( 1'b0), + .S_AXI_AWCACHE(4'h0), + .S_AXI_AWPROT( 3'h0), + .S_AXI_AWQOS( 4'h0), + // }}} + // Write data channel + // {{{ + .S_AXI_WVALID(1'b0), + .S_AXI_WREADY(ign_wready), + .S_AXI_WDATA( {(SLVDW){1'b0}}), + .S_AXI_WSTRB( {(SLVDW/8){1'b0}}), + .S_AXI_WLAST( 1'b0), + // }}} + // Write return channel + // {{{ + .S_AXI_BVALID(ign_bvalid), + .S_AXI_BREADY(1'b1), + .S_AXI_BID( ign_bid), + .S_AXI_BRESP( ign_bresp), + // }}} + // Read address channel + // {{{ + .S_AXI_ARVALID(S_AXI_ARVALID), + .S_AXI_ARREADY(S_AXI_ARREADY), + .S_AXI_ARID( S_AXI_ARID), + .S_AXI_ARADDR( S_AXI_ARADDR), + .S_AXI_ARLEN( S_AXI_ARLEN), + .S_AXI_ARSIZE( S_AXI_ARSIZE), + .S_AXI_ARBURST(S_AXI_ARBURST), + .S_AXI_ARLOCK( S_AXI_ARLOCK), + .S_AXI_ARCACHE(S_AXI_ARCACHE), + .S_AXI_ARPROT( S_AXI_ARPROT), + .S_AXI_ARQOS( S_AXI_ARQOS), + // }}} + // Read data channel + // {{{ + .S_AXI_RVALID(S_AXI_RVALID), + .S_AXI_RREADY(S_AXI_RREADY), + .S_AXI_RID( S_AXI_RID), + .S_AXI_RDATA( S_AXI_RDATA), + .S_AXI_RRESP( S_AXI_RRESP), + .S_AXI_RLAST( S_AXI_RLAST), + // }}} + // }}} + // AXI-lite master interface + // {{{ + // AXI-lite Write interface + // {{{ + .M_AXI_AWVALID(ign_awvalid), + .M_AXI_AWREADY(1'b1), + .M_AXI_AWADDR(ign_awaddr), + .M_AXI_AWPROT(ign_awprot), + // + .M_AXI_WVALID(ign_wvalid), + .M_AXI_WREADY(1'b1), + .M_AXI_WDATA(ign_wdata), + .M_AXI_WSTRB(ign_wstrb), + // + .M_AXI_BVALID(1'b0), + .M_AXI_BREADY(ign_bready), + .M_AXI_BRESP(2'b00), + // }}} + // AXI-lite read interface + // {{{ + .M_AXI_ARVALID(M_AXI_ARVALID), + .M_AXI_ARREADY(M_AXI_ARREADY), + .M_AXI_ARADDR(M_AXI_ARADDR), + .M_AXI_ARPROT(M_AXI_ARPROT), + // + .M_AXI_RVALID(M_AXI_RVALID), + .M_AXI_RREADY(M_AXI_RREADY), + .M_AXI_RDATA(M_AXI_RDATA), + .M_AXI_RRESP(M_AXI_RRESP) + // }}} + // }}} + // }}} + ); + + // }}} + end else begin : RDN + if (OPT_READS) + begin : IMPLEMENT_READS + // {{{ + + // Declarations + // {{{ + wire slv_arvalid, slv_arready; + reg [IW-1:0] slv_arid, mst_arid; + reg [AW-1:0] slv_araddr, mst_araddr; + wire [AW-1:0] slv_next_araddr; + reg [7:0] slv_arlen; + reg slv_arlast, mst_arlast, + mst_arsublast; + reg [2:0] slv_arprot, mst_arprot; + reg [2:0] slv_arsize; + reg [1:0] slv_arburst; + reg [SLVSZ-MSTSZ:0] slv_arbeats, mst_arbeats; + reg [8:0] slv_rlen; + + wire [SLVSZ-MSTSZ-1:0] rfifo_addr; + wire rfifo_end_of_beat, + rfifo_rlast; + wire [4:0] rfifo_count; + // + reg m_arvalid; + wire rfifo_full; + wire rfifo_empty; + reg s_axi_rvalid; + reg [1:0] s_axi_rresp; + reg [IW-1:0] s_axi_rid; + wire [IW-1:0] rfifo_rid; + reg [SLVDW-1:0] s_axi_rdata, next_rdata; + reg s_axi_rlast; + reg [IW-1:0] rid; + wire read_from_rdfifo; + + // + // + // S_AXI_AR* skid buffer + wire skids_arvalid, skids_arready; + wire [IW-1:0] skids_arid; + wire [AW-1:0] skids_araddr; + wire [7:0] skids_arlen; + wire [2:0] skids_arsize, skids_arprot; + wire [1:0] skids_arburst; + // + // S_AXI_R* skid buffer isn't needed + // + // M_AXI_AR* skid buffer isn't needed + // M_AXI_R* skid buffer + wire skidm_rvalid, skidm_rready; + wire [MSTDW-1:0] skidm_rdata; + wire [1:0] skidm_rresp; + // }}} + + // S_AXI_AR* skid buffer + // {{{ + skidbuffer #( + // {{{ + .DW(IW+AW+8+3+2+3), + .OPT_LOWPOWER(OPT_LOWPOWER), + .OPT_OUTREG(0) + // }}} + ) arskid( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY), + .i_data({ S_AXI_ARID, S_AXI_ARADDR, S_AXI_ARLEN, + S_AXI_ARSIZE, S_AXI_ARBURST, S_AXI_ARPROT }), + .o_valid(skids_arvalid), .i_ready(skids_arready), + .o_data({ skids_arid, skids_araddr, skids_arlen, + skids_arsize, skids_arburst, skids_arprot }) + // }}} + ); + // }}} + // M_AXI_R* skid buffer + // {{{ + skidbuffer #( + // {{{ + .DW(MSTDW+2), + .OPT_LOWPOWER(OPT_LOWPOWER), + .OPT_OUTREG(0) + // }}} + ) rskid( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(M_AXI_RVALID), .o_ready(M_AXI_RREADY), + .i_data({ M_AXI_RDATA, M_AXI_RRESP }), + .o_valid(skidm_rvalid), .i_ready(skidm_rready), + .o_data({ skidm_rdata, skidm_rresp }) + // }}} + ); + // }}} + + assign skids_arready = (slv_rlen <= (slv_arready ? 1:0)) + &&(mst_arbeats <=(M_AXI_ARREADY ? 1:0)); + + // slv_* + // {{{ + always @(posedge S_AXI_ACLK) + begin + if (OPT_LOWPOWER && (slv_rlen <= (slv_arready ? 1:0))) + begin + // {{{ + slv_arid <= 0; + slv_araddr <= 0; + slv_arlen <= 0; + slv_arsize <= 0; + slv_arburst <= 0; + slv_arprot <= 0; + + slv_rlen <= 0; + // }}} + end + + if (skids_arvalid && skids_arready) + begin + // {{{ + slv_arid <= skids_arid; + slv_araddr <= skids_araddr; + slv_arlen <= skids_arlen; + slv_arsize <= skids_arsize; + slv_arburst <= skids_arburst; + slv_arlast <= (skids_arlen == 0); + slv_arprot <= skids_arprot; + + slv_rlen <= skids_arlen+1; + // }}} + end else if (slv_arready && slv_rlen > 0) + begin + // {{{ + slv_araddr <= (!OPT_LOWPOWER || slv_rlen > 1) + ? slv_next_araddr : 0; + slv_rlen <= slv_rlen - 1; + slv_arlast <= (slv_rlen <= 2); + // }}} + end + + if (OPT_LOWPOWER && !S_AXI_ARESETN) + begin + // {{{ + slv_arid <= 0; + slv_araddr <= 0; + slv_arlen <= 0; + slv_arsize <= 0; + slv_arburst <= 0; + slv_arprot <= 0; + + slv_arlast <= 1; + // }}} + end + + if (!S_AXI_ARESETN) + slv_rlen <= 0; + end + + assign slv_arvalid = (slv_rlen > 0); +`ifdef FORMAL + always @(*) + if (S_AXI_ARESETN) + begin + assert(slv_arlast == (slv_rlen <= 1)); + assert(slv_rlen <= (slv_arlen + 1)); + end +`endif + // }}} + + // slv_next_araddr + // {{{ + axi_addr #( + // {{{ + .AW(AW), + .DW(SLVDW) + // }}} + ) get_next_slave_addr ( + // {{{ + .i_last_addr(slv_araddr), + .i_size(slv_arsize), + .i_burst(slv_arburst), + .i_len(slv_arlen), + .o_next_addr(slv_next_araddr) + // }}} + ); + // }}} + + // slv_arbeats + // {{{ + always @(*) + if (slv_rlen > 0) + begin + // Master beats to turn this slave beat into + if (slv_arsize >= MSTSZ[2:0]) + slv_arbeats = (1<<(slv_arsize-MSTSZ[2:0])) + - (slv_araddr[MSTSZ-1:0] >> slv_arsize); + else + slv_arbeats = 1; + end else + slv_arbeats = 0; + // }}} + + // mst_araddr, mst_arprot, mst_arbeats + // {{{ + always @(posedge S_AXI_ACLK) + begin + if (slv_arready) + begin + // {{{ + mst_arid <= slv_arid; + mst_araddr <= slv_araddr; + mst_arprot <= slv_arprot; + + // Beats to turn this beat into + mst_arbeats <= slv_arbeats; + mst_arlast <= slv_arlast; + mst_arsublast <= (slv_arbeats <= 1); + + if (OPT_LOWPOWER && slv_rlen == 0) + begin + mst_arid <= 0; + mst_araddr <= 0; + mst_arprot <= 0; + end + // }}} + end else if ((mst_arbeats > 0) + &&(M_AXI_ARVALID && M_AXI_ARREADY)) + begin + // {{{ + mst_araddr <= mst_araddr + (1<<MSTSZ); + mst_araddr[MSTSZ-1:0] <= 0; + mst_arbeats <= mst_arbeats - 1; + + mst_arsublast <= (mst_arbeats <= 2); + // }}} + end + + if (!S_AXI_ARESETN) + begin + // {{{ + mst_arbeats <= 0; + if (OPT_LOWPOWER) + begin + mst_arid <= 0; + mst_araddr <= 0; + mst_arprot <= 0; + end + // }}} + end + end +`ifdef FORMAL + always @(*) + if(OPT_LOWPOWER && S_AXI_ARESETN && mst_arbeats == 0) + begin + assert(mst_arid == 0); + assert(mst_araddr == 0); + assert(mst_arprot == 0); + end +`endif + // }}} + + // m_arvalid + // {{{ + initial m_arvalid = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + m_arvalid <= 0; + else if (slv_arvalid && slv_arready) + m_arvalid <= 1; + else if (M_AXI_ARVALID && M_AXI_ARREADY) + m_arvalid <= (mst_arbeats > 1); +`ifdef FORMAL + always @(*) + if (S_AXI_ARESETN) + assert(M_AXI_ARVALID == (mst_arbeats > 0)); +`endif + // }}} + + assign slv_arready = (mst_arbeats <= (M_AXI_ARREADY ? 1:0)); + assign read_from_rdfifo = skidm_rvalid && skidm_rready + &&(!S_AXI_RVALID || S_AXI_RREADY); + + // Read ID FIFO + // {{{ + sfifo #( + // {{{ + .BW(IW+2+SLVSZ-MSTSZ), + .LGFLEN(LGFIFO) + // }}} + ) ridlnfifo( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + // + .i_wr(M_AXI_ARVALID && M_AXI_ARREADY), + .i_data({ mst_arid, mst_arlast, mst_arsublast, + M_AXI_ARADDR[SLVSZ-1:MSTSZ] }), + .o_full(rfifo_full), .o_fill(rfifo_count), + // + .i_rd(read_from_rdfifo), + .o_data({ rfifo_rid, rfifo_rlast, rfifo_end_of_beat, + rfifo_addr }), + .o_empty(rfifo_empty) + // }}} + ); + // }}} + + assign skidm_rready = (!S_AXI_RVALID || S_AXI_RREADY) + && !rfifo_empty; + + // s_axi_rvalid + // {{{ + initial s_axi_rvalid = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + s_axi_rvalid <= 0; + else if (skidm_rvalid && skidm_rready && rfifo_end_of_beat) + s_axi_rvalid <= 1'b1; + else if (S_AXI_RREADY) + s_axi_rvalid <= 0; + // }}} + + // s_axi_rdata + // {{{ + always @(*) + begin + next_rdata = s_axi_rdata; + if (S_AXI_RVALID) + next_rdata = 0; + if (skidm_rvalid) + next_rdata = next_rdata | ({ {(SLVDW-MSTDW){1'b0}}, skidm_rdata } << (rfifo_addr * MSTDW)); + end + + always @(posedge S_AXI_ACLK) + if (OPT_LOWPOWER && !S_AXI_ARESETN) + s_axi_rdata <= 0; + else if (!S_AXI_RVALID || S_AXI_RREADY) + s_axi_rdata <= next_rdata; + // }}} + + // s_axi_rresp + // {{{ + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + s_axi_rresp <= OKAY; + else if (!S_AXI_RVALID || S_AXI_RREADY) + begin + if (S_AXI_RVALID) + s_axi_rresp <= (skidm_rvalid) ? skidm_rresp : OKAY; + else if (skidm_rvalid) + casez({ s_axi_rresp, skidm_rresp[1] }) + // Let SLVERR take priority over DECERR + 3'b??0: s_axi_rresp <= s_axi_rresp; + 3'b0?1: s_axi_rresp <= skidm_rresp; + 3'b101: s_axi_rresp <= SLVERR; + 3'b111: s_axi_rresp <= skidm_rresp; + endcase + end + // }}} + + // rid + // {{{ + initial rid = 0; + always @(posedge S_AXI_ACLK) + if (read_from_rdfifo) + rid <= rfifo_rid; + // }}} + + // s_axi_rlast + // {{{ + always @(posedge S_AXI_ACLK) + if (!S_AXI_RVALID || S_AXI_RREADY) + begin + if (read_from_rdfifo) + s_axi_rlast <= rfifo_rlast; + else + s_axi_rlast <= 0; + end + // }}} + + // s_axi_rid + // {{{ + initial s_axi_rid = 0; + always @(posedge S_AXI_ACLK) + if ((S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST) + ||(!S_AXI_RVALID && rfifo_end_of_beat)) + s_axi_rid <= (read_from_rdfifo && rfifo_end_of_beat)?rfifo_rid : rid; + // }}} + + // M_AXI_AR* + // {{{ + assign M_AXI_ARVALID= m_arvalid; + assign M_AXI_ARADDR = mst_araddr; + assign M_AXI_ARPROT = mst_arprot; + // }}} + // S_AXI_R* + // {{{ + assign S_AXI_RVALID = s_axi_rvalid; + assign S_AXI_RDATA = s_axi_rdata; + assign S_AXI_RRESP = s_axi_rresp; + assign S_AXI_RLAST = s_axi_rlast; + assign S_AXI_RID = s_axi_rid; + // }}} + // Make Verilator happy + // {{{ + // Verilator lint_off UNUSED + wire unused_read; + assign unused_read = &{ 1'b0, + /* + S_AXI_AWID, S_AXI_AWVALID, + S_AXI_AWLEN, S_AXI_AWBURST, S_AXI_AWSIZE, + S_AXI_AWADDR, + S_AXI_WVALID, S_AXI_WDATA, S_AXI_WSTRB, + S_AXI_WLAST, S_AXI_BREADY, + M_AXI_AWREADY, M_AXI_WREADY, + M_AXI_BRESP, M_AXI_BVALID, + */ + rfifo_count, rfifo_full + }; + // Verilator lint_on UNUSED + // }}} + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + // + // Formal properties, read half + // {{{ + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// +`ifdef FORMAL + // As with the write half, the following is a subset of the + // formal properties used to verify this section of the core. + // It may, or may not, be syntactically correct. I don't + // warrant this version of the design. + // + // Register declarations + // {{{ + localparam F_LGDEPTH = LGFIFO+1+8; + + wire [F_LGDEPTH-1:0] faxi_awr_nbursts; + wire [9-1:0] faxi_wr_pending; + wire [F_LGDEPTH-1:0] faxi_rd_nbursts; + wire [F_LGDEPTH-1:0] faxi_rd_outstanding; + + // + // ... + // + + localparam F_AXIL_LGDEPTH = F_LGDEPTH; + wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding, + faxil_wr_outstanding, + faxil_awr_outstanding; + // }}} + //////////////////////////////////////////////////////////////// + // + // AXI channel properties + // {{{ + //////////////////////////////////////////////////////////////// + // + // + faxi_slave #( + // {{{ + .C_AXI_ID_WIDTH(IW), + .C_AXI_DATA_WIDTH(SLVDW), + .C_AXI_ADDR_WIDTH(AW), + .OPT_EXCLUSIVE(0) + // ... + // }}} + ) faxi( + // {{{ + .i_clk(S_AXI_ACLK), + .i_axi_reset_n(S_AXI_ARESETN), + // Write address + // {{{ + .i_axi_awvalid(1'b0), + .i_axi_awready(1'b0), + .i_axi_awid( skids_arid), + .i_axi_awaddr( skids_araddr), + .i_axi_awlen( 8'h0), + .i_axi_awsize( 3'h0), + .i_axi_awburst(2'h0), + .i_axi_awlock( 0), + .i_axi_awcache(0), + .i_axi_awprot( 0), + .i_axi_awqos( 0), + // }}} + // Write data + // {{{ + .i_axi_wvalid( 1'b0), + .i_axi_wready( 1'b0), + .i_axi_wdata( {(C_S_AXI_DATA_WIDTH ){1'b0}}), + .i_axi_wstrb( {(C_S_AXI_DATA_WIDTH/8){1'b0}}), + .i_axi_wlast( 1'b0), + // }}} + // Write return response + // {{{ + .i_axi_bvalid( 1'b0), + .i_axi_bready( 1'b0), + .i_axi_bid( S_AXI_BID), + .i_axi_bresp( 2'b00), + // }}} + // Read address + // {{{ + .i_axi_arready(skids_arready), + .i_axi_arid( skids_arid), + .i_axi_araddr( skids_araddr), + .i_axi_arlen( skids_arlen), + .i_axi_arsize( skids_arsize), + .i_axi_arburst(skids_arburst), + .i_axi_arlock( 0), + .i_axi_arcache(0), + .i_axi_arprot( 0), + .i_axi_arqos( 0), + .i_axi_arvalid(skids_arvalid), + // }}} + // Read response + // {{{ + .i_axi_rid( S_AXI_RID), + .i_axi_rresp( S_AXI_RRESP), + .i_axi_rvalid( S_AXI_RVALID), + .i_axi_rdata( S_AXI_RDATA), + .i_axi_rlast( S_AXI_RLAST), + .i_axi_rready( S_AXI_RREADY), + // }}} + // Formal property data + // {{{ + .f_axi_awr_nbursts( faxi_awr_nbursts), + .f_axi_wr_pending( faxi_wr_pending), + .f_axi_rd_nbursts( faxi_rd_nbursts), + .f_axi_rd_outstanding(faxi_rd_outstanding), + // + // ... + // + // }}} + // }}} + ); + + always @(*) + begin + assert(faxi_awr_nbursts == 0); + assert(faxi_wr_pending == 0); + assert(faxi_wr_ckvalid == 0); + end + // }}} + //////////////////////////////////////////////////////////////// + // + // AXI-lite properties + // {{{ + //////////////////////////////////////////////////////////////// + // + // + faxil_master #( + // {{{ + .C_AXI_DATA_WIDTH(MSTDW), + .C_AXI_ADDR_WIDTH(AW), + .F_OPT_NO_RESET(1), + .F_AXI_MAXWAIT(5), + .F_AXI_MAXDELAY(4), + .F_AXI_MAXRSTALL(0), + .F_OPT_WRITE_ONLY(1'b0), + .F_OPT_READ_ONLY(1'b1), + .F_LGDEPTH(F_AXIL_LGDEPTH) + // }}} + ) faxil( + // {{{ + .i_clk(S_AXI_ACLK), + .i_axi_reset_n(S_AXI_ARESETN), + // Write address channel + // {{{ + .i_axi_awvalid(1'b0), + .i_axi_awready(1'b0), + .i_axi_awaddr( M_AXI_AWADDR), + .i_axi_awprot( 3'h0), + // }}} + // Write data + // {{{ + .i_axi_wvalid( 1'b0), + .i_axi_wready( 1'b0), + .i_axi_wdata( {(C_M_AXI_DATA_WIDTH ){1'b0}}), + .i_axi_wstrb( {(C_M_AXI_DATA_WIDTH/8){1'b0}}), + // }}} + // Write response + // {{{ + .i_axi_bvalid( 1'b0), + .i_axi_bready( 1'b0), + .i_axi_bresp( 2'b00), + // }}} + // Read address + // {{{ + .i_axi_arvalid(M_AXI_ARVALID), + .i_axi_arready(M_AXI_ARREADY), + .i_axi_araddr( M_AXI_ARADDR), + .i_axi_arprot( M_AXI_ARPROT), + // }}} + // Read data return + // {{{ + .i_axi_rvalid( skidm_rvalid), + .i_axi_rready( skidm_rready), + .i_axi_rdata( skidm_rdata), + .i_axi_rresp( skidm_rresp), + // }}} + // Formal check variables + // {{{ + .f_axi_rd_outstanding(faxil_rd_outstanding), + .f_axi_wr_outstanding(faxil_wr_outstanding), + .f_axi_awr_outstanding(faxil_awr_outstanding) + // }}} + // }}} + ); + + always @(*) + begin + assert(faxil_awr_outstanding == 0); + assert(faxil_wr_outstanding == 0); + end + // }}} +`endif + // }}} + // }}} + end else begin : NO_READ_SUPPORT // if (!OPT_READS) + // {{{ + assign M_AXI_ARVALID= 0; + assign M_AXI_ARADDR = 0; + assign M_AXI_ARPROT = 0; + assign M_AXI_RREADY = 0; + // + assign S_AXI_ARREADY= 0; + assign S_AXI_RVALID = 0; + assign S_AXI_RDATA = 0; + assign S_AXI_RRESP = 0; + assign S_AXI_RLAST = 0; + assign S_AXI_RID = 0; + + // Make Verilator happy + // Verilator lint_off UNUSED + wire unused_read; + assign unused_read = &{ 1'b0, M_AXI_ARREADY, M_AXI_RVALID, + M_AXI_RDATA, M_AXI_RRESP, S_AXI_RREADY, + S_AXI_ARLEN, S_AXI_ARSIZE, S_AXI_ARBURST, + S_AXI_ARADDR, S_AXI_ARVALID, S_AXI_ARID + }; + // Verilator lint_on UNUSED + // }}} + end end endgenerate + // }}} + // Minimal parameter validation + // {{{ + initial begin + if (SLVDW < MSTDW) + begin + $fatal; // Fatal elaboration error + $stop; // Stop any simulation + end + end + // }}} + // Make Verilator happy + // {{{ + // Verilator lint_off UNUSED + wire unused; + assign unused = &{ 1'b0, + S_AXI_AWLOCK, S_AXI_AWCACHE, S_AXI_AWPROT, S_AXI_AWQOS, + // skids_wlast, wfifo_count, rfifo_count + S_AXI_ARLOCK, S_AXI_ARCACHE, S_AXI_ARPROT, S_AXI_ARQOS + }; + // Verilator lint_on UNUSED + // }}} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Formal properties +// {{{ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +`ifdef FORMAL + //////////////////////////////////////////////////////////////////////// + // + // Assume that the two write channels stay within an appropriate + // distance of each other. This is to make certain that the property + // file features are not violated, although not necessary true for + // actual operation + // + + //////////////////////////////////////////////////////////////////////// + // + // Select only write or only read operation + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + generate if (!OPT_WRITES) + begin + always @(*) + begin + assume(!S_AXI_AWVALID); + assume(!S_AXI_WVALID); + assert(!M_AXI_AWVALID); + assert(!M_AXI_WVALID); + assume(!M_AXI_BVALID); + assert(!S_AXI_BVALID); + end + end endgenerate + + generate if (!OPT_READS) + begin + + always @(*) + begin + assume(!S_AXI_ARVALID); + assert(!M_AXI_ARVALID); + end + + end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Lowpower assertions + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + generate if (OPT_LOWPOWER) + begin : F_LOWPOWER + + always @(*) + if (S_AXI_ARESETN) + begin + if (!M_AXI_AWVALID) + begin + // Not supported. + // assert(M_AXI_AWADDR == 0); + // assert(M_AXI_AWPROT == 0); + end + + if (!M_AXI_WVALID) + begin + // assert(M_AXI_WDATA == 0); + // assert(M_AXI_WSTRB == 0); + end + + if (!M_AXI_ARVALID) + begin + assert(M_AXI_ARADDR == 0); + assert(M_AXI_ARPROT == 0); + end + + if (!S_AXI_RVALID) + begin + // These items build over the course of a + // returned burst, so they might not be + // zero when RVALID is zero. + // + // assert(S_AXI_RLAST == 0); + // assert(S_AXI_RDATA == 0); + // assert(S_AXI_RID == 0); + end + end + + end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Cover statements, to show performance + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + generate if (OPT_READS) + begin + // {{{ + reg [3:0] cvr_read_count, cvr_read_count_simple; + + initial cvr_read_count_simple = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + cvr_read_count_simple <= 0; + else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN == 0) + cvr_read_count_simple <= cvr_read_count_simple + 1; + + initial cvr_read_count = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + cvr_read_count <= 0; + else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN > 2) + cvr_read_count <= cvr_read_count + 1; + + // }}} + end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // "Careless" assumptions + // {{{ + //////////////////////////////////////////////////////////////////////// + // + + // }}} +`undef BMC_ASSERT +`endif +// }}} +endmodule |
