aboutsummaryrefslogtreecommitdiffstats
path: root/examples/ecp5_versa/fifobuf.vhdl
blob: 8122839fb496da52bb804ba09c5e149144421c00 (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
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
--
-- Configureable bit size I/O FIFO buffer
--
-- (c) 2010-2013, Martin Strubel <hackfin@section5.ch>
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity FifoBuffer is
	generic (
		ADDR_W          : natural := 6;
		DATA_W          : natural := 16;
		EXTRA_REGISTER  : boolean := false;
		SYN_RAMTYPE     : string  := "block_ram"
	);
	port (
		-- Write enable
		wren      : in  std_logic;
		idata     : in  unsigned(DATA_W-1 downto 0);
		iready    : out std_logic;
		-- Data stream output:
		odata     : out unsigned(DATA_W-1 downto 0);
		oready    : out std_logic;
		rden      : in  std_logic;
		err       : out std_logic;
		reset     : in  std_logic;
		clk       : in  std_logic
	);
end entity FifoBuffer;


architecture behaviour of FifoBuffer is
	constant FULL_COUNT : unsigned(ADDR_W-1 downto 0) := (others => '1');
	constant ZERO_COUNT : unsigned(ADDR_W-1 downto 0) := (others => '0');

	constant INACTIVE_WRITE_PORT :
		unsigned(DATA_W-1 downto 0) := (others => '0');
	signal iptr  : unsigned(ADDR_W-1 downto 0) := ZERO_COUNT;
	signal optr  : unsigned(ADDR_W-1 downto 0) := ZERO_COUNT;
	signal next_iptr  : unsigned(ADDR_W-1 downto 0) := ZERO_COUNT;
	signal next_optr  : unsigned(ADDR_W-1 downto 0) := ZERO_COUNT;
	signal over  : std_logic := '0';

	signal rdata  : unsigned(DATA_W-1 downto 0);

	type state_t is (S_IDLE, S_READY, S_FULL, S_ERROR);
	-- GHDLSYNTH_QUIRK
	-- Needs this initialized, otherwise gets 'optimized away'
	signal state : state_t := S_IDLE;
	-- If we don't initialize, yosys feels like it wants to recode.
	-- signal state : state_t;

	signal int_full      : std_logic; -- Internal "full" flag
	signal int_rden      : std_logic;
	signal int_rden_d    : std_logic;
	signal maybe_full    : std_logic;
	signal maybe_empty   : std_logic;

	signal dready        : std_logic;

	component bram_2psync is
		generic (
			ADDR      : natural := 6;
			DATA      : natural := 16
		);
		port (
			-- Port A
			a_we    : in  std_logic;
			a_addr  : in  unsigned(ADDR-1 downto 0);
			a_write : in  unsigned(DATA-1 downto 0);
			a_read  : out unsigned(DATA-1 downto 0);
			-- Port B
			b_we    : in  std_logic;
			b_addr  : in  unsigned(ADDR-1 downto 0);
			b_write : in  unsigned(DATA-1 downto 0);
			b_read  : out unsigned(DATA-1 downto 0);
			clk     : in  std_logic
		);
	end component bram_2psync;

begin

count:
	process(clk)
	begin
		if rising_edge(clk) then
			if reset = '1' then
				optr <= ZERO_COUNT;
				iptr <= ZERO_COUNT;
			else 
				if wren = '1' then
					iptr <= next_iptr;
				end if;
				if int_rden = '1' then
					optr <= next_optr;
				end if;
			end if;
		end if;
	end process;

	next_iptr <= iptr + 1;
	next_optr <= optr + 1;

	-- These are ambiguous signals, need evaluation of wren/rden!
	maybe_full   <= '1' when optr = next_iptr else '0';
	maybe_empty  <= '1' when iptr = next_optr else '0';

	dready  <= '1' when state = S_READY or state = S_FULL else '0';

fsm:
	process(clk)
	begin
		if rising_edge(clk) then
			if reset = '1' then
				state <= S_IDLE;
			else
				case state is
				when S_IDLE =>
					if wren = '1' then
						state <= S_READY;
					else
						state <= S_IDLE;
					end if;
				when S_READY =>
					if wren = '1' then
						-- We're just getting full
						if maybe_full = '1' and int_rden = '0' then
							state <= S_FULL;
						end if;
					-- We're just getting empty
					elsif maybe_empty = '1' and int_rden = '1' then
						state <= S_IDLE;
					end if;
					-- All other conditions: Remain S_READY
				when S_FULL =>
					if wren = '1' then
						-- It is actually allowed to read and write
						-- simultaneously while FULL.
						if int_rden = '0' then
							state <= S_ERROR;
						else
							state <= S_FULL;
						end if;
					elsif int_rden = '1' then
						state <= S_READY;
					else
						state <= S_FULL;
					end if;
				when S_ERROR =>
				end case;
			end if;
		end if;
	end process;

	over   <= '1' when state = S_ERROR else '0';
	err <= over;

ram:
	bram_2psync
	generic map ( ADDR => ADDR_W, DATA => DATA_W)
	port map (
		a_we    => '0',
		a_addr  => optr,
		a_write => INACTIVE_WRITE_PORT,
		a_read  => rdata,
		b_we    => wren,
		b_addr  => iptr,
		b_write => idata,
		b_read  => open,
		clk => clk
	);


-- This section appends a little pre-read unit to the FIFO
-- to allow higher speed on most architectures.

gen_register:
	if EXTRA_REGISTER generate

	int_rden <= (not int_full or rden) and dready;
preread:
 	process(clk)
 	begin
 		if rising_edge(clk) then
			if reset = '1' then
				int_full <= '0';
			elsif dready = '1' then
				if int_full = '0' then
					int_full <= '1';
					oready <= '1';
				end if;
			elsif int_full = '1' then
				if rden = '1' then
					oready <= '0';
					int_full <= '0';
				else
					oready <= '1';
				end if;
			else
				oready <= '0';
			end if;
			
			int_rden_d <= int_rden;
			if int_rden_d = '1' then
				odata <= rdata;
			end if;
		end if;
	end process;
	end generate;


gen_direct:
	if not EXTRA_REGISTER generate
		int_full <= '1' when state = S_FULL else '0';
		int_rden <= rden;
		odata <= rdata;
		oready <= dready;
	end generate;

	iready <= not int_full;


-- synthesis translate_off

-- Synplify barfs on this, we need to comment out the whole shlorm.

errguard:
	process(clk)
	begin
		if rising_edge(clk) then
			if over = '1' then
				assert false report "FIFO overrun in " & behaviour'path_name
				severity failure;
			end if;
		end if;
	end process;

-- synthesis translate_on

end behaviour;