aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/fosphor/fifo_srl.v
blob: 822a1b769409904d27400407956349e0cd8eb4eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
 * 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