PHY之MDIO解析
概述
管理MII接口的MDIO接口是一个双线的串行接口,用来对PHY芯片等物理层信息进行操作管理
MDIO
MDIO(Management Data Input/Output),对G比特以太网而言,串行通信总线称为管理数据输入输出 (MDIO)。
MDIO由两根线组成,MDC线是数据的随路时钟,最高速率可达几MHz(各PHY芯片有异)。MDIO线是数据的输入输出双向总线,数据是与MDC时钟同步的。
MDIO前后有两种协议, 包括之前的Clause22 以及之后为了弥补Clause22 寄存器空间不足而出的Clause45, Clause 45 向前兼容Clause 22。
Clause 22
Clause 22 的时序图如下:
STA:Station Management
MMD:MDIO Managed Device
PRE:帧前导码,为32个连续“1”比特。
ST:帧开始标志, Clause22 的开始标志为比特“01”。
OP:操作码,CL22中比特“10”表示此帧为一读操作帧,比特“01”表示此帧为一写操作帧。
PHYAD:MMD的物理地址,5个比特,每个MMD都把自己的地址与这5个比特进行比较,若匹配则响应后面的操作,若不匹配,则忽略掉后面的操作。
REGAD:用来选MMD的32个寄存器中的某个寄存器的地址。
TA:状态转换域,若为读操作,则第一比特时MDIO为高阻态,第二比特时由MMD使MDIO置“0”。若为写操作,则MDIO仍由STA控制,连续输出“10”两个比特。
DATA:帧的寄存器的数据域,16比特,若为读操作,则为MMD送到STA的数据,若为写操作,则为STA送到MMD数据。
IDLE:帧结束后的空闲状态,此时MDIO无源驱动,处高阻状态。
Clause 45
Clause 45 的时序图如下:
STA:Station Management
MMD:MDIO Managed Device
PRE:帧前导码,为32个连续“1”比特。
ST:帧开始标志, 为了区别CL22,Clause45 的开始标志为比特“00”。
OP:操作码,Clause45有4种操作码,比特“00”表示设置当前寄存器地址,比特“01”表示写当前寄存器。比特“10”表示读当前寄存器,比特“11”表示读当前寄存器读完后把当前寄存器的值加1,用于顺序读。
PRTAD:Port Address,端口地址, 也称物理地址。
DEVAD:器件地址,CL45新增概念,各值与器件对应如下。
REGAD:用来选MMD的65536个寄存器中的某个寄存器的地址。
TA:状态转换域,若为读操作,则第一比特时MDIO为高阻态,第二比特时由MMD使MDIO置“0”。若为写操作,则MDIO仍由STA控制,连续输出“10”两个比特。
DATA:帧的寄存器的数据域,16比特,若为读操作,则为MMD送到STA的数据,若为写操作,则为STA送到MMD数据。
IDLE:帧结束后的空闲状态,此时MDIO无源驱动,处高阻状态。
Problems with the MDIO
当MDIO通信出现问题,可依次检查以下方面
确保MDC工作在合适的频率
确保MDC以及MDIO有上拉
PHYAD(PRTAD)没有搞错。
MMD 没有处于复位状态。
适当调整MDC的相位。
有些MMD要求帧与帧之间一定要用高阻态分隔
STA MDIO 接口 Verilog 代码如下
`timescale 1ns / 1ps//// Company: // Engineer: // // Create Date: 2019/08/12 10:39:51// Design Name: // Module Name: eth_mdio_interface// Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision:// Revision 0.01 - File Created// Additional Comments: // //module eth_mdio_interface #(parameter MDC_DIVISOR = 100 MDC frequency is clk_i / MDC_DIVISOR. )(input clk_i,input rstn_i,input clause_sel_i,1 : Clause 45; 0 : Clause 22;output reg ready_o,input valid_i,input [1:0] cmd_i,input [25:0] addr_i, Cl45: PHY Addr(5 bit) + Devices Addr(5 bit) + Reg Addr(16 bit); Cl22 : PHY Addr(5 bit) + Reg Addr(5 bit) + invalid(16 bit)//input [15:0] wdata_i, output reg rdata_vld_o,output reg [15:0] rdata_o,output reg mdc_o,input mdio_i,output reg mdio_o,output reg mdio_oen_o);localparam WR_CMD = 2'b01;localparam RD_CMD = 2'b11; localparam RD_INC = 2'b10; not supported//---------------------------------------------------------------------------------------------------// Function - Calculates the log2ceil of the input value//---------------------------------------------------------------------------------------------------function integer log2ceil;input integer val;integer i;begini = 1;log2ceil = 0;while (i < val) beginlog2ceil = log2ceil + 1;i = i << 1; endendendfunction localparam P_MDC_DIVIDE_BITS = log2ceil(MDC_DIVISOR) - 1; // Need to count to (MDC_DIVISOR/2) - 1 reg [P_MDC_DIVIDE_BITS-1:0] mdc_divide; reg mdc_tick; reg mdc_sample; Divide clock to make MDIO clock always @(posedge clk_i, negedge rstn_i) if(!rstn_i)mdc_divide <= 0;else if(mdc_divide == 0) mdc_divide <= (MDC_DIVISOR/2) - 1'b1;else mdc_divide <= mdc_divide - 1'b1;always @(posedge clk_i, negedge rstn_i)if(!rstn_i) mdc_o <= 1'b0;else if(mdc_divide == 0)mdc_o <= ~mdc_o;Data is output on mdc_tick. Delay it slightly from the clock to ensure setup and hold timing is metSample read data just before rising edge of MDCalways @(posedge clk_i, negedge rstn_i)if(!rstn_i) beginmdc_tick <= 1'b0;mdc_sample <= 1'b0;endelse beginmdc_tick <= ~mdc_o & (mdc_divide==(MDC_DIVISOR/2) - (MDC_DIVISOR/4));mdc_sample <= ~mdc_o & (mdc_divide==2);endwire [4:0] cl45_phy_addr = addr_i[25:21]; wire [4:0] cl45_dev_addr = addr_i[20:16]; wire [15:0] cl45_reg_addr = addr_i[15:0]; wire [4:0] cl22_phy_addr = addr_i[25:21]; wire [4:0] cl22_reg_addr = addr_i[20:16]; reg [3:0] state;localparam ST_PREAMBLE1 = 4'd0;localparam ST_IDLE1 = 4'd1;localparam ST_WR_ADDR_CL45 = 4'd2;localparam ST_IDLE2 = 4'd3;localparam ST_PREAMBLE2 = 4'd4;localparam ST_REWR_ADDR_CL45 = 4'd5;localparam ST_WR_ADDR_CL22 = 4'd6;localparam ST_WR_DATA = 4'd7;localparam ST_RD_DATA = 4'd8;wire preamble_set_done;wire wr_addr_cl45_done;wire wr_addr_cl22_done;wire rewr_addr_cl45_done;wire wr_data_done;wire rd_data_done;reg wr_rdn_en;reg cmd_pending;always @(posedge clk_i, negedge rstn_i)if(!rstn_i)state <= ST_IDLE1;else case (state)ST_IDLE1 : if(cmd_pending && mdc_tick)state <= ST_PREAMBLE1; else state <= ST_IDLE1;ST_PREAMBLE1 : if(preamble_set_done) state <= clause_sel_i? ST_WR_ADDR_CL45 : ST_WR_ADDR_CL22;else state <= ST_PREAMBLE1; ST_WR_ADDR_CL45 : if(wr_addr_cl45_done)state <= ST_IDLE2;else state <= ST_WR_ADDR_CL45;ST_IDLE2 : if(mdc_tick) state <= ST_PREAMBLE2;else state <= ST_IDLE2; ST_PREAMBLE2 : if(preamble_set_done)state <= ST_REWR_ADDR_CL45;else state <= ST_PREAMBLE2;ST_REWR_ADDR_CL45 : if(rewr_addr_cl45_done)state <= wr_rdn_en? ST_WR_DATA : ST_RD_DATA; else state <= ST_REWR_ADDR_CL45;ST_WR_ADDR_CL22 : if(wr_addr_cl22_done)state <= wr_rdn_en? ST_WR_DATA : ST_RD_DATA; else state <= ST_WR_ADDR_CL22; ST_WR_DATA : if(wr_data_done) state <= ST_IDLE1;else state <= ST_WR_DATA; ST_RD_DATA : if(rd_data_done) state <= ST_IDLE1;else state <= ST_RD_DATA; default : state <= ST_IDLE1;endcasewire latch_en;assign latch_en = (ready_o && valid_i); wait mdc_tick to startalways @(posedge clk_i, negedge rstn_i)if(!rstn_i)cmd_pending <= 1'b0;else if(latch_en)cmd_pending <= 1'b1;else if(mdc_tick)cmd_pending <= 1'b0;always @(posedge clk_i, negedge rstn_i)if(!rstn_i)wr_rdn_en <= 1'b0;else if(latch_en)wr_rdn_en <= (cmd_i == WR_CMD);reg [5:0] preamble_cnt;always @(posedge clk_i, negedge rstn_i)if(!rstn_i)preamble_cnt <= 6'b0;else if(state != ST_PREAMBLE1 && state != ST_PREAMBLE2) preamble_cnt <= 6'b0; else if(mdc_tick)preamble_cnt <= preamble_cnt + 6'b1;assign preamble_set_done = (preamble_cnt == 6'd31) && mdc_tick;reg [5:0] wr_addr_cl45_cnt;always @(posedge clk_i, negedge rstn_i)if(!rstn_i)wr_addr_cl45_cnt <= 6'b0;else if(state != ST_WR_ADDR_CL45)wr_addr_cl45_cnt <= 6'b0;else if(mdc_tick)wr_addr_cl45_cnt <= wr_addr_cl45_cnt + 6'b1;assign wr_addr_cl45_done = (wr_addr_cl45_cnt == 6'd31) && mdc_tick; reg [4:0] rewr_addr_cl45_cnt; always @(posedge clk_i, negedge rstn_i)if(!rstn_i)rewr_addr_cl45_cnt <= 5'b0;else if(state != ST_REWR_ADDR_CL45) rewr_addr_cl45_cnt <= 5'b0;else if(mdc_tick)rewr_addr_cl45_cnt <= rewr_addr_cl45_cnt + 5'b1;assign rewr_addr_cl45_done = (rewr_addr_cl45_cnt == 5'd13) && mdc_tick;reg [4:0] wr_addr_cl22_cnt;always @(posedge clk_i, negedge rstn_i)if(!rstn_i)wr_addr_cl22_cnt <= 5'b0;else if(state != ST_WR_ADDR_CL22)wr_addr_cl22_cnt <= 5'b0;else if(mdc_tick)wr_addr_cl22_cnt <= wr_addr_cl22_cnt + 5'b1;assign wr_addr_cl22_done = (wr_addr_cl22_cnt == 5'd13) && mdc_tick;reg [4:0] wr_data_cnt;always @(posedge clk_i, negedge rstn_i)if(!rstn_i)wr_data_cnt <= 5'b0;else if(state != ST_WR_DATA)wr_data_cnt <= 5'b0;else if(mdc_tick)wr_data_cnt <= wr_data_cnt + 5'b1;assign wr_data_done = (wr_data_cnt == 5'd17) && mdc_tick;reg [4:0] rd_data_cnt;always @(posedge clk_i, negedge rstn_i)if(!rstn_i)rd_data_cnt <= 5'b0;else if(state != ST_RD_DATA)rd_data_cnt <= 5'b0;else if(mdc_tick)rd_data_cnt <= rd_data_cnt + 5'b1;assign rd_data_done = (rd_data_cnt == 5'd17) && mdc_tick; always @(posedge clk_i, negedge rstn_i)if(!rstn_i)ready_o = 1'b0;else ready_o = (state == ST_IDLE1) && (!valid_i) && (!cmd_pending);reg [1:0] op_code_cl45;always @(*)case (cmd_i)WR_CMD : op_code_cl45 = 2'b01;RD_CMD : op_code_cl45 = 2'b11;RD_INC : op_code_cl45 = 2'b10;default : op_code_cl45 = 2'b11;endcasereg [1:0] op_code_cl22;always @(*)case (cmd_i)WR_CMD : op_code_cl22 = 2'b01;RD_CMD : op_code_cl22 = 2'b10;default : op_code_cl22 = 2'b11;endcasereg [31:0] first_wraddr_sfr_cl45;always @(posedge clk_i)if(latch_en)first_wraddr_sfr_cl45 <= {2'b00, 2'b00, cl45_phy_addr, cl45_dev_addr, 2'b10, cl45_reg_addr};else if((state == ST_WR_ADDR_CL45) && mdc_tick)first_wraddr_sfr_cl45 <= {first_wraddr_sfr_cl45[30:0], 1'b0};reg [13:0] second_wraddr_sfr_cl45;always @(posedge clk_i)if(latch_en)second_wraddr_sfr_cl45 <= {2'b00, op_code_cl45, cl45_phy_addr, cl45_dev_addr};else if((state == ST_REWR_ADDR_CL45) && mdc_tick)second_wraddr_sfr_cl45 <= {second_wraddr_sfr_cl45[12:0], 1'b0};reg [13:0] wraddr_sfr_cl22;always @(posedge clk_i) if(latch_en)wraddr_sfr_cl22 <= {2'b01, op_code_cl22, cl22_phy_addr, cl22_reg_addr};else if((state == ST_WR_ADDR_CL22) && mdc_tick)wraddr_sfr_cl22 <= {wraddr_sfr_cl22[12:0], 1'b0};reg [17:0] wrdata_sfr;always @(posedge clk_i)if(latch_en)wrdata_sfr <= {2'b10, wdata_i};else if((state == ST_WR_DATA) && mdc_tick) wrdata_sfr <= {wrdata_sfr[16:0], 1'b0};reg [15:0] rddata_sfr;always @(posedge clk_i)if((state == ST_RD_DATA) && mdc_sample) rddata_sfr <= {rddata_sfr[14:0], mdio_i};always @(posedge clk_i, negedge rstn_i)if(!rstn_i)mdio_o <= 1'b0;else case (state)ST_WR_ADDR_CL45 : mdio_o <= first_wraddr_sfr_cl45[31];ST_REWR_ADDR_CL45 : mdio_o <= second_wraddr_sfr_cl45[13];ST_WR_ADDR_CL22 : mdio_o <= wraddr_sfr_cl22[13];ST_WR_DATA : mdio_o <= wrdata_sfr[17];default : mdio_o <= 1'b1;endcasealways @(posedge clk_i, negedge rstn_i)if(!rstn_i)mdio_oen_o <= 1'b1;else mdio_oen_o <= (state != ST_RD_DATA)&&(state != ST_IDLE1)&&(state != ST_IDLE2);always @(posedge clk_i, negedge rstn_i)if(!rstn_i) beginrdata_vld_o <= 1'b0;rdata_o <= 16'b0;endelse if(rd_data_done) beginrdata_vld_o <= 1'b1;rdata_o <= rddata_sfr;endelse beginrdata_vld_o <= 1'b0;rdata_o <= rdata_o;endendmodule
实际使用时抓取的波形
————————————————
原文链接:https://blog.csdn.net/yhs18200259681/article/details/99652701
PHY之MDIO解析相关推荐
- Linux 网卡驱动 PHY Mac MDIO kernel 驱动讲解 (未完待续)
前言 会从网卡的硬件结构讲解,然后再到 kernel 驱动调用的讲解 我用Xilinx Zu2cg开发板的实际举例,一般嵌入式设备肯定会预留网口,用作调试开发,组网通讯使用,那么开发板的意义在哪 意义 ...
- Linux系统网卡驱动phy工作原理解析
Linux网卡驱动架构 MAC控制器驱动是芯片厂商集成在SDK中 比如三星控制器.ATMEL控制器,这部分驱动厂商已经写好了, linux下自带通用phy驱动phy_device.c(phy层协议是通 ...
- 以太网PHY 开发与解析
目录 1.PHY芯片介绍 1.1 芯片引脚定义和说明 1.2 PHY芯片功能说明 1.3 供电管理 1.4 寄存器说明 1.4.1 控制寄存器 1.4.2 状态寄存器 1.4.3 PHY ID寄存器 ...
- 【NanoPi T2】 5.uboot gmac网卡驱动(1) - mac控制器,phy芯片,rgmii协议
1.mac控制器,phy芯片,rgmii协议 2.寄存器介绍 3.驱动源码解析 4.nanopi t2 移植rtl8211e网卡驱动(首发) 硬件构成部分 以太网卡或者是ARM开发板的网络部分通常由M ...
- PHY驱动调试之 --- PHY控制器驱动(二)
1. 前言 内核版本:linux 4.9.225,以freescale为例. 2. 概述 PHY芯片为OSI的最底层-物理层(Physical Layer),通过MII/GMII/RMII/SGMII ...
- emac接口与phy交互
nuc970的emac接口 nuc970的EMAC以太网接口与PHY芯片之间的数据交换是通过MII(Media Independent Interface)或RMII(Reduced Media In ...
- 千兆网络PHY芯片 RTL8211E的实践应用(自我总结篇)
这篇算是对FPGA做千兆以太网的电路搭建这一块儿的最全的扫盲篇了吧,废话不多说,直接上干货. 一.千兆以太网的系统搭建 1.硬件系统搭建 以太网MAC模块负责实现以太网MAC子层的功能,完成802.3 ...
- 【驱动】以太网扫盲(三)PHY的控制器驱动框架分析
1. 概述 PHY芯片为OSI的最底层-物理层(Physical Layer),通过MII/GMII/RMII/SGMII/XGMII等多种媒体独立接口(介质无关接口)与数据链路层的MAC芯片相连,并 ...
- phy 驱动与 switch 驱动
phy 驱动与 switch 驱动 phy 驱动 phy 与 cpu 的硬件连接 一般为 MAC-PHY 模式: ----------- | CPU | RGMII/| ------| MII --- ...
- MDIO/MDC接口
http://xiaominwzj.blog.163.com/blog/static/24192202220153201132715/ MDIO接口,MAC与PHY间的管理接口(MII是数据接口),有 ...
最新文章
- 应用 | 红黄蓝的虐童惨案,其实可以用机器学习等技术来避免
- pandas基于条件判断更新dataframe中所有数据列数值内容的值(Conditionally updating all values in pandas Dataframe )
- 信息系统运维安全管理规定(可作为范文参考)
- poj3273---Monthly Expense
- Unable to find the ncurses libraries的解决办法
- enum in c language
- 第三篇:服务消费者(Feign)(Finchley版本)V2.0_dev
- 调账成功 对账失败处理流程反思
- [DFS|回溯法] leetcode 17 电话号码的字母组合
- dp在约会上是什么意思_dp是什么意思网络术语 饭圈为什么喜欢用缩写
- IDEA怎么设置背景图片
- Cloudflare DDNS梅林插件代码
- 字典(JSON)数据写入文件并换行,Python
- OBJECTS IN SEMANTIC TOPOLOGY
- Ubuntu部署TeamTalk文档
- npm install 安装软件,出现 operation not permitted, mkdir
- Java发送网易企业邮箱邮件
- 7.3 向量的数量积与向量积
- SQL Prompt 4试用
- 关于tp-link wr740 v4的刷机救砖的办法(非线刷解决)恢复原版的