19 MDIO 接口读写以太网PHY寄存器

以太网概述

以太网(Ethernet)是应用最普遍的局域网技术。IEEE组织的 IEEE 802.3标准制定了以太网的技术标准,它规定了包括物理层的连线、电子信号和介质访问层协议的内容。以太网凭借其成本低、通信速率高、抗干扰性强等优点被广泛应用在网络远程监控、交换机、工业自动化等对通信速率要求较高的场合。
以太网的可分为标准以太网(10Mbit/s)、快速以太网(100Mbit/s)和千兆以太网(1000Mbit/s),随着以太网技术的飞速发展,市场上也出现了万兆以太网(10Gbit/s),它扩展了 IEEE802.3 协议和 MAC 规范,使其技术支持 10Gbit/s 的传输速率。

以太网电路框图

一个典型的网络通信电路如下图所示
在这里插入图片描述
它包括MAC、PHY、以太网接口三个部分

MII接口、GMII接口、RGMII 接口

MAC 的一侧会根据具体的速率和接口模式提供GMII、RGMII、MII、RMII 等多种并行接口,以下将分别介绍MII、GMII、RGMII 这 3 种接口。
MII 接口:MII 接口应用于 100Mbps 和 10Mbps 以太网模式下,其接口信号连接关系及各信号的介绍如下
在这里插入图片描述
在这里插入图片描述
GMII 接口:GMII 接口应用于 1000Mbps 以太网模式下,其接口信号连接关系及各信号的介绍如下
在这里插入图片描述
在这里插入图片描述
RGMII 接口:RGMII 即是 GMII 的简化版本,将接口信号线数量从 24 根减少到 14 根,时钟频率仍旧为 125MHz,TX/RX 数据宽度从 8 位变为 4 位,RGMII 接口信号连接关系及各信号的介绍如下
在这里插入图片描述
在这里插入图片描述
MII、GMII、RGMII 接口对比:
在这里插入图片描述

MDIO接口

MAC 和 PHY 芯片还有一个配置接口,即 MDIO 接口,通过它可以配置 PHY 芯片的工作模式以及获取 PHY 芯片的若干状态信息(PHY 芯片内部包含一系列寄存器,通过这些寄存器来配置 PHY 芯片的工作模式以及获取 PHY 芯片的若干状态信息,如连接速率、双工模式、自协商状态等)。
MAC 和 PHY 连接示意图如下图所示:
在这里插入图片描述
MDIO 接口的读写通信帧格式如下图所示:
在这里插入图片描述
Preamble:32 位前导码,由 MAC 端发送 32 位逻辑“1”,用于同步 PHY 芯片。
ST(Start of Frame):2 位帧开始信号,用 01 表示。
OP(Operation Code):2 位操作码,读:10 写:01。
PHYAD(PHY Address):5 位 PHY 地址,用于表示与哪个 PHY 芯片通信,因此一个 MAC 上可以连接多个 PHY 芯片。
REGAD(Register Address):5 位寄存器地址,可以表示 32 个寄存器。
TA(Turnaround):2 位转向,在读命令中,MDIO 在此时由 MAC 驱动改为 PHY 驱动,在第一个 TA位,MDIO 引脚为高阻状态,第二个 TA 位,PHY 将 MDIO 引脚拉低,准备发送数据;在写命令中,不需要 MDIO 方向发生变化,MAC 固定输出 2’b10,随后开始写入数据。
DATA:16 位数据,在读命令中,PHY 芯片将对应的 PHYAD 的 REGAD 寄存器的数据写到 DATA中;在写命令中,PHY 芯片将接收到的 DATA 写入 REGAD 寄存器中。需要注意的是,在 DATA 传输的过程中,高位在前,低位在后。
IDLE:空闲状态,此时 MDIO 为无源驱动,处于高阻状态,但一般用上拉电阻使其上拉至高电平。
MDIO 接口读时序图如下图所示:
在这里插入图片描述
MDIO 接口写时序图如下图所示:
在这里插入图片描述

以太网接口

