forked from yol/ethernet_mac
-
Notifications
You must be signed in to change notification settings - Fork 0
/
miim_control.vhd
279 lines (250 loc) · 8.82 KB
/
miim_control.vhd
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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
-- This file is part of the ethernet_mac project.
--
-- For the full copyright and license information, please read the
-- LICENSE.md file that was distributed with this source code.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.ethernet_types.all;
use work.miim_types.all;
use work.miim_registers.all;
entity miim_control is
generic(
-- Ticks to wait before writing any registers after reset low
RESET_WAIT_TICKS : natural := 0;
-- Ticks to wait between polling the status register
POLL_WAIT_TICKS : natural := DEFAULT_POLL_WAIT_TICKS;
-- Activate debug output
DEBUG_OUTPUT : boolean := FALSE
-- Example for Marvell PHY 88E1111 and 125 MHz MIIM clock:
-- RESET_WAIT_TICKS => 1250000 (10 ms at 125 MHz, minimum: 5 ms)
);
port(
reset_i : in std_ulogic;
clock_i : in std_ulogic;
miim_register_address_o : out t_register_address;
miim_data_read_i : in t_data;
miim_data_write_o : out t_data;
miim_req_o : out std_ulogic;
miim_ack_i : in std_ulogic;
miim_we_o : out std_ulogic;
speed_o : out t_ethernet_speed;
link_up_o : out std_ulogic;
-- Only used if DEBUG_OUTPUT is TRUE
debug_fifo_we_o : out std_ulogic;
debug_fifo_write_data_o : out std_ulogic_vector(7 downto 0)
);
end entity;
architecture rtl of miim_control is
signal register_address : t_register_address;
type t_state is (
RESET_WAIT,
WRITE_AUTONEG,
WRITE_GIGABIT_AUTONEG,
WRITE_SOFTRESET,
WAIT_POLL,
READ_STATUS,
READ_SPEED_10_100,
READ_SPEED_1000,
DEBUG_START,
DEBUG_WRITE_REGAD,
DEBUG_WRITE_BYTE1,
DEBUG_WRITE_BYTE2,
WAIT_ACK_LOW,
DEBUG_DONE
);
signal state : t_state := RESET_WAIT;
signal after_ack_state : t_state;
-- Initial register write contents
-- Reset the PHY
constant control_register_reset : t_control_register := (
reset => '1',
loopback => '0',
speed_10_100 => '0',
speed_1000 => '1',
auto_negotiation_enable => '1',
power_down => '0',
isolate => '0',
restart_auto_negotiation => '0',
duplex_mode => '1',
enable_collision_test => '0',
unidirectional_enable => '0'
);
-- Activate only full-duplex 10/100
constant auto_negotiation_set_fd : t_auto_negotiation_advertisement_register_802_3 := (
next_page => '0',
remote_fault => '0',
extended_next_page => '0',
asymmetric_pause => '0',
pause => '0',
advertise_100base_t4 => '0',
advertise_100base_tx_fd => '1',
advertise_100base_tx_hd => '0',
advertise_10base_t_fd => '1',
advertise_10base_t_hd => '0'
);
-- Activate only full-duplex 1000
constant master_slave_set_fd : t_master_slave_control_register := (
test_mode_bits => "000",
master_slave_manual_config_enable => '0',
master_slave_manual_config_value => '0',
port_type_is_multiport => '0',
advertise_1000base_t_fd => '1',
advertise_1000base_t_hd => '0'
);
signal init_done : boolean := FALSE;
signal reset_wait_counter : natural range 0 to RESET_WAIT_TICKS + 1;
signal poll_wait_counter : natural range 0 to POLL_WAIT_TICKS + 1;
signal lp_supports_10 : std_ulogic;
signal lp_supports_100 : std_ulogic;
begin
miim_register_address_o <= register_address;
fsm : process(clock_i)
begin
if rising_edge(clock_i) then
-- Default values
miim_req_o <= '0';
debug_fifo_we_o <= '0';
if reset_i = '1' then
state <= RESET_WAIT;
link_up_o <= '0';
speed_o <= SPEED_UNSPECIFIED;
reset_wait_counter <= 0;
poll_wait_counter <= 0;
else
miim_we_o <= '0';
case state is
-- Initialization
when RESET_WAIT =>
-- Keep in mind that zero is a valid (and the default) value for RESET_WAIT_TICKS
if reset_wait_counter = RESET_WAIT_TICKS then
state <= WRITE_AUTONEG;
end if;
reset_wait_counter <= reset_wait_counter + 1;
when WRITE_AUTONEG =>
-- Advertise 100 MBit/10 MBit full-duplex, no PAUSE support
register_address <= AUTONEG_ADVERTISEMENT_REG;
miim_data_write_o <= auto_negotiation_advertisement_register_802_3_to_data(auto_negotiation_set_fd);
miim_req_o <= '1';
miim_we_o <= '1';
if miim_ack_i = '1' then
miim_req_o <= '0';
state <= WAIT_ACK_LOW;
after_ack_state <= WRITE_GIGABIT_AUTONEG;
end if;
when WRITE_GIGABIT_AUTONEG =>
-- Advertise 1000 MBit full-duplex
register_address <= MASTERSLAVE_CTRL_REG;
miim_data_write_o <= master_slave_control_register_to_data(master_slave_set_fd);
miim_req_o <= '1';
miim_we_o <= '1';
if miim_ack_i = '1' then
miim_req_o <= '0';
state <= WAIT_ACK_LOW;
after_ack_state <= WRITE_SOFTRESET;
end if;
when WRITE_SOFTRESET =>
-- Reset the PHY to apply the autonegotiation values
register_address <= CONTROL_REG;
miim_data_write_o <= control_register_to_data(control_register_reset);
miim_req_o <= '1';
miim_we_o <= '1';
if miim_ack_i = '1' then
miim_req_o <= '0';
init_done <= TRUE;
state <= WAIT_ACK_LOW;
after_ack_state <= WAIT_POLL;
end if;
-- State polling
when WAIT_POLL =>
-- Don't poll continuously to reduce unnecessary switching noise
poll_wait_counter <= poll_wait_counter + 1;
if poll_wait_counter = POLL_WAIT_TICKS then
poll_wait_counter <= 0;
state <= READ_STATUS;
end if;
when READ_STATUS =>
-- Read status register
register_address <= STATUS_REG;
miim_req_o <= '1';
if miim_ack_i = '1' then
-- Link is up when the link status indicator and auto-negotiation is OK
link_up_o <= data_to_status_register(miim_data_read_i).link_status and data_to_status_register(miim_data_read_i).auto_negotiation_complete;
miim_req_o <= '0';
state <= WAIT_ACK_LOW;
after_ack_state <= READ_SPEED_10_100;
end if;
when READ_SPEED_10_100 =>
-- Read link partner ability register
register_address <= AUTONEG_LP_BASEPAGEABILITY_REG;
miim_req_o <= '1';
if miim_ack_i = '1' then
miim_req_o <= '0';
lp_supports_10 <= data_to_auto_negotiation_lp_base_page_ability_register(miim_data_read_i).can_10base_t_fd;
lp_supports_100 <= data_to_auto_negotiation_lp_base_page_ability_register(miim_data_read_i).can_100base_tx_fd;
state <= WAIT_ACK_LOW;
after_ack_state <= READ_SPEED_1000;
end if;
when READ_SPEED_1000 =>
register_address <= MASTERSLAVE_STATUS_REG;
miim_req_o <= '1';
if miim_ack_i = '1' then
miim_req_o <= '0';
-- Detect highest supported data rate
if data_to_master_slave_status_register(miim_data_read_i).lp_1000base_t_fd = '1' then
speed_o <= SPEED_1000MBPS;
elsif lp_supports_100 = '1' then
speed_o <= SPEED_100MBPS;
elsif lp_supports_10 = '1' then
speed_o <= SPEED_10MBPS;
else
-- Nothing is supported
speed_o <= SPEED_UNSPECIFIED;
end if;
state <= WAIT_ACK_LOW;
register_address <= (others => '0');
if DEBUG_OUTPUT = TRUE then
after_ack_state <= DEBUG_START;
else
after_ack_state <= WAIT_POLL;
end if;
end if;
-- Debug states
when DEBUG_START =>
if miim_ack_i = '1' then
state <= DEBUG_WRITE_REGAD;
debug_fifo_we_o <= '1';
debug_fifo_write_data_o <= "000" & std_ulogic_vector(register_address);
else
miim_req_o <= '1';
end if;
when DEBUG_WRITE_REGAD =>
debug_fifo_we_o <= '1';
debug_fifo_write_data_o <= miim_data_read_i(15 downto 8);
state <= DEBUG_WRITE_BYTE1;
when DEBUG_WRITE_BYTE1 =>
debug_fifo_we_o <= '1';
debug_fifo_write_data_o <= miim_data_read_i(7 downto 0);
state <= DEBUG_WRITE_BYTE2;
when DEBUG_WRITE_BYTE2 =>
if register_address = "11111" then
register_address <= (others => '0');
state <= DEBUG_DONE;
else
register_address <= register_address + 1;
after_ack_state <= DEBUG_START;
state <= WAIT_ACK_LOW;
end if;
when DEBUG_DONE =>
--state <= WAIT_DEBUG_START;
state <= WAIT_POLL;
-- Auxiliary state
when WAIT_ACK_LOW =>
if miim_ack_i = '0' then
state <= after_ack_state;
end if;
end case;
end if;
end if;
end process;
end architecture;