////////////////////////////////////////////////////////////////////////////////
//
// Filename:	rtl/axlite2wbsp.v
// {{{
// Project:	WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose:	Take an AXI lite input, and convert it into WB.
//
//	We'll treat AXI as two separate busses: one for writes, another for
//	reads, further, we'll insist that the two channels AXI uses for writes
//	combine into one channel for our purposes.
//
//
// Creator:	Dan Gisselquist, Ph.D.
//		Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
// }}}
// Copyright (C) 2016-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 axlite2wbsp #(
		// {{{
		parameter C_AXI_DATA_WIDTH	= 32,// Width of the AXI R&W data
		parameter C_AXI_ADDR_WIDTH	= 28,	// AXI Address width
		parameter		LGFIFO = 4,
`ifdef	FORMAL
		parameter		F_MAXSTALL = 3,
		parameter		F_MAXDELAY = 3,
`endif
		parameter	[0:0]	OPT_READONLY  = 1'b0,
		parameter	[0:0]	OPT_WRITEONLY = 1'b0,
		localparam		AXILLSB = $clog2(C_AXI_DATA_WIDTH/8)
		// }}}
	) (
		// {{{
		input	wire			i_clk,	// System clock
		input	wire			i_axi_reset_n,

		// AXI write address channel signals
		// {{{
		input	wire			i_axi_awvalid,
		output	wire			o_axi_awready,
		input	wire	[C_AXI_ADDR_WIDTH-1:0]	i_axi_awaddr,
		input	wire	[2:0]		i_axi_awprot,
		// }}}
		// AXI write data channel signals
		// {{{
		input	wire				i_axi_wvalid,
		output	wire				o_axi_wready, 
		input	wire	[C_AXI_DATA_WIDTH-1:0]	i_axi_wdata,
		input	wire	[C_AXI_DATA_WIDTH/8-1:0] i_axi_wstrb,
		// }}}
		// AXI write response channel signals
		// {{{
		output	wire 			o_axi_bvalid,
		input	wire			i_axi_bready,
		output	wire [1:0]		o_axi_bresp,
		// }}}
		// AXI read address channel signals
		// {{{
		input	wire			i_axi_arvalid,
		output	wire			o_axi_arready,
		input	wire	[C_AXI_ADDR_WIDTH-1:0]	i_axi_araddr,
		input	wire	[2:0]		i_axi_arprot,
		// }}}
		// AXI read data channel signals
		// {{{
		output	wire			o_axi_rvalid,
		input	wire			i_axi_rready,
		output	wire [C_AXI_DATA_WIDTH-1:0] o_axi_rdata,
		output	wire [1:0]		o_axi_rresp,
		// }}}
		// Wishbone signals
		// {{{
		// We'll share the clock and the reset
		output	wire			o_reset,
		output	wire			o_wb_cyc,
		output	wire			o_wb_stb,
		output	wire			o_wb_we,
		output	wire [C_AXI_ADDR_WIDTH-AXILLSB-1:0]	o_wb_addr,
		output	wire [C_AXI_DATA_WIDTH-1:0]		o_wb_data,
		output	wire [C_AXI_DATA_WIDTH/8-1:0]		o_wb_sel,
		input	wire			i_wb_stall,
		input	wire			i_wb_ack,
		input	wire [(C_AXI_DATA_WIDTH-1):0]		i_wb_data,
		input	wire			i_wb_err
		// }}}
		// }}}
	);

	// Local definitions
	// {{{
	localparam DW = C_AXI_DATA_WIDTH;
	localparam AW = C_AXI_ADDR_WIDTH-AXILLSB;
	//
	//
	//

	wire	[(AW-1):0]			w_wb_addr, r_wb_addr;
	wire	[(DW-1):0]			w_wb_data;
	wire	[(DW/8-1):0]			w_wb_sel, r_wb_sel;
	wire	r_wb_err, r_wb_cyc, r_wb_stb, r_wb_stall, r_wb_ack;
	wire	w_wb_err, w_wb_cyc, w_wb_stb, w_wb_stall, w_wb_ack;

	// verilator lint_off UNUSED
	wire	r_wb_we, w_wb_we;

	assign	r_wb_we = 1'b0;
	assign	w_wb_we = 1'b1;
	// verilator lint_on  UNUSED