以太网接口类型有 RJ45接口、RJ11 接口(电话线接口)、SC 光纤接口等,其中 RJ45 接口是我们现在最常见的网络设备接口。
在这里插入图片描述
RJ45 接口定义以及各引脚功能在不同通信速率下的定义有区别,如下分别是10M/100M 通信速率下的定义和1000M 的通信速率下的定义
在这里插入图片描述
在这里插入图片描述

YT8531 PHY地址和常用寄存器

PHY 地址

YT8531 芯片的 PHY 地址由 PHYAD0、PHYAD1 和 PHYAD2 引脚决定,可以通过硬件电路设置 PHYAD[2:0]引脚为上拉或者下拉,即分配为高低电平(0 或 1)从而表示不同的地址。
在这里插入图片描述
在这里插入图片描述
从原理图上可以看出,以太网 PHY 芯片 PHYAD2 接上拉电阻,PHYAD1 和 PHYAD0 接下拉电阻,因此 PHY 地址为 5’h04。

PHY 复位

YT8531 芯片复位后,PHY 内部寄存器的数据会恢复默认的状态,并且重新开始和 MAC 进行自协商。YT8531 支持两种复位方式,一种是硬件复位,另外一种是软件复位。硬件复位时通过 PHY_RST_N引脚实现对 PHY 芯片的复位,当 PHY_RST_N 引脚持续 10ms 的低电平时,即可实现对 PHY 芯片的复位。软件复位通过向寄存器地址 0x00 的 Bit[15]写入 1 进行复位,并且在完成复位后,该位会自动清零。

寄存器

YT8531 共有 22 位寄存器,其中常用的主要有3个:基本控制寄存器、基本状态寄存器、特定状态寄存器。

  1. 基本控制寄存器(Basic Control Register,Address 0x00),简写为:BCR,用于芯片的复位和其它功能的控制,各个位的说明如下图所示:
    在这里插入图片描述
    部分常用位的说明如下:
    Bit[15]:软件复位,1:PHY 复位 0:正常模式;
    Bit[14]:内部环回模式,1:内部环回模式 0:正常模式;
    Bit[6] Bit[13]:选择网速带宽 只有在自动协商使能不开启的情况下有效,10:1000Mb/s 01:100Mb/s 00:10Mb/s;
    Bit[12]:自动协商使能 1:自动协商使能 0:自动协商不使能;
    Bit[9]:重启自协商,1:重新开始自协商 0:自协商重启完成。
  2. 基本状态寄存器(Basic Status Register,Address 0x01),简写为:BSR,各个位的说明如下图所示:
    在这里插入图片描述
    部分常用位的说明如下:
    Bit[5]:自协商完成 1:自协商完成 0:正在进行自协商;
    Bit[2]:连接状态, 1:连接成功 0:连接失败。
  3. PHY 特定状态寄存器(PHY Specific Status Register ,Address 0x11),简写为:PHYSR,各个位的说明如下图所示:
    在这里插入图片描述
    部分常用位的说明如下:
    Bit[15:14]:连接速度
    11:保留
    10:1000Mbps
    01:100Mbps
    00:10Mbps

YT8531原理图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码编写

此程序通过一个按键完成对PHY的复位,并通过两个LED指示PHY自动协商的网络连接速度,当 LED0 灯亮的时候,表示当前的网口速率为 10Mbps;当 LED1 亮的时候,表示当前网口的速率为100Mbps;当两个 LED 都亮的时候,表示当前网口的速率为 1000Mbps;当两个 LED 灯都熄灭时,说明当前网络自协商失败,硬件或者网络存在异常。

MDIO读写PHY寄存器模块

实现MDIO总线读写PHY寄存器的功能。

module mdio_driver #(
    parameter MDIO_CLK_PERIOD = 125             //MDIO时钟周期,以系统时钟为参考
)
(
    input  wire        sys_clk,                      //系统时钟
    input  wire        sys_rst_n,                    //系统复位

    input  wire        start,                        //启动MDIO读写寄存器操作
    input  wire        rw,                           //MDIO读写标志,1读,0写
    output wire        idle_flag,                    //MDIO空闲标志
    output wire        done_flag,                    //MDIO读写寄存器操作完成标志

    input  wire [4:0]  phy_addr,                     //phy地址
    input  wire [4:0]  reg_addr,                     //寄存器地址
    input  wire [15:0] tx_data,                      //需要写入寄存器的寄存器数据
    output reg  [15:0] rx_data,                      //从寄存器读取到的寄存器数据

    output reg         mdc,                          //MDIO时钟输出
    output reg         mdio_o,                       //MDIO数据输出
    input  wire        mdio_i,                       //MDIO数据输入
    output reg         mdio_t                        //MDIO数据方向控制,1输入,0输出
);

