赞
踩
接上篇《ZYNQ网络通信之PHY详解》,实例介绍工程中PHY的应用,包括原理图设计、配置和软件设计。
五 ZYNQ PS网络通信
5.1 PHY芯片原理图设计
从原理图可以得知:
① 网络控制器使用的是ZYNQ的PS网络控制器。
② 关于PHY的配置,大部分通过硬件配置完成,体现在P0_CONFIG[4:0]。
③ 使用RGMII接口与MAC层连接,使用铜线外部连接RJ45转接器。
④ MDC/MDIO接口连接到PS_MIO52和PS_MIO53。(原理图中ZYNQ端接口未展示)
5.2 硬件配置
针对上面硬件原理图设计,对基于硬件配置完成的PHY功能进行详细的描述。先通过一个表格梳理硬件配置pin的连接方式和配置参数。
对照下面这个表格,也就是3.5节讲到过的硬件配置对应映射关系。
结合datasheet里面关于寄存器的详细描述,可以得知硬件配置关系如下:
(1) PHY地址为二进制10000,即十进制16。
(2) HWCFG_MODE[3:0]=1011,即RGMII to copper。
(3) PHY自协商模式告知速率、双工等,参数尽量以主机为主。
(4) ENA_XC=1使能自动转接,自协商。
(5) DIS_FC=1关闭自动选择铜线/光纤模式,本工程只外接到铜线接口,通过交叉双绞线连接到RJ45水晶头固定,然后至网线。
(6) DIS_SLEEP=1关闭睡眠模式。
(7) SEL_TWSI=0选择MDC/MDIO接口。
(8) 本设计中定义寄存器4.11:10 to 11 – copper。
(9) GCONFIG0=1011,即关闭DTE选择;50阻抗终端连接到铜线;4个独立的MDC/MDIO接口连接方式;关闭125MHz时钟,由软件进行定义。
(10) GCONFIG1=0111,一些简单配置,主要包含指示灯LED闪烁、上电、中断等。
5.3 软件设计
1、按照xilinx的惯用套路,首先通过查表的方式初始化一个MAC层的网络接口结构体,包括PHY的ID和地址等,然后设置mac允许接收多播地址,设置mac地址并挂载到mac层。代码如下:
instGMACPS.Config = XEmacPs_ConfigTable[XPAR_PS7_ETHERNET_0_DEVICE_ID];
XEmacPs_CfgInitialize(&instGMACPS, &instGMACPS.Config, instGMACPS.Config.BaseAddress);
XEmacPs_SetOptions(&instGMACPS, XEMACPS_MULTICAST_OPTION);
XEmacPs_SetMacAddress(&instGMACPS, (void*)(mac_ethernet_address), 1);
进入需要初始化的项目表项函数XEmacPs_ConfigTable查看初始化项目。
XEmacPs_Config XEmacPs_ConfigTable[XPAR_XEMACPS_NUM_INSTANCES] =
{
{
XPAR_PS7_ETHERNET_0_DEVICE_ID,
XPAR_PS7_ETHERNET_0_BASEADDR,
XPAR_PS7_ETHERNET_0_IS_CACHE_COHERENT
}
};
设置mac地址部分,这里挂载的mac地址存放在自定义的mac_ethernet_address数组里面。
unsigned char mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };
mac地址在MAC层设计的时候就已经存在,但是,我们可以手动写入mac地址进行修改,在网络交换、连接和传输中表现得更加清晰,但是同一个网络拓扑结构中不能出现同样的mac地址。
2、设置MDIO的工作时钟MDC,获取PHY传输速率。先上代码:
/* Read PHY address and number PHY speed */
u32 phy_addr;
u16 phy_reg;
/* setup the MDIO clock with XEmacPs_SetMdioDivisor() */
XEmacPs_SetMdioDivisor(&instGMACPS, MDC_DIV_224);
phy_addr = 16;
ethPhySpeed[phy_addr] = 0;
XEmacPs_PhyRead(&instGMACPS, phy_addr, PHY_DETECT_REG, &phy_reg);
if ((phy_reg != 0xFFFF) && ((phy_reg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) {
xil_printf("XEmacPs detect_phy: PHY detected at address %d. ", phy_addr);
XEmacPs_PhyRead(&instGMACPS, phy_addr, 2, &phy_reg);
if(phy_reg==0x0141)
ethPhySpeed[phy_addr] = ETHPHY_AUTONEGO_Marrvell(&instGMACPS, phy_addr, 0xFFFFFFFF);
xil_printf("XEmacPs PHY AUTONEGO: Speed: %d Mbps @address %d.\r\n", ethPhySpeed[phy_addr], phy_addr);
首先进入时钟MDC设置函数XEmacPs_SetMdioDivisor(),可以看到这样一个注释,详细说明了计算公式和分频后时钟对照表。
在PHY数据手册已经提过,MDC时钟最大不能超过8.3MHz,在这里定义建议不超过2.5MHz,这里定义分频值MDC_DIV_224=0x7,也就是大约2MHz。
然后,读取寄存器1的值,也就是PHY状态值,通过判断语句比较,如果状态正常,打印出PHY地址信息。
再然后,读取寄存器2的值,也就是PHY的标志信息,如果等于0x0141,即确定了这个PHY芯片,根据确定的PHY芯片读取传输速率,读取工作速率是通过函数ETHPHY_AUTONEGO_Marrvell()实现。
接下来重点讲解速率获取函数ETHPHY_AUTONEGO_Marrvell()的实现。代码如下:
u32 ETHPHY_AUTONEGO_Marrvell(XEmacPs* InstancePtr, u32 phy_addr, int flag) { u16 phy_reg; u32 ethPhySpeed; XEmacPs_PhyWrite(InstancePtr, phy_addr, 22, 0); // IEEE_PAGE_ADDRESS_REGISTER XEmacPs_PhyRead(InstancePtr, phy_addr, 4, &phy_reg); // IEEE_AUTONEGO_ADVERTISE_REG phy_reg |= 0xDE0 ; //ASYMMETRIC_PAUSE, PAUSE, ADVERTISE_100FULL, ADVERTISE_100HALF, ADVERTISE_10FULL, ADVERTISE_10HALF, XEmacPs_PhyWrite(InstancePtr, phy_addr, 4, phy_reg); XEmacPs_PhyRead(InstancePtr, phy_addr, 9, &phy_reg); // IEEE_1000_ADVERTISE_REG_OFFSET phy_reg |= 0x300 ; //ADVERTISE_1000FULL, ADVERTISE_1000HALF XEmacPs_PhyWrite(InstancePtr, phy_addr, 9, phy_reg); XEmacPs_PhyRead(InstancePtr, phy_addr, 16, &phy_reg); // IEEE_COPPER_SPECIFIC_CONTROL_REG phy_reg |= ((7<<12)|(1<<11)) ; //TxFIFO_DEPTH(15:14), RxFIFO_DEPTH(13:12), TX_CRS_ASSERT(11) XEmacPs_PhyWrite(InstancePtr, phy_addr, 16, phy_reg); XEmacPs_PhyRead(InstancePtr, phy_addr, 0, &phy_reg); // IEEE_CONTROL_REG_OFFSET phy_reg |= 0x1200 ; //AUTONEGOTIATE_ENABLE, AUTONEGOTIATE_RESTART XEmacPs_PhyWrite(InstancePtr, phy_addr, 0, phy_reg); XEmacPs_PhyRead(InstancePtr, phy_addr, 0, &phy_reg); // IEEE_CONTROL_REG_OFFSET phy_reg |= 0x8000 ; //PHY_SOFT_RESET XEmacPs_PhyWrite(InstancePtr, phy_addr, 0, phy_reg); do { XEmacPs_PhyRead(InstancePtr, phy_addr, 0, &phy_reg); } while (phy_reg&0x8000); for(int timeoutCnt=0; ; timeoutCnt++) { XEmacPs_PhyRead(InstancePtr, phy_addr, 1, &phy_reg); // IEEE_STATUS_REG_OFFSET: for Copper [5]AUTONEGOComplete [2]Linkup if( phy_reg & 0x20 ) { xil_printf(" autonegotiation complete, "); XEmacPs_PhyRead(InstancePtr, phy_addr, 17, &phy_reg); // IEEE_COPPER_PHY_SPEC_STATUS[15:14]10_1G/01_100M/00_10M [10]RealLink if(phy_reg & 0x8000) { xil_printf("link status 1Gbps \r\n"); ethPhySpeed = 1000; } else if(phy_reg & 0x4000) { xil_printf("link status 100Mbps \r\n"); ethPhySpeed = 100; } else { xil_printf("link status 10Mbps \r\n"); ethPhySpeed = 10; } break; } else if(timeoutCnt==AUTONEGO_TIMEOUT_S-1) { xil_printf(" Auto negotiation error, may be add AUTONEGO_TIMEOUT_S(20s) \r\n"); ethPhySpeed = 1; //while(1); break; } else { sleep(1); XEmacPs_PhyRead(InstancePtr, phy_addr, 19, &phy_reg); // IEEE_COPPER_Interrupt_Status xil_printf("+"); } } return ethPhySpeed; }
代码设计中,首先设置寄存器页数为0,即Page0。然后对寄存器4、寄存器9、寄存器16、寄存器0进行配置,随后进行软件复位,等待上述寄存器配置值刷新完成。
接下来就是实际获取速率的过程,先通过读取寄存器1的值判断是否自协商完成,因为自协商会协商本端和对端的速率。
当协商速率完成,开始读取寄存器17的值,查看当前传输速率,并将速率分别定义为1000、100、10赋值给速率变量ethPhySpeed输出。
如果自协商失败或者进入中断状态也进行相应的处理工作,比较简单,详细见代码。
3、设置MAC工作时钟。上代码:
void MACSetUpSLCRDivisors(u32 mac_baseaddr, s32 speed) { u32 slcrBaseAddress = (mac_baseaddr == XPAR_PS7_ETHERNET_0_BASEADDR) ? (XPS_SYS_CTRL_BASEADDR + 0x140) : (XPS_SYS_CTRL_BASEADDR + 0x144); u32 SlcrDiv0; u32 SlcrDiv1; if (speed == 1000) { SlcrDiv0 = 8; SlcrDiv1 = 1; XEmacPs_WriteReg(mac_baseaddr, 4, // XEMACPS_NWCFG_OFFSET XEmacPs_ReadReg(mac_baseaddr, 4)|0x400 ); } else if (speed == 100) { SlcrDiv0 = 8; SlcrDiv1 = 5; XEmacPs_WriteReg(mac_baseaddr, 4, // XEMACPS_NWCFG_OFFSET XEmacPs_ReadReg(mac_baseaddr, 4)&(~0x400)|0x1 ); } else { SlcrDiv0 = 8; SlcrDiv1 = 50; XEmacPs_WriteReg(mac_baseaddr, 4, // XEMACPS_NWCFG_OFFSET XEmacPs_ReadReg(mac_baseaddr, 4)&(~0x400)&(~0x1) ); } Xil_Out32(XPS_SYS_CTRL_BASEADDR + 0x8, 0xDF0D); // SLCR_UNLOCK 关闭写保护 Xil_Out32((UINTPTR)slcrBaseAddress, // GEMx_CLK_CTRL [6:4] 00?-IOPLL,010-ARMPLL,011-DDRPLL (/1.333/1.066GHz) [25:20]-Div1 [13:8]-Div0 (Xil_In32((UINTPTR)slcrBaseAddress)&0xFC0FC0FF&(~0x70)) | ((SlcrDiv1&0x3F)<<20) | ((SlcrDiv0&0x3F)<<8) | 0x1 ); Xil_Out32(XPS_SYS_CTRL_BASEADDR + 0x4, 0x767B); // SLCR_LOCK_ADDR }
MAC工作时钟通过函数MACSetUpSLCRDivisors()设置。函数首先通过函数传入的参数mac地址判断mac控制器为ETHERNET_0。将寄存器slcr偏移0x140,也就是PS端网络控制器0的时钟分频值设置寄存器位置。
接下来就是设置分频值,不同速率对应的时钟不同,故而设置的分频值也不同。函数传入参数速率用于判断该写入什么分频值。然后使能相应的速率。
现在,分频值也有了,分频值应该写入哪个地址也确定了,接下来就是写入分频值到指定地址,写入之前必须先写入0XDF0D关闭slcr寄存器的写保护(SLCR UNLOCK),然后才能操作寄存器slcr,写入分频值到slcr寄存器,接着再写入0X767B使能写保护功能。
4、设置TXD、RXD延时。上代码:
phy_addr=16; XEmacPs_PhyRead(&instGMACPS, phy_addr, 20, &phy_reg); phy_reg |= 0x80; // RX_DELAY_0x80 XEmacPs_PhyWrite(&instGMACPS, phy_addr, 20, phy_reg); // TX_DELAY_0x20 XEmacPs_PhyRead(&instGMACPS, phy_addr, 20, &phy_reg); phy_reg |= 0x02; // TX_DELAY_0x02 XEmacPs_PhyWrite(&instGMACPS, phy_addr, 20, phy_reg); XEmacPs_PhyRead(&instGMACPS, phy_addr, 0, &phy_reg); // phyreg0.15=1 Soft reset to update settings above phy_reg |= 0x8000; XEmacPs_PhyWrite(&instGMACPS, phy_addr, 0, phy_reg); do { XEmacPs_PhyRead(&instGMACPS, phy_addr, 0, &phy_reg); } while (phy_reg&0x8000);
根据前面在章节对于延时操作的描述,只需要控制PHY寄存器20的bit7和bit1即可,在mac进行数据收发过程没有进行数据的延时操作,所以在PHY层全部进行延时操作。所以设置寄存器0_20.7=1,0_20.1=1完成延时操作。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。