////////////////////////////////////////////////////////////////////////////////
//
// Filename:	rtl/axil2axis.v
// {{{
// Project:	WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose:	Demonstrates a simple AXI-Lite interface to drive an AXI-Stream
//		channel.  This can then be used to debug DSP processing.
//
// Registers:	This AXI-lite to AXI-Stream core supports four word-sized
//		addresses.  Byte enables are ignored.
//
//   2'b00, ADDR_SINK
//		Writes to this register will send data to the stream master,
//		with TLAST clear.  Data goes first through a FIFO.  If the
//		FIFO is full, the write will stall.  If it stalls OPT_TIMEOUT
//		cycles, the write will fail and return a bus error.
//
//		Reads from this register will return data from the stream slave,
//		but without consuming it.  Values read here may still be read
//		from the ADDR_SOURCE register later.
//
//   2'b01, ADDR_SOURCE
//		Writes to this register will send data downstream to the stream
//		master as well, but this time with TLAST set.
//
//		Reads from this register will accept a value from the stream
//		slave interface.  The read value contains TDATA.  TLAST is
//		ignored in this read.  If you want access to TLAST, you can get
//		it from the ADDR_FIFO register.
//
//		If there is no data to be read, the read will not and does not
//		stall.  It will instead return a bus error.
//
//   2'b10, ADDR_STATS
//		Since we can, we'll handle some statistics  here.  The top half
//		word contains two counters: a 4-bit counter of TLAST's issued
//		from the stream master, and a 12-bit counter of TDATA values
//		issued.  Neither counter includes data still contained in the
//		FIFO.  If the OPT_SOURCE option is clear, these values will
//		always be zero.
//
//		The second (bottom, or least-significant) halfword contains the
//		same regarding the stream slave.  If OPT_SINK is set, these
//		counters count values read from the core.  If OPT_SINK is clear,
//		so that the stream sink is not truly implemented, then TREADY
//		will be held high and the counter will just count values coming
//		into the core never going into the FIFO.
//
//   2'b11, ADDR_FIFO
//		Working with the core can be a challenge.  You want to make
//		certain that writing to the core  doesn't hang the design, and
//		that reading from the core doesn't cause a bus error.
//
//		Bits 31:16 contain the number of items in the write FIFO, and
//		bits 14:0 contain the number of items in the read FIFO.
//
//		Bit 15 contains whether or not the next item to be read is
//		the last item in a packet, i.e. with TLAST set.
//		
//
// Creator:	Dan Gisselquist, Ph.D.
//		Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
// }}}
// Copyright (C) 2019-2025, Gisselquist Technology, LLC
// {{{
// This file is part of the WB2AXIP project.
//
// The WB2AXIP project contains free software and gateware, licensed under the
// Apache License, Version 2.0 (the "License").  You may not use this project,
// or this file, except in compliance with the License.  You may obtain a copy
// of the License at
// }}}
//	http://www.apache.org/licenses/LICENSE-2.0
// {{{
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations
// under the License.
//
////////////////////////////////////////////////////////////////////////////////
//
`default_nettype none
// }}}
module	axil2axis #(
		// {{{
		//
		// Size of the AXI-lite bus.  These are fixed, since 1) AXI-lite
		// is fixed at a width of 32-bits by Xilinx def'n, and 2) since
		// we only ever have 4 configuration words.
		parameter	C_AXI_ADDR_WIDTH = 4,
		localparam	C_AXI_DATA_WIDTH = 32,
		parameter	C_AXIS_DATA_WIDTH = 16,
		//
		// OPT_SOURCE enables the AXI stream master logic.  If not
		// enabled, M_AXI_TVALID will be held at zero, and the stream
		// master logic may be ignored.
		parameter [0:0] OPT_SOURCE = 1'b1,
		//
		// OPT_SINK enables the AXI stream slave logic.  If not enabled,
		// reads will always return zero, and S_AXIS_TREADY will be
		// held high.
		parameter [0:0] OPT_SINK = 1'b1,
		//
		// If OPT_SIGN_EXTEND is set, values received will be sign
		// extended to fill the full data width on read.  Otherwise
		// the most significant of any unused bits will remain clear.
		parameter [0:0] OPT_SIGN_EXTEND = 1'b0,
		//
		// Data written to this core will be placed into a FIFO before
		// entering the AXI stream master.  LGFIFO is the log, based
		// two, of the number of words in this FIFO.  Similarly, data
		// consumed by AXI stream slave contained in this core will go
		// first into a read FIFO.  Reads from the core will then return
		// data from this FIFO, or a bus error if none is available.
		parameter 	LGFIFO = 5,
		//
		// OPT_TIMEOUT, if non-zero, will allow writes to the stream
		// master, or reads from the stream slave, to stall the core
		// for OPT_TIMEOUT cycles for the stream to be ready.  If the
		// stream isn't ready at this time (i.e. if the write FIFO is
		// still full, or the read FIFO still empty), the result will
		// be returned as a bus error.  Likewise, if OPT_TIMEOUT==0,
		// the core will always return a bus error if ever the write
		// FIFO is full or the read FIFO empty.
		parameter	OPT_TIMEOUT = 5,
		//
		// OPT_LOWPOWER sets outputs to zero if not valid.  This applies
		// to the AXI-lite bus, however, and not the AXI stream FIFOs,
		// since those don't have LOWPOWER support (currently).
		parameter [0:0]	OPT_LOWPOWER = 0
		//
		// This design currently ignores WSTRB, beyond checking that it
		// is not zero.  I see no easy way to add it.  (I'll leave that
		// to you to implement, if you wish.)
		// parameter [0:0]	OPT_WSTRB = 0,
		// }}}
	) (
		// {{{
		input	wire					S_AXI_ACLK,
		input	wire					S_AXI_ARESETN,
		// AXI-lite signals
		// {{{
		input	wire					S_AXI_AWVALID,
		output	wire					S_AXI_AWREADY,
		input	wire	[C_AXI_ADDR_WIDTH-1:0]		S_AXI_AWADDR,
		input	wire	[2:0]				S_AXI_AWPROT,
		//
		input	wire					S_AXI_WVALID,
		output	wire					S_AXI_WREADY,
		input	wire	[C_AXI_DATA_WIDTH-1:0]		S_AXI_WDATA,
		input	wire	[C_AXI_DATA_WIDTH/8-1:0]	S_AXI_WSTRB,
		//
		output	wire					S_AXI_BVALID,
		input	wire					S_AXI_BREADY,
		output	wire	[1:0]				S_AXI_BRESP,
		//
		input	wire					S_AXI_ARVALID,
		output	wire					S_AXI_ARREADY,
		input	wire	[C_AXI_ADDR_WIDTH-1:0]		S_AXI_ARADDR,
		input	wire	[2:0]				S_AXI_ARPROT,
		//
		output	wire					S_AXI_RVALID,
		input	wire					S_AXI_RREADY,
		output	wire	[C_AXI_DATA_WIDTH-1:0]		S_AXI_RDATA,
		output	wire	[1:0]				S_AXI_RRESP,
		// }}}
		// AXI stream slave (sink) signals
		// {{{
		input	wire				S_AXIS_TVALID,
		output	wire				S_AXIS_TREADY,
		input	wire	[C_AXIS_DATA_WIDTH-1:0]	S_AXIS_TDATA,
		input	wire				S_AXIS_TLAST,
		// }}}
		// AXI stream master (source) signals
		// {{{
		output	wire				M_AXIS_TVALID,
		input	wire				M_AXIS_TREADY,
		output	reg	[C_AXIS_DATA_WIDTH-1:0]	M_AXIS_TDATA,
		output	reg				M_AXIS_TLAST
		// }}}
		// }}}
	);

	localparam	ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
	localparam	[1:0]	ADDR_SINK = 2'b00,	// Read from stream
				ADDR_SOURCE = 2'b01, // Write, also sets TLAST
				ADDR_STATS  = 2'b10,
				ADDR_FIFO   = 2'b11;
	localparam	SW = C_AXIS_DATA_WIDTH;

	////////////////////////////////////////////////////////////////////////
	//
	// Register/wire signal declarations
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//
	wire	i_reset = !S_AXI_ARESETN;

	wire				axil_write_ready;
	wire	[C_AXI_ADDR_WIDTH-ADDRLSB-1:0]	awskd_addr;
	//
	wire	[C_AXI_DATA_WIDTH-1:0]	wskd_data;
	wire [C_AXI_DATA_WIDTH/8-1:0]	wskd_strb;
	reg				axil_bvalid, axil_berr;
	//
	wire				axil_read_ready;
	wire	[C_AXI_ADDR_WIDTH-ADDRLSB-1:0]	arskd_addr;
	reg	[C_AXI_DATA_WIDTH-1:0]	axil_read_data;
	reg				axil_read_valid;

	wire			awskd_valid, wskd_valid;
	wire			wfifo_full, wfifo_write, wfifo_empty;
	wire	[LGFIFO:0]	wfifo_fill;
	reg			write_timeout;

	wire			read_timeout;
	reg			axil_rerr;
	reg	[3:0]		read_bursts_completed;
	reg	[11:0]		reads_completed;

	wire	[3:0]	write_bursts_completed;
	wire	[11:0]	writes_completed;


	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// AXI-lite signaling
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//

	//
	// Write signaling
	//
	// {{{
	skidbuffer #(.OPT_OUTREG(0),
			.OPT_LOWPOWER(OPT_LOWPOWER),
			.DW(C_AXI_ADDR_WIDTH-ADDRLSB))
	axilawskid(//
		.i_clk(S_AXI_ACLK), .i_reset(i_reset),
		.i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
		.i_data(S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
		.o_valid(awskd_valid), .i_ready(axil_write_ready),
		.o_data(awskd_addr));

	skidbuffer #(.OPT_OUTREG(0),
			.OPT_LOWPOWER(OPT_LOWPOWER),
			.DW(C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8))
	axilwskid(//
		.i_clk(S_AXI_ACLK), .i_reset(i_reset),
		.i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
		.i_data({ S_AXI_WDATA, S_AXI_WSTRB }),
		.o_valid(wskd_valid), .i_ready(axil_write_ready),
		.o_data({ wskd_data, wskd_strb }));

	assign	axil_write_ready = awskd_valid && wskd_valid
			&& (!S_AXI_BVALID || S_AXI_BREADY)
			&& ((awskd_addr[1] != ADDR_SOURCE[1])
				|| (!wfifo_full || write_timeout));

	//
	// Write timeout generation
	//
	// {{{
	generate if ((OPT_TIMEOUT > 1) && OPT_SOURCE)
	begin : GEN_WRITE_TIMEOUT
		reg				r_write_timeout;
		reg [$clog2(OPT_TIMEOUT)-1:0] write_timer;

		initial	write_timer = OPT_TIMEOUT-1;
		initial	r_write_timeout = 0;
		always @(posedge S_AXI_ACLK)
		if (!S_AXI_ARESETN)
		begin
			write_timer <= OPT_TIMEOUT-1;
			r_write_timeout<= 1'b0;
		end else if (!awskd_valid || !wfifo_full || !wskd_valid
				|| (awskd_addr[1] != ADDR_SOURCE[1])
				|| (S_AXI_BVALID && !S_AXI_BREADY))
		begin
			write_timer <= OPT_TIMEOUT-1;
			r_write_timeout<= 1'b0;
		end else begin
			if (write_timer > 0)
				write_timer <= write_timer - 1;
			r_write_timeout <= (write_timer <= 1);
		end

		assign	write_timeout = r_write_timeout;
`ifdef	FORMAL
		always @(*)
			assert(write_timer <= OPT_TIMEOUT-1);
		always @(*)
			assert(write_timeout == (write_timer == 0));
