MDIO 管理接口是以太网 MAC 和 PHY 之间的接口,用于管理/配置以太网 PHY 芯片。本文主要介绍 MDIO 管理接口定义,以及 MDIO 控制器设计。
目录

MDIO 管理接口是以太网 MAC 和 PHY 之间的接口,用于管理/配置以太网 PHY 芯片。MDIO 接口通过 MDC 和 MDIO 两根引脚访问 PHY 芯片的内部寄存器,详细可参考 IEEE 802.3u secton 22 中的定义。
| 序号 | 名称 | 方向 | 功能说明 |
| 1 | MDC | 单向,MAC -> PHY | MDIO 参考时钟 |
| 2 | MDIO | 双向,MAC <-> PHY | MDIO 数据 |
MDC 信号是由以太网 MAC 提供的参考时钟,MDIO 是数据输入/输出,与 MDC 信号同步运行的双向信号。在硬件设计上,MDIO 引脚需要一个 1.5k 欧姆的上拉电阻,以在空闲和周转期间保持 MDIO 高电平。
多个 PHY 芯片可以共享同一个 MDIO 接口。在交换机或路由器应用中,每个 PHY 芯片会分配一个唯一的地址,并且只能通过该唯一的 PHY 地址对其进行寻址。有关管理寄存器的详细信息,请参阅 PHY 对应芯片手册。
MDIO 管理接口分读和写两种操作方式,读/写帧格式见下表。


MDIO 读/写时序按照先后顺序分别为:32 位前导码、帧头、操作符、PHY 地址、寄存器地址、TA 标志,最后是 16 位数据。

