【以太网通信】MDIO 管理接口及控制器设计

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

目录

1 MDIO 管理接口

2 MDIO 控制器设计


1 MDIO 管理接口

【以太网通信】MDIO 管理接口及控制器设计_第1张图片

        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 对应芯片手册。

2 MDIO 控制器设计

        MDIO 管理接口分读和写两种操作方式,读/写帧格式见下表。

【以太网通信】MDIO 管理接口及控制器设计_第2张图片【以太网通信】MDIO 管理接口及控制器设计_第3张图片

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

【以太网通信】MDIO 管理接口及控制器设计_第4张图片

可以使用有限状态机设计 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;

你可能感兴趣的:(FPGA,以太网通信,fpga开发,以太网通信)