forked from antonblanchard/microwatt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sync_fifo.vhdl
163 lines (138 loc) · 4.37 KB
/
sync_fifo.vhdl
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
-- Synchronous FIFO with a protocol similar to AXI
--
-- The outputs are generated combinationally from the inputs
-- in order to allow for back-to-back transfers with the type
-- of flow control used by busses lite AXI, pipelined WB or
-- LiteDRAM native port when the FIFO is full.
--
-- That means that care needs to be taken by the user not to
-- generate the inputs combinationally from the outputs otherwise
-- it would create a logic loop.
--
-- If breaking that loop is required, a stash buffer could be
-- added to break the flow control "loop" between the read and
-- the write port.
--
library ieee;
use ieee.std_logic_1164.all;
library work;
use work.utils.all;
entity sync_fifo is
generic(
-- Fifo depth in entries
DEPTH : natural := 64;
-- Fifo width in bits
WIDTH : natural := 32;
-- When INIT_ZERO is set, the memory is pre-initialized to 0's
INIT_ZERO : boolean := false
);
port(
-- Control lines:
clk : in std_ulogic;
reset : in std_ulogic;
-- Write port
wr_ready : out std_ulogic;
wr_valid : in std_ulogic;
wr_data : in std_ulogic_vector(WIDTH - 1 downto 0);
-- Read port
rd_ready : in std_ulogic;
rd_valid : out std_ulogic;
rd_data : out std_ulogic_vector(WIDTH - 1 downto 0)
);
end entity sync_fifo;
architecture behaviour of sync_fifo is
subtype data_t is std_ulogic_vector(WIDTH - 1 downto 0);
type memory_t is array(0 to DEPTH - 1) of data_t;
function init_mem return memory_t is
variable m : memory_t;
begin
if INIT_ZERO then
for i in 0 to DEPTH - 1 loop
m(i) := (others => '0');
end loop;
end if;
return m;
end function;
signal memory : memory_t := init_mem;
subtype index_t is integer range 0 to DEPTH - 1;
signal rd_idx : index_t;
signal rd_next : index_t;
signal wr_idx : index_t;
signal wr_next : index_t;
function next_index(idx : index_t) return index_t is
variable r : index_t;
begin
if ispow2(DEPTH) then
r := (idx + 1) mod DEPTH;
else
r := idx + 1;
if r = DEPTH then
r := 0;
end if;
end if;
return r;
end function;
type op_t is (OP_POP, OP_PUSH);
signal op_prev : op_t := OP_POP;
signal op_next : op_t;
signal full, empty : std_ulogic;
signal push, pop : std_ulogic;
begin
-- Current state at last clock edge
empty <= '1' when rd_idx = wr_idx and op_prev = OP_POP else '0';
full <= '1' when rd_idx = wr_idx and op_prev = OP_PUSH else '0';
-- We can accept new data if we aren't full or we are but
-- the read port is going to accept data this cycle
wr_ready <= rd_ready or not full;
-- We can provide data if we aren't empty or we are but
-- the write port is going to provide data this cycle
rd_valid <= wr_valid or not empty;
-- Internal control signals
push <= wr_ready and wr_valid;
pop <= rd_ready and rd_valid;
-- Next state
rd_next <= next_index(rd_idx) when pop = '1' else rd_idx;
wr_next <= next_index(wr_idx) when push = '1' else wr_idx;
with push & pop select op_next <=
OP_PUSH when "10",
OP_POP when "01",
op_prev when others;
-- Read port output
rd_data <= memory(rd_idx) when empty = '0' else wr_data;
-- Read counter
reader: process(clk)
begin
if rising_edge(clk) then
if reset = '1' then
rd_idx <= 0;
else
rd_idx <= rd_next;
end if;
end if;
end process;
-- Write counter and memory write
producer: process(clk)
begin
if rising_edge(clk) then
if reset = '1' then
wr_idx <= 0;
else
wr_idx <= wr_next;
if push = '1' then
memory(wr_idx) <= wr_data;
end if;
end if;
end if;
end process;
-- Previous op latch used for generating empty/full
op: process(clk)
begin
if rising_edge(clk) then
if reset = '1' then
op_prev <= OP_POP;
else
op_prev <= op_next;
end if;
end if;
end process;
end architecture behaviour;