赞
踩
之所以有这篇文章,得益于Alinx的开发板。
首先看下设计的原理图
上面这张图是Alinx官方针对AX7Z020开发板的设计原理图,以太网PHY部分。如果参考这个原理图设计,那么最终测试结果你会发现,PHY芯片的硬件地址为3
!没错,但是原理图中可以按照地址为1
来设计的呀!针对这个问题,在答疑群里给出的答复是“以实际值为准”。我觉得这个肯定是不行的,因为这个3本身就不是正确的值,它的出现是源于不稳定的系统。无非就是在上电器件,PHY芯片检测ADDR的引脚电平是不对的,从原理图中看,应该是LED2/PHYAD1
这个引脚被拉高了,不难看出,是外部RJ45处的灯影响了这里。
分析一下KSZ9031的数据手册,关于这一部分有说明
图中虽然只列出了DVDDH为3.3V和2.5V的情况,但是下面有说明当DVDDH为1.8V的情况,这个时候
是需要电平转换的,才能保证能识别到正确的PHYAD[2:0]的电平。因此这里的电路设计不合理。
但是我的问题还没这么简单,因为我的板子不仅仅识别到地址3
,有时候甚至识别成22
,或者18
。这个我没法再怀疑是外部电路引起的了,因为手册中也有说明。
上图也明确说了,对于KSZ9031,高两位的地址已经默认为00,因此地址最大也只能是7。出现这么大的值,我认为是PHY芯片自己的问题,是不是复位时序有问题?导致它异常?
回归到原理图,我们看到这里的KSZ9031的复位电路,是从ZYNQ接过来的PS_POR_B信号,也就是说ZYNQ上电复位,会带着PHY复位一遍!在PHY复位的时候,会采样并锁存PHYAD[2:0]引脚的电平,当作本PHY的硬件地址。但是别忘了,PHYAD[2:0]的值在PHY上电的时候也会锁存的。也就是说PHY自己上电的时序和ZYNQ给过来的PS_POR_B的信号时序,它俩会不会同时发生?还是有先后,要是两个抢占优先级,那就很有可能出问题。
于是我试着将这里的PS_POR_B信号断开,让PHY芯片自己上电复位,遗憾的是,它还是会识别到22
这个地址,最终导致通信失败!!!
继续猜想,是不是PHY没稳定,要不要等一等再复位?于是,我从ZYNQ的PS端飞了一根线到PHY的RSTn引脚,当程序执行初始化的时候,等个几秒钟,让PHY芯片上电完成稳定(这时候它在上电复位阶段识别到的地址就忽略)。再将RSTn信号拉低一个时钟周期,手动将PHY复位(这时候会再次读取PHYAD[2:0]的值计算地址)。可以预见,问题得到了比较好的解决,之前一直识别成3
的地址此刻正确的识别成了1
,也没有识别到22
这类地址的情况了。
在SDK中没有添加支持KSZ9031的代码,因此需要手动添加到Lwip中,不然无法识别PHY芯片。针对PS端的PHY,需要修改xemacpsif_physpeed.c
这个文件,修改部分的内容简略如下
/****************************************************************************** * ********************************************************************************/ #include "netif/xemacpsif.h" #include "lwipopts.h" #include "xparameters_ps.h" #include "xparameters.h" #if defined (__aarch64__) #include "bspconfig.h" #include "xil_smc.h" #endif /* Advertisement control register. */ #define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ #define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ #define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ #define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ #define ADVERTISE_100 (ADVERTISE_100FULL | ADVERTISE_100HALF) #define ADVERTISE_10 (ADVERTISE_10FULL | ADVERTISE_10HALF) #define ADVERTISE_1000 0x0300 #define IEEE_CONTROL_REG_OFFSET 0 #define IEEE_STATUS_REG_OFFSET 1 #define IEEE_AUTONEGO_ADVERTISE_REG 4 #define IEEE_PARTNER_ABILITIES_1_REG_OFFSET 5 #define IEEE_1000_ADVERTISE_REG_OFFSET 9 #define IEEE_COPPER_SPECIFIC_CONTROL_REG 16 #define IEEE_SPECIFIC_STATUS_REG 17 #define IEEE_COPPER_SPECIFIC_STATUS_REG_2 19 #define IEEE_CONTROL_REG_MAC 21 #define IEEE_PAGE_ADDRESS_REGISTER 22 #define IEEE_CTRL_1GBPS_LINKSPEED_MASK 0x2040 #define IEEE_CTRL_LINKSPEED_MASK 0x0040 #define IEEE_CTRL_LINKSPEED_1000M 0x0040 #define IEEE_CTRL_LINKSPEED_100M 0x2000 #define IEEE_CTRL_LINKSPEED_10M 0x0000 #define IEEE_CTRL_RESET_MASK 0x8000 #define MICREL_PHY_IDENTIFIER 0x22 #define MICREL_PHY_KSZ9031_MODEL 0x220 //#define MICREL_PHY_MMD_CONTROL 0x0D //#define MICREL_PHY_MMD_DATA_REGISTER 0x0E #define IEEE_MMD_ACCESS_CONTROL_REG 13 #define IEEE_MMD_ACCESS_ADDRESS_DATA_REG 14 extern u32_t phyaddrforemac; #define IEEE_SPEED_MASK 0xC000 #define IEEE_SPEED_1000 0x8000 #define IEEE_SPEED_100 0x4000 #define IEEE_CTRL_RESET_MASK 0x8000 #define IEEE_CTRL_AUTONEGOTIATE_ENABLE 0x1000 #define IEEE_STAT_AUTONEGOTIATE_COMPLETE 0x0020 #define IEEE_STAT_AUTONEGOTIATE_RESTART 0x0200 #define IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK 0x0030 #define IEEE_ASYMMETRIC_PAUSE_MASK 0x0800 #define IEEE_PAUSE_MASK 0x0400 #define IEEE_AUTONEG_ERROR_MASK 0x8000 #define PHY_DETECT_REG 1 #define PHY_IDENTIFIER_1_REG 2 #define PHY_IDENTIFIER_2_REG 3 #define PHY_DETECT_MASK 0x1808 #define PHY_MARVELL_IDENTIFIER 0x0141 #define PHY_TI_IDENTIFIER 0x2000 #define PHY_XILINX_PCS_PMA_ID1 0x0174 #define PHY_XILINX_PCS_PMA_ID2 0x0C00 #define XEMACPS_GMII2RGMII_SPEED1000_FD 0x140 #define XEMACPS_GMII2RGMII_SPEED100_FD 0x2100 #define XEMACPS_GMII2RGMII_SPEED10_FD 0x100 #define XEMACPS_GMII2RGMII_REG_NUM 0x10 #define PHY_REGCR 0x0D #define PHY_ADDAR 0x0E #define PHY_RGMIIDCTL 0x86 #define PHY_RGMIICTL 0x32 #define PHY_STS 0x11 #define PHY_TI_CR 0x10 #define PHY_TI_CFG4 0x31 /* Frequency setting */ #define SLCR_LOCK_ADDR (XPS_SYS_CTRL_BASEADDR + 0x4) #define SLCR_UNLOCK_ADDR (XPS_SYS_CTRL_BASEADDR + 0x8) #define SLCR_GEM0_CLK_CTRL_ADDR (XPS_SYS_CTRL_BASEADDR + 0x140) #define SLCR_GEM1_CLK_CTRL_ADDR (XPS_SYS_CTRL_BASEADDR + 0x144) #define SLCR_GEM_SRCSEL_EMIO 0x40 #define SLCR_LOCK_KEY_VALUE 0x767B #define SLCR_UNLOCK_KEY_VALUE 0xDF0D #define SLCR_ADDR_GEM_RST_CTRL (XPS_SYS_CTRL_BASEADDR + 0x214) #define EMACPS_SLCR_DIV_MASK 0xFC0FC0FF #if XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1 || \ XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 #define PCM_PMA_CORE_PRESENT #else #undef PCM_PMA_CORE_PRESENT #endif #ifdef PCM_PMA_CORE_PRESENT #define IEEE_CTRL_RESET 0x9140 #define IEEE_CTRL_ISOLATE_DISABLE 0xFBFF #endif u32_t phymapemac0[32]; u32_t phymapemac1[32]; static u32_t get_phy_speed_ksz9031(XEmacPs *xemacpsp, u32_t phy_addr) { u16_t temp; u16_t control; u16_t status; u16_t status_speed; u32_t timeout_counter = 0; u32_t temp_speed; u32_t phyregtemp; u16_t phy_clk_delay_reg; u16_t phy_rx_delay_reg; xil_printf("Start PHY autonegotiation \r\n"); #if 1 XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_CONTROL_REG, 0x0002);//set up register address for MMD-Device Address 2h XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x0008);//select register 08h for MMD-Device address 2h XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_CONTROL_REG, 0x4002);//select register data for MMD-Device Address 2h,Register 08h //XEmacPs_PhyRead(xemacpsp, phy_addr, MICREL_PHY_MMD_DATA_REGISTER, &phy_clk_delay_reg); //XEmacPs_PhyWrite(xemacpsp, phy_addr, MICREL_PHY_MMD_DATA_REGISTER, 0x00ff); //XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x03ff);//最大值 0.96ns //XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x0000);//-0.9ns XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x01ef);//default XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, &phy_clk_delay_reg); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_CONTROL_REG, 0x0002);//set up register address for MMD-Device Address 2h XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x0005);//select register 05h for MMD-Device address 2h XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_CONTROL_REG, 0x4002); //XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x0000);//-0.42ns //XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0xcccc);//+0.3ns XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x7777);//default XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, &phy_rx_delay_reg); xil_printf("The clk delay register is:%x\r\n",phy_clk_delay_reg); xil_printf("The rx delay register is:%x\r\n",phy_rx_delay_reg); #endif #if 0 XEmacPs_PhyWrite(xemacpsp,phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2); XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control); control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); #endif //Auto-negotiation Advertisement reg XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control);//reg 0x04 control |= IEEE_ASYMMETRIC_PAUSE_MASK;//0x0800 流控 control |= IEEE_PAUSE_MASK;//0x0400 control |= ADVERTISE_100; control |= ADVERTISE_10; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control); //1000Basic-T Control reg XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &control); control |= ADVERTISE_1000; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, control); #if 0 XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, &control);//reg 0x0f control |= (7 << 12); // max number of gigabit attempts / control |= (1 << 11); // enable downshift/ XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, control); #endif //basic control XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);//reg 00 control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; //bit12 control |= IEEE_STAT_AUTONEGOTIATE_RESTART; //bit9 XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); //basic control XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); control |= IEEE_CTRL_RESET_MASK;//software PHY reset, XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); while (1) { XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); if (control & IEEE_CTRL_RESET_MASK)//this bit is self-cleared after a "1" is written to it continue; else break; } XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); xil_printf("Waiting for PHY to complete autonegotiation.\r\n"); while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) { sleep(1); XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_STATUS_REG_2, &temp); xil_printf("Link Status is:%x \r\n",temp); timeout_counter++; if (timeout_counter == 30) { xil_printf("Auto negotiation error \r\n"); return ; } XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); } xil_printf("autonegotiation complete \r\n"); XEmacPs_PhyRead(xemacpsp, phy_addr,0x1f, &status_speed); if ( (status_speed & 0x40) == 0x40)/* 1000Mbps */ return 1000; else if ( (status_speed & 0x20) == 0x20)/* 100Mbps */ return 100; else if ( (status_speed & 0x10) == 0x10)/* 10Mbps */ return 10; else return 0; return XST_SUCCESS; } static u32_t get_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr) { u16_t phy_identity; u32_t RetStatus; XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_IDENTIFIER_1_REG, &phy_identity); if(phy_identity == MICREL_PHY_IDENTIFIER) { RetStatus = get_phy_speed_ksz9031(xemacpsp, phy_addr); xil_printf("Phy %d is KSZ9031\n\r", phy_addr); } else if (phy_identity == PHY_TI_IDENTIFIER) { RetStatus = get_TI_phy_speed(xemacpsp, phy_addr); } else { RetStatus = get_Marvell_phy_speed(xemacpsp, phy_addr); } return RetStatus; } #endif #if defined (CONFIG_LINKSPEED1000) || defined (CONFIG_LINKSPEED100) \ || defined (CONFIG_LINKSPEED10) static u32_t configure_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr, u32_t speed) { u16_t control; u16_t autonereg; XEmacPs_PhyWrite(xemacpsp,phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2); XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control); control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); autonereg |= IEEE_ASYMMETRIC_PAUSE_MASK; autonereg |= IEEE_PAUSE_MASK; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); control &= ~IEEE_CTRL_LINKSPEED_1000M; control &= ~IEEE_CTRL_LINKSPEED_100M; control &= ~IEEE_CTRL_LINKSPEED_10M; if (speed == 1000) { control |= IEEE_CTRL_LINKSPEED_1000M; /* Dont advertise PHY speed of 100 Mbps */ XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); autonereg &= (~ADVERTISE_100); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); /* Dont advertise PHY speed of 10 Mbps */ XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); autonereg &= (~ADVERTISE_10); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); /* Advertise PHY speed of 1000 Mbps */ XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &autonereg); autonereg |= ADVERTISE_1000; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, autonereg); } else if (speed == 100) { control |= IEEE_CTRL_LINKSPEED_100M; /* Dont advertise PHY speed of 1000 Mbps */ XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &autonereg); autonereg &= (~ADVERTISE_1000); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, autonereg); /* Dont advertise PHY speed of 10 Mbps */ XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); autonereg &= (~ADVERTISE_10); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); /* Advertise PHY speed of 100 Mbps */ XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); autonereg |= ADVERTISE_100; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); } else if (speed == 10) { control |= IEEE_CTRL_LINKSPEED_10M; /* Dont advertise PHY speed of 1000 Mbps */ XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &autonereg); autonereg &= (~ADVERTISE_1000); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, autonereg); /* Dont advertise PHY speed of 100 Mbps */ XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); autonereg &= (~ADVERTISE_100); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); /* Advertise PHY speed of 10 Mbps */ XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); autonereg |= ADVERTISE_10; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); } XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control | IEEE_CTRL_RESET_MASK); { volatile s32_t wait; for (wait=0; wait < 100000; wait++); } return 0; } #endif #endif /*PCM_PMA_CORE_PRESENT*/
上述代码段,只保留了更改部分,其余均被删除。
在get_phy_speed_ksz9031
这个函数中,开始阶段我进行了一些配置,这里的配置主要是针对PHY中的delay的。之所以要做这一步,是弥补硬件设计不足的,因为PHY的硬件设计需要做等长,才能保证时序正确和数据正确,否则通信速率会下降,甚至直接不通。我们看下KSZ9031手册上关于delay部分的说明
主要就是配置寄存器来实现对应信号的延时,具体的数据参考手册吧。这里说说寄存器。
看下总的寄存器预览表
可以看到这里的寄存器地址一直到0x1F,也就是一共32个寄存器。这个好像是行业标准,每个PHY都统一定义了这么多个值,之所以这样,就是好管理,就比如ZYNQ官方的Lwip驱动库,针对不同型号的PHY芯片,它只要提供一套驱动代码就可以了。这32个寄存器的功能定义呢,不同型号的PHY芯片定义不尽相同,但是大部分关键定义是相同的,比如Basic Control
/Basic Status
等等。
手册中说明了两种寄存器,即
• Standard registers // Direct register access
• MDIO Manageable device (MMD) registers // Indirect register access
这两种。Standard就是行业规定的必须要有的,MMD这个就是自由发挥的,因为规定的32个寄存器不够用了,但是我还有很多功能,就在这个MMD里面定义了,在KSZ9031这里是MMD,其他芯片里面使用Page的概念。
我们上面寄存器定义delay值,就是操作MMD实现的。具体的操作方法手册中也有说明:
他甚至提供了例程,所以很简单了吧。然后再去分析上面的代码应该就不难了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。