为什么做这个实验:
以太网通讯,需要借助PHY芯片。PHY芯片,完成了差分信号与数字信号之间的转换,是fpga与网口之间通信的桥梁。
在以太网通信中,设备之间的物理层链路均由 PHY 芯片建立。 PHY 芯片有一个配置接口,即 MDIO 接口,可以配置 PHY 芯片的工作模式以及获取 PHY 芯片的若干状态信息。
简介:
以太网:
以太网(Ethernet)是当今现有局域网采用的最通用的通信协议标准, 该标准定义了在局域网中采用的电缆类型和信号处理方法。以太网的分类有标准以太网(10Mbit/s)、 快速以太网(100Mbit/s)和千兆以太网( 1000Mbit/s) 。随着以太网技术的飞速发展, 市场上也出现了万兆以太网(10Gbit/s)。在实际应用中, 千兆以太网理论上最高通信速率为 1000Mbit/s,可以胜任大部分的使用场景。
MDC/MDIO,英文全称Management Data Clock和Management Data Input/Output,是以太网标准IEEE802.3中专门用于MAC和PYH之间管理的串行接口总线,其中MDC是总线时钟信号,MDIO为数据线。
RJ45接口:
以太网通信离不开连接端口的支持, 网络数据连接的端口就是以太网接口。 以太网接口类型有 RJ45 接口(水晶头)、 RJ11 接口(电话线接口)、 SC 光纤接口等。
不同速度下,RJ45接口有不同的定义:
(1)10M/100M 通信速率下的定义
(2)1000M 的通信速率下, RJ45 插座的 8 根线都有用到,且都是双向引脚。
PHY与MDIO:
PHY:
PHY个人理解它应该是辅助以太网通信的数据转换芯片。
从硬件的角度来说,以太网接口电路主要由 MAC(Media Access Control)控制器和物理层接口 PHY(Physical Layer, PHY)两大部分构成。 MAC 指媒体访问控制子层协议,它和 PHY 接口既可以整合到单颗芯片内,也可以独立分开,对于本次设计来说, MAC 控制器由 FPGA 实现, PHY 芯片指开发板板载的以太网芯片。
PHY 在发送数据的时候,接收 MAC 发过来的数据(对 PHY 来说,没有帧的概念,都是数据而不管什么地址,数据还是 CRC),把并行数据转化为串行流数据,按照物理层的编码规则把数据编码转换为模拟信号发送出去,接收数据时的流程反之。
如图:
PHY 还提供了和对端设备连接的重要功能,并通过 LED 灯显示出自己目前的连接状态和工作状态。当我们给网卡接入网线的时候, PHY 芯片不断发出脉冲信号来检测对端是否有设备,它们通过标准的“语言”交流,互相协商并确定连接速度、双工模式、是否采用流控等。通常情况下,协商的结果是两个设备中能同时支持的最大速度和最好的双工模式。这个技术被称为AutoNegotiation,即自协商。
补充以太网芯片PHY的介绍:
地址:
YT8531 芯片的 PHY 地址由 PHYAD0、 PHYAD1 和 PHYAD2 引脚决定,如下图所示: PHY 地址一共有3 位,我们可以通过硬件电路设置 PHYAD[2:0]引脚为上拉或者下拉,即分配为高低电平, 0 或 1,从而表示不同的地址。
所以地址为:0000_0100 == 0x04
复位:
YT8531 芯片复位后, PHY 内部寄存器的数据会恢复默认的状态,并且重新开始和 MAC 进行自协商。YT8531 支持两种复位方式,一种是硬件复位,另外一种是软件复位。硬件复位时通过 PHY_RST_N 引脚实现对 PHY 芯片的复位,当 ETH_RST_N 引脚持续 10ms 的低电平时,即可实现对 PHY 芯片的复位。软件复位通过向寄存器地址 0x00 的 Bit[15]写入 1 进行复位,并且在完成复位后,该位会自动清零。
寄存器:
YT8531 共有 22 位寄存器,这里我们仅介绍本实验用到的三个寄存器,控制寄存器、状态寄存器以及PHY 芯片具体状态寄存器。
可以知道的是这三个寄存器,每个都是16bit。哈哈。
MDIO 接口:
MAC 和 PHY 芯片有一个配置接口,即 MDIO 接口,可以配置 PHY 芯片的工作模式以及获取 PHY 芯片的若干状态信息。 PHY 芯片内部包含一系列寄存器,用户通过这些寄存器来配置 PHY 芯片的工作模式以及获取 PHY 芯片的若干状态信息,如连接速率、双工模式、自协商状态等。 FPGA 通过 MDIO 接口对 PHY芯片内部的寄存器进行配置。通常情况下, PHY 芯片在默认状态下也可以正常工作,在做以太网通信实验时,对 MDIO 接口的配置不是必须的,本章旨在向大家介绍 MDIO 接口以及如何对 MDIO 接口进行读写操作。 MAC 和 PHY 连接示意图如下图所示:
MDIO 接口也称为 SMI 接口(Serial Management Interface,串行管理接口),包括 ETH_MDC(数据管理时钟)和 ETH_MDIO(数据管理输入输出)两条信号线。 ETH_MDC 为 ETH_MDIO 提供时钟, ETH_MDC的最大时钟不能超过 12.5Mhz。 ETH_MDIO 为双向数据引脚,既用于发送数据,也用于接收数据。
MDIO 接口的读写通信帧格式如下图所示:
读命令:先是MAC控制(MDIO),在TA状态,MAC第一位先释放(MDIO),然后PHY拉低(MDIO),然后PHY控制(MDIO),MAC去根据MDC时钟信号,去采集数据(接收/读,PHY发送的数据)。
读时序:
分析:
经过观察,MDIO数据线,数据变化的时间点是在MDC的下降沿,MAC或者PHY对MDIO数据线采样的时间点是在MDC的上升沿。
也就是说在MDC上升沿附近,MDIO数据要保持稳定。下降沿,MDIO可以改变数据。
读时序中,先由MAC控制MDIO发送“32bit前导码”,然后发送“2bit帧开始信号01”,然后发送“2bit操作码10(读op)”,然后发送“5bitPHY的地址”,然后发送“5bit寄存器地址”,然后MAC“释放MDIO,产生ibit的高阻态,下一位由PHY去拉低MDIO,PHY接管数据线MDIO”.
然后PHY,发送16bit数据。
发送完,MDIO处于空闲状态,PHY与MAC都不驱动MDIO,一般由上拉电阻拉高MDIO。
以下是”原子哥“的分析:
写时序
分析:
看图,写时序中,MDIO全程由MAC控制,(后面发送完MDIO无驱动)。
写时序中,先由MAC控制MDIO发送“32bit前导码”,然后发送“2bit帧开始信号01”,然后发送“2bit擦操作码01”,然后发送“5bitPHY地址”,然后发送“5bit寄存器地址”,然后发送“2bit转向码10,(无转向)”,然后发送“16bit的数据,写入PHY的内部寄存器中”。然后MAC释放MDIO,MDIO被上拉电阻拉高或者为高阻态。
以下是“原子哥”的分析:
硬件电路:
以太网的数据传输离不开以太网 PHY(物理层)芯片的支持, 物理层定义了数据发送与接收所需要的电信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。的领航者开发板上使用的 PHY 芯片为裕太车通公司的 YT8531 或 YT8511(原理图上两款芯片设计稍有差异,但网口驱动部分的例程是完全兼容的),其原理图如下图所示:
实验任务:
本节实验任务是使用领航者 ZYNQ 开发板上的以太网接口,完成 MDIO 接口的读写测试实验。板载的触摸按键(TPAD)控制 MDIO 接口进行软复位,并通过两个 LED 灯实时指示当前网口的连接速度。当 LED0 灯亮的时候,表示当前的网口速率为 10Mbps;当 LED1 亮的时候,表示当前网口的速率为100Mbps;当两个 LED 都亮的时候,表示当前网口的速率为 1000Mbps;当两个 LED 灯都熄灭时,说明当前网络自协商失败,硬件或者网络存在异常。
软件复位:往PHY的控制寄存器中写入数据,bit[15] == 1。
硬件复位:直接给PHY_RST_N端口输入低电平。
总体模块设计:
代码:
`include "para.v"
module phy_ctrl (
input wire sys_clk ,
input wire sys_rst_n ,
input wire wr_done ,
input wire [15:0] read_data ,
input wire read_ack ,
input wire soft_rst_flag ,
output reg wr_trigger ,
output reg wr_flag ,
output reg [15:0] write_data ,
output reg [11:0] phy_addr ,
output reg [1:0] led_out
);
// parameter
// parameter `MAX_CNT_100MS = 5_000_000 ;
// localparam
localparam
ADDR_0X00 = 5'h00 ,
ADDR_0X01 = 5'h01 ,
ADDR_0X11 = 5'h11 ,
IDLE = 2'b00 ,
READ0X01 = 2'b01 ,
READ0X11 = 2'b11 ,
WRITE0X00 = 2'b10 ;
// reg
reg [1:0] state_c ;
reg [1:0] state_n ;
reg [23:0] cnt_100ms ;
wire add_cnt_100ms ;
wire end_cnt_100ms ;
reg [1:0] eth_speed_reg ;
reg soft_rst_req ;
// wire
wire IDLEtoREAD0X01 ;
wire IDLEtoWRITE0X00 ;
wire READ0X01toREAD0X11 ;
wire READ0X11toIDLE ;
wire WRITE0X00toIDLE ;
/******************************************************************************************
********************************************main code**************************************
*******************************************************************************************/
// reg signal description
// reg [23:0] cnt_100ms ;
// wire add_cnt_100ms ;
// wire end_cnt_100ms ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_100ms <= 24'd0 ;
else if(add_cnt_100ms) begin
if(end_cnt_100ms)
cnt_100ms <= 24'd0 ;
else
cnt_100ms <= cnt_100ms + 1'b1 ;
end
else
cnt_100ms <= 24'd0 ; // 注意这里,是保持还是归零。
end
// wire add_cnt_100ms ; wire end_cnt_100ms ;
assign add_cnt_100ms = (state_c != WRITE0X00) ;
assign end_cnt_100ms = (add_cnt_100ms && (cnt_100ms == `MAX_CNT_100MS - 1)) ;
// reg [1:0] eth_speed_reg ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
eth_speed_reg <= 2'b11 ;
else if(READ0X11toIDLE)
eth_speed_reg <= read_data[15:14] ;
else if(soft_rst_req)
eth_speed_reg <= 2'b11 ;
else
eth_speed_reg <= eth_speed_reg ;
end
// reg soft_rst_req ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
soft_rst_req <= 1'b0 ;
else if(IDLEtoWRITE0X00)
soft_rst_req <= 1'b0 ;
else if(soft_rst_flag)
soft_rst_req <= 1'b1 ;
else
soft_rst_req <= soft_rst_req ;
end
// reg [1:0] state_c ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= IDLE ;
else
state_c <= state_n ;
end
// reg [1:0] state_n ;
always @(*) begin
case(state_c)
IDLE : if(IDLEtoREAD0X01)
state_n = READ0X01 ;
else if(IDLEtoWRITE0X00)
state_n = WRITE0X00 ;
else
state_n = IDLE ;
READ0X01 : if(READ0X01toREAD0X11)
state_n = READ0X11 ;
else
state_n = READ0X01 ;
READ0X11 : if(READ0X11toIDLE)
state_n = IDLE ;
else
state_n = READ0X11 ;
WRITE0X00 : if(WRITE0X00toIDLE)
state_n = IDLE ;
else
state_n = WRITE0X00 ;
default : state_n = IDLE ;
endcase
end
// wire signal description
assign IDLEtoREAD0X01 = (state_c == IDLE ) && (end_cnt_100ms ) ; // 每隔100ms进行一次读。
assign IDLEtoWRITE0X00 = (state_c == IDLE ) && (soft_rst_req ) ; // 仿真时这里加了取反。
assign READ0X01toREAD0X11 = (state_c == READ0X01 ) && (wr_done ) && ((read_data[5]) && (read_data[2])) ; // 读到的基础状态寄存器,完成自协商与链接。
assign READ0X11toIDLE = (state_c == READ0X11 ) && (wr_done ) ; // 读到网口链接速率。
assign WRITE0X00toIDLE = (state_c == WRITE0X00 ) && (wr_done ) ; // 写时序完成。
// Output signal description
// output reg wr_trigger ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_trigger <= 1'b0 ;
else if(end_cnt_100ms || IDLEtoWRITE0X00 || READ0X01toREAD0X11)
wr_trigger <= 1'b1 ;
else
wr_trigger <= 1'b0 ;
end
// output reg wr_flag ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_flag <= 1'b0 ;
else if(IDLEtoWRITE0X00)
wr_flag <= 1'b1 ;
else
wr_flag <= 1'b0 ;
end
// output reg [15:0] write_data ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
write_data <= 16'h9140; //Bit[15]=1'b1,表示软复位 1001 0001 0100 0000
else
write_data <= write_data;
end
// output reg [11:0] phy_addr ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
phy_addr <= 12'd0 ;
else if(IDLEtoWRITE0X00)
phy_addr <= {5'h04,ADDR_0X00,2'b10} ; // 器件地址5'h04 + 写基础控制寄存器 + 10(转向码)
// phy_addr <= {5'h04,5'h05,2'b10} ; // 仿真时,往0x05中写读数据
else if(IDLEtoREAD0X01)
phy_addr <= {5'h04,ADDR_0X01,2'b10} ;
// phy_addr <= {5'h04,5'h05,2'b10} ;
else if(READ0X01toREAD0X11)
phy_addr <= {5'h04,ADDR_0X11,2'b10} ;
// phy_addr <= {5'h04,5'h05,2'b10} ;
else
phy_addr <= phy_addr ;
end
// output reg [1:0] led_out
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
led_out <= 2'b10 ;
else
case (eth_speed_reg)
2'b00: led_out <= 2'b00 ; // 10Mbps
2'b01: led_out <= 2'b01 ; // 100Mbps
2'b10: led_out <= 2'b11 ; // 1000Mbit/s
2'b11: led_out <= 2'b10 ;
default:led_out <= 2'b10 ;
endcase
end
endmodule
module phy_interface (
input wire sys_clk ,
input wire sys_rst_n ,
input wire wr_trigger ,
input wire wr_flag ,
input wire [11:0] phy_addr , // 器件地址+寄存器地址+转向码
input wire [15:0] write_data ,
inout wire mdio ,
output reg mdc ,
output reg wr_done ,
output reg read_ack ,
output reg [15:0] read_data_po
);
// parameter
parameter
MAX_CNT_DIV4 = 4 ,
STOP_W = 4'b0101 ,
STOP_R = 4'b0110 ;
// localparam
localparam
IDLE = 3'b000 ,
LEAD_CODER = 3'b001 ,
STOP_W_CODER = 3'b011 ,
STOP_R_CODER = 3'b010 ,
ADDR_W_CODER = 3'b110 ,
ADDR_R_CODER = 3'b111 ,
WRITE_DATA = 3'b101 ,
READ_DATA = 3'b100 ;
// reg
reg [2:0] state_c ;
reg [2:0] state_n ;
reg wrORrd_reg ; // 读写状态寄存器,根据传入的标志信号来判断。
reg [7:0] cnt_bit ;
reg [3:0] cnt_div4 ;
reg mdio_out ;
reg mdio_de ;
wire mdio_in ; // reg 应该也是可以的。后期会试试。
// wire
wire IDLEtoLEAD_CODER ;
wire LEAD_CODERtoSTOP_W_CODER ;
wire LEAD_CODERtoSTOP_R_CODER ;
wire STOP_W_CODERtoADDR_W_CODER ;
wire STOP_R_CODERtoADDR_R_CODER ;
wire ADDR_W_CODERtoWRITE_DATA ;
wire ADDR_R_CODERtoREAD_DATA ;
wire WRITE_DATAtoIDLE ;
wire READ_DATAtoIDLE ;
/******************************************************************************************
********************************************main code**************************************
*******************************************************************************************/
// reg signal description
// reg [2:0] state_c ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= IDLE ;
else
state_c <= state_n ;
end
// reg [2:0] state_n ;
always @(*) begin
case(state_c)
IDLE : if(IDLEtoLEAD_CODER)
state_n = LEAD_CODER ;
else
state_n = IDLE ;
LEAD_CODER : if(LEAD_CODERtoSTOP_W_CODER)
state_n = STOP_W_CODER ;
else if(LEAD_CODERtoSTOP_R_CODER)
state_n = STOP_R_CODER ;
else
state_n = LEAD_CODER ;
STOP_W_CODER : if(STOP_W_CODERtoADDR_W_CODER)
state_n = ADDR_W_CODER ;
else
state_n = STOP_W_CODER ;
STOP_R_CODER : if(STOP_R_CODERtoADDR_R_CODER)
state_n = ADDR_R_CODER ;
else
state_n = STOP_R_CODER ;
ADDR_W_CODER : if(ADDR_W_CODERtoWRITE_DATA)
state_n = WRITE_DATA ;
else
state_n = ADDR_W_CODER ;
ADDR_R_CODER : if(ADDR_R_CODERtoREAD_DATA)
state_n = READ_DATA ;
else
state_n = ADDR_R_CODER ;
WRITE_DATA : if(WRITE_DATAtoIDLE)
state_n = IDLE ;
else
state_n = WRITE_DATA ;
READ_DATA : if(READ_DATAtoIDLE)
state_n = IDLE ;
else
state_n = READ_DATA ;
default: state_n = IDLE ;
endcase
end
// wire signal description
assign IDLEtoLEAD_CODER = (state_c == IDLE ) && (wr_trigger) ;
assign LEAD_CODERtoSTOP_W_CODER = (state_c == LEAD_CODER ) && (cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 31) && (wrORrd_reg) ;
assign LEAD_CODERtoSTOP_R_CODER = (state_c == LEAD_CODER ) && (cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 31) && (~wrORrd_reg) ;
assign STOP_W_CODERtoADDR_W_CODER = (state_c == STOP_W_CODER ) && (cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 3 ) ;
assign STOP_R_CODERtoADDR_R_CODER = (state_c == STOP_R_CODER ) && (cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 3 ) ;
assign ADDR_W_CODERtoWRITE_DATA = (state_c == ADDR_W_CODER ) && (cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 11) ;
assign ADDR_R_CODERtoREAD_DATA = (state_c == ADDR_R_CODER ) && (cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 11) ;
assign WRITE_DATAtoIDLE = (state_c == WRITE_DATA ) && (cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 15) ;
assign READ_DATAtoIDLE = (state_c == READ_DATA ) && (cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 15) ;
// reg wrORrd_reg ; // 读写状态寄存器,根据传入的标志信号来判断。
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
wrORrd_reg <= 1'b0 ;
else if(wr_trigger && wr_flag) //
wrORrd_reg <= 1'b1 ;
else if((wr_trigger && ~wr_flag) || (state_c == IDLE)) // 这两句的逻辑就不能调换。
wrORrd_reg <= 1'b0 ;
else
wrORrd_reg <= wrORrd_reg ;
end
// reg [7:0] cnt_bit ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_bit <= 8'd0 ;
else
case (state_c)
IDLE : cnt_bit <= 8'd0 ;
LEAD_CODER : if((cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 31))
cnt_bit <= 8'd0 ;
else if(cnt_div4 == MAX_CNT_DIV4 - 1)
cnt_bit <= cnt_bit + 1'b1 ;
else
cnt_bit <= cnt_bit ;
STOP_W_CODER : if((cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 3))
cnt_bit <= 8'd0 ;
else if(cnt_div4 == MAX_CNT_DIV4 - 1)
cnt_bit <= cnt_bit + 1'b1 ;
else
cnt_bit <= cnt_bit ;
STOP_R_CODER : if((cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 3))
cnt_bit <= 8'd0 ;
else if(cnt_div4 == MAX_CNT_DIV4 - 1)
cnt_bit <= cnt_bit + 1'b1 ;
else
cnt_bit <= cnt_bit ;
ADDR_W_CODER : if((cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 11))
cnt_bit <= 8'd0 ;
else if(cnt_div4 == MAX_CNT_DIV4 - 1)
cnt_bit <= cnt_bit + 1'b1 ;
else
cnt_bit <= cnt_bit ;
ADDR_R_CODER : if((cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 11))
cnt_bit <= 8'd0 ;
else if(cnt_div4 == MAX_CNT_DIV4 - 1)
cnt_bit <= cnt_bit + 1'b1 ;
else
cnt_bit <= cnt_bit ;
WRITE_DATA : if((cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 15))
cnt_bit <= 8'd0 ;
else if(cnt_div4 == MAX_CNT_DIV4 - 1)
cnt_bit <= cnt_bit + 1'b1 ;
else
cnt_bit <= cnt_bit ;
READ_DATA : if((cnt_div4 == MAX_CNT_DIV4 - 1) && (cnt_bit == 15))
cnt_bit <= 8'd0 ;
else if(cnt_div4 == MAX_CNT_DIV4 - 1)
cnt_bit <= cnt_bit + 1'b1 ;
else
cnt_bit <= cnt_bit ;
default : cnt_bit <= 8'd0 ;
endcase
end
// reg [3:0] cnt_div4 ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_div4 <= 4'd0 ;
else if((state_c != IDLE)&&(cnt_div4 == (MAX_CNT_DIV4 - 1)))
cnt_div4 <= 4'd0 ;
else if(state_c != IDLE)
cnt_div4 <= cnt_div4 + 1'b1 ;
else
cnt_div4 <= 4'd0 ;
end
// reg mdio_out ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
mdio_out <= 1'b1 ;
else
case (state_c)
IDLE : mdio_out <= 1'b1 ;
LEAD_CODER : mdio_out <= 1'b1 ;
STOP_W_CODER : mdio_out <= STOP_W [ 3-cnt_bit] ;
STOP_R_CODER : mdio_out <= STOP_R [ 3-cnt_bit] ;
ADDR_W_CODER : mdio_out <= phy_addr [11-cnt_bit] ;
ADDR_R_CODER : mdio_out <= phy_addr [11-cnt_bit] ;
WRITE_DATA : mdio_out <= write_data[15-cnt_bit] ;
READ_DATA : mdio_out <= 1'b1 ;
default : mdio_out <= 1'b1 ;
endcase
end
// reg mdio_de ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
mdio_de <= 1'b0 ;
else if(((state_c == ADDR_R_CODER) && (cnt_bit >= 9) && (cnt_div4 == MAX_CNT_DIV4 - 1)) || state_c == IDLE)
mdio_de <= 1'b0 ;
else if(state_c == LEAD_CODER)
mdio_de <= 1'b1 ;
else
mdio_de <= mdio_de ;
end
// wire mdio_in ; // reg 应该也是可以的。后期会试试。
assign mdio_in = mdio ;
// Output signal description
// inout wire mdio ,
assign mdio = (mdio_de) ? mdio_out : 1'bz ;
// output reg mdc ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
mdc <= 1'b1 ;
else if((state_c != IDLE) && (cnt_div4 == 1))
mdc <= 1'b0 ;
else if((state_c != IDLE) && (cnt_div4 == MAX_CNT_DIV4 - 1))
mdc <= 1'b1 ;
else
mdc <= mdc ;
end
// output reg wr_done ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
wr_done <= 1'b0 ;
else if(WRITE_DATAtoIDLE || READ_DATAtoIDLE)
wr_done <= 1'b1 ;
else
wr_done <= 1'b0 ;
end
// output reg read_ack ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
read_ack <= 1'b0 ;
else if((state_c == ADDR_R_CODER) && (cnt_bit == 11) && (cnt_div4 == (MAX_CNT_DIV4 >> 1)) && (~mdio_in) )
read_ack <= 1'b1 ;
else
read_ack <= 1'b0 ;
end
// output reg [15:0] read_data_po
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
read_data_po <= 16'd0 ;
else if((state_c == READ_DATA) && (cnt_div4 == (MAX_CNT_DIV4 - 1)))
read_data_po[15 - cnt_bit] <= mdio_in ;
else if(state_c == LEAD_CODER)
read_data_po <= 16'd0 ;
else
read_data_po <= read_data_po ;
end
endmodule
仿真:
上板验证:
对初学者的忠告与建议:
不要完全没有自己的思考,全部照搬照抄例程,把自己的想法与设计加入进工程中。
没有大想法就加小想法,活学活用。
在学的时候不断反思:
为什么这么设计,这么设计的好处在哪里,有没有更好的设计。
设计:整体框图,模块划分,时序设计,状态机设计,代码设计。