因为大家手上的ZYNQ在硬件资源上各有不同,所以本章节将演示如何将Xillinux 系统移植到不同的主板上。
由于每个人的使用场景和硬件配置都不相同,本文将提供一个Xillinux系统移植流程的参考示例,以帮助大家了解整个移植过程。至于移植的成功与否,大家自行尝试,也祝大家好运! 有任何问题可在本文下方进行留言。 有关Xillinux的更多内容大家可以参考Xillinux 的官方信息。
本文的操作是在vivado2018.3上实现的,如果是其他版本的vivado ,大家请自行尝试。
Xillinux 官方信息
- Xillinux的官方主页: https://xillybus.com/xillinux
- 入门指南(中文): https://xillybus.com/downloads/doc/zh/xillybus_getting_started_zynq_zh.pdf
- Xillybus的文档页面(所有指南都有中文翻译): https://xillybus.com/doc
- 关于使用许可(英文),对于有商用需求的请参看本页 : https://www.xillybus.com/licensing
Xillinux运行的硬件配置推荐
- 主芯片是ZYNQ系列芯片, 可以是任意型号任意封装。
- PS端的DDR需要大于等于512MB
- 一路HDMI 输出接口(需要是IO模拟)
- 一路USB HOST接口 (如没有USB功能系统也可启动,但是无法使用鼠标键盘访问桌面)
- 一路UART接口 (PL或PS端都可以)
- 一路千兆网络 (PL或PS端都可以)
- 两个或两个以上接在PL端的LED指示灯(非必须)
- 两个或两个以上接在PL端的按键 (非必须)
移植的工作介绍
- Kernel :移植的工作不涉及Linux kernel 的编译或者任何相关的东西,因为所有ZYNQ的主板运行Xillinux均是基于相同的kernel。
- Vivado 工程: 因为每一块ZYNQ主板在硬件配置上都各有不同,所以Vivado部分我们需要根据自己的主板调整主板的型号,时钟,DDR, UART, 网络,以及IO等相关功能的设置。
- Boot.bin 文件: 当我们的工程在型号,资源,时钟等地方有所更改的时候,为了系统可以正常启动,我们也需要自己重新生成Boot.bin文件。
- device tree文件:系统资源的改变,同样也意味着device tree 需要做对应的修改。
Vivado示例工程 和device tree的样本文件都在下文中的基础boot partition kit(demo bundle 示例包)中。
开始我们的移植工作
一、下载boot partition kit 示例包
因为大家的硬件配置千差玩别,所以这里特别提供了一份基础功能的 boot partition kit(demo bundle 示例包)供大家移植和修改(示例包已尽可能精简,方便大家更容易移植适配到其他主板上)
- 本示例包工程对应的ZYNQ主板硬件参数如下:
- 主芯片型号: XC7Z020CLG400-1
- DDR3: 512MB (256M x 16bits ) MT41K256M16
- HDMI输出: 接在PL端 (IO 模拟)
- USB : USB HOST X1
- UART : UARTX1(PS)
- GigE :千兆以太网 X1 (RGMII),PHY 芯片 Rtl8211e
- LED & KEY : LED X2 (PL) , KEY X2 (PL)
- PS 时钟 :33.33M
- xillinux-eval-basic 下载地址如下 : xillinux-eval-basic-1.0b (vivado 版本对应2018.3 或以上)
备注 :此demo bundle 示例包在 Lemon Zynq主板的示例框架上修改而来,ps输入时钟已修改成了更常用的33.33m,而Lemon Zynq 上使用的PS时钟是50M,所以此demo bundle 工程生成的结果并不能直接应用在Lemon Zynq的主板上(需要将33.33修改成50m 才可以, devicetree 也要响应的修改成50)
二、解压并打开boot partition kit 示例包
1. 解压缩刚刚下载的示例压缩包(解压缩的路径不能带有中文字符)
2. 启动Vivado。请确保所使用的vivado版本是 2018.3 或以上。
3. 打开 Vivado后 , 从上方菜单栏中选择 Tools > Run Tcl Script… 。在弹出的窗口中选择项目路径中的 verilog文件夹下的xillydemo-vivado.tcl 文件。系统将会自动加载tcl,并创建一个新的 Vivado工程,该工程包含生成bitstream所需的源文件以及各种设置。
4. 当Vivado窗口底部的“Tcl Console”状态栏中出现 INFO: Project created: xillydemo, 代表工程已经创建成功。
三、修改Vivado工程
(这里只针对几个比较典型需要修改注意的点进行介绍,有些内容可能并不适用于您手中的主板,请根据不同的配置选择性参考)
1 )修改主芯片型号
如果主板上的ZYNQ型号并不是 XC7Z020CLG400-1 型号的芯片,请先修改工程中芯片的完整型号。(如果是,就不需要修改),点选Settings
在Project device 中按右侧的“三个点”按键,然后在弹出的界面中按下图修改对应的ZYNQ 芯片型号(Search 中输入自己主板的ZYNQ 型号),之后按OK 确认, 然后在Settings 中按下Apply 应用,并按下OK退出
如果弹出下列菜单,请点NO就可以
如果型号有修改,需要对所有的IP模块进行更新, Reports -> Report IP Status
在弹出的选项中 选 Upgrade Selected,
之后在弹出的界面中点OK 确认即可,修改成功
2)修改PS主时钟 (BLOCK DESIGN )
如果主板上PS的主时钟不是33.33Mhz(ZYNQ主板默认的PS时钟是33.33M,但是有一些市面上比较火的主板,如Digilent 的PYNQ-Z1 ,TUI的PYNQ-Z2, 以及Digilent 的Arty Z7 等比较流行的ZYNQ主板所使用的PS时钟是50Mhz的)那这里需要对应的进行修改PS的输入时钟。(备注如果修改了 PS的 CLK,那对应的 Device tree 中也需要修改)
a ) OPEN BLOCK DESIGN -> 双击ZYNQ 7 Processing System 打开Z7设置页
b ) Clock Configuration 界面中将 Input Frequency 由默认的33.333333 修改成主板对应的PS时钟
c)修改了主时钟之后,对应的 Device tree 中也需要修改对应的内容, 下文中会介绍到。
3 ) DDR的修改
各家的主板 DDR 的型号 和容量通常不同, 按照实际板子上的型号(或者兼容的型号)进行修改(这块不清楚的可以询问主板生产厂商),位宽按实际的填,如果是单颗粒DDR一般是16bit,如果是双颗粒DDR一般是32bit(如果DDR的容量有修改,那么 devicetree 中也对应需要进行容量的调整)
4) 修改PS部分BANK 1 I/O 的BANK电压(BANK 501)
这部分的设置会对SD卡,网络 ,USB部分功能的稳定性产生影响,所以需要将这部分电压按照主板实际的BANK电压先进行设置。(可以查询主板的原理图,或者询问主板的生产厂商)
5)UART的设置
UART部分,不论使用的是UART0, 又或者是UART1, 请都保留两种UART接口的使能状态都是开启的(如果禁用了某一个UART口,有可能会出现系统启动失败的情况,这点需要注意)。 对于用不到的UART口,可以直接在工程中将用不到的uart端口映射到没有使用到的MIO即可。
a ) UART 接在PS端
假设电路上的UART使用的是mio 14-15脚,那么在设置界面,只需要 勾选上UART0 ,然后 在IO栏里选择MIO 14-15即可 , 切记UART 1也必须勾选上(可以将UART 1映射到没有使用的MIO),如果硬件上对应的串口mio 接在UART1端,操作也是相同的。
b ) UART 接在PL端
假设电路上的UART 是接在PL端的, 这里直接将 UART 0 对应的IO 修改成EMIO 输出即可。 切记UART 1也必须勾选上(可以将UART 1映射到没有使用的MIO)
如果是接在PL的情况,那记保存设置后 右键Z7的UART口,然后点选External ,将UART资源引出
6 ) 网络设置
网络设置也和串口一样,分两种情况 a )接 PS端, b )接PL端, 接PL端会相对比较复杂。备注,这里都是针对rgmii 的千兆接口的phy来介绍的, 如果是其他接口或者百兆phy大家自行尝试。
a ) 千兆PHY 芯片接在PS端 , 使能ENET0 和 MDIO ,然后在IO端选择对应的MIO 端口(和主板需要对应)
如果硬件上网络PHY的Reset 信号接在PS端,可以使能GPIO MIO功能,再对ENET0 reset脚进行设置。如果PHY芯片的这个Reset信号在硬件上同样接有RC上电复位,那这部分不设置也是可以的(但是要确确保工作时reset脚的电平高于 PHY芯片datasheet 上的Reset引脚 Vih 电压)
如果PHY是接PS MIO端的,还需要检查网络时钟部分的设置以免最后不能访问网络,这里ENET0 的Clock Source 一定要是IO PLL (Clock Configuration 界面)
b ) 千兆PHY 芯片接在PL端
这里把ENET0通过EMIO引出来 ,并且选中MDIO功能(也用EMIO方式),之后点选OK 来保存ZYNQ的配置
同样的,1.如果硬件上网络PHY的Reset 信号接在PS端,可以使能GPIO MIO功能,再对ENET0 reset脚进行设置。如果PHY芯片的这个Reset信号在硬件上同样接有RC上电复位,那这部分不设置也是可以的(但是要确确保工作时reset脚的电平高于 PHY芯片datasheet 上的Reset引脚 Vih 电压),2.如果这个Reset信号接 PL端的话,情况也类似,可以人为在PL端给复位逻辑,也可以不接(前提和1一致)
如果PHY是接PL 端用EMIO方式的,那这里的ENET0 的Clock Source 会变成External,这里和接在MIO方式的是不同的(Clock Configuration 界面)
由于 ZYNQ的PS 调用EMIO方式 默认输出是GMII,而我们修改的目标千兆phy是RGMII接口的,所以这里我们需要增加一个 GMII和RGMII转换的IP模块 (Gmii to Rgmii)
打开 GMII TO RGMII模块 设置更改成下面
又因为GMII TO RGMII 是高电平复位 ,但是ZYNQ 是输出低电平复位的,所以这里需要增加一个反相器(搜索utility, 在检索出来的选项里 选择 utility vector logic)
在设置页里改成 not(反相器)将位宽改成1
按下图方式连接好各个模块(复位 时钟 网络)
并通过右键 然后选择Make external 的方式引出RGMII 和MDIO_PHY功能引脚
这里还要对Z7的模块进行一个修改,因为gmii to rgmii 模块的clkin 在手册上写要求是200mhz,所以我们这里需要对ZYNQ核的输出时钟频率进行修改
重新双击ZYNQ的核,将FCLK_CLK0的频率改成200 (demo 中的工程默认就是200M了)
到此,Block Design 主要的修改部分已经完成
8) 关于Block Automation
建议在正确连接好必要的信号线之后,不要使用Block Automation功能,当使用 Block Automation时,某些信号会作为 block design的 ports (GPIO_0、 DDR 和FIXED_IO)进行连接。然而不管你是否将这些信号引出,这些信号在物理层面都是和芯片的IO硬件连接的,因为这些信号属于 PS本身。因此,在设计中不引出这些信号,反而会让程序看起来更为简洁。
9) 修改顶层文件 xillydemo.v (这部分内容请结合实际的Block Design 的改动来进行修改)
a) 如果板子上的网络 和 UART 两个功能是连接在PS 的MIO端 ,那xillydemo.v不需要大的修改, 仅根据你的需求修改LED , GPIO 等即可。 (xillydemo.v上的 IO口多数是预留的)
b)如果板子的UART是接在PL端, 那程序上需要增加UART的端口定义 ,以及和BD模块的信号连接部分。
在模块端口定义处 增加下列内容
input uart_rxd,
output uart_txd,
在 vivado_system vivado_system_i 的模块例化内容中,添加下列内容:
.UART_0_0_rxd(uart_rxd), .UART_0_0_txd(uart_txd)
c ) 同理如果板子的千兆网络PHY芯片是接在PL端, 那程序上需要增加RGMII的端口定义 ,以及和BD模块的信号连接部分。
在模块端口定义处 增加下列内容
output MDIO_PHY_mdc,
inout MDIO_PHY_mdio_io,
input [3:0]RGMII_rd,
input RGMII_rx_ctl,
input RGMII_rxc,
output [3:0]RGMII_td,
output RGMII_tx_ctl,
output RGMII_txc,
为MDIO 添加 iobuf 模块
wire MDIO_PHY_mdio_i, MDIO_PHY_mdio_o, MDIO_PHY_mdio_t;
IOBUF MDIO_PHY_0_mdio_iobuf
(.I(MDIO_PHY_mdio_o),
.IO(MDIO_PHY_mdio_io),
.O(MDIO_PHY_mdio_i),
.T(MDIO_PHY_mdio_t));
在 vivado_system vivado_system_i 的模块例化内容中,添加下列内容:
.MDIO_PHY_0_mdc(MDIO_PHY_mdc),
.MDIO_PHY_0_mdio_i(MDIO_PHY_mdio_i),
.MDIO_PHY_0_mdio_o(MDIO_PHY_mdio_o),
.MDIO_PHY_0_mdio_t(MDIO_PHY_mdio_t),
.RGMII_0_rd(RGMII_rd),
.RGMII_0_rx_ctl(RGMII_rx_ctl),
.RGMII_0_rxc(RGMII_rxc),
.RGMII_0_td(RGMII_td),
.RGMII_0_tx_ctl(RGMII_tx_ctl),
.RGMII_0_txc(RGMII_txc),
c )剩下的例如 J5, J6 是连接到排针的端口,可以根据实际使用情况进行修改和删减, clk_50是PL的时钟输入,实际并未使用仅作预留。
10)修改XDC约束文件
a ) 这里包含时序约束和 管脚约束, 如果xillydemo.v改动不是很大, 那只需要参考xillydemo.xdc 将里面所有用到的信号的管脚号修改成和主板匹配的管脚号即可。
b ) 如果主板的串口是EMIO方式连接到PL的,并且在xillydemo.v中也增加了串口的相应部分内容,那在xdc约束中增加下列串口引脚的约束(其中的管脚号需要根据实际情况修改)
set_property -dict {PACKAGE_PIN M17 IOSTANDARD LVCMOS33} [get_ports uart_rxd]
set_property -dict {PACKAGE_PIN L17 IOSTANDARD LVCMOS33} [get_ports uart_txd]
c )如果主板的网络是EMIO方式连接到PL的,并且在xillydemo.v中也增加了串口的相应部分内容,那在xdc约束中增加下列串口引脚的约束(其中的管脚号需要根据实际情况修改)
# RGMII PHY
set_property -dict {PACKAGE_PIN G21 IOSTANDARD LVCMOS33} [get_ports MDIO_PHY_mdc]
set_property -dict {PACKAGE_PIN H22 IOSTANDARD LVCMOS33} [get_ports MDIO_PHY_mdio_io]
set_property -dict {PACKAGE_PIN A22 IOSTANDARD LVCMOS33} [get_ports {RGMII_rd[0]}]
set_property -dict {PACKAGE_PIN A18 IOSTANDARD LVCMOS33} [get_ports {RGMII_rd[1]}]
set_property -dict {PACKAGE_PIN A19 IOSTANDARD LVCMOS33} [get_ports {RGMII_rd[2]}]
set_property -dict {PACKAGE_PIN B20 IOSTANDARD LVCMOS33} [get_ports {RGMII_rd[3]}]
set_property -dict {PACKAGE_PIN A21 IOSTANDARD LVCMOS33} [get_ports RGMII_rx_ctl]
set_property -dict {PACKAGE_PIN B19 IOSTANDARD LVCMOS33} [get_ports RGMII_rxc]
set_property -dict {PACKAGE_PIN E21 IOSTANDARD LVCMOS33} [get_ports {RGMII_td[0]}]
set_property -dict {PACKAGE_PIN F21 IOSTANDARD LVCMOS33} [get_ports {RGMII_td[1]}]
set_property -dict {PACKAGE_PIN F22 IOSTANDARD LVCMOS33} [get_ports {RGMII_td[2]}]
set_property -dict {PACKAGE_PIN G20 IOSTANDARD LVCMOS33} [get_ports {RGMII_td[3]}]
set_property -dict {PACKAGE_PIN G22 IOSTANDARD LVCMOS33} [get_ports RGMII_tx_ctl]
set_property -dict {PACKAGE_PIN D21 IOSTANDARD LVCMOS33} [get_ports RGMII_txc]
set_property SLEW FAST [get_ports {RGMII_td[0]}]
set_property SLEW FAST [get_ports {RGMII_td[1]}]
set_property SLEW FAST [get_ports {RGMII_td[2]}]
set_property SLEW FAST [get_ports {RGMII_td[3]}]
set_property SLEW FAST [get_ports RGMII_tx_ctl]
set_property SLEW FAST [get_ports RGMII_txc]
同时,在时序约束的地方也需要修改成下列内容(增加网络部分的时序约束)
set_clock_groups -asynchronous \
-group [get_clocks -include_generated_clocks clk_fpga_0] \
-group [get_clocks -include_generated_clocks clk_fpga_1] \
-group [get_clocks -include_generated_clocks clk_fpga_2] \
-group [get_clocks -include_generated_clocks clk_50] \
-group [get_clocks -include_generated_clocks RGMII_rxc]
d )剩下的例如 J5, J6 是连接到排针的端口,可以根据实际使用情况进行修改和删减, clk_50是PL的时钟输入,实际并未使用仅作预留。
四、对Vivado 工程进行编译和综合,得到bit文件
完成上述操作后,就可以对整个vivado 工程进行编译和综合了,并最终生成bitstream文件。 这个过程将持续几分钟。正常情况下不应该出现任何错误或严重的警告(Critical Warnings)
1)按下绿色箭头对工程进行编译
2)按下Generate Bitstream 完成综合以及生成bit文件 (需要等待很久)
3)在此过程结束后,可以在verilog/vivado/xillydemo.runs/impl_1/xillydemo.bit 路径下找到新生成的bitstream文件。
五、创建boot.bin文件
每次我们更改 Zynq block的参数时,我们都应该重新创建 boot.bin 。(hardware platform , FSBL也必须重新更新或创建)
1)File→Export→Export hardware…在弹出的对话框中勾选“include bitstream”,点击“OK”确认
2)File→Lauch SDK,在弹出的对话框中,保存默认,点击“OK”。系统将自动打开SDK开发环境
3)添加FSBL部分
在SDK中新建工程如下 file→new→Application Project
设置工程名fsbl,点next
选择FSBL模板
4) 下载我们预先编译好的 u-boot.elf (备注,请根据uart 在ps资源中的端口号来下载对应版本的u-boot文件),并解压缩压缩包,得到 u-boot.elf文件
- UART0 版:u-boot_uart_0
- UART1 版:UART 1版本的u-boot 待编译后上传
5 ) 创建uboot.bin
a )点选我们的FSBL 工程,之后点 Xilinux -> Create Boot Image 创建启动镜像
b ) 如果操作没问题, 在弹出的界面的列表中会出现FSBL.ELF 以及xillydemo.bit
这里我们需要保留FSBL.elf (如果FSBL.elf没有出现,可以手动添加文件,在SDK\FSBL\DEBUG目录下), 接着删除xillydemo.bit(选中xillydemo.bit,然后点选Delete)
c ) 添加刚才解压缩的u-boot.elf (点选Add,然后在弹出的界面中选中刚才我们解压缩的u-boot.elf文件)
d )现在我们已经有FSBL.elf 和u-boot.elf两个 文件了, 之后按下Create Image 生成启动镜像文件。之后我们就可以在SDK\FSBL\BOOTIMAGE\文件夹下找到刚刚生成的 BOOT.bin 文件了。
六、修改device tree
我们的demo bundle 示例包中有一份 devicetree.dts (devicetree 文件夹下)的示例文件,我们可以直接打开并在这份文件上进行修改(在win下可以直接用记事本打开)。
1 ) 默认的devicetree 是假设PS端输入时钟是33.33M情况下的,如果你的主板是50M或者其他时钟,除了之前的Block Design 中ZYNQ模块的时钟需要修改外,devicetree的运行时钟也需要对应的改动。
找到 ps-clk-frequency = <33333333>; 里面的33333333代表PS-CLK输入是在33.333333M的意思,如果你的主频是50M,对应的修改成 ps-clk-frequency = <50000000>;即可
2) 修改对应的主板名称 (这步并不重要)
xillinux { board = "Lemon-zynq"; } ;
3) 通过修改chosen 可以调整命令行终端对应的串口号, 波特率等 (如果你的串口输出默认是uart 0 ,就不需要修改)
chosen { bootargs = "console=ttyPS0,115200n8 console=tty0 consoleblank=0 root=/dev/mmcblk0p2 rw rootwait earlyprintk"; linux,stdout-path = "/amba@0/serial@e0000000"; } ;
如果你需要将uart 0 修改成uart 1, 这里将bootargs
中的 console
参数从 ttyPS0
更改为 ttyPS1
。确保波特率和其他参数保持不变, 修改 stdout-path
: 确保 linux,stdout-path
指向 ttyPS1
如下。 (当然默认的 UART 1对应的设备树内容也需要添加, 可以参考UART 0对应的内容进行添加)
chosen {
bootargs = "console=ttyPS1,115200n8 console=tty0 consoleblank=0 root=/dev/mmcblk0p2 rw rootwait earlyprintk";
linux,stdout-path = "/amba@0/serial@e0001000"; // 确保路径与实际设备树节点一致
} ;
4 ) 下面是网络部分的设备树内容,默认是的当作接PS端的
ps7_ethernet_0: ps7-ethernet@e000b000 { #address-cells = <1>; #size-cells = <0>; clock-names = "pclk", "hclk", "tx_clk"; clocks = <&clkc 30>, <&clkc 30>, <&clkc 13>; compatible = "cdns,gem"; interrupt-parent = <&ps7_scugic_0>; interrupts = <0 22 4>; phy-handle = <ð_phy0>; phy-mode = "rgmii-id"; reg = <0xe000b000 0x1000>; eth_phy0: phy@0 { device_type = "ethernet-phy"; reg = <0>; }; } ;
如果的网络PHY是接PL端的,那接口会有不同,Emio方式的 rgmii接口是 PS端的GMII接口通过PL端rgmii2gmii模块转换实现的,所以对于PS来说,网络接口就是gmii的,所以这里需要将 phy-mode修改成 gmii
phy-mode = "gmii";
6) 修改DDR
示例devicetree 默认的 内存是256M x 16bit (512MB),
ps7_ddr_0: memory@0 { device_type = "memory"; reg = <0x0 0x20000000>; } ;
如果你的主板对应 256M x 32bit (1G容量)则需要将内容修改如下:(vivado工程block design 的ZYNQ DDR设置也需要对应修改,上文中有介绍)
ps7_ddr_0: memory@0 {
device_type = "memory";
reg = <0x0 0x40000000>;
};
5) 将修改后的 devicetree.dts 转换成 devicetree.dtb文件 (在Ubuntu 下操作)
dtc -I dts -O dtb -o devicetree.dtb devicetree.dts
这样我们就得到了修改后的devicetree.dtb文件
结尾
经过上述这些操作,我们已经得到了xillinux启动所必须的三个文件了
- devicetree.dtb
- BOOT.bin
- xillydemo.bit
接下来的操作可以参考Xillinux 对应的一、二、三章节正常进行。(第二章的三个文件,用本文生成的三个文件来替代)
大家实际移植的过程中可能会遇到和本文不一致的地方,移植过程中遇到的任何问题都可以在本站结尾进行留言。也祝大家移植过程能够顺利。Enjoy it !