上一篇博文中实验了PS端移植LwIP库的演示程序。本篇接下来基于Vivado17.4整理比较详细的PL端移植过程。
1、新建一个空工程,名称为net_lwip_pl。
2、FPGA芯片选择xc7z100ffg900-2。
1、工程建好以后,接下来要配置PS与PL两端的系统硬件,这样我们可以借助串口和APU核通过PL端连接的网络IP接通网络。首先单击左侧导航栏的Create Block Design建立图形文件取名top,单击OK。
2、在图形文件中单击+号,在搜索栏输入zynq找到PS端的IP核
3、双击添加ARM核的IP。
4、双击IP核进入配置界面,因为从PL端建立网络连接,所以需要AXI总线扩展。首先使能GP0和HP0
5、网络要通过串口测试,所以在Perioheral I/O Pins中开通串口
6、网络MAC的IP核Enet0需要开通
7、网络MACIP核Enet0控制外接PHY芯片的MDIO接口需要开通
8、以太网控制器采用RGMII工作方式,所以工作电压修改为:LVCMOS 1.8V
9、因为开发板DDR内存与默认不同,所以选择MT41J256M16 RE-125,单击OK退出。
10、使能中断引脚
11、搜索“eth”,添加一个“AXI 1G/2.5G Ethernet Subsystem”
12、双击刚才添加的模块,修改参数,物理接口选择“RGMII”,其他参数默认
13、单击自动运行生成其它附属IP
14、弹出窗口勾选全部选项,单击OK
15、单击自动连接生成端口引脚
16、弹出窗口勾选全部选项,单击OK
17、处理中断连接,搜索“conc”添加“Concat”
18、双击刚添加的模块,修改端口数量为“4”
19、将 4 个中断信号连接起来汇成总线
20、然后和 ZYNQ 的“IRQ_F2P”接口相连
21、处理以太网模块参考时钟,以太网模块需要一个 200Mhz 时钟和一个125Mhz 时钟,我们修改为通过外部 200Mhz 的晶振输入然后通过 PLL 模块,选择“axi_ethernet_0_refclk”的“clk_in1”引脚,右键单击
22、选择“Disconnect Pin”
23、双击“axi_ethernet_0_refclk”修改时钟输入频率为 200Mhz,Source 选择差分
24、然后右键单击“clk_in1”引脚选择“Make External”
25、修改“axi_ethernet_0_refclk”的"clk_in1 的端口名称为“sys_clk”
26、修改其他端口的名称
27、至此PL端开通网络的系统硬件配置完毕。通过Create HDL Wrapper将原理图工程转换为Verilog顶层工程
1、PL端的引脚需要指明连接,所以右击并新建约束文件,文件名称为top.xdc
2、双击打开top.xdc文件,添加约束内容如下:
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]
set_property BITSTREAM.CONFIG.UNUSEDPIN PULLUP [current_design]
############## clock define##################
create_clock -period 5.000 [get_ports sys_clk_clk_p]
set_property PACKAGE_PIN F9 [get_ports sys_clk_clk_p]
set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_clk_p]
set_property PACKAGE_PIN B14 [get_ports {mdio_mdc }]
set_property PACKAGE_PIN A14 [get_ports {mdio_mdio_io }]
set_property PACKAGE_PIN B15 [get_ports {phy_rst_n }]
set_property PACKAGE_PIN E13 [get_ports {rgmii_rxc }]
set_property PACKAGE_PIN D13 [get_ports {rgmii_rx_ctl }]
set_property PACKAGE_PIN F15 [get_ports {rgmii_rd[0] }]
set_property PACKAGE_PIN F14 [get_ports {rgmii_rd[1] }]
set_property PACKAGE_PIN E12 [get_ports {rgmii_rd[2] }]
set_property PACKAGE_PIN F13 [get_ports {rgmii_rd[3] }]
set_property PACKAGE_PIN K15 [get_ports {rgmii_txc }]
set_property PACKAGE_PIN J15 [get_ports {rgmii_tx_ctl }]
set_property PACKAGE_PIN G14 [get_ports {rgmii_td[0] }]
set_property PACKAGE_PIN G15 [get_ports {rgmii_td[1] }]
set_property PACKAGE_PIN K13 [get_ports {rgmii_td[2] }]
set_property PACKAGE_PIN L13 [get_ports {rgmii_td[3] }]
set_property IOSTANDARD LVCMOS18 [get_ports {mdio_mdc }]
set_property IOSTANDARD LVCMOS18 [get_ports {mdio_mdio_io }]
set_property IOSTANDARD LVCMOS18 [get_ports {phy_rst_n }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_rxc }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_rx_ctl }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_rd[0] }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_rd[1] }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_rd[2] }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_rd[3] }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_txc }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_tx_ctl }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_td[0] }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_td[1] }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_td[2] }]
set_property IOSTANDARD LVCMOS18 [get_ports {rgmii_td[3] }]
1、编译生成bit文件(等待时间较长)
2、单击File菜单,导出bit文件的硬件信息给SDK应用。
3、导出对话框要勾选bit文件,单击OK。
至此,系统硬件部分bit文件编译导出完成,接下来将进入软件编程环节。
1、LwIP是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。在PS端跑裸机程序的时候因为没有操作系统所以要借助LwIP连接网络。SDK环境内嵌了LwIP的开发库保存在X:\Xilinx\SDK\2017.4\data\embeddedsw\ThirdParty\sw_services中(X为Vivado安装盘),Vivado17.4版本的SDK内嵌的是lwip141_v2_0。
2、但是在黑金的AX7Z100开发板中,以太网PHY芯片采用了MICREL公司的KSZ9031型号的芯片类型,与系统默认的PHY芯片不一致,所以SDK开发包中的LwIP库需要打补丁。打开lwip141_v2_0文件夹,进入lwip141_v2_0\src\contrib\ports\xilinx\netif文件夹找到xaxiemacif_physpeed.c文件。
3、打开xaxiemacif_physpeed.c文件在宏定义部分添加PHY芯片的ID信息。
...
#define DP83867_RGMII_RX_CLOCK_DELAY_MASK 0x0003
/* TI DP83867 PHY Registers */
#define DP83867_R32_RGMIICTL1 0x32
#define DP83867_R86_RGMIIDCTL 0x86
// === Add by Yang ziheng ===//
#define MICREL_PHY_IDENTIFIER 0x22
#define MICREL_PHY_KSZ9031_MODEL 0x220
// === Add end ===//
#define TI_PHY_REGCR 0xD
#define TI_PHY_ADDDR 0xE
#define TI_PHY_PHYCTRL 0x10
#define TI_PHY_CFGR2 0x14
...
4、在代码中搜索get_IEEE_phy_speed函数添加补丁代码。
unsigned get_IEEE_phy_speed(XAxiEthernet *xaxiemacp)
{
u16 phy_identifier;
u16 phy_model;
u8 phytype;
#ifdef XPAR_AXIETHERNET_0_BASEADDR
u32 phy_addr = detect_phy(xaxiemacp);
/* Get the PHY Identifier and Model number */
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG, &phy_identifier);
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_2_REG, &phy_model);
/* Depending upon what manufacturer PHY is connected, a different mask is
* needed to determine the specific model number of the PHY. */
if (phy_identifier == MARVEL_PHY_IDENTIFIER) {
phy_model = phy_model & MARVEL_PHY_MODEL_NUM_MASK;
if (phy_model == MARVEL_PHY_88E1116R_MODEL) {
return get_phy_speed_88E1116R(xaxiemacp, phy_addr);
} else if (phy_model == MARVEL_PHY_88E1111_MODEL) {
return get_phy_speed_88E1111(xaxiemacp, phy_addr);
}
} else if (phy_identifier == TI_PHY_IDENTIFIER) {
phy_model = phy_model & TI_PHY_DP83867_MODEL;
phytype = XAxiEthernet_GetPhysicalInterface(xaxiemacp);
if (phy_model == TI_PHY_DP83867_MODEL && phytype == XAE_PHY_TYPE_SGMII) {
return get_phy_speed_TI_DP83867_SGMII(xaxiemacp, phy_addr);
}
if (phy_model == TI_PHY_DP83867_MODEL) {
return get_phy_speed_TI_DP83867(xaxiemacp, phy_addr);
}
}
// === Add by Yang ziheng ===//
else if (phy_identifier == MICREL_PHY_IDENTIFIER) {
xil_printf("Phy addr: %d ID: %x(KSZ9031)\r\n", phy_addr, phy_identifier);
return get_phy_speed_ksz9031(xaxiemacp, phy_addr);
}
// === Add end ===//
else {
LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet get_IEEE_phy_speed: Detected PHY with unknown identifier/model.\r\n"));
}
#endif
#ifdef PCM_PMA_CORE_PRESENT
return get_phy_negotiated_speed(xaxiemacp, phy_addr);
#endif
}
5、在文件中添加补丁代码中的KSZ9031型号芯片速度检测函数get_phy_speed_ksz9031
// === Add by Yang ziheng ===//
unsigned int get_phy_speed_ksz9031(XAxiEthernet* xaxiemacp, u32 phy_addr)
{
u16 control;
u16 status;
u16 partner_capabilities;
xil_printf("Start PHY autonegotiation \r\n");
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2);
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, &control);
//control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK;
control &= ~(0x10);
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, control);
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control);
control |= IEEE_ASYMMETRIC_PAUSE_MASK;
control |= IEEE_PAUSE_MASK;
control |= ADVERTISE_100;
control |= ADVERTISE_10;
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control);
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &control);
control |= ADVERTISE_1000;
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, control);
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, &control);
control |= (7 << 12); /* max number of gigabit attempts */
control |= (1 << 11); /* enable downshift */
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, control);
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE;
control |= IEEE_STAT_AUTONEGOTIATE_RESTART;
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
control |= IEEE_CTRL_RESET_MASK;
XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);
while (1) {
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
if (control & IEEE_CTRL_RESET_MASK)
continue;
else
break;
}
xil_printf("Waiting for PHY to complete autonegotiation.\r\n");
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
while (!(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE)) {
sleep(1);
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET,
&status);
}
xil_printf("autonegotiation complete \r\n");
XAxiEthernet_PhyRead(xaxiemacp, phy_addr, 0x1f, &partner_capabilities);
if ((partner_capabilities & 0x40) == 0x40)/* 1000Mbps */
return 1000;
else if ((partner_capabilities & 0x20) == 0x20)/* 100Mbps */
return 100;
else if ((partner_capabilities & 0x10) == 0x10)/* 10Mbps */
return 10;
else
return 0;
}
// === Add end ===//
6、单击File菜单SDK选项进入SDK开发环境,新建App工程取名main,单击下一步。
7、选择lwIP Echo Server后单击Finish。
8、工程文件建好后如下图
1、在SDK开发环境中右击main工程文件,编译工程
2、在运行配置窗口勾选复位和FPGA编程选项,单击Run
3、在打开的UART0口的超级终端程序中显示网络信息(如显示不全,上面操作有误)
4、另打开超级终端程序,以telnet模式登录开发板,键盘输入字符会被回写显示,实验完毕。