1 |
3 |
wzab |
-------------------------------------------------------------------------------
|
2 |
|
|
-- Title : FPGA Ethernet interface - block sending packets via MII Phy
|
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 |
7 |
wzab |
-- Last update: 2013-06-16
|
11 |
3 |
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 |
|
|
library ieee;
|
24 |
|
|
use ieee.std_logic_1164.all;
|
25 |
|
|
use ieee.numeric_std.all;
|
26 |
|
|
library work;
|
27 |
|
|
use work.PCK_CRC32_D4.all;
|
28 |
|
|
|
29 |
|
|
entity eth_sender is
|
30 |
|
|
|
31 |
|
|
port (
|
32 |
|
|
-- Configuration
|
33 |
|
|
peer_mac : in std_logic_vector(47 downto 0);
|
34 |
|
|
my_mac : in std_logic_vector(47 downto 0);
|
35 |
|
|
my_ether_type : in std_logic_vector(15 downto 0);
|
36 |
|
|
set_number : in unsigned(15 downto 0);
|
37 |
|
|
pkt_number : in unsigned(15 downto 0);
|
38 |
|
|
retry_number : in unsigned(15 downto 0);
|
39 |
|
|
transm_delay : in unsigned(31 downto 0);
|
40 |
|
|
-- System interface
|
41 |
|
|
clk : in std_logic;
|
42 |
|
|
rst_n : in std_logic;
|
43 |
|
|
-- Control interface
|
44 |
|
|
ready : out std_logic;
|
45 |
|
|
start : in std_logic;
|
46 |
|
|
-- Data memory interface
|
47 |
|
|
tx_mem_addr : out std_logic_vector(13 downto 0);
|
48 |
|
|
tx_mem_data : in std_logic_vector(31 downto 0);
|
49 |
|
|
-- TX Phy interface
|
50 |
|
|
Tx_Clk : in std_logic;
|
51 |
|
|
Tx_En : out std_logic;
|
52 |
|
|
TxD : out std_logic_vector(3 downto 0)
|
53 |
|
|
);
|
54 |
|
|
|
55 |
|
|
end eth_sender;
|
56 |
|
|
|
57 |
|
|
|
58 |
|
|
architecture beh1 of eth_sender is
|
59 |
|
|
|
60 |
|
|
type T_ETH_SENDER_STATE is (WST_IDLE, WST_SEND_PREAMB, WST_SEND_SOF,
|
61 |
|
|
WST_SEND_HEADER, WST_SEND_DATA, WST_SEND_CRC,
|
62 |
|
|
WST_SEND_COMPLETED);
|
63 |
|
|
|
64 |
|
|
type T_ETH_SENDER_REGS is record
|
65 |
|
|
state : T_ETH_SENDER_STATE;
|
66 |
|
|
ready : std_logic;
|
67 |
|
|
count : integer;
|
68 |
|
|
nibble : integer;
|
69 |
|
|
mem_addr : unsigned (7 downto 0);
|
70 |
|
|
crc32 : std_logic_vector(31 downto 0);
|
71 |
|
|
end record;
|
72 |
|
|
|
73 |
|
|
constant ETH_SENDER_REGS_INI : T_ETH_SENDER_REGS := (
|
74 |
|
|
state => WST_IDLE,
|
75 |
|
|
ready => '1',
|
76 |
|
|
count => 0,
|
77 |
|
|
nibble => 0,
|
78 |
|
|
mem_addr => (others => '0'),
|
79 |
|
|
crc32 => (others => '0')
|
80 |
|
|
) ;
|
81 |
|
|
|
82 |
|
|
signal r, r_n : T_ETH_SENDER_REGS := ETH_SENDER_REGS_INI;
|
83 |
|
|
|
84 |
|
|
type T_ETH_SENDER_COMB is record
|
85 |
|
|
TxD : std_logic_vector(3 downto 0);
|
86 |
|
|
Tx_En : std_logic;
|
87 |
|
|
mem_addr : unsigned(7 downto 0);
|
88 |
|
|
end record;
|
89 |
|
|
|
90 |
|
|
constant ETH_SENDER_COMB_DEFAULT : T_ETH_SENDER_COMB := (
|
91 |
|
|
TxD => (others => '0'),
|
92 |
|
|
Tx_En => '0',
|
93 |
|
|
mem_addr => (others => '0')
|
94 |
|
|
);
|
95 |
|
|
|
96 |
|
|
signal c : T_ETH_SENDER_COMB := ETH_SENDER_COMB_DEFAULT;
|
97 |
|
|
|
98 |
|
|
signal s_header : std_logic_vector(6*32-1 downto 0) := (others => '0');
|
99 |
|
|
constant HEADER_LEN : integer := 6*8; -- 6 words,
|
100 |
|
|
-- 8 nibbles each
|
101 |
|
|
|
102 |
|
|
function select_nibble (
|
103 |
|
|
constant vec : std_logic_vector;
|
104 |
|
|
constant nibble_num : integer)
|
105 |
|
|
return std_logic_vector is
|
106 |
|
|
variable byte_num : integer;
|
107 |
|
|
variable v_byte : std_logic_vector(7 downto 0);
|
108 |
|
|
variable v_nibble : std_logic_vector(3 downto 0);
|
109 |
|
|
begin
|
110 |
|
|
-- first select byte
|
111 |
|
|
byte_num := nibble_num / 2;
|
112 |
|
|
v_byte := vec(vec'left-byte_num*8 downto vec'left-byte_num*8-7);
|
113 |
|
|
-- then select nibble (lower nibble is sent first!)
|
114 |
|
|
if nibble_num mod 2 = 0 then
|
115 |
|
|
v_nibble := v_byte(3 downto 0);
|
116 |
|
|
else
|
117 |
|
|
v_nibble := v_byte(7 downto 4);
|
118 |
|
|
end if;
|
119 |
|
|
return v_nibble;
|
120 |
|
|
end select_nibble;
|
121 |
|
|
|
122 |
|
|
function rev(a : in std_logic_vector)
|
123 |
|
|
return std_logic_vector is
|
124 |
|
|
variable result : std_logic_vector(a'range);
|
125 |
|
|
alias aa : std_logic_vector(a'reverse_range) is a;
|
126 |
|
|
begin
|
127 |
|
|
for i in aa'range loop
|
128 |
|
|
result(i) := aa(i);
|
129 |
|
|
end loop;
|
130 |
|
|
return result;
|
131 |
|
|
end; -- function reverse_any_bus
|
132 |
|
|
|
133 |
|
|
signal tx_rst_n, tx_rst_n_0, tx_rst_n_1 : std_logic := '0';
|
134 |
|
|
signal update_flag_0, update_flag_1, update_flag : std_logic := '0';
|
135 |
|
|
|
136 |
|
|
signal start_0, tx_start, tx_start_1, tx_start_0 : std_logic := '0';
|
137 |
|
|
signal tx_ready, ready_0, ready_1 : std_logic := '0';
|
138 |
|
|
|
139 |
|
|
type T_STATE1 is (ST1_IDLE, ST1_WAIT_NOT_READY, ST1_WAIT_NOT_START,
|
140 |
|
|
ST1_WAIT_READY);
|
141 |
|
|
signal state1 : T_STATE1;
|
142 |
|
|
|
143 |
|
|
type T_STATE2 is (ST2_IDLE, ST2_WAIT_NOT_READY, ST2_WAIT_READY);
|
144 |
|
|
signal state2 : T_STATE2;
|
145 |
|
|
begin -- beh1
|
146 |
|
|
|
147 |
|
|
-- Packet header
|
148 |
|
|
s_header <= peer_mac & my_mac & my_ether_type & x"a5a5" &
|
149 |
|
|
std_logic_vector(set_number(15 downto 0)) &
|
150 |
|
|
std_logic_vector(pkt_number(5 downto 0)) &
|
151 |
|
|
std_logic_vector(retry_number(9 downto 0)) &
|
152 |
|
|
std_logic_vector(transm_delay);
|
153 |
|
|
-- Connection of the signals
|
154 |
|
|
|
155 |
|
|
-- The memory address is built from the packet number (6 bits) and word
|
156 |
|
|
-- number (8 bits)
|
157 |
|
|
tx_mem_addr <= std_logic_vector(pkt_number(5 downto 0)) & std_logic_vector(c.mem_addr);
|
158 |
|
|
|
159 |
|
|
-- Main state machine used to send the packet
|
160 |
|
|
-- W calej maszynie trzeba jeszcze dodac obsluge kolizji!!!
|
161 |
|
|
-- Oprocz tego trzeba przeanalizowac poprawnosc przejsc miedzy domenami zegara
|
162 |
|
|
|
163 |
|
|
|
164 |
|
|
snd1 : process (Tx_Clk, tx_rst_n)
|
165 |
|
|
begin
|
166 |
|
|
if tx_rst_n = '0' then -- asynchronous reset (active low)
|
167 |
|
|
r <= ETH_SENDER_REGS_INI;
|
168 |
|
|
TxD <= (others => '0');
|
169 |
|
|
Tx_En <= '0';
|
170 |
|
|
elsif Tx_Clk'event and Tx_Clk = '1' then -- rising clock edge
|
171 |
|
|
r <= r_n;
|
172 |
|
|
-- To minimize glitches and propagation delay, let's add pipeline register
|
173 |
|
|
Tx_En <= c.Tx_En;
|
174 |
|
|
TxD <= c.TxD;
|
175 |
|
|
end if;
|
176 |
|
|
end process snd1; -- snd1
|
177 |
|
|
|
178 |
|
|
snd2 : process (r, s_header, tx_mem_data, tx_start)
|
179 |
|
|
variable v_TxD : std_logic_vector(3 downto 0);
|
180 |
|
|
begin -- process snd1
|
181 |
|
|
-- default values
|
182 |
|
|
c <= ETH_SENDER_COMB_DEFAULT;
|
183 |
|
|
r_n <= r;
|
184 |
|
|
case r.state is
|
185 |
|
|
when WST_IDLE =>
|
186 |
|
|
r_n.ready <= '1';
|
187 |
|
|
if tx_start = '1' then
|
188 |
|
|
r_n.ready <= '0';
|
189 |
|
|
r_n.state <= WST_SEND_PREAMB;
|
190 |
|
|
r_n.count <= 15;
|
191 |
|
|
end if;
|
192 |
|
|
when WST_SEND_PREAMB =>
|
193 |
|
|
-- Trzeba dodac wykrywanie kolizji!
|
194 |
|
|
c.TxD <= x"5";
|
195 |
|
|
c.Tx_En <= '1';
|
196 |
|
|
r_n.count <= r.count - 1;
|
197 |
|
|
if r.count = 1 then
|
198 |
|
|
r_n.state <= WST_SEND_SOF;
|
199 |
|
|
end if;
|
200 |
|
|
when WST_SEND_SOF =>
|
201 |
|
|
c.TxD <= x"D";
|
202 |
|
|
c.Tx_En <= '1';
|
203 |
|
|
-- Prepare for sending of header
|
204 |
|
|
r_n.crc32 <= (others => '1');
|
205 |
|
|
r_n.state <= WST_SEND_HEADER;
|
206 |
|
|
r_n.count <= 0;
|
207 |
|
|
when WST_SEND_HEADER =>
|
208 |
|
|
v_TxD := select_nibble(s_header, r.count);
|
209 |
|
|
c.TxD <= v_TxD;
|
210 |
|
|
c.Tx_En <= '1';
|
211 |
|
|
r_n.crc32 <= nextCRC32_D4(rev(v_TxD), r.crc32);
|
212 |
|
|
if r.count < HEADER_LEN-1 then
|
213 |
|
|
r_n.count <= r.count + 1;
|
214 |
|
|
else
|
215 |
|
|
r_n.count <= 0;
|
216 |
|
|
r_n.nibble <= 0;
|
217 |
|
|
r_n.mem_addr <= (others => '0');
|
218 |
|
|
c.mem_addr <= (others => '0');
|
219 |
|
|
r_n.state <= WST_SEND_DATA;
|
220 |
|
|
end if;
|
221 |
|
|
when WST_SEND_DATA =>
|
222 |
|
|
-- send the data nibble by nibble
|
223 |
5 |
wzab |
v_TxD := select_nibble(tx_mem_data, r.nibble);
|
224 |
3 |
wzab |
c.TxD <= v_TxD;
|
225 |
|
|
c.Tx_En <= '1';
|
226 |
|
|
r_n.crc32 <= nextCRC32_D4(rev(v_TxD), r.crc32);
|
227 |
|
|
if r.nibble < 7 then
|
228 |
|
|
r_n.nibble <= r.nibble + 1;
|
229 |
|
|
c.mem_addr <= r.mem_addr;
|
230 |
|
|
else
|
231 |
|
|
r_n.nibble <= 0;
|
232 |
|
|
-- Check, if we have sent all the data
|
233 |
|
|
if r.mem_addr < 255 then
|
234 |
|
|
r_n.mem_addr <= r.mem_addr + 1;
|
235 |
|
|
c.mem_addr <= r.mem_addr + 1;
|
236 |
|
|
else
|
237 |
|
|
-- We send the CRC
|
238 |
|
|
r_n.state <= WST_SEND_CRC;
|
239 |
|
|
end if;
|
240 |
|
|
end if;
|
241 |
|
|
when WST_SEND_CRC =>
|
242 |
5 |
wzab |
v_TxD := r.crc32(31-r.nibble*4 downto 28-r.nibble*4);
|
243 |
3 |
wzab |
c.TxD <= not rev(v_TxD);
|
244 |
|
|
c.Tx_En <= '1';
|
245 |
|
|
if r.nibble < 7 then
|
246 |
|
|
r_n.nibble <= r.nibble + 1;
|
247 |
|
|
else
|
248 |
6 |
wzab |
r_n.count <= 24; -- generate the IFG - 24 nibbles = 96
|
249 |
3 |
wzab |
-- bits
|
250 |
|
|
r_n.state <= WST_SEND_COMPLETED;
|
251 |
|
|
end if;
|
252 |
|
|
when WST_SEND_COMPLETED =>
|
253 |
|
|
if r.count > 0 then
|
254 |
|
|
r_n.count <= r.count - 1;
|
255 |
|
|
else
|
256 |
|
|
r_n.ready <= '1';
|
257 |
|
|
r_n.state <= WST_IDLE;
|
258 |
|
|
end if;
|
259 |
|
|
end case;
|
260 |
|
|
end process snd2;
|
261 |
|
|
|
262 |
|
|
|
263 |
|
|
-- Synchronization of the reset signal for the Tx_Clk domain
|
264 |
|
|
process (Tx_Clk, rst_n)
|
265 |
|
|
begin -- process
|
266 |
|
|
if rst_n = '0' then -- asynchronous reset (active low)
|
267 |
|
|
tx_rst_n_0 <= '0';
|
268 |
|
|
tx_rst_n_1 <= '0';
|
269 |
|
|
tx_rst_n <= '0';
|
270 |
|
|
elsif Tx_Clk'event and Tx_Clk = '1' then -- rising clock edge
|
271 |
|
|
tx_rst_n_0 <= rst_n;
|
272 |
|
|
tx_rst_n_1 <= tx_rst_n_0;
|
273 |
|
|
tx_rst_n <= tx_rst_n_1;
|
274 |
|
|
end if;
|
275 |
|
|
end process;
|
276 |
|
|
|
277 |
|
|
-- Synchronization of signals passing clock domains
|
278 |
|
|
-- Signal start is sent from the Clk domain.
|
279 |
|
|
-- When it is asserted, we must immediately deassert signal ready,
|
280 |
|
|
-- then generate the synchronized start and after internal ready
|
281 |
|
|
-- is asserted, we can output it again...
|
282 |
|
|
|
283 |
|
|
-- Ustawienie na 1 takt zegara "clk" sygnalu start powinno zainicjowac wysylanie
|
284 |
|
|
-- w tym bloku musimy zadbac o stosowne wydluzenie sygnalu start i jego synchronizacje
|
285 |
|
|
-- miedzy domenami zegara...
|
286 |
|
|
process (clk, rst_n)
|
287 |
|
|
begin -- process
|
288 |
|
|
if rst_n = '0' then -- asynchronous reset (active low)
|
289 |
|
|
ready <= '0';
|
290 |
7 |
wzab |
ready_0 <= '0';
|
291 |
3 |
wzab |
ready_1 <= '0';
|
292 |
|
|
state2 <= ST2_IDLE;
|
293 |
|
|
elsif clk'event and clk = '1' then -- rising clock edge
|
294 |
|
|
ready_1 <= tx_ready;
|
295 |
|
|
ready_0 <= ready_1;
|
296 |
|
|
case state2 is
|
297 |
|
|
when ST2_IDLE =>
|
298 |
|
|
if start = '1' and ready_0 = '1' then
|
299 |
|
|
start_0 <= '1';
|
300 |
|
|
ready <= '0';
|
301 |
|
|
state2 <= ST2_WAIT_NOT_READY;
|
302 |
|
|
else
|
303 |
|
|
ready <= ready_0; -- Needed to provide correct start!
|
304 |
|
|
end if;
|
305 |
|
|
when ST2_WAIT_NOT_READY =>
|
306 |
|
|
if ready_0 = '0' then
|
307 |
|
|
start_0 <= '0';
|
308 |
|
|
state2 <= ST2_WAIT_READY;
|
309 |
|
|
end if;
|
310 |
|
|
when ST2_WAIT_READY =>
|
311 |
|
|
if ready_0 = '1' then
|
312 |
|
|
ready <= '1';
|
313 |
|
|
state2 <= ST2_IDLE;
|
314 |
|
|
end if;
|
315 |
|
|
when others => null;
|
316 |
|
|
end case;
|
317 |
|
|
end if;
|
318 |
|
|
end process;
|
319 |
|
|
|
320 |
|
|
process (Tx_Clk, tx_rst_n)
|
321 |
|
|
begin -- process
|
322 |
|
|
if tx_rst_n = '0' then -- asynchronous reset (active low)
|
323 |
|
|
tx_start <= '0';
|
324 |
|
|
tx_start_0 <= '0';
|
325 |
|
|
state1 <= ST1_IDLE;
|
326 |
|
|
tx_ready <= '1';
|
327 |
|
|
elsif Tx_Clk'event and Tx_Clk = '1' then -- rising clock edge
|
328 |
7 |
wzab |
tx_start_0 <= start_0;
|
329 |
3 |
wzab |
tx_start <= tx_start_0;
|
330 |
|
|
case state1 is
|
331 |
|
|
when ST1_IDLE =>
|
332 |
|
|
if tx_start = '1' then
|
333 |
|
|
tx_ready <= '0'; -- this should cause tx_start to go low
|
334 |
|
|
state1 <= ST1_WAIT_NOT_READY;
|
335 |
|
|
end if;
|
336 |
|
|
when ST1_WAIT_NOT_READY =>
|
337 |
|
|
if r.ready = '0' then
|
338 |
|
|
state1 <= ST1_WAIT_NOT_START;
|
339 |
|
|
end if;
|
340 |
|
|
when ST1_WAIT_NOT_START =>
|
341 |
|
|
if tx_start = '0' then
|
342 |
|
|
state1 <= ST1_WAIT_READY;
|
343 |
|
|
end if;
|
344 |
|
|
when ST1_WAIT_READY =>
|
345 |
|
|
if r.ready = '1' then
|
346 |
|
|
tx_ready <= '1';
|
347 |
|
|
state1 <= ST1_IDLE;
|
348 |
|
|
end if;
|
349 |
|
|
when others => null;
|
350 |
|
|
end case;
|
351 |
|
|
end if;
|
352 |
|
|
end process;
|
353 |
|
|
|
354 |
|
|
end beh1;
|