`ifdef	FORMAL
	// {{{
	// Verilator lint_off UNUSED
	localparam		F_LGDEPTH = LGFIFO+1;
	wire	[LGFIFO:0]	f_wr_fifo_first, f_rd_fifo_first,
				f_wr_fifo_mid,   f_rd_fifo_mid,
				f_wr_fifo_last,  f_rd_fifo_last;
	// Verilator lint_on  UNUSED
	wire	[(F_LGDEPTH-1):0]	f_wb_nreqs, f_wb_nacks,
					f_wb_outstanding;
	wire	[(F_LGDEPTH-1):0]	f_wb_wr_nreqs, f_wb_wr_nacks,
					f_wb_wr_outstanding;
	wire	[(F_LGDEPTH-1):0]	f_wb_rd_nreqs, f_wb_rd_nacks,
					f_wb_rd_outstanding;
	wire			f_pending_awvalid, f_pending_wvalid;
	// }}}
`endif
	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// AXI-lite write channel to WB processing
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//
	generate if (!OPT_READONLY)
	begin : AXI_WR
		// {{{
		axilwr2wbsp #(
			// {{{
			// .F_LGDEPTH(F_LGDEPTH),
			// .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
			.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH), // .AW(AW),
			.LGFIFO(LGFIFO)
			// }}}
		) axi_write_decoder (
			// {{{
			.i_clk(i_clk), .i_axi_reset_n(i_axi_reset_n),
			//
			.i_axi_awvalid(i_axi_awvalid),
			.o_axi_awready(o_axi_awready),
			.i_axi_awaddr( i_axi_awaddr),
			.i_axi_awprot( i_axi_awprot),
			//
			.i_axi_wvalid( i_axi_wvalid),
			.o_axi_wready( o_axi_wready),
			.i_axi_wdata(  i_axi_wdata),
			.i_axi_wstrb(  i_axi_wstrb),
			//
			.o_axi_bvalid(o_axi_bvalid),
			.i_axi_bready(i_axi_bready),
			.o_axi_bresp(o_axi_bresp),
			//
			.o_wb_cyc(  w_wb_cyc),
			.o_wb_stb(  w_wb_stb),
			.o_wb_addr( w_wb_addr),
			.o_wb_data( w_wb_data),
			.o_wb_sel(  w_wb_sel),
			.i_wb_stall(w_wb_stall),
			.i_wb_ack(  w_wb_ack),
			.i_wb_err(  w_wb_err)
`ifdef	FORMAL
			// {{{
			,
			.f_first(f_wr_fifo_first),
			.f_mid(  f_wr_fifo_mid),
			.f_last( f_wr_fifo_last),
			.f_wpending({ f_pending_awvalid, f_pending_wvalid })
			// }}}
`endif
			// }}}
		);
		// }}}
	end else begin : EMPTY_WRITES
		// {{{
		assign	w_wb_cyc  = 0;
		assign	w_wb_stb  = 0;
		assign	w_wb_addr = 0;
		assign	w_wb_data = 0;
		assign	w_wb_sel  = 0;
		assign	o_axi_awready = 0;
		assign	o_axi_wready  = 0;
		assign	o_axi_bvalid  = (i_axi_wvalid);
		assign	o_axi_bresp   = 2'b11;