`endif
	end else begin : NO_WRITE_TIMEOUT

		assign	write_timeout = 1'b1;

	end endgenerate
	// }}}
	

	initial	axil_bvalid = 0;
	always @(posedge S_AXI_ACLK)
	if (i_reset)
		axil_bvalid <= 0;
	else if (axil_write_ready)
		axil_bvalid <= 1;
	else if (S_AXI_BREADY)
		axil_bvalid <= 0;

	assign	S_AXI_BVALID = axil_bvalid;

	initial	axil_berr = 0;
	always @(posedge S_AXI_ACLK)
	if (OPT_LOWPOWER && i_reset)
		axil_berr <= 0;
	else if (axil_write_ready)
		axil_berr <= (wfifo_full)&&(awskd_addr[1]==ADDR_SOURCE[1]);
	else if (OPT_LOWPOWER && S_AXI_BREADY)
		axil_berr <= 1'b0;

	assign	S_AXI_BRESP = { axil_berr, 1'b0 };
	// }}}

	//
	// AXI-stream source (Write) FIFO
	//
	// {{{
	assign	wfifo_write = axil_write_ready && awskd_addr[1]==ADDR_SOURCE[1]
			&& wskd_strb != 0 && !wfifo_full;

	generate if (OPT_SOURCE)
	begin : GEN_SOURCE_FIFO

		sfifo #(.BW(SW+1), .LGFLEN(LGFIFO))
		source(.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
			.i_wr(wfifo_write),
			.i_data({awskd_addr[0]==ADDR_SOURCE[0],
					wskd_data[SW-1:0]}),
			.o_full(wfifo_full), .o_fill(wfifo_fill),
			.i_rd(M_AXIS_TREADY),
				.o_data({ M_AXIS_TLAST, M_AXIS_TDATA }),
				.o_empty(wfifo_empty));

		assign	M_AXIS_TVALID = !wfifo_empty;

	end else begin : NO_SOURCE_FIFO

		assign	M_AXIS_TVALID = 1'b0;
		assign	M_AXIS_TDATA  = 0;
		assign	M_AXIS_TLAST  = 0;

		assign	wfifo_full = 1'b0;
		assign	wfifo_fill = 0;

	end endgenerate
	// }}}

	//
	// AXI-stream consumer/sink (Read) FIFO
	//
	// {{{
	wire			rfifo_empty, rfifo_full, rfifo_last, read_rfifo;
	wire	[LGFIFO:0]	rfifo_fill;
	wire	[SW-1:0]	rfifo_data;

	generate if (OPT_SINK)
	begin : GEN_SINK_FIFO

		sfifo #(.BW(SW+1), .LGFLEN(LGFIFO))
		sink(.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN),
			.i_wr(S_AXIS_TVALID && S_AXIS_TREADY),
			.i_data({S_AXIS_TLAST, S_AXIS_TDATA}),
			.o_full(rfifo_full), .o_fill(rfifo_fill),
			.i_rd(read_rfifo),
				.o_data({ rfifo_last, rfifo_data }),
				.o_empty(rfifo_empty));

		assign	S_AXIS_TREADY = !rfifo_full;
		assign	read_rfifo =(axil_read_ready && arskd_addr== ADDR_SINK)
				&& !rfifo_empty;

	end else begin : NO_SINK

		assign	S_AXIS_TREADY = 1'b1;

		assign rfifo_empty = 1'b1;
		assign rfifo_data  = 0;
		assign rfifo_last  = 1'b1;
		assign rfifo_fill  = 0;

	end endgenerate
	// }}}

	//
	// Read timeout generation
	//
	// {{{
	generate if (OPT_SINK && OPT_TIMEOUT > 1)
	begin : GEN_READ_TIMEOUT
		reg [$clog2(OPT_TIMEOUT)-1:0]	read_timer;
		reg				r_read_timeout;

		initial	read_timer = OPT_TIMEOUT-1;
		initial	r_read_timeout = 1'b0;
		always @(posedge S_AXI_ACLK)
		if (!S_AXI_ARESETN)
		begin
			read_timer <= OPT_TIMEOUT-1;
			r_read_timeout<= 1'b0;
		end else if (!arskd_valid || (S_AXI_RVALID && !S_AXI_RREADY)
				||!rfifo_empty
				||(arskd_addr[1] != ADDR_SINK[1]))
		begin
			read_timer <= OPT_TIMEOUT-1;
			r_read_timeout<= 1'b0;
		end else begin
			if (read_timer > 0)
				read_timer <= read_timer - 1;
			r_read_timeout <= (read_timer <= 1);
		end

		assign	read_timeout = r_read_timeout;

`ifdef	FORMAL
		always @(*)
			assert(read_timer <= OPT_TIMEOUT-1);
		always @(*)
			assert(read_timeout == (read_timer == 0));
