/*
 * fifo_srl.v
 *
 * Very small/light-weight FIFO using SRL.
 * Only for synchronous design. Has a fixed depth of 15 or 31 entries and
 * always work in the so-called first-word-fall-thru mode.
 *
 * Copyright (C) 2014  Ettus Corporation LLC
 * Copyright 2018 Ettus Research, a National Instruments Company
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 *
 * vim: ts=4 sw=4
 */

`ifdef SIM
`default_nettype none
`endif

module fifo_srl #(
	parameter integer WIDTH = 4,
	parameter integer LOG2_DEPTH = 5,	// 4 or 5
	parameter integer AFULL_LEVEL = -1	// -1 -> No AFULL

)(
	input  wire [WIDTH-1:0] di,
	input  wire wren,
	output wire full,
	output wire afull,

	output reg  [WIDTH-1:0] do = {WIDTH{1'b0}},
	input  wire rden,
	output reg  empty,

	input  wire clk,
	input  wire rst
);

	genvar i;

	// Signals
	wire [WIDTH-1:0] srl_q;
	reg  [LOG2_DEPTH-1:0] srl_addr;
	wire srl_addr_ce;

	wire srl_write;
	wire srl_read;

	wire srl_full;
	wire srl_afull;
	reg  srl_empty;
	wire srl_aempty;

	// Instanciate the SRLs
	generate
		if (LOG2_DEPTH == 6) begin
			wire [WIDTH-1:0] srl0_q31, srl0_q, srl1_q;

			for (i=0; i<WIDTH; i=i+1)
			begin : srl_64
				SRLC32E srl_I0 (
					.Q(srl0_q[i]),
					.Q31(srl0_q31[i]),
					.A(srl_addr[4:0]),
					.CE(srl_write),
					.CLK(clk),
					.D(di[i])
				);

				SRLC32E srl_I1 (
					.Q(srl1_q[i]),
					.A(srl_addr[4:0]),
					.CE(srl_write),
					.CLK(clk),
					.D(srl0_q31[i])
				);

				MUXF7 mux_I (
					.O(srl_q[i]),
					.I0(srl0_q[i]),
					.I1(srl1_q[i]),
					.S(srl_addr[5])
				);
			end
		end else if (LOG2_DEPTH == 5) begin
			for (i=0; i<WIDTH; i=i+1)
				SRLC32E srl_I (
					.Q(srl_q[i]),
					.A(srl_addr),
					.CE(srl_write),
					.CLK(clk),
					.D(di[i])
				);
		end else if (LOG2_DEPTH == 4) begin
			for (i=0; i<WIDTH; i=i+1)
				SRL16E srl_I (
					.Q(srl_q[i]),
					.A0(srl_addr[0]),
					.A1(srl_addr[1]),
					.A2(srl_addr[2]),
					.A3(srl_addr[3]),
					.CE(srl_write),
					.CLK(clk),
					.D(di[i])
				);
		end
	endgenerate

	// Address counter
	assign srl_addr_ce = srl_write ^ srl_read;

	always @(posedge clk)
	begin
		if (rst)
			srl_addr <= {LOG2_DEPTH{1'b1}};
		else if (srl_addr_ce) begin
			if (srl_write)
				srl_addr <= srl_addr + 1;
			else
				srl_addr <= srl_addr - 1;
		end
	end

	// SRL status
	assign srl_full = srl_addr == {{(LOG2_DEPTH-1){1'b1}}, 1'b0};

	generate
		if (AFULL_LEVEL != -1) begin
			assign srl_afull = (srl_addr >= AFULL_LEVEL) && ~&(srl_addr);
		end else begin
			assign srl_afull = 1'b0;
		end
	endgenerate

	assign srl_aempty = &(~srl_addr);

	always @(posedge clk)
	begin
		if (rst)
			srl_empty <= 1'b1;
		else if (srl_addr_ce)
			srl_empty <= srl_aempty & srl_read;
	end

	// Output register (to capture whatever comes out from SRL)
	always @(posedge clk)
	begin
		if (srl_read)
			do <= srl_q;
	end

	// Control and flag generation
		// Write/Full is easy
	assign srl_write = wren;
	assign full = srl_full;
	assign afull = srl_afull;

		// Read/Empty is tricky
	always @(posedge clk)
	begin
		if (rst)
			empty <= 1'b1;
		else if (rden | srl_read)
			empty <= srl_empty;
	end

	assign srl_read = (rden | empty) & ~srl_empty;

endmodule // fifo_srl