`ifdef	FORMAL
		assign	f_wr_fifo_first = 0;
		assign	f_wr_fifo_mid   = 0;
		assign	f_wr_fifo_last  = 0;
		assign	f_pending_awvalid=0;
		assign	f_pending_wvalid =0;
`endif
		// }}}
	end endgenerate
	assign	w_wb_we = 1'b1;
	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// AXI-lite read channel to WB processing
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//

	generate if (!OPT_WRITEONLY)
	begin : AXI_RD
		// {{{
		axilrd2wbsp #(
			// {{{
			// .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
			.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
			.LGFIFO(LGFIFO)
			// }}}
		) axi_read_decoder(
			// {{{
			.i_clk(i_clk), .i_axi_reset_n(i_axi_reset_n),
			//
			.i_axi_arvalid(i_axi_arvalid),
			.o_axi_arready(o_axi_arready),
			.i_axi_araddr( i_axi_araddr),
			.i_axi_arprot( i_axi_arprot),
			//
			.o_axi_rvalid(o_axi_rvalid),
			.i_axi_rready(i_axi_rready),
			.o_axi_rdata( o_axi_rdata),
			.o_axi_rresp( o_axi_rresp),
			//
			.o_wb_cyc(  r_wb_cyc),
			.o_wb_stb(  r_wb_stb),
			.o_wb_addr( r_wb_addr),
			.o_wb_sel(  r_wb_sel),
			.i_wb_stall(r_wb_stall),
			.i_wb_ack(  r_wb_ack),
			.i_wb_data( i_wb_data),
			.i_wb_err(  r_wb_err)
`ifdef	FORMAL
			// {{{
			,
			.f_first(f_rd_fifo_first),
			.f_mid(  f_rd_fifo_mid),
			.f_last( f_rd_fifo_last)
			// }}}
`endif
			// }}}
		);
		// }}}
	end else begin : EMPTY_READS
		// {{{
		assign	r_wb_cyc  = 0;
		assign	r_wb_stb  = 0;
		assign	r_wb_addr = 0;
		//
		assign o_axi_arready = 1'b1;
		assign o_axi_rvalid  = (i_axi_arvalid)&&(o_axi_arready);
		assign o_axi_rresp   = (i_axi_arvalid) ? 2'b11 : 2'b00;
		assign o_axi_rdata   = 0;
`ifdef	FORMAL
		assign	f_rd_fifo_first = 0;
		assign	f_rd_fifo_mid   = 0;
		assign	f_rd_fifo_last  = 0;
`endif
		// }}}
	end endgenerate

	// }}}
	////////////////////////////////////////////////////////////////////////
	//
	// The arbiter that pastes the two sides together
	// {{{
	////////////////////////////////////////////////////////////////////////
	//
	//


	generate if (OPT_READONLY)
	begin : ARB_RD
		// {{{
		assign	o_wb_cyc  = r_wb_cyc;
		assign	o_wb_stb  = r_wb_stb;
		assign	o_wb_we   = 1'b0;
		assign	o_wb_addr = r_wb_addr;
		assign	o_wb_data = 32'h0;
		assign	o_wb_sel  = r_wb_sel;
		assign	r_wb_ack  = i_wb_ack;
		assign	r_wb_stall= i_wb_stall;
		assign	r_wb_ack  = i_wb_ack;
		assign	r_wb_err  = i_wb_err;

`ifdef	FORMAL
		fwb_master #(.DW(DW), .AW(AW),
			.F_LGDEPTH(F_LGDEPTH),
			.F_MAX_STALL(F_MAXSTALL),
			.F_MAX_ACK_DELAY(F_MAXDELAY))
		f_wb(i_clk, !i_axi_reset_n,
			o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
				o_wb_sel,
			i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
			f_wb_nreqs, f_wb_nacks, f_wb_outstanding);

		assign f_wb_rd_nreqs = f_wb_nreqs;
		assign f_wb_rd_nacks = f_wb_nacks;
		assign f_wb_rd_outstanding = f_wb_outstanding;
		//
		assign f_wb_wr_nreqs = 0;
		assign f_wb_wr_nacks = 0;
		assign f_wb_wr_outstanding = 0;
`endif
		// }}}
	end else if (OPT_WRITEONLY)
	begin : ARB_WR
		// {{{
		assign	o_wb_cyc  = w_wb_cyc;
		assign	o_wb_stb  = w_wb_stb;
		assign	o_wb_we   = 1'b1;
		assign	o_wb_addr = w_wb_addr;
		assign	o_wb_data = w_wb_data;
		assign	o_wb_sel  = w_wb_sel;
		assign	w_wb_ack  = i_wb_ack;
		assign	w_wb_stall= i_wb_stall;
		assign	w_wb_ack  = i_wb_ack;
		assign	w_wb_err  = i_wb_err;

