phy调试2

硬件接口

嵌入式网络硬件分为两部分:MAC 和 PHY,大家都是通过看数据手册来判断一款 SOC 是否支持网络,如果一款芯片数据手册说自己支持网络,一般都是说的这款 SOC 内置 MAC,MAC 类似 I2C 控制器、SPI 控制器一样的外设。但是光有 MAC还不能直接驱动网络,还需要另外一个芯片:PHY,因此对于内置 MAC 的 SOC,其外部必须搭配一个 PHY 芯片。

内部的 MAC 外设会通过 MII 或者 RMII 接口来连接外部的 PHY 芯片,MII/RMII 接口用来

传输网络数据。另外主控需要配置或读取 PHY 芯片,也就是读写 PHY 的内部寄存器,所以还需要一个控制接口,叫做 MIDO,MDIO 很类似 IIC,也是两根线,一根数据线叫做 MDIO,一根时钟线叫做 MDC。SOC 内部 MAC 外设与外部 PHY 芯片的连接如图。
在这里插入图片描述

MAC符合 IEEE802.3-2002 标准,MAC层支持双工、半双工局域网。MAC 可编程、可以作为 NIC 卡或其他一些交换器件。根据 IETF RFC 2819 协议,MAC 实现了 RMON(Remote Network Monitoring)计数功能。MAC 内核拥有硬件加速处理单元来提高网络性能,硬件加速单元用于处理 TCP/IP、UDP、ICMP 等协议。通过硬件来处理帧头等信息,效果要比用一大堆软件处理要好很多。ENET 外设有一个专用的 DMA, 此 DMA 用于在 ENET 外设和 SOC 之间传输数据,并且支持可编程的增强型的缓冲描述符,用以支持 IEEE 1588。

MII/RMII 接口

MII 接口

MII 全称是 Media Independent Interface,直译过来就是介质独立接口,它是 IEEE-802.3 定

义的以太网标准接口,MII 接口用于以太网 MAC 连接 PHY 芯片。连接示意图如图所示。
在这里插入图片描述

RMII 接口

RMII 全称是 Reduced Media Independent Interface,翻译过来就是精简的介质独立接口,也

就是 MII 接口的精简版本。RMII 接口只需要 7 根数据线,相比 MII 直接减少了 9 根,极大的方便了板子布线,RMII 接口连接 PHY 芯片的示意图如图所示:
在这里插入图片描述

MDIO 接口

MDIO 全称是 Management Data Input/Output,直译过来就是管理数据输入输出接口,是一

个简单的两线串行接口,一根 MDIO 数据线,一根 MDC 时钟线。驱动程序可以通过 MDIO 和 MDC 这两根线访问 PHY 芯片的任意一个寄存器。MDIO 接口支持多达 32 个 PHY。同一时刻内只能对一个 PHY 进行操作,那么如何区分这 32 个 PHY 芯片呢?和 IIC 一样,使用器件地址即可。同一 MDIO 接口下的所有 PHY 芯片,其器件地址不能冲突,必须保证唯一,具体器件地址值要查阅相应的 PHY 数据手册。

RJ45 接口

网络设备是通过网线连接起来的,插入网线的叫做 RJ45 座。

RJ45 座要与 PHY 芯片连接在一起,但是中间需要一个网络变压器,网络变压器用于隔离以及滤波等,网络变压器也是一个芯片。

但是现在很多 RJ45 座子内部已经集成了网络变压器,RJ45 座子上一般有两个灯,一个黄色(橙色),一个绿色,绿色亮的话表示网络连接正常,黄色闪烁的话说明当前正在进行网络通信。这两个灯由 PHY 芯片控制,PHY 芯片会有两个引脚来连接 RJ45 座上的这两个灯。内部 MAC+外部 PHY+RJ45 座(内置网络变压器)就组成了一个完整的嵌入式网络接口硬件,如图所示:
在这里插入图片描述

调试遇到问题

移植问题

IO资源分配

