-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfifo_core.vhd
393 lines (323 loc) · 13.1 KB
/
fifo_core.vhd
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
-- Greg Stitt
-- University of Florida
-- Description:
-- This file implements the fifo_core entity, which defines the architecture the
-- fifo entity (see fifo.vhd).
--
-- The entity contains architectures for using flips (FF) or memory (MEMORY)
-- when synthesized.
-- Notes:
-- The fifo protects against invalid writes (i.e. when full) and invalid reads
-- (i.e. when empty)
--
-- use_bram = true and same_cycle_output = true is not supported by
-- all FPGAs.
--
-- All FIFO depths are currently rounded up to the nearest power of two.
-- Used entities:
-- ram (in BRAM architecture)
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.math_custom.all;
-------------------------------------------------------------------------------
-- Generics Description
-- width : the width of the FIFO in bits (required)
-- depth : the depth of the FIFO in words (required)
-- almost_full_count : the amount of words in the FIFO when almost_full
-- is asserted (required)
-- use_bram : uses bram when true, uses LUTs/FFs when false (required)
-- same_cycle_output : output appears in same cycle as read when true, one
-- cycle later when false (required)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Port Description:
-- clk : clock input
-- rst : reset input (asynchronous)
-- rd : read input (active high)
-- wr : write input (active high)
-- empty : empty output (active high)
-- full : full output (active high)
-- almost_full : asserted when count >= almost_full_count (active high)
-- input : fifo input
-- output : fifo output
-------------------------------------------------------------------------------
entity fifo_core is
generic(width : positive;
depth : positive;
almost_full_count : natural;
use_bram : boolean;
same_cycle_output : boolean);
port(clk : in std_logic;
rst : in std_logic;
rd : in std_logic;
wr : in std_logic;
empty : out std_logic;
full : out std_logic;
almost_full : out std_logic;
count : out std_logic_vector(bitsNeeded(depth)-1 downto 0);
input : in std_logic_vector(width-1 downto 0);
output : out std_logic_vector(width-1 downto 0));
end fifo_core;
-- FF architecture
-- This architecture implements the FIFO with flip-flops
architecture FF of fifo_core is
type reg_array is array (0 to depth-1) of std_logic_vector(width-1 downto 0);
signal regs : reg_array;
signal count_r, next_count : unsigned(bitsNeeded(depth)-1 downto 0);
signal valid_wr : std_logic;
signal valid_rd : std_logic;
signal empty_s : std_logic;
signal full_s : std_logic;
begin
-- create a shift register to act as the fifo
-- always writes to register 0
process(clk, rst)
begin
if (rst = '1') then
-- initialize all registers to 0 (unnessary, useful for debugging)
for i in 0 to depth-1 loop
regs(i) <= (others => '0');
end loop;
elsif (rising_edge(clk)) then
-- shift in input everytime there is a valid write
if (valid_wr = '1') then
regs(0) <= input;
for i in 0 to depth-2 loop
regs(i+1) <= regs(i);
end loop;
end if;
end if;
end process;
-- assign the 1-cycle delayed output if applicable
U_OUTPUT_NEXT_CYCLE : if same_cycle_output = false generate
process(clk, rst)
begin
if (rst = '1') then
output <= (others => '0');
elsif(rising_edge(clk)) then
-- count-1 is the front of the fifo
if (count_r = 0) then
-- special case when fifo is empty. One alternative is to
-- increase the fifo depth to be a power of two,
-- in which case this isn't necessary.
output <= regs(0);
else
output <= regs(to_integer(count_r-1));
end if;
end if;
end process;
end generate;
-- assign the output in the same cycle if applicable
U_OUTPUT_SAME_CYCLE : if same_cycle_output = true generate
process(regs, count_r)
begin
-- count_r-1 is the front of the fifo
if (count_r = 0) then
-- special case when fifo is empty. One alternative is to
-- require the fifo depth to be a power of two,
-- in which case this isn't necessary.
output <= regs(0);
else
output <= regs(to_integer(count_r-1));
end if;
end process;
end generate;
-- update empty flag
empty_s <= '1' when count_r = 0 else '0';
empty <= empty_s;
-- update full flag
--full_s <= '0' when rd = '1' else
-- '1' when count_r = depth else '0';
full_s <= '1' when count_r = depth else '0';
full <= full_s;
-- update almost full flag
--almost_full <= '1' when count_r >= depth-almost_full_space else '0';
almost_full <= '1' when count_r >= almost_full_count else '0';
-- determine valid write and read
valid_wr <= wr and not full_s;
valid_rd <= rd and not empty_s;
-- update count_r based on read and write signals
process(valid_rd, valid_wr, count_r)
variable count_v : unsigned(bitsNeeded(depth)-1 downto 0);
begin
count_v := count_r;
if (valid_rd = '1' and valid_wr = '0') then
count_v := count_v - 1;
elsif (valid_rd = '0' and valid_wr = '1') then
count_v := count_v + 1;
end if;
next_count <= count_v;
end process;
-- create count register
process(clk, rst)
begin
if (rst = '1') then
count_r <= to_unsigned(0, count_r'length);
elsif (rising_edge(clk)) then
count_r <= next_count;
end if;
end process;
count <= std_logic_vector(count_r);
end FF;
architecture MEMORY of fifo_core is
signal rd_addr : unsigned(bitsNeeded(depth-1)-1 downto 0);
signal rd_addr_adjusted : unsigned(bitsNeeded(depth-1)-1 downto 0);
signal wr_addr : unsigned(bitsNeeded(depth-1)-1 downto 0);
signal ram_out : std_logic_vector(width-1 downto 0);
signal valid_wr : std_logic;
signal valid_rd : std_logic;
signal empty_s : std_logic;
signal full_s : std_logic;
signal count_r, next_count : unsigned(bitsNeeded(depth)-1 downto 0);
begin
-- implement FIFO using BRAM
BRAM : if use_bram = true generate
SAME_CYCLE : if same_cycle_output = true generate
-- adjust the rd address to account for the one cycle delay in the
-- block ram. This ensures that the correct output remains until
-- the the next read. When a read does occur, this reads the next
-- location in memory to ensure that data is available on the next
-- cycle.
rd_addr_adjusted <= rd_addr when valid_rd = '0' else rd_addr+1;
-- use RAM with synchronous read during write. This is necessary to
-- ensure that an output is available 1 cycle after a write, which
-- is the same time that the empty flag is cleared.
U_RAM : entity work.ram(SYNC_READ_DURING_WRITE)
generic map (
word_width => width,
addr_width => bitsNeeded(depth-1),
num_words => 2**bitsNeeded(depth-1))
port map (
clk => clk,
wen => valid_wr,
waddr => std_logic_vector(wr_addr),
wdata => input,
raddr => std_logic_vector(rd_addr_adjusted),
rdata => ram_out);
-- avoid warning about unused signal
output <= ram_out;
end generate SAME_CYCLE;
NOT_SAME_CYCLE : if same_cycle_output = false generate
-- avoids warning about unused signal for these generics
rd_addr_adjusted <= rd_addr;
-- use RAM with synchronous reads
U_RAM : entity work.ram(SYNC_READ)
generic map (
word_width => width,
addr_width => bitsNeeded(depth-1),
num_words => 2**bitsNeeded(depth-1))
port map (
clk => clk,
wen => valid_wr,
waddr => std_logic_vector(wr_addr),
wdata => input,
raddr => std_logic_vector(rd_addr_adjusted),
rdata => ram_out);
-- avoid warning about unused signal
output <= ram_out;
end generate NOT_SAME_CYCLE;
end generate BRAM;
-- implement FIFO using distributed RAM, LUTs, or any RAM that supports
-- asynchronous reads
-- synthesis tools might convert this to another resource if FPGA memory
-- does not support asynchronous reads
DIST_RAM : if use_bram = false generate
SAME_CYCLE : if same_cycle_output = true generate
-- avoids warning about unused signal for these generics
rd_addr_adjusted <= rd_addr;
-- use RAM with asynchronous reads (not supported by all FPGAs)
U_RAM : entity work.ram(ASYNC_READ)
generic map (
word_width => width,
addr_width => bitsNeeded(depth-1),
num_words => 2**bitsNeeded(depth-1))
port map (
clk => clk,
wen => valid_wr,
waddr => std_logic_vector(wr_addr),
wdata => input,
raddr => std_logic_vector(rd_addr_adjusted),
rdata => ram_out);
-- avoids warning about unused signal for these generics
output <= ram_out;
end generate SAME_CYCLE;
NOT_SAME_CYCLE : if same_cycle_output = false generate
-- avoids warning about unused signal for these generics
rd_addr_adjusted <= rd_addr;
-- use RAM with asynchronous reads (not supported by all FPGAs)
U_RAM : entity work.ram(ASYNC_READ)
generic map (
word_width => width,
addr_width => bitsNeeded(depth-1),
num_words => 2**bitsNeeded(depth-1))
port map (
clk => clk,
wen => valid_wr,
waddr => std_logic_vector(wr_addr),
wdata => input,
raddr => std_logic_vector(rd_addr_adjusted),
rdata => ram_out);
-- add a register to delay the output by a cycle
process(clk, rst)
begin
if (rst = '1') then
output <= (others => '0');
elsif (rising_edge(clk)) then
output <= ram_out;
end if;
end process;
end generate NOT_SAME_CYCLE;
end generate DIST_RAM;
-- update empty flag
--empty_s <= '1' when wr_addr = rd_addr else '0';
empty_s <= '1' when count_r = 0 else '0';
empty <= empty_s;
-- update full flag
--full_s <= '0' when rd = '1' else
-- '1' when wr_addr + 1 = rd_addr else '0';
full_s <= '1' when count_r = depth else '0';
full <= full_s;
-- update almost_full flag
almost_full <= '1' when count_r >= almost_full_count else '0';
-- determine valid write and read
valid_wr <= wr and not full_s;
valid_rd <= rd and not empty_s;
-- update wr and rd addresses
process(clk, rst)
begin
if (rst = '1') then
wr_addr <= (others => '0');
rd_addr <= (others => '0');
elsif rising_edge(clk) then
if (valid_rd = '1') then
rd_addr <= rd_addr + 1;
end if;
if (valid_wr = '1') then
wr_addr <= wr_addr + 1;
end if;
end if;
end process;
-- update count_r based on read and write signals
process(valid_rd, valid_wr, count_r)
variable count_v : unsigned(bitsNeeded(depth)-1 downto 0);
begin
count_v := count_r;
if (valid_rd = '1' and valid_wr = '0') then
count_v := count_v - 1;
elsif (valid_rd = '0' and valid_wr = '1') then
count_v := count_v + 1;
end if;
next_count <= count_v;
end process;
-- create count register
process(clk, rst)
begin
if (rst = '1') then
count_r <= to_unsigned(0, count_r'length);
elsif (rising_edge(clk)) then
count_r <= next_count;
end if;
end process;
count <= std_logic_vector(count_r);
end MEMORY;