前言
最近工作碰到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在半双工模式下有效。