Phy控制由imx8端移到AG550端后,需要分配一个IO作为phy的复位引脚。但是AG550端的IO电平都是1.8V,而phy的复位需要3.3V,因此需要电平转换芯片。设备树中分配reset复位引脚后,发现phy驱动挂载瞬间,复位引脚电平产生一段脉冲(多次拉高拉低),为了排除影响,驱动中去掉复位引脚,手动拉高拉低复位引脚,发现拉高复位引脚时,电平稳定,拉低复位引脚时,电平为规律正弦波。去掉上拉电阻后,拉低引脚时,电平稳定。原因是上拉电阻阻止把电平拉低。导致不断重复拉低电平。
在这里插入图片描述

MDC/MDIO上拉电阻

现象:phy驱动可以挂载上,但是不能link上。

测量发现MDC的电平为2.2V,不是3.3V,因此减小上拉电阻的阻值,使MDC的电平为3.3V。当选用上拉电阻为1.1k时,phy不能link上,示波器测量 phy端MDC波形,高电平处波峰,较狭小。低电平为320mV。

当选用上拉电阻为470R时,phy能link上,示波器测量 phy端MDC波形,高电平处波峰,较平缓。低电平为400mV。

测试几组数据发现MDC对高电平要求比较敏感,最终选用530R的电阻。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

修改MDC 的频率

ql-ol-kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

MDC clock随着输入的时钟源动态设置。
在这里插入图片描述

带宽上不去,丢包严重

使用iperf工具测量以太网性能,发现带宽为100M时,丢包率为98%。

MDC为控制链路,不会影响性能,应排查数据链路。AG550与switch之间的RGMII由spi配置,与原来一致;switch与phy之间的sgmii,与原来的配置一致。

使用8Q端控制phy的板卡测试,排查转接板的影响,发现千兆转接板异常。回复后,千兆以太网性能与8Q端控制时,性能基本相同。

主从配置

使用mdio接口直接写,弊端是不能操作非01的设备。

#!/bin/sh

if [ ! -n "$1" ]; then
​    echo "Please enter parameters: 1-master; 0-slave"
​    exit 0
fi

if [ $1 -eq 1 ]; then
​    ./mdio eth0 0x0834 0xc001
fi

if [ $1 -eq 0 ]; then
​    ./mdio eth0 0x0834 0x8001
fi

相关配置

sgmii配置

在这里插入图片描述
在这里插入图片描述

Mdio

#include <stdio.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>
#include <unistd.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);  //创建本地socket,基于UDP协议
​	reteck(sockfd);

​	//get phy address in smi bus
​	ret = ioctl(sockfd, SIOCGMIIPHY, &ifr);    //获取phy 地址
​	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);       //读phy寄存器
​		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);    //写phy寄存器
​		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;
} 

SIOCGMIIPHY 和 SIOCSMIIREG 命令

使用ioctl命令时,可以看到上述两个命令的存在,

在头文件 include/linux/sockios.h,定义如下:

#define SIOCETHTOOL 0x8946 /* Ethtool interface */ ethtool 接口

#define SIOCGMIIPHY 0x8947 /* Get address of MII PHY in use. */ 获取MII phy的地址

#define SIOCGMIIREG 0x8948 /* Read MII PHY register. */ 读取 MII phy 寄存器

#define SIOCSMIIREG 0x8949 /* Write MII PHY register. */ 写MII phy 寄存器

在这里插入图片描述

strtoul()

unsigned long int strtoul(const char *str, char **endptr, int base)

参数

str – 要转换为无符号长整数的字符串。

endptr – 对类型为 char* 的对象的引用,其值由函数设置为 str 中数值后的下一个字符。

base – 基数,必须介于 2 和 36(包含)之间,或者是特殊值 0。

如果base为0,且字符串不是以0x(或者0X)开头,则按十进制进行转化。如果base为0或者16,并且字符串以0x(或者0X)开头,那么,x(或者X)被忽略,字符串按16进制转化。如果base不等于0和16,并且字符串以0x(或者0X)开头,那么x被视为非法字符。

参考
https://zhuanlan.zhihu.com/p/26205006
https://blog.51cto.com/u_15127513/3619547

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值