通过MDIO(输入输出数据引脚,可以读取,也可以写入,需要三态门控制)和MDC(时钟)两个引脚对PHY芯片进行配置信息,读取信息(包括读取速度,连接状态等)。在用以太网时,PHY芯片首先要进行自协商,确定对方接口速度(千兆/百兆,向下协商自适应)
1. MDIO协议时序(上图下面部分)
协议格式如下:
1.Pre:前导码,32bit 全是1,用来同步通信 32bit
2.Start:开始字段,01表示开始通信 2bit
3.Read OP(operation):用来标识都还是写,读:10;写:01 2bit
4.PHY Address:PHY地址(用来标识链接哪一个PHY芯片),查看原理图 5bit
5.Reg Address:寄存器地址(包括状态寄存器等多种类型,RO:只读;RW:可读可写) 5bit
6.Turn Around:转变,Z0,高阻态低电平,双向数据线,需要Z来缓冲。MAC代表主机,在发送完寄存器地址后,想要读取对方的PHY芯片,此时主机方的数据线要拉成高阻态(挂一个上拉电阻),这样对对方PHY芯片没有影响,PHY芯片从高阻态拉低以后就可以传输数据。因为就一根数据线,这两个bit相当于起到缓冲作用。 2bit
7.Reg Data:数据,写入数据或读出数据。 16bit
8.idle:空闲 1bit
一共65bit
协议时序如下:
需要操作的寄存器:
1.读出Link状态:BMSR(base mode status regester,状态寄存器):0x01,协议格式中Reg_Data第三个bit代表连接状态
2.读出自协商的结果:BMCR(base mode control regester,控制寄存器):0x00(16bit),协议格式中Reg_Data的第6和13位代表速度
更正:真值表中10状态代表1000Mbps.
代码架构
两个部分:MDIO驱动模块:产生数据,并以串行方式发送出去;MDIO数据控制模块;两个模块间会有数据交互:
控制模块向驱动模块的信号有:1.Phy_addr 2.Reg_addr 3.WriteH_ReadL(读写状态) 4.写操作的话要有Reg_write_data
5.操作有效信号Operation_valid
驱动模块向控制模块的信号有:1.读操作的话有Reg_read_data 2.操作准备信号Operation_ready(和上面的5作为握手信号)
将两个模块封装,下面介绍封装好后大模块信号:
i_clk:输入时钟信号
i_rst:输入复位信号
o_phy_link:反馈网卡链接状态信号
o_phy_speed:网卡速度
o_valid:指示当前link和speed两个状态是有效的(可有可无)
Phy_mdio:输出PHY芯片的数据
Phy_mdc:输出PHY芯片的时钟
输入参数信号:
CLK_DIV_NUMBER:输入时钟分频值,在FPGA中,PLL最小可以分频出1Mhz时钟,但MDIO总线速率一般在几十K左右,因此要对1M时钟进一步分频
OPERATION_NUMBER:操作周期,读取PHY芯片的时间间隔
另外要增加一个模块:
CLK_180_Shift模块: 因为在FPGA中,时钟上升沿和数据改变是同沿的,不满足PHY芯片的时序关系,因此要在内部i_clk时钟有一个180°的反相,输出phy_MDC;而i_clk要连接MDIO驱动模块Mdio_clk,保证相位不变。因此phy_MDC和Mdio_clk相位相差180°,保证PHY芯片时钟上升沿时刻,数据在中间稳定状态,不会出现亚稳态。
代码部分
1.Phy_Init_module: 顶层模块,调用Phy_drive,可以包含很多PHY芯片进行配置
2.Phy_drive: mdio控制模块,对一个以太网PHY芯片进行配置,调用Mdio_Drive
3.Mdio_Drive: mdio驱动模块,产生MDIO时序
设计方法:从最底层开始设计
debug心得
这里是自保持,应该是寄存器本身信号,不要粗心。
Phy_drive的握手信号这里,应该是寄存器的有效保持(作为mdio_drive的输入信号r_operation_act,表明phy_drive读有效,不要写成mdio_drive的读有效信号w_reg_read_vaild);和写准备好(是mdio_drive的输出信号),谨记,要搞清楚有效信号。
PHY芯片的时钟与mdio时钟差 180°,不要忘记。
延迟一拍的一种写法,可以学习一下。
以及三态门控制信号,不要忘记一次操作结束后的归0条件,读的话,到第46位就结束,写的话到65位结束。
三态门的一种写法,控制信号为1时,正常输出,控制信号为0时,输出高组态。
三态门的第二种写法,使用原语
经典三段式状态机写法:
先对current和next进行时序打拍,然后组合逻辑对current进行状态分析,然后对每个状态涉及到的信号进行设计。
每个状态单独计数的一种计数器写法,当current与next状态不同,即跳变的时候,计数器清零,否则自加一。
例化模块的一类输入信号,在写时序逻辑的时候,可以一块进行编写。其他的一般单独写控制信号。
握手成功后,对寄存器进行初始化,也就是按照协议格式填写65bit,操作运行时,左移位寄存器,最高位输出给1位output寄存器。
读取数据也是左移位寄存器,与写入类似。
模块的输出信号要有与之对应的寄存器连接,或者在定义输出信号的时候,就定义为reg型,如:output reg o_reg_read_vaild。
每个寄存器定义以后,要记住后面要对该寄存器进行操作。