当前位置:   article > 正文

ZYNQ网络通信之PHY应用(型号:88E1145)_zynq网口通信

zynq网口通信

接上篇《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);
  • 1
  • 2
  • 3
  • 4

进入需要初始化的项目表项函数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
	}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

设置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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

首先进入时钟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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

代码设计中,首先设置寄存器页数为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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

根据前面在章节对于延时操作的描述,只需要控制PHY寄存器20的bit7和bit1即可,在mac进行数据收发过程没有进行数据的延时操作,所以在PHY层全部进行延时操作。所以设置寄存器0_20.7=1,0_20.1=1完成延时操作。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号