以太网概述
以太网(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个:基本控制寄存器、基本状态寄存器、特定状态寄存器。
- 基本控制寄存器(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:自协商重启完成。 - 基本状态寄存器(Basic Status Register,Address 0x01),简写为:BSR,各个位的说明如下图所示:
部分常用位的说明如下:
Bit[5]:自协商完成 1:自协商完成 0:正在进行自协商;
Bit[2]:连接状态, 1:连接成功 0:连接失败。 - 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]}]