`endif
	end else begin : NO_READ_TIMEOUT

		assign	read_timeout = 1'b1;

	end endgenerate
	// }}}

	//
	// Read signaling
	//
	// {{{
	wire	arskd_valid;

	skidbuffer #(.OPT_OUTREG(0),
			.OPT_LOWPOWER(OPT_LOWPOWER),
			.DW(C_AXI_ADDR_WIDTH-ADDRLSB))
	axilarskid(//
		.i_clk(S_AXI_ACLK), .i_reset(i_reset),
		.i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
		.i_data(S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
		.o_valid(arskd_valid), .i_ready(axil_read_ready),
		.o_data(arskd_addr));

	assign	axil_read_ready = arskd_valid
			&& (!S_AXI_RVALID || S_AXI_RREADY)
			&& ((arskd_addr[1] != ADDR_SINK[1])
				|| (!rfifo_empty || read_timeout));

	initial	axil_read_valid = 1'b0;
	always @(posedge S_AXI_ACLK)
	if (i_reset)
		axil_read_valid <= 1'b0;
	else if (axil_read_ready)
		axil_read_valid <= 1'b1;
	else if (S_AXI_RREADY)
		axil_read_valid <= 1'b0;

	assign	S_AXI_RVALID = axil_read_valid;

	always @(posedge S_AXI_ACLK)
	if (OPT_LOWPOWER && !S_AXI_ARESETN)
		axil_rerr <= 1'b0;
	else if (axil_read_ready)
		axil_rerr <= rfifo_empty && (arskd_addr[1] == ADDR_SINK[1]);
	else if (OPT_LOWPOWER && S_AXI_RREADY)
		axil_rerr <= 1'b0;

	assign	S_AXI_RRESP = { axil_rerr, 1'b0 };
	// }}}

	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// AXI-lite register logic
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//

	//
	// Read data counting : reads_completed and read_bursts_completed
	// {{{
	initial	reads_completed = 0;
	initial	read_bursts_completed = 0;
	always @(posedge S_AXI_ACLK)
	if (!S_AXI_ARESETN)
	begin
		// {{{
		reads_completed <= 0;
		read_bursts_completed <= 0;
		// }}}
	end else if (!OPT_SINK)
	begin
		// {{{
		reads_completed <= reads_completed + (S_AXIS_TVALID ? 1:0);
		read_bursts_completed <= read_bursts_completed
				+ ((S_AXIS_TVALID && S_AXIS_TLAST) ? 1:0);
		// }}}
	end else if (read_rfifo && !rfifo_empty)
	begin
		// {{{
		reads_completed <= reads_completed + 1;
		read_bursts_completed <= read_bursts_completed + (rfifo_last ? 1:0);
		// }}}
	end
	// }}}

	//
	// Write data counting
	// {{{
	generate if (OPT_SOURCE)
	begin : GEN_WRITES_COMPLETED
		// {{{
		reg	[3:0]	r_write_bursts_completed;
		reg	[11:0]	r_writes_completed;

		initial	r_writes_completed = 0;
		initial	r_write_bursts_completed = 0;
		always @(posedge S_AXI_ACLK)
		if (!S_AXI_ARESETN)
		begin
			r_writes_completed <= 0;
			r_write_bursts_completed <= 0;
		end else if (M_AXIS_TVALID && M_AXIS_TREADY)
		begin
			r_writes_completed <= writes_completed + 1;
			r_write_bursts_completed <= write_bursts_completed
					+ (M_AXIS_TLAST ? 1:0);
		end

		assign	writes_completed = r_writes_completed;
		assign	write_bursts_completed = r_write_bursts_completed;
		// }}}
	end else begin : NO_COMPLETION_COUNTERS // No AXI-stream source logic
		// {{{
		assign	writes_completed = 0;
		assign	write_bursts_completed = 0;
		// }}}
	end endgenerate
	// }}}

	//
	// Read data register
	// {{{
	initial	axil_read_data = 0;
	always @(posedge S_AXI_ACLK)
	if (OPT_LOWPOWER && !S_AXI_ARESETN)
		axil_read_data <= 0;
	else if (!S_AXI_RVALID || S_AXI_RREADY)
	begin
		axil_read_data <= 0;
		casez(arskd_addr)
		{ ADDR_SINK[1], 1'b? }:	begin
			if (OPT_SIGN_EXTEND && rfifo_data[SW-1])
				axil_read_data <= -1;
			axil_read_data[SW-1:0] <= rfifo_data;
			end
		ADDR_STATS: begin
			axil_read_data[31:28] <= write_bursts_completed;
			axil_read_data[27:16] <= writes_completed;
			axil_read_data[15:12] <= read_bursts_completed;
			axil_read_data[11:0]  <= reads_completed;
			end
		ADDR_FIFO:	begin
			// FIFO information
			axil_read_data[16 +: LGFIFO+1] <= wfifo_fill;
			axil_read_data[15] <= rfifo_last;
			axil_read_data[LGFIFO:0] <= rfifo_fill;
			end
		endcase

		if (OPT_LOWPOWER && !axil_read_ready)
			axil_read_data <= 0;
	end

	assign	S_AXI_RDATA  = axil_read_data;
	// }}}

	// Make Verilator happy
	// {{{
	// Verilator lint_off UNUSED
	wire	unused;
	assign	unused = &{ 1'b0, S_AXI_AWPROT, S_AXI_ARPROT,
			S_AXI_ARADDR[ADDRLSB-1:0],
			S_AXI_AWADDR[ADDRLSB-1:0],
			wskd_data[C_AXI_DATA_WIDTH-1:SW] };
	// Verilator lint_on  UNUSED
	// }}}
	// }}}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Formal properties used in verfiying this core
// {{{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
`ifdef	FORMAL
	// Register definitions
	// {{{
	reg	f_past_valid;
	initial	f_past_valid = 0;
	always @(posedge S_AXI_ACLK)
		f_past_valid <= 1;

	always @(*)
	if (!f_past_valid)
		assume(!S_AXI_ARESETN);
	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// The AXI-lite control interface
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//
	localparam	F_AXIL_LGDEPTH = 4;
	wire	[F_AXIL_LGDEPTH-1:0]	faxil_rd_outstanding,
					faxil_wr_outstanding,
					faxil_awr_outstanding;

	faxil_slave #(
		// {{{
		.C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
		.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
		.F_LGDEPTH(F_AXIL_LGDEPTH),
		.F_AXI_MAXWAIT(OPT_TIMEOUT + 2),
		.F_AXI_MAXDELAY(OPT_TIMEOUT + 2),
		.F_AXI_MAXRSTALL(2),
		.F_OPT_COVER_BURST(4)
		// }}}
	) faxil(
		// {{{
		.i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
		//
		.i_axi_awvalid(S_AXI_AWVALID),
		.i_axi_awready(S_AXI_AWREADY),
		.i_axi_awaddr( S_AXI_AWADDR),
		.i_axi_awprot( S_AXI_AWPROT),
		//
		.i_axi_wvalid(S_AXI_WVALID),
		.i_axi_wready(S_AXI_WREADY),
		.i_axi_wdata( S_AXI_WDATA),
		.i_axi_wstrb( S_AXI_WSTRB),
		//
		.i_axi_bvalid(S_AXI_BVALID),
		.i_axi_bready(S_AXI_BREADY),
		.i_axi_bresp( S_AXI_BRESP),
		//
		.i_axi_arvalid(S_AXI_ARVALID),
		.i_axi_arready(S_AXI_ARREADY),
		.i_axi_araddr( S_AXI_ARADDR),
		.i_axi_arprot( S_AXI_ARPROT),
		//
		.i_axi_rvalid(S_AXI_RVALID),
		.i_axi_rready(S_AXI_RREADY),
		.i_axi_rdata( S_AXI_RDATA),
		.i_axi_rresp( S_AXI_RRESP),
		//
		.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== (S_AXI_BVALID ? 1:0)
			+(S_AXI_AWREADY ? 0:1));

		assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0)
			+(S_AXI_WREADY ? 0:1));

		assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0)
			+(S_AXI_ARREADY ? 0:1));
	end
	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// Verifying the packet counters
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//
	reg	[11:0]	f_reads, f_writes;
	reg	[3:0]	f_read_pkts, f_write_pkts;

	//
	// Mirror the read counter
	//
	initial	f_reads      = 0;
	initial	f_read_pkts  = 0;

	always @(posedge S_AXI_ACLK)
	if (!S_AXI_ARESETN)
	begin
		f_reads <= 0;
		f_read_pkts <= 0;
	end else if (OPT_SINK && (axil_read_ready
			&& arskd_addr == ADDR_SINK && !rfifo_empty))
	begin
		f_reads <= f_reads + 1;
		f_read_pkts <= f_read_pkts + (rfifo_last ? 1:0);
	end else if (!OPT_SINK && S_AXIS_TVALID)
	begin
		f_reads <= f_reads + 1;
		f_read_pkts <= f_read_pkts + (S_AXIS_TLAST ? 1:0);
	end

	always @(*)
		assert(f_reads == reads_completed);
	always @(*)
		assert(f_read_pkts == read_bursts_completed);

	always @(*)
	if (!OPT_SINK)
		assert(S_AXIS_TREADY);

	//
	// Mirror the write counter
	//
	initial	f_writes     = 0;
	initial	f_write_pkts = 0;
	always @(posedge S_AXI_ACLK)
	if (!S_AXI_ARESETN)
	begin
		f_writes <= 0;
		f_write_pkts <= 0;
	end else if (OPT_SOURCE && M_AXIS_TVALID && M_AXIS_TREADY)
	begin
		f_writes <= f_writes + 1;
		f_write_pkts <= f_write_pkts + (M_AXIS_TLAST ? 1:0);
	end

	always @(*)
	if (!OPT_SOURCE)
	begin
		assert(f_writes == 0);
		assert(f_write_pkts == 0);
	end

	always @(*)
	begin
		assert(f_writes == writes_completed);
		assert(f_write_pkts == write_bursts_completed);
	end
	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// Verify the read result
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//
	always @(posedge S_AXI_ACLK)
	if (f_past_valid && $past(S_AXI_ARESETN && axil_read_ready))
	begin
		assert(S_AXI_RVALID);
		case($past(arskd_addr))
		ADDR_SINK: begin
			assert(S_AXI_RDATA[SW-1:0] == $past(rfifo_data));
			if (SW < C_AXI_DATA_WIDTH)
			begin
				if (OPT_SIGN_EXTEND && $past(rfifo_data[SW-1]))
					assert(&S_AXI_RDATA[C_AXI_DATA_WIDTH-1:SW]);
				else
					assert(S_AXI_RDATA[C_AXI_DATA_WIDTH-1:SW] == 0);
			end end
		// 1: assert(S_AXI_RDATA == $past(r1));
		ADDR_STATS: begin
			assert(S_AXI_RRESP == 2'b00);
			assert(S_AXI_RDATA[31:28]
					== $past(write_bursts_completed));
			assert(S_AXI_RDATA[27:16] == $past(writes_completed));

			assert(S_AXI_RDATA[15:12]
					== $past(read_bursts_completed));
			assert(S_AXI_RDATA[11:0] == $past(reads_completed));
			end
		ADDR_FIFO: begin
			assert(S_AXI_RRESP == 2'b00);
			if (LGFIFO < 16)
				assert(S_AXI_RDATA[31:16+LGFIFO+1] == 0);
			assert(S_AXI_RDATA[16+: LGFIFO+1]==$past(wfifo_fill));
			assert(S_AXI_RDATA[15] == $past(rfifo_last));
			if (LGFIFO < 15)
				assert(S_AXI_RDATA[14:LGFIFO+1] == 0);
			assert(S_AXI_RDATA[ 0+: LGFIFO+1]==$past(rfifo_fill));
			end
		default: begin end
		endcase
	end

	//
	// Check that our low-power only logic works by verifying that anytime
	// S_AXI_RVALID is inactive, then the outgoing data is also zero.
	//
	always @(*)
	if (OPT_LOWPOWER && !S_AXI_RVALID)
		assert(S_AXI_RDATA == 0);

	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// The AXI-stream interfaces
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//

	// Slave/consumer properties
	always @(posedge S_AXI_ACLK)
	if (!f_past_valid || !$past(S_AXI_ARESETN))
	begin
		assume(!S_AXIS_TVALID);
	end else if ($past(S_AXIS_TVALID && !S_AXIS_TREADY))
	begin
		assume(S_AXIS_TVALID);
		assume($stable(S_AXIS_TDATA));
		assume($stable(S_AXIS_TLAST));
	end

	// Master/producer/source properties
	always @(posedge S_AXI_ACLK)
	if (!f_past_valid || !$past(S_AXI_ARESETN))
	begin
		assert(!M_AXIS_TVALID);
	end else if ($past(M_AXIS_TVALID && !M_AXIS_TREADY))
	begin
		assert(M_AXIS_TVALID);
		assert($stable(M_AXIS_TDATA));
		assert($stable(M_AXIS_TLAST));
	end
	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// Cover checks
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//

	always @(*)
		cover(S_AXI_ARESETN && writes_completed == 16);

	always @(*)
		cover(S_AXI_ARESETN && reads_completed == 16);

	always @(*)
		cover(S_AXI_ARESETN && writes_completed == 16
				&& reads_completed == 16);

	always @(*)
		cover(S_AXI_BVALID && S_AXI_BRESP != 2'b00);

	always @(*)
		cover(S_AXI_RVALID && S_AXI_RRESP != 2'b00);

	// }}}
	// }}}
`endif
endmodule
