1`timescale 1ns / 1ps
2/*
3 * This software is Copyright (c) 2016 Denis Burykin
4 * [denis_burykin yahoo com], [denis-burykin2014 yandex ru]
5 * and it is hereby released to the general public under the following terms:
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted.
8 *
9 *
10 * Output Limit FIFO (high-speed output).
11 *
12 * Features:
13 *
14 * - Configurable width and depth
15 * - 1st word Fall-Through
16 * - Does not output on its own (reports EMPTY) when mode_limit == 1.
17 *
18 * - When reg_output_limit asserted:
19 * -- Reports amount ready for output (output_limit) in WIDTH-bit words
20 * -- Starts output of that amount, asserts output_limit_not_done
21 * -- Deasserts output_limit_not_done when finished
22 *
23 * - Does not require extra components from IP Coregen
24 * - The design is unable for asynchronous operation
25 * - Size equals to RAM size minus 1 word.
26 *
27 */
28
29module output_limit_fifo #(
30	// ADDR_MSB==11 : 8 Kbytes
31	parameter ADDR_MSB = 11,
32	parameter WIDTH = 16
33	)(
34	input rst,
35	input CLK,
36
37	input [WIDTH-1:0] din,
38	input wr_en,
39	output full,
40
41	output [WIDTH-1:0] dout,
42	input rd_en,
43	output empty,
44
45	input mode_limit, // turn on output limit
46	input reg_output_limit,
47	output [15:0] output_limit,
48	output reg output_limit_not_done
49	);
50
51	reg [ADDR_MSB:0] addra = 0;
52	reg [ADDR_MSB:0] output_limit_addr = 0;
53	reg [ADDR_MSB:0] output_limit_r = 0;
54	reg [ADDR_MSB:0] addrb = 0;
55
56	// 1st Word Fall-Through
57	reg wft = 0;
58	assign empty = rst || ~wft;
59
60	assign output_limit = { {15-ADDR_MSB{1'b0}}, output_limit_r };
61
62	assign full = rst || (addra + 1'b1 == addrb);
63	wire ena = wr_en && !full;
64
65	always @(posedge CLK) begin
66		if (rst) begin
67			addra <= 0;
68			output_limit_addr <= 0;
69			output_limit_r <= 0;
70		end
71		else begin
72			if (ena) begin
73				addra <= addra + 1'b1;
74			end
75
76			if (!mode_limit || reg_output_limit) begin
77				output_limit_addr <= addra;
78				output_limit_r <= addra - output_limit_addr;
79			end
80		end // ~rst
81	end
82
83	wire ram_empty_or_limit = (output_limit_addr == addrb);
84
85	wire enb = (!ram_empty_or_limit && (empty || rd_en));
86	reg enb_r = 0;
87
88	wire [15:0] ram_out;
89	reg [15:0] dout_r;
90	assign dout = dout_r;
91
92	always @(posedge CLK) begin
93		if (rst) begin
94			addrb <= 0;
95			wft <= 0;
96			enb_r <= 0;
97		end
98		else begin
99			if (empty || rd_en)
100				enb_r <= enb;
101
102			if (enb) begin
103				addrb <= addrb + 1'b1;
104			end
105
106			if (enb_r) begin
107				if (!wft || rd_en) begin
108					wft <= 1;
109					dout_r <= ram_out;
110				end
111			end // enb_r
112			else if (rd_en)
113				wft <= 0;
114		end // ~rst
115
116		output_limit_not_done <= ~ram_empty_or_limit;
117	end
118
119
120	(* RAM_STYLE = "BLOCK" *)
121	reg [WIDTH-1:0] ram [2**(ADDR_MSB+1)-1:0];
122	reg [WIDTH-1:0] ram_out_r;
123	assign ram_out = ram_out_r;
124
125	always @(posedge CLK) begin
126		if (ena)
127			ram[addra] <= din;
128		if (enb)
129			ram_out_r <= ram[addrb];
130	end
131
132endmodule
133