1 |
145 |
lanttu |
-------------------------------------------------------------------------------
|
2 |
|
|
-- Title : HIBI TX control
|
3 |
|
|
-- Project : Nocbench, Funbase
|
4 |
|
|
-- Description: TX controller for hibi
|
5 |
|
|
--
|
6 |
|
|
-- Arbitrates and forwards data to the bus from tx fifo (=from IP)
|
7 |
|
|
-- or CFG mem.
|
8 |
|
|
-- Reply to config mem read will only be sent at the
|
9 |
|
|
-- beginning of our turn, so it won't come out immediately.
|
10 |
|
|
--
|
11 |
|
|
-- File : tx_control.vhd
|
12 |
|
|
-- Authors : Antti Alhonen,
|
13 |
|
|
-- Lasse Lehtonen
|
14 |
|
|
-- Company : Tampere University of Technology
|
15 |
|
|
-------------------------------------------------------------------------------
|
16 |
|
|
-- Last update: 2012-02-06
|
17 |
|
|
-- Standard : VHDL'93
|
18 |
|
|
-- TO DO:
|
19 |
|
|
-- keep_slot_g,
|
20 |
|
|
-- dyn_arb_enable_c should be generic param
|
21 |
|
|
-------------------------------------------------------------------------------
|
22 |
|
|
-- Revisions :
|
23 |
|
|
-- Date Version Author Description
|
24 |
|
|
--
|
25 |
|
|
-- 2010-07-02 1.0 alho-nenä Created
|
26 |
|
|
-- Outputs data_out and fifo_re are now COMBINATORIAL. Only address has
|
27 |
|
|
-- to be registered because it needs to be retransferred if a transfer is
|
28 |
|
|
-- suspended and restarted. No retransferring is needed in case of FULL.
|
29 |
|
|
-- Output data mux is simple.
|
30 |
|
|
--
|
31 |
|
|
-- Old version had internal path of 7.0 ns + bus path of 7.8 ns on STRATIX II.
|
32 |
|
|
-- This version is much simpler and the combined path seems to be only 8.8 ns.
|
33 |
|
|
-- This version needs a version of rx_ctrl that has no combinatorial path from
|
34 |
|
|
-- comm_in to fifo_full.
|
35 |
|
|
--
|
36 |
|
|
-- 2010-10-13 ase Added support for config mem reading
|
37 |
|
|
-- 2010-10-24 ase Added support for SAD mode
|
38 |
|
|
--
|
39 |
|
|
-------------------------------------------------------------------------------
|
40 |
|
|
-------------------------------------------------------------------------------
|
41 |
|
|
-- Funbase IP library Copyright (C) 2011 TUT Department of Computer Systems
|
42 |
|
|
--
|
43 |
|
|
-- This file is part of HIBI
|
44 |
|
|
--
|
45 |
|
|
-- This source file may be used and distributed without
|
46 |
|
|
-- restriction provided that this copyright statement is not
|
47 |
|
|
-- removed from the file and that any derivative work contains
|
48 |
|
|
-- the original copyright notice and the associated disclaimer.
|
49 |
|
|
--
|
50 |
|
|
-- This source file is free software; you can redistribute it
|
51 |
|
|
-- and/or modify it under the terms of the GNU Lesser General
|
52 |
|
|
-- Public License as published by the Free Software Foundation;
|
53 |
|
|
-- either version 2.1 of the License, or (at your option) any
|
54 |
|
|
-- later version.
|
55 |
|
|
--
|
56 |
|
|
-- This source is distributed in the hope that it will be
|
57 |
|
|
-- useful, but WITHOUT ANY WARRANTY; without even the implied
|
58 |
|
|
-- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
59 |
|
|
-- PURPOSE. See the GNU Lesser General Public License for more
|
60 |
|
|
-- details.
|
61 |
|
|
--
|
62 |
|
|
-- You should have received a copy of the GNU Lesser General
|
63 |
|
|
-- Public License along with this source; if not, download it
|
64 |
|
|
-- from http://www.opencores.org/lgpl.shtml
|
65 |
|
|
-------------------------------------------------------------------------------
|
66 |
|
|
|
67 |
|
|
library ieee;
|
68 |
|
|
use ieee.std_logic_1164.all;
|
69 |
|
|
use ieee.numeric_std.all;
|
70 |
|
|
|
71 |
|
|
use work.hibiv3_pkg.all;
|
72 |
|
|
|
73 |
|
|
entity tx_control is
|
74 |
|
|
|
75 |
|
|
generic (
|
76 |
|
|
counter_width_g : integer;
|
77 |
|
|
id_width_g : integer;
|
78 |
|
|
data_width_g : integer;
|
79 |
|
|
addr_width_g : integer;
|
80 |
|
|
comm_width_g : integer;
|
81 |
|
|
n_agents_g : integer;
|
82 |
|
|
cfg_re_g : integer;
|
83 |
|
|
keep_slot_g : integer;
|
84 |
|
|
separate_addr_g : integer
|
85 |
|
|
);
|
86 |
|
|
port (
|
87 |
|
|
clk : in std_logic;
|
88 |
|
|
rst_n : in std_logic;
|
89 |
|
|
|
90 |
|
|
lock_in : in std_logic;
|
91 |
|
|
full_in : in std_logic;
|
92 |
|
|
|
93 |
|
|
cfg_ret_addr_in : in std_logic_vector(addr_width_g-1 downto 0);
|
94 |
|
|
cfg_data_in : in std_logic_vector
|
95 |
|
|
(data_width_g-1-(separate_addr_g*addr_width_g) downto 0);
|
96 |
|
|
cfg_re_in : in std_logic;
|
97 |
|
|
|
98 |
|
|
curr_slot_own_in : in std_logic;
|
99 |
|
|
curr_slot_ends_in : in std_logic;
|
100 |
|
|
next_slot_own_in : in std_logic;
|
101 |
|
|
next_slot_starts_in : in std_logic;
|
102 |
|
|
max_send_in : in std_logic_vector(counter_width_g-1 downto 0);
|
103 |
|
|
n_agents_in : in std_logic_vector(id_width_g-1 downto 0);
|
104 |
|
|
prior_in : in std_logic_vector(id_width_g-1 downto 0);
|
105 |
|
|
-- 0 round-robin, 1 priority, 2 combined, 3 dyn_arb (rand)
|
106 |
|
|
arb_type_in : in std_logic_vector(1 downto 0);
|
107 |
|
|
|
108 |
|
|
av_in : in std_logic;
|
109 |
|
|
data_in : in std_logic_vector(data_width_g-1 downto 0);
|
110 |
|
|
comm_in : in std_logic_vector(comm_width_g-1 downto 0);
|
111 |
|
|
one_d_in : in std_logic;
|
112 |
|
|
empty_in : in std_logic;
|
113 |
|
|
|
114 |
|
|
av_out : out std_logic;
|
115 |
|
|
data_out : out std_logic_vector(data_width_g-1 downto 0);
|
116 |
|
|
comm_out : out std_logic_vector(comm_width_g-1 downto 0);
|
117 |
|
|
lock_out : out std_logic;
|
118 |
|
|
cfg_rd_rdy_out : out std_logic;
|
119 |
|
|
re_out : out std_logic
|
120 |
|
|
);
|
121 |
|
|
|
122 |
|
|
end tx_control;
|
123 |
|
|
|
124 |
|
|
|
125 |
|
|
architecture rtl of tx_control is
|
126 |
|
|
|
127 |
|
|
-- Select arbitation type.
|
128 |
|
|
-- Move to this to generic parameter!
|
129 |
|
|
constant dyn_arb_enable_c : integer := 1;
|
130 |
|
|
|
131 |
|
|
-- Internal signals and registers for arbitration
|
132 |
|
|
signal prior_match : std_logic;
|
133 |
|
|
signal prior : std_logic_vector(id_width_g-1 downto 0);
|
134 |
|
|
signal dyn_arb_prior : std_logic_vector(id_width_g-1 downto 0);
|
135 |
|
|
signal prior_counter_arb_r : std_logic_vector(id_width_g-1 downto 0);
|
136 |
|
|
signal arb_type_r : std_logic_vector(1 downto 0);
|
137 |
|
|
signal can_write_r : std_logic;
|
138 |
|
|
signal can_write_r_r : std_logic;
|
139 |
|
|
signal new_turn : std_logic; -- pulse when turn starts
|
140 |
|
|
signal new_turn_stay_r : std_logic;
|
141 |
|
|
signal new_turn_ack : std_logic;
|
142 |
|
|
signal new_turn_ack_r : std_logic;
|
143 |
|
|
|
144 |
|
|
constant switch_arb_c : integer := 4096; -- how often change between prior
|
145 |
|
|
-- and round-robin, [cycles]
|
146 |
|
|
signal switch_arb_r : integer range 0 to switch_arb_c-1;
|
147 |
|
|
|
148 |
|
|
|
149 |
|
|
-- Retransfer needs registers for storing one word tx
|
150 |
|
|
signal addr_r : std_logic_vector(addr_width_g-1 downto 0);
|
151 |
|
|
signal comm_r : std_logic_vector(comm_width_g-1 downto 0);
|
152 |
|
|
signal data_r : std_logic_vector(data_width_g-1 downto 0);
|
153 |
|
|
signal tx_interrupted_r : std_logic; -- for separate addr
|
154 |
|
|
signal retransfer : std_logic; -- for separate addr
|
155 |
|
|
signal retransfer_r : std_logic; -- for separate addr
|
156 |
|
|
|
157 |
|
|
-- Keep track what we are doing and how many have been sent
|
158 |
|
|
signal writing : std_logic; -- writing to bus
|
159 |
|
|
signal reading : std_logic; -- reading the FIFO
|
160 |
|
|
signal reading_r : std_logic;
|
161 |
|
|
signal send_counter_r : unsigned(counter_width_g-1 downto 0); -- #words
|
162 |
|
|
signal av_out_s : std_logic; -- combinatorial output (must be read also)
|
163 |
|
|
|
164 |
|
|
|
165 |
|
|
-- Signals for responding to confg read operations
|
166 |
|
|
signal cfg_rd_rdy_r : std_logic;
|
167 |
|
|
signal cfg_rd_ack : std_logic;
|
168 |
|
|
signal last_was_cfg_rd_r : std_logic;
|
169 |
|
|
signal cfg_data_in_r : std_logic_vector
|
170 |
|
|
(data_width_g-1-(separate_addr_g*addr_width_g) downto 0);
|
171 |
|
|
|
172 |
|
|
|
173 |
|
|
-- Dynamic Arbitration Algorithm is implemented with a separate component
|
174 |
|
|
component dyn_arb
|
175 |
|
|
generic (
|
176 |
|
|
id_width_g : integer;
|
177 |
|
|
n_agents_g : integer
|
178 |
|
|
);
|
179 |
|
|
port (
|
180 |
|
|
clk : in std_logic;
|
181 |
|
|
rst_n : in std_logic;
|
182 |
|
|
bus_lock_in : in std_logic;
|
183 |
|
|
arb_agent_out : out std_logic_vector(id_width_g-1 downto 0));
|
184 |
|
|
end component;
|
185 |
|
|
|
186 |
|
|
|
187 |
|
|
begin -- rtl
|
188 |
|
|
|
189 |
|
|
av_out <= av_out_s;
|
190 |
|
|
|
191 |
|
|
-----------------------------------------------------------------------------
|
192 |
|
|
-- 1. CONFIG READ, rest affecting this can be found in connect_output process
|
193 |
|
|
-----------------------------------------------------------------------------
|
194 |
|
|
config_read_enabled : if cfg_re_g = 1 generate
|
195 |
|
|
|
196 |
|
|
cfg_rd_rdy_out <= cfg_rd_rdy_r;
|
197 |
|
|
|
198 |
|
|
cfg_rd_rdy_p : process (clk, rst_n) is
|
199 |
|
|
variable cfg_rd_rdy_v : std_logic;
|
200 |
|
|
begin -- process cfg_rd_rdy_p
|
201 |
|
|
if rst_n = '0' then -- asynchronous reset (active low)
|
202 |
|
|
|
203 |
|
|
cfg_rd_rdy_r <= '1';
|
204 |
|
|
last_was_cfg_rd_r <= '0';
|
205 |
|
|
cfg_data_in_r <= (others => '0');
|
206 |
|
|
|
207 |
|
|
elsif clk'event and clk = '1' then -- rising clock edge
|
208 |
|
|
|
209 |
|
|
-- Block the rx ctrl from accessing config memory until reply has been sent
|
210 |
|
|
if cfg_re_in = '1' then
|
211 |
|
|
cfg_rd_rdy_v := '0';
|
212 |
|
|
cfg_data_in_r <= cfg_data_in;
|
213 |
|
|
elsif cfg_rd_ack = '1' then
|
214 |
|
|
cfg_rd_rdy_v := '1';
|
215 |
|
|
else
|
216 |
|
|
cfg_rd_rdy_v := cfg_rd_rdy_r;
|
217 |
|
|
end if;
|
218 |
|
|
|
219 |
|
|
cfg_rd_rdy_r <= cfg_rd_rdy_v;
|
220 |
|
|
|
221 |
|
|
if separate_addr_g = 0 then
|
222 |
|
|
last_was_cfg_rd_r <= cfg_rd_ack;
|
223 |
|
|
else
|
224 |
|
|
last_was_cfg_rd_r <= writing and not cfg_rd_rdy_v and new_turn_ack;
|
225 |
|
|
end if;
|
226 |
|
|
|
227 |
|
|
|
228 |
|
|
end if;
|
229 |
|
|
end process cfg_rd_rdy_p;
|
230 |
|
|
|
231 |
|
|
end generate config_read_enabled;
|
232 |
|
|
|
233 |
|
|
config_read_disabled : if cfg_re_g = 0 generate
|
234 |
|
|
|
235 |
|
|
cfg_rd_rdy_out <= '1';
|
236 |
|
|
last_was_cfg_rd_r <= '0';
|
237 |
|
|
cfg_data_in_r <= (others => '0');
|
238 |
|
|
|
239 |
|
|
end generate config_read_disabled;
|
240 |
|
|
|
241 |
|
|
|
242 |
|
|
-------------------------------------------------------------------------------
|
243 |
|
|
-- 2. OLD VALUE REGISTERING
|
244 |
|
|
-------------------------------------------------------------------------------
|
245 |
|
|
-- Old thingys (command, address and data) are needed when a transfer
|
246 |
|
|
-- got interrupted and will be restarted later
|
247 |
|
|
|
248 |
|
|
register_olds : process (clk, rst_n)
|
249 |
|
|
variable tx_interrupted_v : std_logic;
|
250 |
|
|
begin
|
251 |
|
|
if rst_n = '0' then -- asynchronous reset (active low)
|
252 |
|
|
|
253 |
|
|
can_write_r_r <= '0';
|
254 |
|
|
new_turn_ack_r <= '0';
|
255 |
|
|
new_turn_stay_r <= '0';
|
256 |
|
|
--addr_r <= (others => '0');
|
257 |
|
|
--comm_r <= (others => '0');
|
258 |
|
|
--data_r <= (others => '0');
|
259 |
|
|
tx_interrupted_r <= '0';
|
260 |
|
|
retransfer_r <= '0';
|
261 |
|
|
reading_r <= '0';
|
262 |
|
|
|
263 |
|
|
elsif clk'event and clk = '1' then -- rising clock edge
|
264 |
|
|
|
265 |
|
|
reading_r <= reading;
|
266 |
|
|
|
267 |
|
|
-- Check if the previous transfer completed
|
268 |
|
|
if separate_addr_g = 1 then
|
269 |
|
|
|
270 |
|
|
retransfer_r <= retransfer;
|
271 |
|
|
|
272 |
|
|
if reading_r = '1' and can_write_r_r = '1' and full_in = '1' then
|
273 |
|
|
tx_interrupted_v := '1';
|
274 |
|
|
elsif retransfer_r = '1' and full_in = '0' then
|
275 |
|
|
tx_interrupted_v := '0';
|
276 |
|
|
else
|
277 |
|
|
tx_interrupted_v := tx_interrupted_r;
|
278 |
|
|
end if;
|
279 |
|
|
|
280 |
|
|
tx_interrupted_r <= tx_interrupted_v;
|
281 |
|
|
|
282 |
|
|
else
|
283 |
|
|
tx_interrupted_v := '0';
|
284 |
|
|
end if;
|
285 |
|
|
|
286 |
|
|
|
287 |
|
|
-- Multiplexed mode stores all the addresses, separate addr style onyl
|
288 |
|
|
-- when interrupted
|
289 |
|
|
if separate_addr_g = 0 and av_in = '1' and empty_in = '0' then
|
290 |
|
|
addr_r <= data_in(addr_width_g-1 downto 0);
|
291 |
|
|
|
292 |
|
|
elsif separate_addr_g = 1
|
293 |
|
|
and tx_interrupted_v = '0' and full_in = '0' then
|
294 |
|
|
|
295 |
|
|
data_r <= data_in;
|
296 |
|
|
comm_r <= comm_in;
|
297 |
|
|
|
298 |
|
|
end if;
|
299 |
|
|
|
300 |
|
|
|
301 |
|
|
can_write_r_r <= can_write_r;
|
302 |
|
|
new_turn_ack_r <= new_turn_ack;
|
303 |
|
|
|
304 |
|
|
-- The register new_turn_stay_r gets '1' when own turn starts. It returns to '0'
|
305 |
|
|
-- when it's acknowledged by rising edge of new_turn_ack.
|
306 |
|
|
if new_turn = '1' then
|
307 |
|
|
new_turn_stay_r <= '1';
|
308 |
|
|
end if;
|
309 |
|
|
|
310 |
|
|
if new_turn_ack = '1' and new_turn_ack_r = '0' then
|
311 |
|
|
new_turn_stay_r <= '0';
|
312 |
|
|
end if;
|
313 |
|
|
|
314 |
|
|
end if;
|
315 |
|
|
end process register_olds;
|
316 |
|
|
|
317 |
|
|
-------------------------------------------------------------------------------
|
318 |
|
|
-- 3. Count how many words have been sent. No effect during TDMA slot, though.
|
319 |
|
|
-------------------------------------------------------------------------------
|
320 |
|
|
send_counting : process (clk, rst_n)
|
321 |
|
|
begin -- process send_counting
|
322 |
|
|
if rst_n = '0' then -- asynchronous reset (active low)
|
323 |
|
|
send_counter_r <= (others => '0');
|
324 |
|
|
elsif clk'event and clk = '1' then -- rising clock edge
|
325 |
|
|
if writing = '1' and curr_slot_own_in = '0' then
|
326 |
|
|
send_counter_r <= send_counter_r +1;
|
327 |
|
|
else
|
328 |
|
|
send_counter_r <= (others => '0');
|
329 |
|
|
end if;
|
330 |
|
|
end if;
|
331 |
|
|
end process send_counting;
|
332 |
|
|
|
333 |
|
|
-------------------------------------------------------------------------------
|
334 |
|
|
-- 4. CHECK IF IT'S OUR TURN OR NOT
|
335 |
|
|
-------------------------------------------------------------------------------
|
336 |
|
|
-- Register can_write_r is up when it's our turn and we can take the bus
|
337 |
|
|
evaluate_can_write : process (clk, rst_n)
|
338 |
|
|
begin -- process evaluate_can_write
|
339 |
|
|
if rst_n = '0' then -- asynchronous reset (active low)
|
340 |
|
|
can_write_r <= '0';
|
341 |
|
|
|
342 |
|
|
elsif clk'event and clk = '1' then -- rising clock edge
|
343 |
|
|
|
344 |
|
|
can_write_r <= '0';
|
345 |
|
|
|
346 |
|
|
if curr_slot_own_in = '1' then
|
347 |
|
|
-- Own TDMA slot
|
348 |
|
|
can_write_r <= '1';
|
349 |
|
|
|
350 |
|
|
elsif can_write_r = '1' then
|
351 |
|
|
-- We already have the turn, check if it ends or not
|
352 |
|
|
can_write_r <= '1';
|
353 |
|
|
|
354 |
|
|
-- Own turn ends, if max #words sent, own slot ends, another wrapper's
|
355 |
|
|
-- slot starts, or
|
356 |
|
|
if (send_counter_r >= unsigned(max_send_in (counter_width_g-1 downto 0))
|
357 |
|
|
and (av_out_s = '0' or separate_addr_g = 1)) --
|
358 |
|
|
or ((curr_slot_own_in = '1' and curr_slot_ends_in = '1') -- tdma slot ends
|
359 |
|
|
or (next_slot_own_in = '0' and next_slot_starts_in = '1'))
|
360 |
|
|
or (lock_in = '1' and writing = '0') -- someone takes it
|
361 |
|
|
or writing = '0' -- we stop using it for some reason
|
362 |
|
|
then
|
363 |
|
|
can_write_r <= '0';
|
364 |
|
|
end if;
|
365 |
|
|
|
366 |
|
|
elsif prior_match = '1' and lock_in = '0' then
|
367 |
|
|
-- We got a turn through competition reserving.
|
368 |
|
|
if can_write_r = '0' and can_write_r_r = '0' then
|
369 |
|
|
-- This condition prevents us from taking the bus again right away
|
370 |
|
|
-- when we lost it.
|
371 |
|
|
can_write_r <= '1';
|
372 |
|
|
end if;
|
373 |
|
|
|
374 |
|
|
end if;
|
375 |
|
|
|
376 |
|
|
end if;
|
377 |
|
|
end process evaluate_can_write;
|
378 |
|
|
|
379 |
|
|
|
380 |
|
|
new_turn <= can_write_r and not can_write_r_r;
|
381 |
|
|
|
382 |
|
|
-------------------------------------------------------------------------------
|
383 |
|
|
-- 5. OUTPUT DATA MUX AND CONTROL
|
384 |
|
|
-- 5a) MULTIPLEXED ADDRESS AND DATA BUSES
|
385 |
|
|
-----------------------------------------------------------------------------
|
386 |
|
|
norm_connect_output : if separate_addr_g = 0 generate
|
387 |
|
|
|
388 |
|
|
connect_output : process (empty_in, new_turn, new_turn_stay_r, av_in,
|
389 |
|
|
comm_in, can_write_r, data_in, addr_r, full_in,
|
390 |
|
|
last_was_cfg_rd_r, cfg_rd_rdy_r, cfg_ret_addr_in,
|
391 |
|
|
cfg_data_in_r, new_turn_ack_r)
|
392 |
|
|
begin
|
393 |
|
|
|
394 |
|
|
new_turn_ack <= '0'; -- defaults
|
395 |
|
|
cfg_rd_ack <= '0';
|
396 |
|
|
writing <= '0';
|
397 |
|
|
reading <= '0';
|
398 |
|
|
lock_out <= '0';
|
399 |
|
|
|
400 |
|
|
if cfg_rd_rdy_r = '0' and can_write_r = '1'
|
401 |
|
|
and (new_turn = '1' or new_turn_stay_r = '1') then
|
402 |
|
|
-- Send the config return address
|
403 |
|
|
|
404 |
|
|
-- This branch should be optimized away when cfg_re_g == 0
|
405 |
|
|
-- as condition will always be false
|
406 |
|
|
|
407 |
|
|
av_out_s <= '1';
|
408 |
|
|
comm_out <= MSG_WRNP_c;
|
409 |
|
|
data_out <= (others => '0');
|
410 |
|
|
data_out(addr_width_g-1 downto 0) <= cfg_ret_addr_in;
|
411 |
|
|
|
412 |
|
|
if full_in = '0' then
|
413 |
|
|
lock_out <= '1';
|
414 |
|
|
reading <= '0';
|
415 |
|
|
writing <= '1';
|
416 |
|
|
new_turn_ack <= '1';
|
417 |
|
|
end if;
|
418 |
|
|
|
419 |
|
|
elsif cfg_rd_rdy_r = '0' and new_turn_ack_r = '1'
|
420 |
|
|
and can_write_r = '1' then
|
421 |
|
|
-- Send the read config data
|
422 |
|
|
|
423 |
|
|
-- This branch should be optimized away when cfg_re_g == 0
|
424 |
|
|
-- as condition will always be false
|
425 |
|
|
|
426 |
|
|
av_out_s <= '0';
|
427 |
|
|
comm_out <= MSG_WRNP_c;
|
428 |
|
|
data_out <= cfg_data_in_r;
|
429 |
|
|
|
430 |
|
|
if full_in = '0' then
|
431 |
|
|
reading <= '0';
|
432 |
|
|
writing <= '1';
|
433 |
|
|
cfg_rd_ack <= '1';
|
434 |
|
|
lock_out <= '1';
|
435 |
|
|
end if;
|
436 |
|
|
|
437 |
|
|
elsif empty_in = '0' and can_write_r = '1'
|
438 |
|
|
and (new_turn = '1' or new_turn_stay_r = '1' or last_was_cfg_rd_r = '1')
|
439 |
|
|
then
|
440 |
|
|
-- We want to send some data, and we just* got our turn
|
441 |
|
|
-- First we always send address
|
442 |
|
|
|
443 |
|
|
-- *Reply to config read may have been sent already, if cfg_re_g == 1
|
444 |
|
|
|
445 |
|
|
|
446 |
|
|
if full_in = '0' then
|
447 |
|
|
writing <= '1';
|
448 |
|
|
lock_out <= '1';
|
449 |
|
|
new_turn_ack <= not last_was_cfg_rd_r; -- don't ack if not necessary
|
450 |
|
|
end if;
|
451 |
|
|
|
452 |
|
|
if av_in = '1' then
|
453 |
|
|
-- We have a new address in FIFO
|
454 |
|
|
data_out <= data_in;
|
455 |
|
|
if full_in = '0' then
|
456 |
|
|
reading <= '1';
|
457 |
|
|
end if;
|
458 |
|
|
else
|
459 |
|
|
-- Retransfer the old address from register, don't read fifo.
|
460 |
|
|
data_out <= (others => '0');
|
461 |
|
|
data_out(addr_width_g-1 downto 0) <= addr_r;
|
462 |
|
|
reading <= '0';
|
463 |
|
|
end if;
|
464 |
|
|
|
465 |
|
|
av_out_s <= '1';
|
466 |
|
|
comm_out <= comm_in;
|
467 |
|
|
|
468 |
|
|
|
469 |
|
|
elsif empty_in = '0' and can_write_r = '1' then
|
470 |
|
|
-- Just transfer the data.
|
471 |
|
|
if full_in = '0' then
|
472 |
|
|
writing <= '1';
|
473 |
|
|
lock_out <= '1';
|
474 |
|
|
reading <= '1';
|
475 |
|
|
end if;
|
476 |
|
|
|
477 |
|
|
data_out <= data_in;
|
478 |
|
|
av_out_s <= av_in;
|
479 |
|
|
comm_out <= comm_in;
|
480 |
|
|
|
481 |
|
|
else
|
482 |
|
|
-- Nothing to transfer.
|
483 |
|
|
writing <= '0';
|
484 |
|
|
data_out <= (others => '0');
|
485 |
|
|
lock_out <= '0';
|
486 |
|
|
comm_out <= (others => '0');
|
487 |
|
|
av_out_s <= '0';
|
488 |
|
|
reading <= '0';
|
489 |
|
|
|
490 |
|
|
|
491 |
|
|
end if;
|
492 |
|
|
end process connect_output;
|
493 |
|
|
|
494 |
|
|
end generate norm_connect_output;
|
495 |
|
|
|
496 |
|
|
-----------------------------------------------------------------------------
|
497 |
|
|
-- 5. OUTPUT DATA MUX AND CONTROL
|
498 |
|
|
-- 5b) SEPARATED ADDRESS AND DATA BUSES
|
499 |
|
|
-----------------------------------------------------------------------------
|
500 |
|
|
sad_connect_output : if separate_addr_g = 1 generate
|
501 |
|
|
|
502 |
|
|
sad_connect_output : process (empty_in, new_turn, new_turn_stay_r, av_in,
|
503 |
|
|
comm_in, can_write_r, data_in,
|
504 |
|
|
full_in, last_was_cfg_rd_r, cfg_rd_rdy_r,
|
505 |
|
|
cfg_ret_addr_in, cfg_data_in_r,
|
506 |
|
|
tx_interrupted_r, data_r, comm_r)
|
507 |
|
|
begin
|
508 |
|
|
|
509 |
|
|
new_turn_ack <= '0'; -- defaults
|
510 |
|
|
cfg_rd_ack <= '0';
|
511 |
|
|
retransfer <= '0';
|
512 |
|
|
writing <= '0';
|
513 |
|
|
lock_out <= '0';
|
514 |
|
|
reading <= '0';
|
515 |
|
|
|
516 |
|
|
if last_was_cfg_rd_r = '1' and full_in = '0' then
|
517 |
|
|
cfg_rd_ack <= '1';
|
518 |
|
|
end if;
|
519 |
|
|
|
520 |
|
|
if cfg_rd_rdy_r = '0' and can_write_r = '1'
|
521 |
|
|
and (new_turn = '1' or new_turn_stay_r = '1') then
|
522 |
|
|
-- Send config return address
|
523 |
|
|
|
524 |
|
|
-- This branch should be optimized away when cfg_re_g == 0
|
525 |
|
|
-- as condition will always be false
|
526 |
|
|
|
527 |
|
|
av_out_s <= '1';
|
528 |
|
|
|
529 |
|
|
comm_out <= MSG_WRNP_c;
|
530 |
|
|
data_out(data_width_g-1 downto data_width_g-addr_width_g) <=
|
531 |
|
|
cfg_ret_addr_in;
|
532 |
|
|
data_out(data_width_g-addr_width_g-1 downto 0) <=
|
533 |
|
|
cfg_data_in_r;
|
534 |
|
|
|
535 |
|
|
if full_in = '0' then
|
536 |
|
|
reading <= '0';
|
537 |
|
|
writing <= '1';
|
538 |
|
|
new_turn_ack <= '1';
|
539 |
|
|
lock_out <= '1';
|
540 |
|
|
end if;
|
541 |
|
|
|
542 |
|
|
elsif (empty_in = '0' or tx_interrupted_r = '1') and can_write_r = '1'
|
543 |
|
|
and (new_turn = '1' or new_turn_stay_r = '1' or last_was_cfg_rd_r = '1')
|
544 |
|
|
then
|
545 |
|
|
-- We want to send, and we just* got our turn
|
546 |
|
|
-- *Reply to config read may have been sent already, if cfg_re_g == 1
|
547 |
|
|
|
548 |
|
|
if full_in = '0' then
|
549 |
|
|
writing <= '1';
|
550 |
|
|
lock_out <= '1';
|
551 |
|
|
new_turn_ack <= not last_was_cfg_rd_r; -- don't ack if not necessary
|
552 |
|
|
end if;
|
553 |
|
|
|
554 |
|
|
if tx_interrupted_r = '1' then
|
555 |
|
|
-- We need to retransfer the previous data(incl. addr)
|
556 |
|
|
data_out <= data_r;
|
557 |
|
|
comm_out <= comm_r;
|
558 |
|
|
if full_in = '0' then
|
559 |
|
|
reading <= '0';
|
560 |
|
|
retransfer <= '1';
|
561 |
|
|
end if;
|
562 |
|
|
else
|
563 |
|
|
data_out <= data_in;
|
564 |
|
|
comm_out <= comm_in;
|
565 |
|
|
if full_in = '0' then
|
566 |
|
|
reading <= '1';
|
567 |
|
|
end if;
|
568 |
|
|
end if;
|
569 |
|
|
|
570 |
|
|
av_out_s <= '1';
|
571 |
|
|
|
572 |
|
|
|
573 |
|
|
elsif empty_in = '0' and can_write_r = '1' then
|
574 |
|
|
-- Just transfer the data.
|
575 |
|
|
data_out <= data_in;
|
576 |
|
|
av_out_s <= av_in;
|
577 |
|
|
comm_out <= comm_in;
|
578 |
|
|
|
579 |
|
|
if full_in = '0' then
|
580 |
|
|
writing <= '1';
|
581 |
|
|
lock_out <= '1';
|
582 |
|
|
reading <= '1';
|
583 |
|
|
end if;
|
584 |
|
|
|
585 |
|
|
else
|
586 |
|
|
-- Nothing to transfer.
|
587 |
|
|
writing <= '0';
|
588 |
|
|
data_out <= (others => '0');
|
589 |
|
|
lock_out <= '0';
|
590 |
|
|
comm_out <= (others => '0');
|
591 |
|
|
av_out_s <= '0';
|
592 |
|
|
reading <= '0';
|
593 |
|
|
|
594 |
|
|
|
595 |
|
|
end if;
|
596 |
|
|
end process sad_connect_output;
|
597 |
|
|
|
598 |
|
|
end generate sad_connect_output;
|
599 |
|
|
|
600 |
|
|
re_out <= reading;
|
601 |
|
|
|
602 |
|
|
-------------------------------------------------------------------------------
|
603 |
|
|
-- 6. ARBITRATION SCHEMES
|
604 |
|
|
-------------------------------------------------------------------------------
|
605 |
|
|
|
606 |
|
|
-- Process Count_Priorities:
|
607 |
|
|
-- Arbitration type depends port on arb_type_in:
|
608 |
|
|
-- round-robin ("00"), priority ("01") and
|
609 |
|
|
-- prior+round-robin ("10").
|
610 |
|
|
-- Dynamic arbitration ("11") is done in a separate entity.
|
611 |
|
|
Count_Priorities : process (clk, rst_n)
|
612 |
|
|
begin -- process Count_Priorities
|
613 |
|
|
if rst_n = '0' then -- asynchronous reset (active low)
|
614 |
|
|
prior_counter_arb_r <= (others => '0');
|
615 |
|
|
switch_arb_r <= 0;
|
616 |
|
|
arb_type_r <= (others => '0');
|
617 |
|
|
|
618 |
|
|
elsif clk'event and clk = '1' then -- rising clock edge
|
619 |
|
|
|
620 |
|
|
-- Assign internal arb_type-register according to ctrl-signal coming from
|
621 |
|
|
-- cfg_mem.
|
622 |
|
|
if arb_type_in = "00" then
|
623 |
|
|
-- round-robin
|
624 |
|
|
arb_type_r <= "00";
|
625 |
|
|
|
626 |
|
|
elsif arb_type_in = "01" then
|
627 |
|
|
-- priority, actually 01
|
628 |
|
|
arb_type_r <= "01";
|
629 |
|
|
|
630 |
|
|
elsif arb_type_in = "10" then
|
631 |
|
|
-- prior+roundrob, "10"
|
632 |
|
|
|
633 |
|
|
if switch_arb_r = switch_arb_c - 1 then
|
634 |
|
|
switch_arb_r <= 0;
|
635 |
|
|
arb_type_r <= "0" & not arb_type_r (0);
|
636 |
|
|
|
637 |
|
|
else
|
638 |
|
|
switch_arb_r <= switch_arb_r + 1;
|
639 |
|
|
arb_type_r <= arb_type_r;
|
640 |
|
|
end if;
|
641 |
|
|
|
642 |
|
|
else
|
643 |
|
|
arb_type_r <= "11";
|
644 |
|
|
end if; -- arb_type_in
|
645 |
|
|
|
646 |
|
|
|
647 |
|
|
-- Increase priority_counter when bus is idle
|
648 |
|
|
if lock_in = '0' and arb_type_r /= "11" then
|
649 |
|
|
|
650 |
|
|
if prior_counter_arb_r = n_agents_in (id_width_g-1 downto 0) then
|
651 |
|
|
-- Priorities rollover and start from 1 (zero is not allowed)
|
652 |
|
|
prior_counter_arb_r <= std_logic_vector(to_unsigned(1, id_width_g));
|
653 |
|
|
else
|
654 |
|
|
prior_counter_arb_r <= std_logic_vector(unsigned(prior_counter_arb_r) +1);
|
655 |
|
|
end if;
|
656 |
|
|
|
657 |
|
|
|
658 |
|
|
else
|
659 |
|
|
-- real arbitration types. now only prior + round rob
|
660 |
|
|
if arb_type_r = "00" then
|
661 |
|
|
-- In round-robin, priority remains the samem when
|
662 |
|
|
-- bus reserved,
|
663 |
|
|
prior_counter_arb_r <= prior_counter_arb_r;
|
664 |
|
|
|
665 |
|
|
elsif arb_type_r = "01" then
|
666 |
|
|
-- In priority, priority counter starts over from 1 when bus gets reserved
|
667 |
|
|
prior_counter_arb_r <= std_logic_vector(to_unsigned(1, id_width_g));
|
668 |
|
|
|
669 |
|
|
else
|
670 |
|
|
-- "lottery"
|
671 |
|
|
-- counter is assigned below in separate process (inside if-generate)
|
672 |
|
|
-- prior_counter_arb_r <= arb_agent_r;
|
673 |
|
|
end if;
|
674 |
|
|
|
675 |
|
|
end if; -- lock & arb_type
|
676 |
|
|
end if; --rst_n
|
677 |
|
|
|
678 |
|
|
end process Count_Priorities;
|
679 |
|
|
|
680 |
|
|
-- This component randomizes who gets the turn. Number of
|
681 |
|
|
-- lottery tickets changes dynamically so that active units get more.
|
682 |
|
|
-- This can be disabled to minimize area.
|
683 |
|
|
dyn : if dyn_arb_enable_c = 1 generate
|
684 |
|
|
dyn_arb_1 : dyn_arb
|
685 |
|
|
generic map (
|
686 |
|
|
id_width_g => id_width_g,
|
687 |
|
|
n_agents_g => n_agents_g
|
688 |
|
|
)
|
689 |
|
|
port map (
|
690 |
|
|
clk => clk,
|
691 |
|
|
rst_n => rst_n,
|
692 |
|
|
bus_lock_in => lock_in,
|
693 |
|
|
arb_agent_out => dyn_arb_prior
|
694 |
|
|
);
|
695 |
|
|
|
696 |
|
|
-- Select either counter value or one from dyn_arb
|
697 |
|
|
assign_priocount : process (dyn_arb_prior, arb_type_in, prior_counter_arb_r)
|
698 |
|
|
begin -- process assign priocount
|
699 |
|
|
if arb_type_in = "11" then
|
700 |
|
|
prior <= dyn_arb_prior;
|
701 |
|
|
else
|
702 |
|
|
prior <= prior_counter_arb_r;
|
703 |
|
|
end if;
|
704 |
|
|
end process assign_priocount;
|
705 |
|
|
end generate dyn;
|
706 |
|
|
|
707 |
|
|
-- Check if own priority matches (=own turn begins)
|
708 |
|
|
Check_Prior : process (prior_in, prior)
|
709 |
|
|
begin
|
710 |
|
|
if prior = prior_in (id_width_g-1 downto 0) then
|
711 |
|
|
prior_match <= '1';
|
712 |
|
|
else
|
713 |
|
|
prior_match <= '0';
|
714 |
|
|
end if;
|
715 |
|
|
end process Check_Prior;
|
716 |
|
|
|
717 |
|
|
|
718 |
|
|
notdyn : if dyn_arb_enable_c /= 1 generate
|
719 |
|
|
assert arb_type_in /= "11"
|
720 |
|
|
report "ERROR! ARB TYPE RANDOM BUT DYN ARB ENABLE = 0"
|
721 |
|
|
severity failure;
|
722 |
|
|
prior <= prior_counter_arb_r;
|
723 |
|
|
end generate notdyn;
|
724 |
|
|
|
725 |
|
|
|
726 |
|
|
end rtl;
|