//时钟周期的一半
localparam MDIO_CLK_PERIOD_HALF = MDIO_CLK_PERIOD / 2;

//MDIO时钟周期计数器
reg [31:0] clk_period_count;

//MDIO传输计数
reg [7:0] transfer_count;

//MDIO忙标志
reg busy_flag;


//空闲标志输出
assign idle_flag = ~busy_flag;
//完成标志输出
assign done_flag = (transfer_count == 8'd64) ? 1'b1 : 1'b0;

//启动MDIO传输
always @(posedge sys_clk) begin
    if(!sys_rst_n)
        busy_flag <= 1'b0;
    else if((busy_flag == 1'b0) && (start == 1'b1))
        busy_flag <= 1'b1;
    else if((transfer_count == 8'd63) && (clk_period_count == (MDIO_CLK_PERIOD - 1)))
        busy_flag <= 1'b0;
    else
        busy_flag <= busy_flag;
end

//时钟分频计数
always @(posedge sys_clk) begin
    if(!sys_rst_n)
        clk_period_count <= 32'd0;
    else if(busy_flag == 1'b1) begin
        if(clk_period_count < (MDIO_CLK_PERIOD - 1))
            clk_period_count <= clk_period_count + 32'd1;
        else
            clk_period_count <= 32'd0;
    end
    else
        clk_period_count <= 32'd0;
end

//传输计数
always @(posedge sys_clk) begin
    if(!sys_rst_n)
        transfer_count <= 8'd0;
    else if(busy_flag == 1'b1) begin
        if(clk_period_count == (MDIO_CLK_PERIOD - 1))
            transfer_count <= transfer_count + 8'd1;
        else
            transfer_count <= transfer_count;
    end
    else
        transfer_count <= 8'd0;
end

//mdc输出
always @(posedge sys_clk) begin
    if(!sys_rst_n)
        mdc <= 1'd1;
    else if(busy_flag == 1'b1) begin
        if(clk_period_count < (MDIO_CLK_PERIOD_HALF - 1))
            mdc <= 1'd0;
        else
            mdc <= 1'd1;
    end
    else
        mdc <= 1'd1;
end

//控制mdio
always @(posedge sys_clk) begin
    if(!sys_rst_n) begin
        mdio_o <= 1'd1;
        mdio_t <= 1'd1;
        rx_data <= 32'h0;
    end
    else if(busy_flag == 1'b1) begin
        if((transfer_count >= 0) && (transfer_count < 32)) begin
            //32位前导码,即 32 位逻辑"1"
            mdio_t <= 1'd0;
            mdio_o <= 1'd1;
        end
        
        if((transfer_count >= 32) && (transfer_count < 34)) begin
            //2 位帧开始信号,用 01 表示
            mdio_t <= 1'd0;
            case(transfer_count)
                8'd32: mdio_o <= 1'b0;
                8'd33:mdio_o <= 1'b1;
            endcase
        end
        
        if((transfer_count >= 34) && (transfer_count < 36)) begin
            //2 位操作码,读:10 写:01
            mdio_t <= 1'd0;
            if(rw == 1'b1) begin
                case(transfer_count)
                    8'd34: mdio_o <= 1'b1;
                    8'd35:mdio_o <= 1'b0;
                endcase
            end
            else begin
                case(transfer_count)
                    8'd34: mdio_o <= 1'b0;
                    8'd35:mdio_o <= 1'b1;
                endcase
            end
        end
        
        if((transfer_count >= 36) && (transfer_count < 41)) begin
            //5 位 PHY 地址,用于表示与哪个 PHY 芯片通信
            mdio_t <= 1'd0;
            mdio_o <= phy_addr[4 - (transfer_count - 36)];
        end
        
        if((transfer_count >= 41) && (transfer_count < 46)) begin
            //5 位寄存器地址,可以表示 32 个寄存器
            mdio_t <= 1'd0;
            mdio_o <= reg_addr[4 - (transfer_count - 41)];
        end
        
        if((transfer_count >= 46) && (transfer_count < 48)) begin
            //2 位转向
            //在读命令过程中,第一个 TA 位 MAC 将 MDIO 引脚为高阻状态,第二个 TA 位,PHY 将 MDIO 引脚拉低
            //在写命令过程中,不需要 MDIO 方向发生变化,MAC 固定输出 2'b10
            if(rw == 1'b1) begin
                mdio_t <= 1'd1;
                mdio_o <= 1'd1;
            end
            else begin
                mdio_t <= 1'd0;
                case(transfer_count)
                    8'd46: mdio_o <= 1'b1;
                    8'd47:mdio_o <= 1'b0;
                endcase
            end
        end
        
        if((transfer_count >= 48) && (transfer_count < 64)) begin
            //16 位数据
            if(rw == 1'b1) begin
                mdio_t <= 1'd1;
                //在上升沿时刻采样
                if(clk_period_count == (MDIO_CLK_PERIOD_HALF - 1))
                    rx_data[15 - (transfer_count - 48)] = mdio_i;
            end
            else begin
                mdio_t <= 1'd0;
                mdio_o <= tx_data[15 - (transfer_count - 48)];
            end
        end
    end
    else begin
        mdio_o <= 1'd1;
        mdio_t <= 1'd1;
    end
end

endmodule

YT8531 PHY控制模块

实现PHY复位,并在复位完成后轮询连接状态的功能。

module yt8531_ctrl #(
    parameter MDIO_CLK_PERIOD = 125,                    //MDIO时钟周期,以系统时钟为参考
    parameter IDLE_DELAY_COUNT_MAX = 32'd1_000_000      //空闲状态计数器,当在空闲状态计数达到此值则退出空闲状态
)
(
    input  wire        sys_clk,                         //系统时钟
    input  wire        sys_rst_n,                       //系统复位

    input  wire        soft_rst_req,                    //软件复位请求
    output reg         soft_rst_busy,                   //软件忙标志,1'b1以收到软件复位请求,即将复位或者正在复位,1'b0 软件复位完成
    output reg  [1:0]  link_state,                      //连接状态2'b11 1000Mbps,2'b10 100Mbps,2'b01 10Mbps,2'b00 其他情况

    output wire        mdc,                             //MDIO时钟输出
    output wire        mdio_o,                          //MDIO数据输出
    input  wire        mdio_i,                          //MDIO数据输入
    output wire        mdio_t                           //MDIO数据方向控制,1输入,0输出
);

localparam IDLE_STATE = 8'b0000001;                     //空闲状态
localparam WRITE_BCR_STATE = 8'b0000010;                //写BCR状态,向BCR写入16'h9140,用于复位PHY
localparam READ_BSR_STATE = 8'b0000100;                 //读BSR状态,通过BSR的bit5可以得到自协商状态,通过通过BSR的bit2可以得到连接状态
localparam READ_PHYSR_STATE = 8'b0001000;               //读PHYSR状态,通过PHYSR的bit[15:14]可以得到连接速率

reg  [3:0]  mdio_start_flag;                            //MDIO传输启动标志,启动时置1,传输完成置0
reg         mdio_start;                                 //启动MDIO读写寄存器操作
reg         mdio_rw;                                    //MDIO读写标志,1读,0写
wire        mdio_idle_flag;                             //MDIO空闲标志
wire        mdio_done_flag;                             //MDIO读写寄存器操作完成标志

reg  [4:0]  mdio_phy_addr;                              //phy地址
reg  [4:0]  mdio_reg_addr;                              //寄存器地址
reg  [15:0] mdio_tx_data;                               //需要写入寄存器的寄存器数据
wire [15:0] mdio_rx_data;                               //从寄存器读取到的寄存器数据

reg  [31:0] idle_count;                                 //空闲状态计数器

reg  [15:0] BSR;                                        //BSR寄存器的值
reg  [15:0] PHYSR;                                      //PHYSR寄存器的值

reg  [7:0]  current_state;                              //当前状态
reg  [7:0]  next_state;                                 //下一刻的状态
reg  [3:0]  state_done;                                 //当前状态结束标志,切换到下一个状态

//处理复位请求,当soft_rst_req收到复位请求时将soft_rst_busy设置为1,当复位结束时将soft_rst_busy设置为0
//上电默认进行一次复位
always @(posedge sys_clk) begin
    if(!sys_rst_n)
        soft_rst_busy <= 1'b1;
    else if((soft_rst_req == 1'b1) && (soft_rst_busy == 1'b0))
        soft_rst_busy <= 1'b1;
    else if((current_state == WRITE_BCR_STATE) && (state_done[1] == 1'b1))
        soft_rst_busy <= 1'b0;
    else
        soft_rst_busy <= soft_rst_busy;
end

//状态跳转
always @(posedge sys_clk) begin
    if(!sys_rst_n)
        current_state <= IDLE_STATE;
    else
        current_state <= next_state;
end

//根据当前状态确定下一刻状态
always @(*) begin
    case(current_state)
        IDLE_STATE: begin
            if((state_done == 4'b0) && (soft_rst_busy == 1'b1))
                next_state = WRITE_BCR_STATE;
            else if(idle_count == (IDLE_DELAY_COUNT_MAX - 1))
                next_state = READ_BSR_STATE;
            else
                next_state = IDLE_STATE;
        end
        WRITE_BCR_STATE: begin
            if(state_done[1] == 1'b1)
                next_state = IDLE_STATE;
            else
                next_state = WRITE_BCR_STATE;
        end
        READ_BSR_STATE: begin
            if(state_done[2] == 1'b1)
                next_state = READ_PHYSR_STATE;
            else
                next_state = READ_BSR_STATE;
        end
        READ_PHYSR_STATE: begin
            if(state_done[3] == 1'b1)
                next_state = IDLE_STATE;
            else
                next_state = READ_PHYSR_STATE;
        end
        default: begin
            next_state = IDLE_STATE;
        end
    endcase
end

//控制寄存器读写
always @(posedge sys_clk) begin
    if(!sys_rst_n) begin
        mdio_start_flag <= 4'b0;
        mdio_start <= 1'b0;
        mdio_rw <= 1'b0;
        mdio_phy_addr <= 5'h00;
        mdio_reg_addr <= 5'h00;
        mdio_tx_data <= 16'h0000;
        state_done <= 4'b0;
    end
    else begin
        case(current_state)
            IDLE_STATE: begin
                mdio_start_flag <= 4'b0;
                mdio_start <= 1'b0;
                mdio_rw <= 1'b0;
                mdio_phy_addr <= 5'h00;
                mdio_reg_addr <= 5'h00;
                mdio_tx_data <= 16'h00;
                state_done <= 4'b0;
            end
            WRITE_BCR_STATE: begin
                if((mdio_idle_flag == 1'b1) && (mdio_start == 1'b0) && (mdio_start_flag[1] == 1'b0)) begin
                    mdio_start_flag[1] <= 1'b1;
                    mdio_start <= 1'b1;
                    mdio_rw <= 1'b0;
                    mdio_phy_addr <= 5'h04;
                    mdio_reg_addr <= 5'h00;
                    mdio_tx_data <= 16'h9140;
                end
                else if(mdio_idle_flag == 1'b0)
                    mdio_start <= 1'b0;
                else
                    mdio_start <= mdio_start;

                if((mdio_start_flag[1] == 1'b1) && (mdio_done_flag == 1'b1))
                    state_done[1] <= 1'b1;
                else
                    state_done[1] <= state_done[1];
            end
            READ_BSR_STATE: begin
                if((mdio_idle_flag == 1'b1) && (mdio_start == 1'b0) && (mdio_start_flag[2] == 1'b0)) begin
                    mdio_start_flag[2] <= 1'b1;
                    mdio_start <= 1'b1;
                    mdio_rw <= 1'b1;
                    mdio_phy_addr <= 5'h04;
                    mdio_reg_addr <= 5'h01;
                    mdio_tx_data <= 16'h0000;
                end
                else if(mdio_idle_flag == 1'b0)
                    mdio_start <= 1'b0;
                else
                    mdio_start <= mdio_start;

                if((mdio_start_flag[2] == 1'b1) && (mdio_done_flag == 1'b1))
                    state_done[2] <= 1'b1;
                else
                    state_done[2] <= state_done[2];
            end
            READ_PHYSR_STATE: begin
                if((mdio_idle_flag == 1'b1) && (mdio_start == 1'b0) && (mdio_start_flag[3] == 1'b0)) begin
                    mdio_start_flag[3] <= 1'b1;
                    mdio_start <= 1'b1;
                    mdio_rw <= 1'b1;
                    mdio_phy_addr <= 5'h04;
                    mdio_reg_addr <= 5'h11;
                    mdio_tx_data <= 16'h0000;
                end
                else if(mdio_idle_flag == 1'b0)
                    mdio_start <= 1'b0;
                else
                    mdio_start <= mdio_start;

                if((mdio_start_flag[3] == 1'b1) && (mdio_done_flag == 1'b1))
                    state_done[3] <= 1'b1;
                else
                    state_done[3] <= state_done[3];
            end
            default: begin
                mdio_start_flag <= 4'b0;
                mdio_start <= 1'b0;
                mdio_rw <= 1'b0;
                mdio_phy_addr <= 5'h00;
                mdio_reg_addr <= 5'h00;
                mdio_tx_data <= 16'h00;
                state_done <= 4'b0;
            end
        endcase
    end
end

//锁存读取的寄存器值
always @(posedge sys_clk) begin
    if(!sys_rst_n) begin
        BSR <= 16'h0;
        PHYSR <= 16'h0;
    end
    else begin
        case(current_state)
            WRITE_BCR_STATE: begin
                BSR <= 16'h0;
                PHYSR <= 16'h0;
            end
            READ_BSR_STATE: begin
                if(mdio_done_flag == 1'b1)
                    BSR <= mdio_rx_data;
                else
                    BSR <= BSR;
            end
            READ_PHYSR_STATE: begin
                if(mdio_done_flag == 1'b1)
                    PHYSR <= mdio_rx_data;
                else
                    PHYSR <= PHYSR;
            end
            default: begin
                BSR <= BSR;
                PHYSR <= PHYSR;
            end
        endcase
    end
end

//空闲状态计时
always @(posedge sys_clk) begin
    if(!sys_rst_n)
        idle_count <= 32'h0;
    else if(current_state == IDLE_STATE) begin
        if(idle_count < (IDLE_DELAY_COUNT_MAX - 1))
            idle_count <= idle_count + 32'b1;
        else
            idle_count <= idle_count;
    end
    else
        idle_count <= 32'h0;
end

//连接状态输出
always @(posedge sys_clk) begin
    if(!sys_rst_n)
        link_state <= 2'b00;
    else if((BSR[5] == 1'b1) && (BSR[2] == 1'b1)) begin
        if(PHYSR[15:14] == 2'b11)
            link_state <= 2'b00;
        else if(PHYSR[15:14] == 2'b10)
            link_state <= 2'b11;
        else if(PHYSR[15:14] == 2'b01)
            link_state <= 2'b10;
        else
            link_state <= 2'b01;
    end
    else
        link_state <= 2'b00;
end

mdio_driver #(
    .MDIO_CLK_PERIOD(MDIO_CLK_PERIOD)
)
u_mdio_driver_inst0(
    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),

    .start(mdio_start),
    .rw(mdio_rw),
    .idle_flag(mdio_idle_flag),
    .done_flag(mdio_done_flag),

    .phy_addr(mdio_phy_addr),
    .reg_addr(mdio_reg_addr),
    .tx_data(mdio_tx_data),
    .rx_data(mdio_rx_data),

    .mdc(mdc),
    .mdio_o(mdio_o),
    .mdio_i(mdio_i),
    .mdio_t(mdio_t)
);

endmodule

顶层模块编写

在顶层模块中实现了按键上升沿检测,并例化了“YT8531 PHY控制模块”模块。

module mdio_test #(
    parameter MDIO_CLK_PERIOD = 12,                     //MDIO时钟周期,以系统时钟为参考
    parameter IDLE_DELAY_COUNT_MAX = 32'd1_000_000      //空闲状态计数器,当在空闲状态计数达到此值则退出空闲状态
)
(
    input  wire        sys_clk,                         //系统时钟
    input  wire        sys_rst_n,                       //系统复位

    input  wire        touch_key,                       //触摸按键
    output wire [1:0]  led,                             //LED连接速率指示

    output wire        mdc,                             //MDIO时钟输出
    inout  wire        mdio,                            //MDIO数据
    output wire        eth_rst                          //以太网复位信号
);

wire        soft_rst_req;                 //软件复位请求
wire        soft_rst_busy;                //软件忙标志,1'b1以收到软件复位请求,即将复位或者正在复位,1'b0 软件复位完成
wire [1:0]  link_state;                   //连接状态2'b11 1000Mbps,2'b10 100Mbps,2'b01 10Mbps,2'b00 其他情况

wire        mdio_o;                       //MDIO数据输出
wire        mdio_i;                       //MDIO数据输入
wire        mdio_t;                       //MDIO数据方向控制,1输入,0输出

//外部输入按键延时3拍,用于消除亚稳态,并检测上升沿(在FPGA中,对于外部输入信号均需要延迟两拍在使用,以消除亚稳态)
reg         key_d1;
reg         key_d2;
reg         key_d3;


//按键信号延迟3拍
always @(posedge sys_clk) begin
    if(!sys_rst_n) begin
        key_d1 <= 1'b1;
        key_d2 <= 1'b1;
        key_d3 <= 1'b1;
    end
    else begin
        key_d1 <= touch_key;
        key_d2 <= key_d1;
        key_d3 <= key_d2;
    end
end

//按键下降沿检测
assign soft_rst_req = key_d3 & (~key_d2);

//硬件复位
assign eth_rst = sys_rst_n;

//连接状态输出
assign led = link_state;

//用IOBUF将mdio合并
IOBUF u_IOBUF_inst0(
    .IO(mdio),      //连接到外部IO引脚

    .T(mdio_t),     //输入输出控制,0输出(将I输出到IO),1输入(将IO设置为高阻态,此时O为IO输入电平)
    .I(mdio_o),     //IOBUF输入,应接用户逻辑输出
    .O(mdio_i)      //IOBUF输出,应接用户逻辑输入
);

//例化YT8532控制模块
yt8531_ctrl #(
    .MDIO_CLK_PERIOD(MDIO_CLK_PERIOD),
    .IDLE_DELAY_COUNT_MAX(IDLE_DELAY_COUNT_MAX)
)
yt8531_ctrl_inst0(
    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),

    .soft_rst_req(soft_rst_req),
    .soft_rst_busy(soft_rst_busy),
    .link_state(link_state),

    .mdc(mdc),
    .mdio_o(mdio_o),
    .mdio_i(mdio_i),
    .mdio_t(mdio_t)
);

endmodule

约束文件

#------------------------------系统时钟和复位-----------------------------------
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
#----------------------以太网---------------------------
set_property -dict {PACKAGE_PIN M20 IOSTANDARD LVCMOS33} [get_ports mdc]
set_property -dict {PACKAGE_PIN N22 IOSTANDARD LVCMOS33} [get_ports mdio]
set_property -dict {PACKAGE_PIN N20 IOSTANDARD LVCMOS33} [get_ports eth_rst]
#----------------------------------触摸按键-------------------------------------
set_property -dict {PACKAGE_PIN V8 IOSTANDARD LVCMOS15} [get_ports touch_key]
#-----------------------------------LED-----------------------------------------
set_property -dict {PACKAGE_PIN V9 IOSTANDARD LVCMOS15} [get_ports {led[0]}]
set_property -dict {PACKAGE_PIN Y8 IOSTANDARD LVCMOS15} [get_ports {led[1]}]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值