前言
最近工作碰到RGMII
需要调整TXC delay
的情况,而Soc
端的MAC
控制器不支持调整,所以只能在phy
端调整。而要确认这个delay
,也需要方法。这个过程就是phytranning
的过程。
参考RK3568的做法,其代码 drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c
的做法,主要是把phy
设置成loopback
模式,然后它的做法挺巧妙的,就是直接在驱动中用dma
去发出一个报文,然后dma
接收直接去拿ddr
中的skb
,对比是否都是一样的。
参考这个思路,我们可以先把phy
设置为回环模式,然后代码写个for
循环,设置phy
不同的delay
值,然后发出报文,对收到的报文进行对比比较。
所以写代码前就要针对这个phy
的回环模式是否设置成功了,需要先验证一下。验证思路就是配置了回环模式后,发出ping
包,tcpdump
抓到的包是不是一模一样的ping
包就可以。这里有两点需要注意。第一,设置网卡为混杂模式,这样网卡就不会过滤掉不属于它的mac
地址的帧了,当然tcpdump
已经做好这一步了。第二,就是因为是ping
包会发起广播的arp
包,为了抓包好看,直接配了一条静态arp
。虽然说如果回环模式设置的对,应该也会收到原封不动的arp
报文,但是它不像ping
那样可以看seq
对比。
过程
查看当前信息,因为我现在是接着网线的,link up
的状态。
然后我们设置phy
为internal loopback
,根据手册用phyreg
工具来设置reg0 bit14
为1
。工具源码是从网上copy
的,我放到最后了。
# ./phyreg eth0 0
# ./phyreg eth0 0 0x5040
# ./phyreg eth0 0
加一条静态arp
arp -s 192.168.1.102 70:a6:cc:80:18:4e
配置混杂模式(虽然tcpdump
也会帮我们做啦)
ifconfig eth0 promisc
配置phy
进入回环模式,然后ping 192.168.1.102
,tcpdump
抓包
# ./phyreg eth0 0 0x5040
可以看到抓到了两个一模一样的icmp request
包,证明配置回环模式没有问题。
参考链接
代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <linux/mii.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <linux/types.h>
#include <netinet/in.h>
#define reteck(ret) \
if(ret < 0){ \
printf("%m! \"%s\" : line: %d\n", __func__, __LINE__); \
goto lab; \
}
#define help() \
printf("mdio:\n"); \
printf("read operation: mdio reg_addr\n"); \
printf("write operation: mdio reg_addr value\n"); \
printf("For example:\n"); \
printf("mdio eth0 1\n"); \
printf("mdio eth0 0 0x12\n\n"); \
exit(0);
int sockfd;
int main(int argc, char *argv[]){
if(argc == 1 || !strcmp(argv[1], "-h")){
help();
}
struct mii_ioctl_data *mii = NULL;
struct ifreq ifr;
int ret;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, argv[1], IFNAMSIZ - 1);
sockfd = socket(PF_LOCAL, SOCK_DGRAM, 0);
reteck(sockfd);
//get phy address in smi bus
ret = ioctl(sockfd, SIOCGMIIPHY, &ifr);
reteck(ret);
mii = (struct mii_ioctl_data*)&ifr.ifr_data;
if(argc == 3){
mii->reg_num = (uint16_t)strtoul(argv[2], NULL, 0);
ret = ioctl(sockfd, SIOCGMIIREG, &ifr);
reteck(ret);
printf("read phy addr: 0x%x reg: 0x%x value : 0x%x\n\n", mii->phy_id, mii->reg_num, mii->val_out);
}else if(argc == 4){
mii->reg_num = (uint16_t)strtoul(argv[2], NULL, 0);
mii->val_in = (uint16_t)strtoul(argv[3], NULL, 0);
ret = ioctl(sockfd, SIOCSMIIREG, &ifr);
reteck(ret);
printf("write phy addr: 0x%x reg: 0x%x value : 0x%x\n\n", mii->phy_id, mii->reg_num, mii->val_in);
}
lab:
close(sockfd);
return 0;
}
相关概念
回环模式
回环模式分好几种,有mac
可以自己回环,有phy
可以自己回环。然后phy
自己回环,又分三种模式,参考此链接的说法。且对于phy
来说,回环模式不一定三种都支持。
三种回环模式分别为:
-
Internal loopback mode
,内回环模式 -
External loopback mode
,外回环模式前两种模式都是用于检查
MAC
和PHY
之间的发送与接收数据链路,支持三种速率(10/100/1000Mbps)
与全双工模式。以太网帧由MAC
端发出,PHY
芯片将接收到的帧发回MAC
端。一般内部回环就跟我们前面配置的一样,配置reg0 bit14
,外部回环则不需要配置。 -
Lineside (Remote) loopback mode
,线回环模式也叫远端模拟回环模式,主要用于检查差分线、
RJ45
连接器或者以太网线缆的工作情况,只支持1000M
全双工模式。
delay
根据 RGMII
标准,时钟的上升和下降沿采样,且时钟信号需要比数据信号 delay 1~2ns
来保证setup/hold
时间,这个 delay
可以发送端加,也可以接收端加,甚至在早期的 RGMII
标准中通过时钟信号的板上走线延迟来达到此目的。可参考此链接。
mac跟phy的连接
参考此链接,复习一下,下面以MII
接口为例
TXD(Transmit Data)[3:0]: 数据发送信号,共4根信号线;
RXD(Receive Data)[3:0]:数据接收信号,共4根信号线;
TX_ER(Transmit Error): 发送数据错误提示信号,同步于TX_CLK,高电平有效,表示TX_ER有效期内传输的数据无效。对于10Mbps速率下,TX_ER不起作用;
RX_ER(Receive Error): 接收数据错误提示信号,同步于RX_CLK,高电平有效,表示RX_ER有效期内传输的数据无效。对于10Mbps速率下,RX_ER不起作用;
TX_EN(Transmit Enable): 发送使能信号,只有在TX_EN有效期内传的数据才有效;
RX_DV(Reveive Data Valid): 接收数据有效信号,作用类似于发送通道的TX_EN;
TX_CLK:发送参考时钟,100Mbps速率下,时钟频率为25MHz,10Mbps速率下,时钟频率为2.5MHz。注意,TX_CLK时钟的方向是从PHY侧指向MAC侧的,因此此时钟是由PHY提供的。
RX_CLK:接收数据参考时钟,100Mbps速率下,时钟频率为25MHz,10Mbps速率下,时钟频率为2.5MHz。RX_CLK也是由PHY侧提供的。
CRS:Carrier Sense,载波侦测信号,不需要同步于参考时钟,只要有数据传输,CRS就有效,另外,CRS只有PHY在半双工模式下有效;
COL:Collision Detectd,冲突检测信号,不需要同步于参考时钟,只有PHY在半双工模式下有效。