1 |
15 |
wzab |
-------------------------------------------------------------------------------
|
2 |
|
|
-- Title : FPGA Ethernet interface - descriptor manager
|
3 |
|
|
-- Project :
|
4 |
|
|
-------------------------------------------------------------------------------
|
5 |
|
|
-- File : desc_manager.vhd
|
6 |
|
|
-- Author : Wojciech M. Zabolotny (wzab@ise.pw.edu.pl)
|
7 |
|
|
-- License : BSD License
|
8 |
|
|
-- Company :
|
9 |
|
|
-- Created : 2012-03-30
|
10 |
45 |
wzab |
-- Last update: 2017-01-23
|
11 |
15 |
wzab |
-- Platform :
|
12 |
|
|
-- Standard : VHDL'93
|
13 |
|
|
-------------------------------------------------------------------------------
|
14 |
|
|
-- Description: This file implements the state machine, which manages the
|
15 |
|
|
-- table of packet descriptors, used to resend only not confirmed packets
|
16 |
|
|
-------------------------------------------------------------------------------
|
17 |
|
|
-- Copyright (c) 2012
|
18 |
|
|
-------------------------------------------------------------------------------
|
19 |
|
|
-- Revisions :
|
20 |
|
|
-- Date Version Author Description
|
21 |
|
|
-- 2012-03-30 1.0 WZab Created
|
22 |
|
|
-------------------------------------------------------------------------------
|
23 |
|
|
|
24 |
|
|
|
25 |
|
|
library ieee;
|
26 |
|
|
use ieee.std_logic_1164.all;
|
27 |
|
|
use ieee.numeric_std.all;
|
28 |
|
|
use std.textio.all;
|
29 |
|
|
library work;
|
30 |
|
|
use work.pkt_ack_pkg.all;
|
31 |
|
|
use work.desc_mgr_pkg.all;
|
32 |
|
|
use work.pkt_desc_pkg.all;
|
33 |
|
|
|
34 |
|
|
entity desc_memory is
|
35 |
|
|
|
36 |
|
|
port (
|
37 |
|
|
clk : in std_logic;
|
38 |
|
|
desc_we : in std_logic;
|
39 |
|
|
desc_addr : in integer range 0 to N_OF_PKTS-1;
|
40 |
|
|
desc_out : in pkt_desc;
|
41 |
|
|
desc_in : out pkt_desc);
|
42 |
|
|
|
43 |
|
|
end desc_memory;
|
44 |
|
|
|
45 |
|
|
architecture beh1 of desc_memory is
|
46 |
|
|
|
47 |
|
|
type T_PKT_DESC_MEM is array (0 to N_OF_PKTS-1) of std_logic_vector(pkt_desc_width-1 downto 0);
|
48 |
|
|
signal desc_mem : T_PKT_DESC_MEM := (others => (others => '0'));
|
49 |
|
|
signal din : std_logic_vector(pkt_desc_width-1 downto 0) := (others => '0');
|
50 |
|
|
signal dout : std_logic_vector(pkt_desc_width-1 downto 0) := (others => '0');
|
51 |
|
|
signal rdaddr : integer range 0 to N_OF_PKTS-1;
|
52 |
|
|
|
53 |
43 |
wzab |
|
54 |
15 |
wzab |
begin -- beh1
|
55 |
|
|
|
56 |
|
|
din <= pkt_desc_to_stlv(desc_out);
|
57 |
|
|
desc_in <= stlv_to_pkt_desc(dout);
|
58 |
|
|
|
59 |
|
|
process (clk)
|
60 |
|
|
begin -- process
|
61 |
|
|
if (clk'event and clk = '1') then -- rising clock edge
|
62 |
|
|
if (desc_we = '1') then
|
63 |
|
|
desc_mem(desc_addr) <= din;
|
64 |
|
|
end if;
|
65 |
|
|
rdaddr <= desc_addr;
|
66 |
|
|
end if;
|
67 |
|
|
end process;
|
68 |
|
|
dout <= desc_mem(rdaddr);
|
69 |
|
|
|
70 |
|
|
end beh1;
|
71 |
|
|
|
72 |
|
|
library ieee;
|
73 |
|
|
use ieee.std_logic_1164.all;
|
74 |
|
|
use ieee.numeric_std.all;
|
75 |
|
|
use std.textio.all;
|
76 |
|
|
library work;
|
77 |
|
|
use work.pkt_ack_pkg.all;
|
78 |
|
|
use work.desc_mgr_pkg.all;
|
79 |
|
|
use work.pkt_desc_pkg.all;
|
80 |
|
|
|
81 |
|
|
entity desc_manager is
|
82 |
43 |
wzab |
|
83 |
15 |
wzab |
generic (
|
84 |
|
|
LOG2_N_OF_PKTS : integer := LOG2_N_OF_PKTS;
|
85 |
|
|
N_OF_PKTS : integer := N_OF_PKTS
|
86 |
|
|
); -- Number of packet_logi buffers
|
87 |
|
|
|
88 |
|
|
port (
|
89 |
|
|
-- Data input interface
|
90 |
|
|
dta : in std_logic_vector(63 downto 0);
|
91 |
|
|
dta_we : in std_logic;
|
92 |
18 |
wzab |
dta_eod : in std_logic;
|
93 |
15 |
wzab |
dta_ready : out std_logic;
|
94 |
|
|
-- ETH Sender interface
|
95 |
|
|
pkt_number : out unsigned(31 downto 0);
|
96 |
|
|
seq_number : out unsigned(15 downto 0);
|
97 |
|
|
cmd_response_out : out std_logic_vector(12*8-1 downto 0);
|
98 |
|
|
snd_cmd_start : out std_logic;
|
99 |
|
|
snd_start : out std_logic;
|
100 |
18 |
wzab |
flushed : out std_logic;
|
101 |
15 |
wzab |
snd_ready : in std_logic;
|
102 |
|
|
|
103 |
|
|
-- Data memory interface
|
104 |
|
|
dmem_addr : out std_logic_vector(LOG2_NWRDS_IN_PKT+LOG2_N_OF_PKTS-1 downto 0);
|
105 |
|
|
dmem_dta : out std_logic_vector(63 downto 0);
|
106 |
|
|
dmem_we : out std_logic;
|
107 |
|
|
-- Interface to the ACK FIFO
|
108 |
|
|
ack_fifo_empty : in std_logic;
|
109 |
|
|
ack_fifo_rd_en : out std_logic;
|
110 |
|
|
ack_fifo_dout : in std_logic_vector(pkt_ack_width-1 downto 0);
|
111 |
|
|
-- User command interface
|
112 |
|
|
cmd_code : out std_logic_vector(15 downto 0);
|
113 |
|
|
cmd_seq : out std_logic_vector(15 downto 0);
|
114 |
|
|
cmd_arg : out std_logic_vector(31 downto 0);
|
115 |
|
|
cmd_run : out std_logic;
|
116 |
18 |
wzab |
cmd_retr_s : out std_logic;
|
117 |
15 |
wzab |
cmd_ack : in std_logic;
|
118 |
|
|
cmd_response_in : in std_logic_vector(8*12-1 downto 0);
|
119 |
26 |
wzab |
retr_count : out std_logic_vector(31 downto 0);
|
120 |
15 |
wzab |
--
|
121 |
|
|
transmit_data : in std_logic;
|
122 |
|
|
transm_delay : out unsigned(31 downto 0);
|
123 |
|
|
--
|
124 |
|
|
dbg : out std_logic_vector(3 downto 0);
|
125 |
|
|
--
|
126 |
|
|
clk : in std_logic;
|
127 |
|
|
rst_n : in std_logic);
|
128 |
|
|
|
129 |
|
|
end desc_manager;
|
130 |
|
|
|
131 |
|
|
architecture dmgr_a1 of desc_manager is
|
132 |
|
|
|
133 |
|
|
constant PKT_CNT_MAX : integer := 3000;
|
134 |
|
|
|
135 |
|
|
|
136 |
|
|
function is_bigger (
|
137 |
|
|
constant v1, v2 : unsigned(15 downto 0))
|
138 |
|
|
return boolean is
|
139 |
|
|
variable res : boolean;
|
140 |
|
|
variable tmp : unsigned(15 downto 0);
|
141 |
|
|
begin -- function is_bigger
|
142 |
|
|
-- subtract v2-v1 modulo 2**16
|
143 |
|
|
tmp := v2-v1;
|
144 |
|
|
-- if the result is "negative" - bit 15 is '1'
|
145 |
|
|
-- and we consider v1 to be "bigger" (in modulo sense) than v2
|
146 |
|
|
if tmp(15) = '1' then
|
147 |
|
|
return true;
|
148 |
|
|
else
|
149 |
|
|
return false;
|
150 |
|
|
end if;
|
151 |
43 |
wzab |
|
152 |
15 |
wzab |
end function is_bigger;
|
153 |
|
|
|
154 |
|
|
-- To simplify description of state machines, all registers are grouped
|
155 |
|
|
-- in a record :
|
156 |
|
|
|
157 |
|
|
type T_DESC_MGR_REGS is record
|
158 |
|
|
cmd_ack : std_logic;
|
159 |
|
|
cmd_ack_0 : std_logic;
|
160 |
|
|
cmd_run : std_logic;
|
161 |
|
|
cmd_retr : std_logic;
|
162 |
|
|
cmd_code : unsigned(15 downto 0);
|
163 |
|
|
cmd_seq : unsigned(15 downto 0);
|
164 |
|
|
cmd_arg : unsigned(31 downto 0);
|
165 |
|
|
pkt : unsigned(31 downto 0);
|
166 |
|
|
cur_pkt : unsigned(31 downto 0);
|
167 |
|
|
seq : unsigned(15 downto 0);
|
168 |
|
|
ack_seq : unsigned(15 downto 0);
|
169 |
|
|
retr_flag : std_logic;
|
170 |
18 |
wzab |
flushed : std_logic;
|
171 |
15 |
wzab |
all_pkt_count : integer range 0 to PKT_CNT_MAX;
|
172 |
|
|
retr_pkt_count : integer range 0 to PKT_CNT_MAX;
|
173 |
|
|
retr_delay : unsigned(31 downto 0);
|
174 |
26 |
wzab |
retr_count : unsigned(31 downto 0);
|
175 |
15 |
wzab |
transm_delay : unsigned(31 downto 0);
|
176 |
|
|
nxt : unsigned(LOG2_N_OF_PKTS-1 downto 0);
|
177 |
|
|
tail_ptr : unsigned(LOG2_N_OF_PKTS-1 downto 0);
|
178 |
|
|
head_ptr : unsigned(LOG2_N_OF_PKTS-1 downto 0);
|
179 |
|
|
retr_ptr : unsigned(LOG2_N_OF_PKTS-1 downto 0); -- Number of the packet buffer, which is retransmitted
|
180 |
|
|
-- when equal to head_ptr -
|
181 |
|
|
-- retransmission is finished
|
182 |
|
|
retr_nxt : unsigned(LOG2_N_OF_PKTS-1 downto 0); -- buffer, which will be
|
183 |
|
|
-- retransmitted next
|
184 |
|
|
-- when equal to head_ptr -- no retransmission
|
185 |
|
|
-- is performed
|
186 |
|
|
end record;
|
187 |
|
|
|
188 |
|
|
constant DESC_MGR_REGS_INI : T_DESC_MGR_REGS := (
|
189 |
|
|
retr_delay => (others => '0'),
|
190 |
26 |
wzab |
retr_count => (others => '0'),
|
191 |
|
|
transm_delay => to_unsigned(16, 32),
|
192 |
15 |
wzab |
all_pkt_count => 0,
|
193 |
|
|
retr_pkt_count => 0,
|
194 |
|
|
cmd_ack_0 => '0',
|
195 |
|
|
cmd_ack => '0',
|
196 |
|
|
cmd_run => '0',
|
197 |
|
|
cmd_retr => '0',
|
198 |
|
|
cmd_code => (others => '0'),
|
199 |
|
|
cmd_seq => (others => '0'),
|
200 |
|
|
cmd_arg => (others => '0'),
|
201 |
|
|
pkt => (others => '0'),
|
202 |
|
|
seq => (others => '0'),
|
203 |
|
|
ack_seq => (others => '0'),
|
204 |
|
|
retr_flag => '0',
|
205 |
18 |
wzab |
flushed => '0',
|
206 |
15 |
wzab |
cur_pkt => (others => '0'),
|
207 |
|
|
nxt => (others => '0'),
|
208 |
|
|
tail_ptr => (others => '0'),
|
209 |
|
|
head_ptr => (others => '0'),
|
210 |
|
|
retr_ptr => (others => '0'),
|
211 |
|
|
retr_nxt => (others => '0')
|
212 |
|
|
);
|
213 |
|
|
|
214 |
|
|
-- To simplify setting of outputs of my Mealy state machine, all combinatorial
|
215 |
|
|
-- outputs are grouped in a record
|
216 |
|
|
type T_DESC_MGR_COMB is record
|
217 |
|
|
dta_buf_free : std_logic;
|
218 |
|
|
desc_addr : unsigned(LOG2_N_OF_PKTS-1 downto 0);
|
219 |
|
|
desc_we : std_logic;
|
220 |
|
|
ack_rd : std_logic;
|
221 |
|
|
snd_start : std_logic;
|
222 |
|
|
snd_cmd_start : std_logic;
|
223 |
|
|
desc_out : pkt_desc;
|
224 |
|
|
end record;
|
225 |
|
|
|
226 |
|
|
constant DESC_MGR_COMB_DEFAULT : T_DESC_MGR_COMB :=
|
227 |
|
|
(
|
228 |
|
|
dta_buf_free => '0',
|
229 |
|
|
desc_addr => (others => '0'),
|
230 |
|
|
desc_we => '0',
|
231 |
|
|
ack_rd => '0',
|
232 |
|
|
snd_start => '0',
|
233 |
|
|
snd_cmd_start => '0',
|
234 |
|
|
desc_out => (
|
235 |
|
|
confirmed => '0',
|
236 |
|
|
valid => '0',
|
237 |
|
|
sent => '0',
|
238 |
18 |
wzab |
flushed => '0',
|
239 |
15 |
wzab |
pkt => (others => '0'),
|
240 |
|
|
seq => (others => '0')
|
241 |
|
|
)
|
242 |
|
|
);
|
243 |
|
|
|
244 |
|
|
type T_DESC_MGR_STATE is (ST_DMGR_IDLE, ST_DMGR_CMD, ST_DMGR_START, ST_DMGR_RST, ST_DMGR_RST1,
|
245 |
|
|
ST_DMGR_ACK1, ST_DMGR_INS1, ST_DMGR_INS2, ST_DMGR_ACK_TAIL,
|
246 |
|
|
ST_DMGR_ACK_TAIL_1,
|
247 |
|
|
ST_DMGR_RETR, ST_DMGR_RETR_2);
|
248 |
|
|
|
249 |
|
|
signal desc_in : pkt_desc;
|
250 |
|
|
|
251 |
|
|
signal r, r_i : T_DESC_MGR_REGS := DESC_MGR_REGS_INI;
|
252 |
|
|
signal c : T_DESC_MGR_COMB;
|
253 |
|
|
signal dmgr_state, dmgr_state_next : T_DESC_MGR_STATE := ST_DMGR_RST;
|
254 |
|
|
attribute keep : string;
|
255 |
|
|
attribute keep of dmgr_state : signal is "true";
|
256 |
|
|
|
257 |
20 |
wzab |
signal dta_buf_full : std_logic := '0';
|
258 |
|
|
signal dta_buf_flush : std_logic := '0';
|
259 |
|
|
signal stored_dta_eod : std_logic := '0';
|
260 |
15 |
wzab |
|
261 |
|
|
signal ack_pkt_in : pkt_ack;
|
262 |
|
|
|
263 |
|
|
signal wrd_addr : integer range 0 to NWRDS_IN_PKT-1; -- We use 64-bit words, so the
|
264 |
|
|
-- data word address is between
|
265 |
|
|
-- 0 and 1023
|
266 |
|
|
|
267 |
|
|
component desc_memory
|
268 |
|
|
port (
|
269 |
|
|
clk : in std_logic;
|
270 |
|
|
desc_we : in std_logic;
|
271 |
|
|
desc_addr : in integer range 0 to N_OF_PKTS-1;
|
272 |
|
|
desc_out : in pkt_desc;
|
273 |
|
|
desc_in : out pkt_desc);
|
274 |
|
|
end component;
|
275 |
|
|
|
276 |
|
|
|
277 |
|
|
begin -- dmgr_a1
|
278 |
|
|
|
279 |
26 |
wzab |
retr_count <= std_logic_vector(r.retr_count);
|
280 |
|
|
|
281 |
15 |
wzab |
transm_delay <= r.transm_delay;
|
282 |
|
|
pkt_number <= r.pkt;
|
283 |
|
|
seq_number <= r.seq;
|
284 |
18 |
wzab |
flushed <= r.flushed;
|
285 |
15 |
wzab |
dta_ready <= not dta_buf_full;
|
286 |
|
|
snd_start <= c.snd_start;
|
287 |
|
|
ack_fifo_rd_en <= c.ack_rd;
|
288 |
|
|
|
289 |
18 |
wzab |
cmd_code <= std_logic_vector(r.cmd_code);
|
290 |
|
|
cmd_seq <= std_logic_vector(r.cmd_seq);
|
291 |
|
|
cmd_arg <= std_logic_vector(r.cmd_arg);
|
292 |
|
|
cmd_run <= r.cmd_run;
|
293 |
|
|
cmd_retr_s <= r.cmd_retr;
|
294 |
|
|
snd_cmd_start <= c.snd_cmd_start;
|
295 |
|
|
|
296 |
15 |
wzab |
ack_pkt_in <= stlv_to_pkt_ack(ack_fifo_dout);
|
297 |
|
|
|
298 |
|
|
-- Transmit command response only when the command is completed
|
299 |
18 |
wzab |
-- (to avoid transmiting unstable values, which could e.g. affect
|
300 |
15 |
wzab |
-- packet CRC calculations)
|
301 |
|
|
cmd_response_out <= cmd_response_in when r.cmd_ack = r.cmd_run else (others => '0');
|
302 |
|
|
|
303 |
|
|
-- Packet descriptors are stored in the desc_memory
|
304 |
|
|
|
305 |
|
|
desc_memory_1 : desc_memory
|
306 |
|
|
port map (
|
307 |
|
|
clk => clk,
|
308 |
|
|
desc_we => c.desc_we,
|
309 |
|
|
desc_addr => to_integer(c.desc_addr),
|
310 |
|
|
desc_out => c.desc_out,
|
311 |
|
|
desc_in => desc_in);
|
312 |
|
|
|
313 |
|
|
-- Process used to fill the buffer memory with the data to be transmitted
|
314 |
|
|
-- We simply write words to the memory buffer pointed by r.head_ptr
|
315 |
|
|
-- When we write the last (0xff-th) word, we signal that the buffer
|
316 |
18 |
wzab |
-- is full.
|
317 |
|
|
-- Additionally, when the buffer is partially filled, but the transmission
|
318 |
|
|
-- is stopped, we should also signal, that the buffer must be transmitted.
|
319 |
|
|
-- However in this case we should also inform the recipient about it.
|
320 |
|
|
-- How we can do it?
|
321 |
15 |
wzab |
dta_rcv : process (clk, rst_n)
|
322 |
|
|
begin -- process dta_rcv
|
323 |
|
|
if rst_n = '0' then -- asynchronous reset (active low)
|
324 |
20 |
wzab |
wrd_addr <= 0;
|
325 |
|
|
dta_buf_flush <= '0';
|
326 |
|
|
dta_buf_full <= '0';
|
327 |
|
|
dmem_we <= '0';
|
328 |
|
|
stored_dta_eod <= '0';
|
329 |
15 |
wzab |
elsif clk'event and clk = '1' then -- rising clock edge
|
330 |
|
|
dmem_we <= '0';
|
331 |
|
|
-- if we signalled "data full", we are only waiting for
|
332 |
|
|
-- dta_buf_free;
|
333 |
20 |
wzab |
-- However even in this state we must receive the "dta_eod" signal
|
334 |
15 |
wzab |
if dta_buf_full = '1' then
|
335 |
20 |
wzab |
if dta_eod = '1' then
|
336 |
|
|
stored_dta_eod <= '1';
|
337 |
|
|
end if;
|
338 |
15 |
wzab |
if c.dta_buf_free = '1' then
|
339 |
18 |
wzab |
dta_buf_full <= '0';
|
340 |
|
|
dta_buf_flush <= '0';
|
341 |
|
|
wrd_addr <= 0;
|
342 |
15 |
wzab |
end if;
|
343 |
|
|
else
|
344 |
18 |
wzab |
-- end of data is signalled, mark the last buffer as full
|
345 |
20 |
wzab |
if (dta_eod = '1') or (stored_dta_eod = '1') then
|
346 |
|
|
-- Clear the stored eod
|
347 |
|
|
stored_dta_eod <= '0';
|
348 |
18 |
wzab |
-- In the last word of the packet, write the number of written words
|
349 |
20 |
wzab |
dmem_addr <= std_logic_vector(r.head_ptr) &
|
350 |
43 |
wzab |
std_logic_vector(to_unsigned(NWRDS_IN_PKT-1, LOG2_NWRDS_IN_PKT));
|
351 |
18 |
wzab |
dmem_dta <= std_logic_vector(to_unsigned(wrd_addr, 64));
|
352 |
|
|
dmem_we <= '1';
|
353 |
|
|
dta_buf_flush <= '1';
|
354 |
|
|
dta_buf_full <= '1';
|
355 |
15 |
wzab |
-- if data write requested - write it
|
356 |
18 |
wzab |
elsif dta_we = '1' then
|
357 |
15 |
wzab |
dmem_addr <= std_logic_vector(r.head_ptr) &
|
358 |
|
|
std_logic_vector(to_unsigned(wrd_addr, LOG2_NWRDS_IN_PKT));
|
359 |
|
|
dmem_we <= '1';
|
360 |
|
|
dmem_dta <= dta;
|
361 |
|
|
if wrd_addr < NWRDS_IN_PKT-1 then
|
362 |
|
|
wrd_addr <= wrd_addr + 1;
|
363 |
|
|
else
|
364 |
18 |
wzab |
dta_buf_flush <= '0';
|
365 |
|
|
dta_buf_full <= '1';
|
366 |
15 |
wzab |
end if;
|
367 |
|
|
end if;
|
368 |
|
|
end if;
|
369 |
|
|
end if;
|
370 |
|
|
end process dta_rcv;
|
371 |
|
|
|
372 |
|
|
|
373 |
|
|
c1 : process (ack_fifo_empty, ack_pkt_in, cmd_ack, desc_in,
|
374 |
18 |
wzab |
dmgr_state, dta_buf_full, dta_buf_flush, r, snd_ready)
|
375 |
15 |
wzab |
begin -- process c1
|
376 |
|
|
c <= DESC_MGR_COMB_DEFAULT; -- set defaults
|
377 |
|
|
r_i <= r; -- avoid latches
|
378 |
|
|
-- Synchronize command acknowledge lines
|
379 |
|
|
r_i.cmd_ack_0 <= cmd_ack;
|
380 |
|
|
r_i.cmd_ack <= r.cmd_ack_0;
|
381 |
|
|
if r.retr_delay /= to_unsigned(0, r.retr_delay'length) then
|
382 |
|
|
r_i.retr_delay <= r.retr_delay-1;
|
383 |
|
|
end if;
|
384 |
45 |
wzab |
dbg <= x"0"; -- default to avoid latch
|
385 |
15 |
wzab |
dmgr_state_next <= dmgr_state;
|
386 |
|
|
-- State machine
|
387 |
|
|
case dmgr_state is
|
388 |
|
|
when ST_DMGR_RST =>
|
389 |
|
|
dbg <= x"1";
|
390 |
|
|
dmgr_state_next <= ST_DMGR_RST1;
|
391 |
|
|
when ST_DMGR_RST1 =>
|
392 |
|
|
-- We should initialize the 0th position of list descriptors
|
393 |
|
|
dbg <= x"2";
|
394 |
|
|
c.desc_addr <= r.head_ptr;
|
395 |
|
|
c.desc_out <= desc_in;
|
396 |
|
|
c.desc_out.confirmed <= '0';
|
397 |
|
|
c.desc_out.valid <= '0';
|
398 |
|
|
c.desc_out.sent <= '0';
|
399 |
|
|
c.desc_out.pkt <= to_unsigned(0, 32);
|
400 |
|
|
c.desc_we <= '1';
|
401 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
402 |
|
|
when ST_DMGR_IDLE =>
|
403 |
|
|
dbg <= x"3";
|
404 |
|
|
-- First we check, if there are any packets to acknowledge
|
405 |
|
|
-- or commands to execute
|
406 |
|
|
if ack_fifo_empty = '0' then
|
407 |
18 |
wzab |
if (to_integer(ack_pkt_in.cmd) = FCMD_ACK) or
|
408 |
|
|
(to_integer(ack_pkt_in.cmd) = FCMD_NACK) then
|
409 |
|
|
-- Prepare for reading of the command.
|
410 |
|
|
c.desc_addr <= ack_pkt_in.pkt(LOG2_N_OF_PKTS-1 downto 0);
|
411 |
|
|
dmgr_state_next <= ST_DMGR_ACK1;
|
412 |
|
|
else
|
413 |
|
|
-- This is a command which requires sending of response.
|
414 |
|
|
-- This will be handled by the cmd_proc block (in case
|
415 |
|
|
-- of START and STOP it is not the most efficient way,
|
416 |
|
|
-- but still sufficient).
|
417 |
15 |
wzab |
-- Always request transmission of result
|
418 |
|
|
r_i.cmd_retr <= '1';
|
419 |
|
|
-- Check if this is a new command (just checking the sequence number,
|
420 |
|
|
-- to avoid more complex logic)
|
421 |
|
|
if ack_pkt_in.seq /= r.cmd_seq then
|
422 |
|
|
-- If no, store the command and it's argument, and order it to be executed
|
423 |
|
|
r_i.cmd_code <= ack_pkt_in.cmd;
|
424 |
|
|
r_i.cmd_seq <= ack_pkt_in.seq;
|
425 |
|
|
r_i.cmd_arg <= ack_pkt_in.pkt;
|
426 |
|
|
end if;
|
427 |
|
|
c.ack_rd <= '1'; -- Confirm, that the command was read
|
428 |
|
|
dmgr_state_next <= ST_DMGR_CMD;
|
429 |
|
|
end if;
|
430 |
|
|
elsif dta_buf_full = '1' then
|
431 |
|
|
-- We should handle reception of data.
|
432 |
|
|
-- If the previously filled buffer is full, pass it for transmission,
|
433 |
|
|
-- and allocate the next one.
|
434 |
|
|
--
|
435 |
|
|
-- Calculate the number of the packet, which shoud be the next "head"
|
436 |
|
|
-- packet. We utilize the fact, that calculations are performed modulo
|
437 |
|
|
-- N_OF_PKTS (because pointers have length of LOG2_N_OF_PKTS)
|
438 |
|
|
r_i.nxt <= r.head_ptr + 1;
|
439 |
|
|
-- Prepare for reading of the current "head" descriptor
|
440 |
|
|
c.desc_addr <= r.head_ptr;
|
441 |
|
|
dmgr_state_next <= ST_DMGR_INS1;
|
442 |
|
|
elsif (r.tail_ptr /= r.head_ptr) and (r.retr_delay = to_unsigned(0, r.retr_delay'length)) then
|
443 |
|
|
-- We need to (re)transmit some buffers
|
444 |
|
|
-- prepare reading of the descriptor, which should be transmitted
|
445 |
|
|
c.desc_addr <= r.retr_nxt;
|
446 |
|
|
dmgr_state_next <= ST_DMGR_RETR;
|
447 |
|
|
elsif r.cmd_retr = '1' and (r.cmd_ack = r.cmd_run) and (r.retr_delay = to_unsigned(0, r.retr_delay'length)) then
|
448 |
|
|
-- No data waiting for transmission, and the command response should
|
449 |
|
|
-- be transmitted
|
450 |
|
|
if snd_ready = '1' then
|
451 |
|
|
r_i.retr_delay <= r.transm_delay;
|
452 |
|
|
r_i.cmd_retr <= '0';
|
453 |
|
|
c.snd_cmd_start <= '1';
|
454 |
|
|
end if;
|
455 |
|
|
end if;
|
456 |
|
|
when ST_DMGR_CMD =>
|
457 |
|
|
r_i.cmd_run <= not r.cmd_run;
|
458 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
459 |
|
|
when ST_DMGR_INS1 =>
|
460 |
|
|
dbg <= x"4";
|
461 |
|
|
-- First we check, if there is free space, r.nxt is the number of the
|
462 |
|
|
-- future head packet.
|
463 |
|
|
if (r.nxt = r.tail_ptr) then
|
464 |
|
|
-- No free place! The packet, which we would like to fill is still
|
465 |
|
|
-- occupied.
|
466 |
|
|
-- Return to idle, waiting until something is freed.
|
467 |
|
|
-- In this case we should also force retransmission
|
468 |
|
|
if r.retr_delay = 0 then
|
469 |
|
|
c.desc_addr <= r.retr_nxt;
|
470 |
|
|
dmgr_state_next <= ST_DMGR_RETR;
|
471 |
|
|
else
|
472 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
473 |
|
|
end if;
|
474 |
|
|
else
|
475 |
|
|
-- We can fill the next buffer
|
476 |
|
|
-- First we mark the previous head packet
|
477 |
|
|
-- as valid and not confirmed
|
478 |
18 |
wzab |
-- We also set the "flushed" status appropriately
|
479 |
15 |
wzab |
c.desc_addr <= r.head_ptr;
|
480 |
|
|
c.desc_out <= desc_in;
|
481 |
|
|
c.desc_out.confirmed <= '0';
|
482 |
|
|
c.desc_out.valid <= '1';
|
483 |
18 |
wzab |
if dta_buf_flush = '1' then
|
484 |
|
|
c.desc_out.flushed <= '1';
|
485 |
|
|
else
|
486 |
|
|
c.desc_out.flushed <= '0';
|
487 |
|
|
end if;
|
488 |
|
|
c.desc_we <= '1';
|
489 |
15 |
wzab |
-- Now we move the "head" pointer
|
490 |
18 |
wzab |
r_i.head_ptr <= r.nxt;
|
491 |
15 |
wzab |
-- Increase the packet number!
|
492 |
|
|
-- We utilize the fact, that packet number automatically
|
493 |
|
|
-- wraps to 0 after sending of 2**32 packets!
|
494 |
18 |
wzab |
r_i.cur_pkt <= r.cur_pkt + 1;
|
495 |
|
|
dmgr_state_next <= ST_DMGR_INS2;
|
496 |
15 |
wzab |
end if;
|
497 |
|
|
when ST_DMGR_INS2 =>
|
498 |
|
|
dbg <= x"5";
|
499 |
|
|
-- We fill the new head descriptor
|
500 |
|
|
c.desc_addr <= r.head_ptr;
|
501 |
|
|
c.desc_out.pkt <= r.cur_pkt;
|
502 |
|
|
c.desc_out.confirmed <= '0';
|
503 |
|
|
c.desc_out.valid <= '0';
|
504 |
|
|
c.desc_out.sent <= '0';
|
505 |
18 |
wzab |
c.desc_out.flushed <= '0';
|
506 |
15 |
wzab |
c.desc_we <= '1';
|
507 |
|
|
-- Signal, that the buffer is freed
|
508 |
|
|
c.dta_buf_free <= '1';
|
509 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
510 |
|
|
when ST_DMGR_ACK1 =>
|
511 |
|
|
dbg <= x"6";
|
512 |
|
|
-- In this state the desc memory should respond with the data of the
|
513 |
|
|
-- buffered packet, so we can state, if this packet is really correctly
|
514 |
|
|
-- acknowledged (here we also ignore the NACK packets!
|
515 |
18 |
wzab |
case to_integer(ack_pkt_in.cmd) is
|
516 |
|
|
when FCMD_ACK =>
|
517 |
|
|
if (ack_pkt_in.pkt = desc_in.pkt) and
|
518 |
|
|
(desc_in.valid = '1') then
|
519 |
|
|
-- This is really correct, unconfirmed packet
|
520 |
|
|
-- Increase the counter of not-repeated ACK packets
|
521 |
|
|
-- Write the confirmation
|
522 |
|
|
c.desc_addr <= ack_pkt_in.pkt(LOG2_N_OF_PKTS-1 downto 0);
|
523 |
|
|
c.desc_out <= desc_in;
|
524 |
|
|
c.desc_out.valid <= '0';
|
525 |
|
|
c.desc_out.confirmed <= '1';
|
526 |
|
|
c.desc_we <= '1';
|
527 |
|
|
-- Here we also handle the case, if the acknowledged packet was
|
528 |
|
|
-- the one which is now scheduled for retransmission...
|
529 |
|
|
if ack_pkt_in.pkt(LOG2_N_OF_PKTS-1 downto 0) = r.retr_nxt then
|
530 |
|
|
r_i.retr_nxt <= r.retr_nxt + 1;
|
531 |
|
|
end if;
|
532 |
|
|
-- Check, if we need to update the "tail" pointer
|
533 |
|
|
if r.tail_ptr = ack_pkt_in.pkt(LOG2_N_OF_PKTS-1 downto 0) then
|
534 |
|
|
c.ack_rd <= '1';
|
535 |
|
|
dmgr_state_next <= ST_DMGR_ACK_TAIL;
|
536 |
|
|
else
|
537 |
|
|
-- If this is not the tail pointer, it means, that some packets
|
538 |
|
|
-- or acknowledgements have been lost
|
539 |
|
|
-- We trigger retransmission of those packets
|
540 |
|
|
r_i.ack_seq <= ack_pkt_in.seq;
|
541 |
|
|
r_i.retr_nxt <= r.tail_ptr;
|
542 |
|
|
-- Set the flag stating that only "earlier" packets should be retransmitted
|
543 |
|
|
r_i.retr_flag <= '1';
|
544 |
|
|
c.ack_rd <= '1';
|
545 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
546 |
|
|
end if;
|
547 |
|
|
else
|
548 |
|
|
-- This packet was already confirmed
|
549 |
|
|
-- just flush the ack_fifo
|
550 |
|
|
c.ack_rd <= '1';
|
551 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
552 |
|
|
end if;
|
553 |
43 |
wzab |
when FCMD_NACK =>
|
554 |
18 |
wzab |
-- This was a NACK command, currently we simply ignore it
|
555 |
|
|
-- (later on we will use it to trigger retransmission).
|
556 |
15 |
wzab |
c.ack_rd <= '1';
|
557 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
558 |
18 |
wzab |
when others => null;
|
559 |
|
|
end case;
|
560 |
15 |
wzab |
when ST_DMGR_ACK_TAIL =>
|
561 |
|
|
dbg <= x"7";
|
562 |
|
|
c.desc_addr <= r.tail_ptr;
|
563 |
|
|
dmgr_state_next <= ST_DMGR_ACK_TAIL_1;
|
564 |
|
|
when ST_DMGR_ACK_TAIL_1 =>
|
565 |
|
|
dbg <= x"8";
|
566 |
|
|
-- In this state we update the "tail" pointer if necessary
|
567 |
|
|
if r.tail_ptr /= r.head_ptr then
|
568 |
|
|
if desc_in.confirmed = '1' then
|
569 |
|
|
r_i.tail_ptr <= r.tail_ptr + 1; -- it will wrap to 0 automatically!
|
570 |
|
|
c.desc_addr <= r.tail_ptr + 1;
|
571 |
|
|
-- We remain in that state, to check the next packet descriptor
|
572 |
|
|
else
|
573 |
|
|
-- We return to idle
|
574 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
575 |
|
|
end if;
|
576 |
|
|
else
|
577 |
|
|
-- Buffer is empty - return to idle
|
578 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
579 |
|
|
end if;
|
580 |
|
|
when ST_DMGR_RETR =>
|
581 |
|
|
dbg <= x"9";
|
582 |
|
|
-- Here we handle the transmission of a new packet,
|
583 |
|
|
-- retransmission of not confirmed packet
|
584 |
|
|
-- We must be sure, that the transmitter is ready
|
585 |
|
|
if snd_ready = '0' then
|
586 |
|
|
-- transmitter not ready, return to idle
|
587 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
588 |
|
|
else
|
589 |
|
|
-- We will be able to send the next packet, but let's check if
|
590 |
|
|
-- this is not the currently filled packet
|
591 |
|
|
if r.retr_nxt = r.head_ptr then
|
592 |
|
|
-- All packets (re)transmitted, go to the begining of the list
|
593 |
|
|
r_i.retr_nxt <= r.tail_ptr;
|
594 |
|
|
-- Clear the flag stating that only packets older than the last
|
595 |
|
|
-- acknowledged should be transmitted
|
596 |
|
|
r_i.retr_flag <= '0';
|
597 |
|
|
-- and return to idle.
|
598 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
599 |
|
|
else
|
600 |
|
|
-- before jumping to ST_DMGR_RETR, the address bus
|
601 |
|
|
-- was set to the address of r.retr_nxt, so now
|
602 |
|
|
-- we can read the descriptor, and check if the packet
|
603 |
|
|
-- needs to be retransmitted at all...
|
604 |
|
|
r_i.pkt <= desc_in.pkt;
|
605 |
18 |
wzab |
r_i.flushed <= desc_in.flushed;
|
606 |
15 |
wzab |
r_i.retr_ptr <= r.retr_nxt;
|
607 |
|
|
r_i.retr_nxt <= r.retr_nxt + 1;
|
608 |
|
|
if desc_in.valid = '1' and desc_in.confirmed = '0' and
|
609 |
|
|
((r.retr_flag = '0') or is_bigger(r.ack_seq, desc_in.seq)) then
|
610 |
|
|
if desc_in.sent = '1' then
|
611 |
|
|
-- Increase count of retransmitted packets for
|
612 |
|
|
-- adaptive adjustment of delay
|
613 |
|
|
if r.retr_pkt_count < PKT_CNT_MAX then
|
614 |
|
|
r_i.retr_pkt_count <= r.retr_pkt_count + 1;
|
615 |
|
|
end if;
|
616 |
26 |
wzab |
-- Adjust the cumulative retransmission counter
|
617 |
|
|
r_i.retr_count <= r.retr_count + 1;
|
618 |
15 |
wzab |
end if;
|
619 |
|
|
-- Increase count of all packets for adaptive adjustment
|
620 |
|
|
-- of delay
|
621 |
|
|
if r.all_pkt_count < PKT_CNT_MAX then
|
622 |
|
|
r_i.all_pkt_count <= r.all_pkt_count + 1;
|
623 |
|
|
end if;
|
624 |
|
|
-- Mark the packet as sent
|
625 |
|
|
c.desc_addr <= r.retr_nxt;
|
626 |
|
|
c.desc_out <= desc_in;
|
627 |
|
|
c.desc_out.sent <= '1';
|
628 |
|
|
-- increase the sequential number
|
629 |
|
|
r_i.seq <= r.seq + 1;
|
630 |
|
|
-- store the packet sequential number
|
631 |
|
|
c.desc_out.seq <= r.seq + 1;
|
632 |
|
|
c.desc_we <= '1';
|
633 |
|
|
dmgr_state_next <= ST_DMGR_RETR_2;
|
634 |
|
|
else
|
635 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
636 |
|
|
end if;
|
637 |
|
|
end if;
|
638 |
|
|
end if;
|
639 |
|
|
when ST_DMGR_RETR_2 =>
|
640 |
|
|
dbg <= x"a";
|
641 |
|
|
-- In this state, we simply trigger the sender!
|
642 |
|
|
c.snd_start <= '1';
|
643 |
|
|
if r.cmd_ack = r.cmd_run then
|
644 |
|
|
-- command response will be transmitted, so clear the related flag
|
645 |
|
|
r_i.cmd_retr <= '0';
|
646 |
|
|
end if;
|
647 |
|
|
r_i.retr_delay <= r.transm_delay;
|
648 |
|
|
-- And we update the delay using the packet statistics
|
649 |
|
|
-- You may change the constants used in expressions
|
650 |
|
|
-- below to change speed of adjustment
|
651 |
|
|
if r.all_pkt_count >= PKT_CNT_MAX then
|
652 |
26 |
wzab |
if r.retr_pkt_count < PKT_CNT_MAX/300 then
|
653 |
15 |
wzab |
if r.transm_delay > 16 then
|
654 |
|
|
r_i.transm_delay <= r.transm_delay-r.transm_delay/16;
|
655 |
|
|
end if;
|
656 |
26 |
wzab |
elsif r.retr_pkt_count > PKT_CNT_MAX/100 then
|
657 |
15 |
wzab |
if r.transm_delay < 1000000 then
|
658 |
|
|
r_i.transm_delay <= r.transm_delay+r.transm_delay/4;
|
659 |
|
|
end if;
|
660 |
|
|
end if;
|
661 |
|
|
r_i.all_pkt_count <= 0;
|
662 |
|
|
r_i.retr_pkt_count <= 0;
|
663 |
|
|
end if;
|
664 |
|
|
dmgr_state_next <= ST_DMGR_IDLE;
|
665 |
|
|
when others => null;
|
666 |
|
|
end case;
|
667 |
|
|
end process c1;
|
668 |
|
|
|
669 |
|
|
-- Synchronous process
|
670 |
|
|
process (clk, rst_n)
|
671 |
|
|
begin -- process
|
672 |
|
|
if rst_n = '0' then -- asynchronous reset (active low)
|
673 |
|
|
r <= DESC_MGR_REGS_INI;
|
674 |
|
|
dmgr_state <= ST_DMGR_RST;
|
675 |
|
|
elsif clk'event and clk = '1' then -- rising clock edge
|
676 |
|
|
r <= r_i;
|
677 |
|
|
dmgr_state <= dmgr_state_next;
|
678 |
|
|
end if;
|
679 |
|
|
end process;
|
680 |
|
|
|
681 |
|
|
end dmgr_a1;
|
682 |
|
|
|
683 |
|
|
|
684 |
|
|
|