`ifdef FORMAL
		fwb_master #(.DW(DW), .AW(AW),
			.F_LGDEPTH(F_LGDEPTH),
			.F_MAX_STALL(F_MAXSTALL),
			.F_MAX_ACK_DELAY(F_MAXDELAY))
		f_wb(i_clk, !i_axi_reset_n,
			o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
				o_wb_sel,
			i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
			f_wb_nreqs, f_wb_nacks, f_wb_outstanding);

		assign f_wb_wr_nreqs = f_wb_nreqs;
		assign f_wb_wr_nacks = f_wb_nacks;
		assign f_wb_wr_outstanding = f_wb_outstanding;
		//
		assign f_wb_rd_nreqs = 0;
		assign f_wb_rd_nacks = 0;
		assign f_wb_rd_outstanding = 0;
`endif
		// }}}
	end else begin : ARB_WB
		// {{{
		wbarbiter #(
			// {{{
			.DW(DW), .AW(AW)
`ifdef	FORMAL
			, .F_LGDEPTH(F_LGDEPTH),
			.F_MAX_STALL(F_MAXSTALL),
			.F_MAX_ACK_DELAY(F_MAXDELAY)
`endif
			// }}}
		) readorwrite(
			// {{{
			.i_clk(i_clk), .i_reset(!i_axi_reset_n),
			// Channel A - Reads
			// {{{
			.i_a_cyc(r_wb_cyc), .i_a_stb(r_wb_stb),
				.i_a_we(1'b0),
				.i_a_adr(r_wb_addr),
				.i_a_dat(w_wb_data),
				.i_a_sel(r_wb_sel),
				.o_a_stall(r_wb_stall),
				.o_a_ack(r_wb_ack),
				.o_a_err(r_wb_err),
			// }}}
			// Channel B
			// {{{
			.i_b_cyc(w_wb_cyc), .i_b_stb(w_wb_stb),
				.i_b_we(1'b1),
				.i_b_adr(w_wb_addr),
				.i_b_dat(w_wb_data),
				.i_b_sel(w_wb_sel),
				.o_b_stall(w_wb_stall),
				.o_b_ack(w_wb_ack),
				.o_b_err(w_wb_err),
			// }}}
			// Arbitrated outgoing channel
			// {{{
			.o_cyc(o_wb_cyc), .o_stb(o_wb_stb), .o_we(o_wb_we),
				.o_adr(o_wb_addr),
				.o_dat(o_wb_data),
				.o_sel(o_wb_sel),
				.i_stall(i_wb_stall),
				.i_ack(i_wb_ack),
				.i_err(i_wb_err)
			// }}}
`ifdef	FORMAL
			// {{{
			,
			.f_nreqs(f_wb_nreqs), .f_nacks(f_wb_nacks),
				.f_outstanding(f_wb_outstanding),
			.f_a_nreqs(f_wb_rd_nreqs), .f_a_nacks(f_wb_rd_nacks),
				.f_a_outstanding(f_wb_rd_outstanding),
			.f_b_nreqs(f_wb_wr_nreqs), .f_b_nacks(f_wb_wr_nacks),
				.f_b_outstanding(f_wb_wr_outstanding)
			// }}}
`endif
			// }}}
		);
		// }}}
	end endgenerate
	// }}}
	assign	o_reset = (i_axi_reset_n == 1'b0);

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Formal properties
// {{{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
`ifdef	FORMAL
	// Formal declarations
	// {{{
	reg	f_past_valid;

	initial	f_past_valid = 1'b0;
	always @(posedge i_clk)
		f_past_valid <= 1'b1;


	wire	[(F_LGDEPTH-1):0]	f_axi_rd_outstanding,
					f_axi_wr_outstanding,
					f_axi_awr_outstanding;

	wire	[LGFIFO:0]	f_awr_fifo_axi_used,
				f_rd_fifo_axi_used;
	// }}}

	initial	assume(!i_axi_reset_n);
	always @(*)
	if (!f_past_valid)
		assume(!i_axi_reset_n);

	faxil_slave #(
		// {{{
		// .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
		.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
		.F_LGDEPTH(F_LGDEPTH),
		.F_AXI_MAXWAIT(0),
		.F_AXI_MAXDELAY(0)
		// }}}
	) f_axi(
		// {{{
		.i_clk(i_clk), .i_axi_reset_n(i_axi_reset_n),
		// AXI write address channnel
		.i_axi_awvalid(i_axi_awvalid),
		.i_axi_awready(o_axi_awready),
		.i_axi_awaddr( i_axi_awaddr),
		.i_axi_awprot( i_axi_awprot),
		// AXI write data channel
		.i_axi_wvalid( i_axi_wvalid),
		.i_axi_wready( o_axi_wready),
		.i_axi_wdata(  i_axi_wdata),
		.i_axi_wstrb(  i_axi_wstrb),
		// AXI write acknowledgement channel
		.i_axi_bvalid(o_axi_bvalid),
		.i_axi_bready(i_axi_bready),
		.i_axi_bresp( o_axi_bresp),
		// AXI read address channel
		.i_axi_arvalid(i_axi_arvalid),
		.i_axi_arready(o_axi_arready),
		.i_axi_araddr( i_axi_araddr),
		.i_axi_arprot( i_axi_arprot),
		// AXI read data return
		.i_axi_rvalid( o_axi_rvalid),
		.i_axi_rready( i_axi_rready),
		.i_axi_rdata(  o_axi_rdata),
		.i_axi_rresp(  o_axi_rresp),
		// Quantify where we are within a transaction
		.f_axi_rd_outstanding( f_axi_rd_outstanding),
		.f_axi_wr_outstanding( f_axi_wr_outstanding),
		.f_axi_awr_outstanding(f_axi_awr_outstanding)
		// }}}
	);

	assign	f_awr_fifo_axi_used = f_wr_fifo_first - f_wr_fifo_last;
	assign	f_rd_fifo_axi_used  = f_rd_fifo_first - f_rd_fifo_last;

	always @(*)
	begin
		assert(f_axi_rd_outstanding  == f_rd_fifo_axi_used);
		assert(f_axi_awr_outstanding == f_awr_fifo_axi_used+ (f_pending_awvalid?1:0));
		assert(f_axi_wr_outstanding  == f_awr_fifo_axi_used+ (f_pending_wvalid?1:0));
	end

	always @(*)
	if (OPT_READONLY)
	begin
		assert(f_axi_awr_outstanding == 0);
		assert(f_axi_wr_outstanding  == 0);
	end

	always @(*)
	if (OPT_WRITEONLY)
	begin
		assert(f_axi_rd_outstanding == 0);
	end

	//
	initial assert((!OPT_READONLY)||(!OPT_WRITEONLY));

	always @(*)
	if (OPT_READONLY)
	begin
		assume(i_axi_awvalid == 0);
		assume(i_axi_wvalid == 0);

		assert(o_axi_bvalid == 0);
	end

	always @(*)
	if (OPT_WRITEONLY)
	begin
		assume(i_axi_arvalid == 0);
		assert(o_axi_rvalid == 0);
	end

	// Make Verilator happy
	// {{{
	// Verilator lint_off UNUSED
	wire	unused_formal;
	assign	unused_formal = &{ 1'b0, f_wb_nreqs, f_wb_nacks,
		f_wb_outstanding, f_wb_wr_nreqs, f_wb_wr_nacks,
		f_wb_wr_outstanding, f_wb_rd_nreqs, f_wb_rd_nacks,
		f_wb_rd_outstanding };
	// Verilator lint_on  UNUSED
	// }}}
`endif
// }}}
endmodule
`ifndef	YOSYS
`default_nettype wire
`endif
