////////////////////////////////////////////////////////////////////////////////
//
// Filename:	rtl/axilite2axi.v
// {{{
// Project:	WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose:	
//
// 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 axilite2axi #(
		// {{{
		parameter	C_AXI_ID_WIDTH	= 4,
				C_AXI_ADDR_WIDTH= 32,
				C_AXI_DATA_WIDTH= 32,
		parameter [C_AXI_ID_WIDTH-1:0]	C_AXI_WRITE_ID = 0,
						C_AXI_READ_ID = 0
		// }}}
	) (
		// {{{
		input	wire				ACLK,
		input	wire				ARESETN,
		// Slave AXI interface
		// {{{
		// Slave write signals
		input	wire				S_AXI_AWVALID,
		output	wire				S_AXI_AWREADY,
		input	wire	[C_AXI_ADDR_WIDTH-1:0]	S_AXI_AWADDR,
		input	wire	[3-1:0]			S_AXI_AWPROT,
		// Slave write data signals
		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,
		// Slave return write response
		output	wire				S_AXI_BVALID,
		input	wire				S_AXI_BREADY,
		output	wire	[2-1:0]			S_AXI_BRESP,
		// Slave read signals
		input	wire				S_AXI_ARVALID,
		output	wire				S_AXI_ARREADY,
		input	wire	[C_AXI_ADDR_WIDTH-1:0]	S_AXI_ARADDR,
		input	wire	[3-1:0]			S_AXI_ARPROT,
		// Slave read data signals
		output	wire				S_AXI_RVALID,
		input	wire				S_AXI_RREADY,
		output	wire	[C_AXI_DATA_WIDTH-1:0]	S_AXI_RDATA,
		output	wire	[2-1:0]			S_AXI_RRESP,
		// }}}
		// Master AXI interface
		// {{{
		// Master interface write address
		output	wire				M_AXI_AWVALID,
		input	wire				M_AXI_AWREADY,
		output	wire	[C_AXI_ID_WIDTH-1:0]	M_AXI_AWID,
		output	wire	[C_AXI_ADDR_WIDTH-1:0]	M_AXI_AWADDR,
		output	wire	[8-1:0]			M_AXI_AWLEN,
		output	wire	[3-1:0]			M_AXI_AWSIZE,
		output	wire	[2-1:0]			M_AXI_AWBURST,
		output	wire				M_AXI_AWLOCK,
		output	wire	[4-1:0]			M_AXI_AWCACHE,
		output	wire	[3-1:0]			M_AXI_AWPROT,
		output	wire	[4-1:0]			M_AXI_AWQOS,
		// Master write data
		output	wire				M_AXI_WVALID,
		input	wire				M_AXI_WREADY,
		output	wire	[C_AXI_DATA_WIDTH-1:0]	M_AXI_WDATA,
		output	wire	[C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
		output	wire				M_AXI_WLAST,
		// Master write response
		input	wire				M_AXI_BVALID,
		output	wire				M_AXI_BREADY,
		input	wire	[C_AXI_ID_WIDTH-1:0]	M_AXI_BID,
		input	wire	[1:0]			M_AXI_BRESP,
		// Master interface read address
		output	wire				M_AXI_ARVALID,
		input	wire				M_AXI_ARREADY,
		output	wire	[C_AXI_ID_WIDTH-1:0]	M_AXI_ARID,
		output	wire	[C_AXI_ADDR_WIDTH-1:0]	M_AXI_ARADDR,
		output	wire	[8-1:0]			M_AXI_ARLEN,
		output	wire	[3-1:0]			M_AXI_ARSIZE,
		output	wire	[2-1:0]			M_AXI_ARBURST,
		output	wire				M_AXI_ARLOCK,
		output	wire	[4-1:0]			M_AXI_ARCACHE,
		output	wire	[3-1:0]			M_AXI_ARPROT,
		output	wire	[4-1:0]			M_AXI_ARQOS,
		// Master interface read data return
		input	wire				M_AXI_RVALID,
		output	wire				M_AXI_RREADY,
		input	wire	[C_AXI_ID_WIDTH-1:0]	M_AXI_RID,
		input	wire	[C_AXI_DATA_WIDTH-1:0]	M_AXI_RDATA,
		input	wire				M_AXI_RLAST,
		input	wire	[2-1:0]			M_AXI_RRESP
		// }}}
		// }}}
	);

	localparam	ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
	assign	M_AXI_AWID    = C_AXI_WRITE_ID;
	assign	M_AXI_AWADDR  = S_AXI_AWADDR;
	assign	M_AXI_AWLEN   = 0;
	assign	M_AXI_AWSIZE  = ADDRLSB[2:0];
	assign	M_AXI_AWBURST = 0;
	assign	M_AXI_AWLOCK  = 0;
	assign	M_AXI_AWCACHE = 4'b0011; // As recommended by Xilinx UG1037
	assign	M_AXI_AWPROT  = S_AXI_AWPROT;
	assign	M_AXI_AWQOS   = 0;
	assign	M_AXI_AWVALID = S_AXI_AWVALID;
	assign	S_AXI_AWREADY = M_AXI_AWREADY;
	// Master write data
	assign	M_AXI_WDATA   = S_AXI_WDATA;
	assign	M_AXI_WLAST   = 1;
	assign	M_AXI_WSTRB   = S_AXI_WSTRB;
	assign	M_AXI_WVALID  = S_AXI_WVALID;
	assign	S_AXI_WREADY  = M_AXI_WREADY;
	// Master write response
	assign	S_AXI_BRESP   = M_AXI_BRESP;
	assign	S_AXI_BVALID  = M_AXI_BVALID;
	assign	M_AXI_BREADY  = S_AXI_BREADY;
	//
	//
	assign	M_AXI_ARID    = C_AXI_READ_ID;
	assign	M_AXI_ARADDR  = S_AXI_ARADDR;
	assign	M_AXI_ARLEN   = 0;
	assign	M_AXI_ARSIZE  = ADDRLSB[2:0];
	assign	M_AXI_ARBURST = 0;
	assign	M_AXI_ARLOCK  = 0;
	assign	M_AXI_ARCACHE = 4'b0011; // As recommended by Xilinx UG1037
	assign	M_AXI_ARPROT  = S_AXI_ARPROT;
	assign	M_AXI_ARQOS   = 0;
	assign	M_AXI_ARVALID = S_AXI_ARVALID;
	assign	S_AXI_ARREADY = M_AXI_ARREADY;
	//
	assign	S_AXI_RDATA   = M_AXI_RDATA;
	assign	S_AXI_RRESP   = M_AXI_RRESP;
	assign	S_AXI_RVALID  = M_AXI_RVALID;
	assign	M_AXI_RREADY  = S_AXI_RREADY;

	// Make Verilator happy
	// Verilator lint_off UNUSED
	wire	unused;
	assign	unused = &{ 1'b0, ACLK, ARESETN, M_AXI_RLAST, M_AXI_RID, M_AXI_BID };
	// Verilator lint_on UNUSED

`ifdef	FORMAL
	//
	// The following are some of the properties used to verify this design
	//

	localparam	F_LGDEPTH = 10;

	wire	[F_LGDEPTH-1:0]	faxil_awr_outstanding,
				faxil_wr_outstanding, faxil_rd_outstanding;

	faxil_slave #(
			.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
			.C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
			.F_LGDEPTH(10),
			.F_AXI_MAXRSTALL(4),
			.F_AXI_MAXWAIT(9),
			.F_AXI_MAXDELAY(0)
	) faxil(.i_clk(ACLK), .i_axi_reset_n(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_awr_outstanding(faxil_awr_outstanding),
		.f_axi_wr_outstanding(faxil_wr_outstanding),
		.f_axi_rd_outstanding(faxil_rd_outstanding));

	//
	// ...
	//

	faxi_master #(
			.C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
			.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
			.C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
			.OPT_MAXBURST(0),
			.OPT_EXCLUSIVE(1'b0),
			.OPT_NARROW_BURST(1'b0),
			.F_OPT_NO_RESET(1'b1),
			.F_LGDEPTH(10),
			.F_AXI_MAXSTALL(4),
			.F_AXI_MAXRSTALL(0),
			.F_AXI_MAXDELAY(4)
	) faxi(.i_clk(ACLK), .i_axi_reset_n(ARESETN),
		//
		.i_axi_awready(M_AXI_AWREADY),
		.i_axi_awid(   M_AXI_AWID),
		.i_axi_awaddr( M_AXI_AWADDR),
		.i_axi_awlen(  M_AXI_AWLEN),
		.i_axi_awsize( M_AXI_AWSIZE),
		.i_axi_awburst(M_AXI_AWBURST),
		.i_axi_awlock( M_AXI_AWLOCK),
		.i_axi_awcache(M_AXI_AWCACHE),
		.i_axi_awprot( M_AXI_AWPROT),
		.i_axi_awqos(  M_AXI_AWQOS),
		.i_axi_awvalid(M_AXI_AWVALID),
		//
		.i_axi_wready(M_AXI_WREADY),
		.i_axi_wdata( M_AXI_WDATA),
		.i_axi_wstrb( M_AXI_WSTRB),
		.i_axi_wlast( M_AXI_WLAST),
		.i_axi_wvalid(M_AXI_WVALID),
		//
		.i_axi_bready(M_AXI_BREADY),
		.i_axi_bid(   M_AXI_BID),
		.i_axi_bresp( M_AXI_BRESP),
		.i_axi_bvalid(M_AXI_BVALID),
		//
		.i_axi_arready(M_AXI_ARREADY),
		.i_axi_arid(   M_AXI_ARID),
		.i_axi_araddr( M_AXI_ARADDR),
		.i_axi_arlen(  M_AXI_ARLEN),
		.i_axi_arsize( M_AXI_ARSIZE),
		.i_axi_arburst(M_AXI_ARBURST),
		.i_axi_arlock( M_AXI_ARLOCK),
		.i_axi_arcache(M_AXI_ARCACHE),
		.i_axi_arprot( M_AXI_ARPROT),
		.i_axi_arqos(  M_AXI_ARQOS),
		.i_axi_arvalid(M_AXI_ARVALID),
		//
		.i_axi_rid(   M_AXI_RID),
		.i_axi_rdata( M_AXI_RDATA),
		.i_axi_rready(M_AXI_RREADY),
		.i_axi_rresp( M_AXI_RRESP),
		.i_axi_rvalid(M_AXI_RVALID),
		//
		.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)
		//
		// ...
		//
		);


	////////////////////////////////////////////////////////////////////////
	//
	// Induction rule checks
	//
	////////////////////////////////////////////////////////////////////////
	//
	//

	// First rule: the AXI solver isn't permitted to have any more write
	// bursts outstanding than the AXI-lite interface has.
	always @(*)
		assert(faxi_awr_nbursts == faxil_awr_outstanding);

	//
	// Second: Since our bursts are limited to one value each, and since
	// we can't send address bursts ahead of their data counterparts,
	// (AXI property limitation) faxi_wr_pending can only ever be one or
	// zero, and the counters must match
	always @(*)
	begin
		//
		// ...
		//

		if (faxil_awr_outstanding != 0)
		begin
			assert((faxil_awr_outstanding == faxil_wr_outstanding)
			||(faxil_awr_outstanding-1 == faxil_wr_outstanding));
		end

		if (faxil_awr_outstanding == 0)
			assert(faxil_wr_outstanding == 0);
	end

	//
	// ...
	//

	////////////////////////////////////////////////////////////////////////
	//
	// Known "bugs"
	//
	////////////////////////////////////////////////////////////////////////
	//
	// These are really more limitations in the AXI properties than
	// bugs in the code, but they need to be documented here.
	//
	generate if (C_AXI_DATA_WIDTH > 8)
	begin
		// The AXI-lite property checker doesn't validate WSTRB
		// values like it should.  If we don't force the AWADDR
		// LSBs to zero, the solver will pick an invalid WSTRB.
		//
		always @(*)
			assume(S_AXI_AWADDR[$clog2(C_AXI_DATA_WIDTH)-3:0] == 0);
		always @(*)
			assert(faxi_wr_addr[$clog2(C_AXI_DATA_WIDTH)-3:0] == 0);
	end endgenerate

	//
	// The AXI solver can't handle write transactions without either a
	// concurrent or proceeding write address burst.  Here we make that
	// plain.  It's not likely to cause any problems, but still worth
	// noting.
	always @(*)
	if (faxil_awr_outstanding == faxil_wr_outstanding)
		assume(S_AXI_AWVALID || !S_AXI_WVALID);
	else if (faxil_awr_outstanding > faxil_wr_outstanding)
		assume(!S_AXI_AWVALID);

	//
	// We need to make certain that our counters never overflow.  This is
	// an assertion within the property checkers, so we need an appropriate
	// matching assumption here.  In practice, F_LGDEPTH could be set
	// so arbitrarily large that this assumption would never get hit,
	// but the solver doesn't know that.
	always @(*)
	if (faxil_rd_outstanding >= {{(F_LGDEPTH-1){1'b1}}, 1'b0 })
		assume(!S_AXI_ARVALID);

	always @(*)
	if (faxil_awr_outstanding >= {{(F_LGDEPTH-1){1'b1}}, 1'b0 })
		assume(!S_AXI_AWVALID);

`endif
endmodule
`ifndef	YOSYS
`default_nettype wire
`endif