可以使用有限状态机设计 MDIO 控制器,设计代码如下:
- library ieee;
- use ieee.std_logic_1164.all;
- use ieee.std_logic_arith.all;
- use ieee.std_logic_unsigned.all;
-
- entity mdio_core is
- port(
- -- System level
- sys_rst : in std_logic;
- sys_clk : in std_logic;
-
- -- MDIO control/data ports
- mdio_req : in std_logic;
- mdio_ack : out std_logic;
- mdio_done : out std_logic;
- phy_addr : in std_logic_vector(7 downto 0);
- reg_addr : in std_logic_vector(7 downto 0);
- reg_wdata : in std_logic_vector(15 downto 0);
- reg_rdata : out std_logic_vector(15 downto 0);
- reg_rw : in std_logic;
-
- -- MDIO Interface
- phy_mdc : out std_logic;
- phy_mdio_id : in std_logic;
- phy_mdio_od : out std_logic;
- phy_mdio_oe : out std_logic
- );
- end entity;
- architecture behav of mdio_core is
- -- internal signal declarations
- type state is(
- idle,
- preamble,
- start,
- op_read,
- op_write,
- write_addr,
- turn_around,
- read_data,
- write_data
- );
- signal pstate : state := idle;
- signal phy_mdio_id_r1 : std_logic;
- signal phy_mdio_id_r2 : std_logic;
- signal reg_rdwr_r : std_logic;
- signal mdc_buf : std_logic_vector(7 downto 0);
- signal buf_mdio_ack : std_logic;
- signal pulse_cnt : std_logic_vector(5 downto 0);
- signal step_cnt : std_logic_vector(6 downto 0);
- signal addr_buf : std_logic_vector(9 downto 0);
- signal reg_wdata_buf : std_logic_vector(15 downto 0);
- signal reg_rdata_buf : std_logic_vector(15 downto 0);
- ---------------------------------------------------------
- begin
- ---------------------------------------------------------
- phy_mdc <= mdc_buf(mdc_buf'high);
- process(sys_rst,sys_clk)
- begin
- if sys_rst = '1' then
- pulse_cnt <= (0 => '1', others => '0');
- elsif rising_edge(sys_clk) then
- if pstate /= idle then
- if pulse_cnt(5) = '1' then
- pulse_cnt <= (0 => '1', others => '0');
- else
- pulse_cnt <= pulse_cnt + 1;
- end if;
- else
- pulse_cnt <= (others => '0');
- end if;
- end if;
- end process;
- process(sys_rst,sys_clk)
- begin
- if sys_rst = '1' then
- mdc_buf <= (others => '1');
- elsif rising_edge(sys_clk) then
- mdc_buf <= mdc_buf(mdc_buf'high-1 downto 0) & pulse_cnt(4);
- end if;
- end process;
-
- process(sys_rst,sys_clk)
- begin
- if sys_rst = '1' then
- phy_mdio_id_r1 <= '1';
- phy_mdio_id_r2 <= '1';
- elsif rising_edge(sys_clk) then
- phy_mdio_id_r1 <= phy_mdio_id;
- phy_mdio_id_r2 <= phy_mdio_id_r1;
- end if;
- end process;
-
- --================================================================
- mdio_ack <= buf_mdio_ack;
- reg_rdata <= reg_rdata_buf;
-
- process(sys_rst,sys_clk)
- begin
- if sys_rst = '1' then
- step_cnt <= (0 => '1', others => '0');
- elsif rising_edge(sys_clk) then
- case(pstate) is
- when idle =>
- step_cnt <= (0 => '1', others => '0');
-
- when preamble =>
- if mdc_buf(1 downto 0) = "10" then
- if step_cnt(5) = '1' then
- step_cnt <= (0 => '1', others => '0');
- else
- step_cnt <= step_cnt + 1;
- end if;
- end if;
-
- when start | op_read | op_write | turn_around =>
- if mdc_buf(1 downto 0) = "10" then
- if step_cnt(1) = '1' then
- step_cnt <= (0 => '1', others => '0');
- else
- step_cnt <= step_cnt + 1;
- end if;
- end if;
-
- when write_addr =>
- if mdc_buf(1 downto 0) = "10" then
- if step_cnt(3) = '1' and step_cnt(1) = '1' then
- step_cnt <= (0 => '1', others => '0');
- else
- step_cnt <= step_cnt + 1;
- end if;
- end if;
-
- when write_data | read_data =>
- if mdc_buf(1 downto 0) = "10" then
- if step_cnt(4) = '1' then
- step_cnt <= (0 => '1', others => '0');
- else
- step_cnt <= step_cnt + 1;
- end if;
- end if;
-
- when others => NULL;
-
- end case;
- end if;
- end process;
-
- process(sys_rst,sys_clk)
- begin
- if sys_rst = '1' then
- pstate <= idle;
- reg_rdwr_r <= '0';
- buf_mdio_ack <= '0';
- mdio_done <= '0';
- elsif rising_edge(sys_clk) then
- case(pstate) is
- when idle =>
- mdio_done <= '0';
-
- if mdio_req = '1' and buf_mdio_ack = '1' then
- buf_mdio_ack <= '0';
- reg_rdwr_r <= reg_rw;
- pstate <= preamble;
- elsif mdio_req = '1' and buf_mdio_ack = '0' then
- buf_mdio_ack <= '1';
- pstate <= idle;
- end if;
-
- when preamble =>
- if mdc_buf(1 downto 0) = "10" and step_cnt(5) = '1' then
- pstate <= start;
- else
- pstate <= preamble;
- end if;
-
- when start =>
- if mdc_buf(1 downto 0) = "10" then
- if step_cnt(1) = '1' and reg_rdwr_r = '1' then
- pstate <= op_write;
- elsif step_cnt(1) = '1' and reg_rdwr_r = '0' then
- pstate <= op_read;
- end if;
- end if;
-
- when op_read | op_write =>
- -- Send Read or Write OP Code
- if mdc_buf(1 downto 0) = "10" and step_cnt(1) = '1' then
- pstate <= write_addr;
- end if;
-
- when write_addr =>
- -- Send PHY Address and REG Address
- if mdc_buf(1 downto 0) = "10" then
- if step_cnt(3) = '1' and step_cnt(1) = '1' then
- pstate <= turn_around;
- end if;
- else
- pstate <= write_addr;
- end if;
-
- when turn_around =>
- if mdc_buf(1 downto 0) = "10" then
- if step_cnt(1) = '1' and reg_rdwr_r = '1' then
- pstate <= write_data;
- elsif step_cnt(1) = '1' and reg_rdwr_r = '0' then
- pstate <= read_data;
- end if;
- else
- pstate <= turn_around;
- end if;
-
- when write_data | read_data =>
- if mdc_buf(1 downto 0) = "10" and step_cnt(4) = '1' then
- pstate <= idle;
- mdio_done <= '1';
- end if;
-
- when others => NULL;
-
- end case;
- end if;
- end process;
-
- process(sys_rst,sys_clk)
- begin
- if sys_rst = '1' then
- addr_buf <= (others => '0');
- reg_wdata_buf <= (others => '0');
- reg_rdata_buf <= (others => '0');
- elsif rising_edge(sys_clk) then
- case(pstate) is
- when idle =>
- if mdio_req = '1' and buf_mdio_ack = '1' then
- reg_wdata_buf <= reg_wdata;
- addr_buf <= phy_addr(4 downto 0) & reg_addr(4 downto 0);
- end if;
-
- when preamble =>
- reg_rdata_buf <= (others => '1');
-
- when write_addr =>
- if mdc_buf(1 downto 0) = "10" then
- addr_buf <= addr_buf(8 downto 0) & '0';
- end if;
-
- when write_data =>
- if mdc_buf(1 downto 0) = "10" then
- reg_wdata_buf <= reg_wdata_buf(14 downto 0) & '0';
- end if;
-
- when read_data =>
- if mdc_buf(1 downto 0)= "01" then
- reg_rdata_buf <= reg_rdata_buf(14 downto 0) & phy_mdio_id_r2;
- end if;
-
- when others => NULL;
-
- end case;
- end if;
- end process;
-
- mdio_od_pro: process(sys_rst,sys_clk)
- begin
- if sys_rst = '1' then
- phy_mdio_od <= '1';
- elsif rising_edge(sys_clk) then
- case(pstate) is
- when idle | read_data | preamble =>
- phy_mdio_od <= '1';
-
- when start | op_write =>
- case(step_cnt(1 downto 0)) is
- when "01" => phy_mdio_od <= '0';
- when "10" => phy_mdio_od <= '1';
- when others => phy_mdio_od <= '1';
- end case;
-
- when op_read =>
- case(step_cnt(1 downto 0)) is
- when "01" => phy_mdio_od <= '1';
- when "10" => phy_mdio_od <= '0';
- when others => phy_mdio_od <= '1';
- end case;
-
- when write_addr =>
- phy_mdio_od <= addr_buf(9);
-
- when turn_around =>
- if reg_rdwr_r = '1' then
- -- Turn around for write
- case(step_cnt(1 downto 0)) is
- when "01" => phy_mdio_od <= '1';
- when "10" => phy_mdio_od <= '0';
- when others => phy_mdio_od <= '1';
- end case;
- else
- -- Turn around for read
- phy_mdio_od <= '1';
- end if;
-
- when write_data =>
- phy_mdio_od <= reg_wdata_buf(15);
-
- when others =>
- phy_mdio_od <= '1';
-
- end case;
- end if;
- end process;
-
- mdio_oe_pro: process(sys_rst,sys_clk)
- begin
- if sys_rst = '1' then
- phy_mdio_oe <= '0';
- elsif rising_edge(sys_clk) then
- case(pstate) is
- when idle | read_data =>
- phy_mdio_oe <= '0';
-
- when preamble | start | op_write | op_read | write_addr | write_data =>
- phy_mdio_oe <= '1';
-
- when turn_around =>
- if reg_rdwr_r = '1' then
- phy_mdio_oe <= '1';
- else
- phy_mdio_oe <= '0';
- end if;
-
- when others =>
- phy_mdio_oe <= '0';
-
- end case;
- end if;
- end process;
- end architecture;