zynq或zynqmp通过emio和gmii to rgmii ip使用pl端以太网调试

主要内容:

以太网调试问题中gmii to rgmii问题较多:
主要涉及到IP 参数配置、Linux设备树等


Vivado关键配置:

  1. PHY Address 默认8,这里不是填写phy地址,一定要和phy地址不一样才能工作,这里用于虚拟一个phy设置,用于配置gmii to rgmii的工作速率,地址是0x10
  2. Provide 2ns skew on RGMII TXC,这个参数就是要不要把TXC延时2ns,这个选Skew added by
    PHY,就是用phy芯片来延时。

zynq或zynqmp通过emio和gmii to rgmii ip使用pl端以太网调试_第1张图片

  1. Shared Logic 页面里,一般选“Inclue Shared Logic in Core”,PL端设计,这里适用于一路GMII
    to RGMII,如果有多路可能要另行处理

zynq或zynqmp通过emio和gmii to rgmii ip使用pl端以太网调试_第2张图片

  1. 复位:gmii_to_rgmii tx_reset和rx_reset复位通过一个反相器连接到PS的复位,因为这个复位时高电平复位,phy芯片本身复位可以直接连接到PS复位

zynq或zynqmp通过emio和gmii to rgmii ip使用pl端以太网调试_第3张图片

  1. 时钟:gmii_to_rgmii的clkin输入要特别注意,zynq系列输入时200Mhz时钟,zynqmp系列用375Mhz

调试

Vivado核对无误后编译调试,由于Linux下遇到问题调试比较麻烦,笔者一般先在裸机下把接口调通。

裸机lwip调试

默认的lwip只支持TI、Marvell的2款phy,新版可能还支持Realtek的,也就是说支持的phy非常少,笔者项目中用到ksz9031就不支持,需要自己修改代码xemacpsif_physpeed.c
zynq或zynqmp通过emio和gmii to rgmii ip使用pl端以太网调试_第4张图片
主要修改点在函数get_IEEE_phy_speed中,get_IEEE_phy_speed主要是获取phy的协商状况,需要自己根据具体的phy添加相应的函数,例如:get_phy_speed_ksz9031

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);
	} else if (phy_identity == PHY_TI_IDENTIFIER) {
		RetStatus = get_TI_phy_speed(xemacpsp, phy_addr);
	} else if (phy_identity == PHY_REALTEK_IDENTIFIER) {
		RetStatus = get_Realtek_phy_speed(xemacpsp, phy_addr);
	} else {
		RetStatus = get_Marvell_phy_speed(xemacpsp, phy_addr);
	}

	return RetStatus;
}

get_phy_speed_ksz9031具体实现

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;

	xil_printf("Start PHY autonegotiation \r\n");

	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, &control);
	control |= IEEE_ASYMMETRIC_PAUSE_MASK;
	control |= IEEE_PAUSE_MASK;
	control |= ADVERTISE_100;
	control |= ADVERTISE_10;
	XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control);

	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);

	XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
	XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG,
																&control);
	control |= (7 << 12);	/* max number of gigabit attempts */
	control |= (1 << 11);	/* enable downshift */
	XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG,
																control);
	XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
	control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE;
	control |= IEEE_STAT_AUTONEGOTIATE_RESTART;
	XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);

	XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
	control |= IEEE_CTRL_RESET_MASK;
	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)
			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);
		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;
}

最重要一点:在phy_setup_emacps函数里定义了gmii to rgmii的相关操作,但是默认没有开启,需要定义宏定义 XPAR_GMII2RGMIICON_0N_ETH0_ADDR 或 XPAR_GMII2RGMIICON_0N_ETH1_ADDR,这2个宏定义就是ETH0 或 ETH1 使用GMII2RGMII,然后根据vivado里的地址定义宏定义,例如#define XPAR_GMII2RGMIICON_0N_ETH0_ADDR 8

 #define XPAR_GMII2RGMIICON_0N_ETH0_ADDR 8
u32_t phy_setup_emacps (XEmacPs *xemacpsp, u32_t phy_addr)
{
	u32_t link_speed;
	u32_t conv_present = 0;
	u32_t convspeeddupsetting = 0;
	u32_t convphyaddr = 0;

#ifdef XPAR_GMII2RGMIICON_0N_ETH0_ADDR
	convphyaddr = XPAR_GMII2RGMIICON_0N_ETH0_ADDR;
	conv_present = 1;
#endif
#ifdef XPAR_GMII2RGMIICON_0N_ETH1_ADDR
	convphyaddr = XPAR_GMII2RGMIICON_0N_ETH1_ADDR;
	conv_present = 1;
#endif

#ifdef  CONFIG_LINKSPEED_AUTODETECT
	link_speed = get_IEEE_phy_speed(xemacpsp, phy_addr);
	if (link_speed == 1000) {
		SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,1000);
		convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED1000_FD;
	} else if (link_speed == 100) {
		SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,100);
		convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED100_FD;
	} else if (link_speed != XST_FAILURE){
		SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,10);
		convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED10_FD;
	} else {
		xil_printf("Phy setup error \r\n");
		return XST_FAILURE;
	}
#elif	defined(CONFIG_LINKSPEED1000)
	SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,1000);
	link_speed = 1000;
	configure_IEEE_phy_speed(xemacpsp, phy_addr, link_speed);
	convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED1000_FD;
	sleep(1);
#elif	defined(CONFIG_LINKSPEED100)
	SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,100);
	link_speed = 100;
	configure_IEEE_phy_speed(xemacpsp, phy_addr, link_speed);
	convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED100_FD;
	sleep(1);
#elif	defined(CONFIG_LINKSPEED10)
	SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,10);
	link_speed = 10;
	configure_IEEE_phy_speed(xemacpsp, phy_addr, link_speed);
	convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED10_FD;
	sleep(1);
#endif
	if (conv_present) {
		XEmacPs_PhyWrite(xemacpsp, convphyaddr,
		XEMACPS_GMII2RGMII_REG_NUM, convspeeddupsetting);
	}

	xil_printf("link speed for phy address %d: %d\r\n", phy_addr, link_speed);
	return link_speed;
}

Linux下调试

Linux主要使用Xilinx的petalinux,petalinux会自动根据vivado导出的hdf或xsa文件生成设备树和一些Linux配置文件,非常方便,简单几步就可以完成Linux系统的定制。

首先配置GMII2RGMII驱动
zynq或zynqmp通过emio和gmii to rgmii ip使用pl端以太网调试_第5张图片
文本方式就是:

CONFIG_XILINX_GMII2RGMII=y

还需要设备树,修改的设备树文件是project-spec\meta-user\recipes-bsp\device-tree\files\system-user.dtsi

&gem1 {
	gmii2rgmii-phy-handle = <&gmii_to_rgmii_0>;
	phy-handle = <&phy1>;
	ps7_ethernet_1_mdio: mdio {
		#address-cells = <1>;
		#size-cells = <0>;
		phy1: phy@1 { 
			device_type = "ethernet-phy";
		}; 
		gmii_to_rgmii_0: phy@8 {
			compatible = "xlnx,gmii-to-rgmii-1.0";
			device_type = "ethernet-phy";
			reg = <8>;
			phy-handle = <&phy1>;
		};
	};
};

修改完成就可以了,但是打印信息会提示找不到时钟,这个没有影响

注意问题:

由于每个版本的Linux驱动稍微有差异,导致有些版本能工作,有些版本不能工作,每个版本都需要细微调整一下。

任何细节都可能导致不成功,如果你在使用过程中遇到问题,欢迎交流!

你可能感兴趣的:(zynq,arm,fpga开发)