使用传统方式在 ZYNQ 上移植 Linux。
reference: https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841738/Getting+Started
硬件平台: PYNQ-Z2
Vivado版本: 2018.2
Vivado 工程创建
创建一个 zynq 的 vivado 工程,最小系统就行了,blockdesign 如图:
这里添加了一个 AXI-GPIO,是为了后面练习驱动。
生成 bitstream 后,导出到 sdk。
下载需要的资源
Repository Name | Content | |
---|---|---|
1 | https://github.com/Xilinx/linux-xlnx.git | The Linux kernel with Xilinx patches and drivers |
2 | https://github.com/Xilinx/u-boot-xlnx.git | The u-boot bootloader with Xilinx patches and drivers |
3 | https://github.com/Xilinx/device-tree-xlnx.git | Device Tree generator plugin for xsdk |
4 | https://git.kernel.org/pub/scm/utils/dtc/dtc.git | Device Tree compiler (required to build U-Boot) |
5 | https://github.com/Xilinx/arm-trusted-firmware.git | ARM Trusted Firmware (required for Zynq UltraScale+ MPSoC and Versal platforms) |
6 | https://github.com/Xilinx/xen.git | Xilinx Xen branch for Zynq UltraScale+ and Versal platforms |
7 | https://github.com/Xilinx/embeddedsw.git | Xilinx embeddedsw repository for bare-metal applications such as FSBL, PMU Firmware, PLM |
配置环境
Build Device Tree Compiler (dtc)
Tools Required: vivado sdk
Source Required: Device Tree compiler
下载Device Tree compiler,在目录 dtc 下运行:
1 | make |
会在当前目录生成 dtc
,一个可执行文件,将其添加到 PATH
中:
1 | export PATH=`pwd`:$PATH # 或者把 `pwd` 换为 <dtc> 目录 |
其他
1 | # export PATH=/home/fitz/linux-on-zynq/linux-xlnx/scripts/dtc:$PATH |
创建设备树文件
Tools Required: vivado sdk
Source Required: device-tree-xlnx
下载与 vivado 版本对应的 device-tree-xlnx
在 vivado sdk 中添加设备树插件:
SDK Menu: Xilinx > Repositories > New…
<device-tree-xlnx>
> OK
然后创建一个 BSP,选择 device_tree 模板:
生成的 .dts/.dtsi 在 /device_tree_bsp_0/ folder 目录下,会使用到文件有:system-top.dts, pcw.dtsi, pl.dtsi
这几个文件在后面编译 u-boot 的时候会用到,在编译 u-boot 的时候同时生成 .dtb
编译 U-BOOT & 编译设备树
Tools Required: vivado sdk
Source Required: u-boot-xlnx
下载与 vivado 版本对应的 u-boot-xlnx,需要创建/修改 4 个文件:
u-boot-xlnx/include/configs/zynq_pynqz2.h(新建)
1
2
3
4
5
6
7
8
9
10
11
/* Define PYNQZ2 PS Clock Frequency to 50MHz */这里面定义了 PS 时钟频率 50 MHz
u-boot-xlnx/include/configs/zynq-common.h(修改)
将:
1
2
3
4
5
6
7
8"sdboot=if mmcinfo; then " \
"run uenvboot; " \
"echo Copying Linux from SD to RAM... && " \
"load mmc 0 ${kernel_load_address} ${kernel_image} && " \
"load mmc 0 ${devicetree_load_address} ${devicetree_image} && " \
"load mmc 0 ${ramdisk_load_address} ${ramdisk_image} && " \
"bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}; " \
"fi\0" \改为:
1
2
3
4
5
6
7"sdboot=if mmcinfo; then " \
"run uenvboot; " \
"echo Copying Linux from SD to RAM... && " \
"load mmc 0 ${kernel_load_address} ${kernel_image} && " \
"load mmc 0 ${devicetree_load_address} ${devicetree_image} && " \
"bootm ${kernel_load_address} - ${devicetree_load_address}; " \
"fi\0" \修改 sboot 这段,这段表示从 SD 卡启动时,文件加载的情况,我们将文件系统放在了 SD 卡的第二个分区,因此不需要 load ramdisk,这里的
-
号,左右都要有空格,意思是替代的地址,那么就要把-
作为${ramdisk_load_address}
的替代,告诉 u-boot 和内核没有文件系统,启动的时候就不会加载它。u-boot-xlnx/configs/zynq_pynqz2_defconfig
zynq_pynqz2_defconfig 可以参考同目录下的 u-boot-xlnx/configs/zynq_zybo_defconfig,稍作修改:
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
61
62
63
64
65
66
67
68
69
70CONFIG_ARM=y
CONFIG_SYS_CONFIG_NAME="zynq_pynqz2"
CONFIG_ARCH_ZYNQ=y
CONFIG_SYS_TEXT_BASE=0x4000000
CONFIG_SPL_STACK_R_ADDR=0x200000
CONFIG_DEFAULT_DEVICE_TREE="zynq-pynqz2"
CONFIG_DEBUG_UART=y
CONFIG_DISTRO_DEFAULTS=y
CONFIG_FIT=y
CONFIG_FIT_VERBOSE=y
CONFIG_FIT_SIGNATURE=y
CONFIG_BOOTCOMMAND="run $modeboot || run distro_bootcmd"
# CONFIG_DISPLAY_CPUINFO is not set
CONFIG_SPL=y
CONFIG_SPL_STACK_R=y
CONFIG_SPL_OS_BOOT=y
CONFIG_SPL_DM_MMC=y
CONFIG_SYS_PROMPT="Zynq> "
CONFIG_CMD_THOR_DOWNLOAD=y
CONFIG_CMD_DFU=y
CONFIG_CMD_FPGA_LOADBP=y
CONFIG_CMD_FPGA_LOADFS=y
CONFIG_CMD_FPGA_LOADMK=y
CONFIG_CMD_FPGA_LOADP=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_SF=y
CONFIG_CMD_USB=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_CMD_TFTPPUT=y
CONFIG_CMD_CACHE=y
CONFIG_CMD_EXT4_WRITE=y
CONFIG_OF_EMBED=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
CONFIG_NET_RANDOM_ETHADDR=y
CONFIG_SPL_DM_SEQ_ALIAS=y
CONFIG_DFU_MMC=y
CONFIG_DFU_RAM=y
CONFIG_FPGA_XILINX=y
CONFIG_DM_GPIO=y
CONFIG_MMC=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ZYNQ=y
CONFIG_SPI_FLASH=y
CONFIG_SPI_FLASH_BAR=y
CONFIG_SPI_FLASH_SPANSION=y
CONFIG_PHY_MARVELL=y
CONFIG_PHY_REALTEK=y
CONFIG_PHY_XILINX=y
CONFIG_ZYNQ_GEM=y
CONFIG_ZYNQ_GEM_SPI_MAC_OFFSET=0x20
CONFIG_DEBUG_UART_ZYNQ=y
CONFIG_DEBUG_UART_BASE=0xe0000000
CONFIG_DEBUG_UART_CLOCK=50000000
CONFIG_ZYNQ_SERIAL=y
CONFIG_ZYNQ_QSPI=y
CONFIG_USB=y
CONFIG_USB_EHCI=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_ZYNQ=y
CONFIG_USB_ULPI_VIEWPORT=y
CONFIG_USB_ULPI=y
CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_MANUFACTURER="Xilinx"
CONFIG_USB_GADGET_VENDOR_NUM=0x03fd
CONFIG_USB_GADGET_PRODUCT_NUM=0x0300
CONFIG_CI_UDC=y
CONFIG_USB_GADGET_DOWNLOAD=y在这里指定了 device tree,因此需要将前一个步骤中生成的 system-top.dts, pcw.dtsi, pl.dtsi 复制到 u-boot-xlnx/arch/arm/dts/ 目录下,将 system-top.dts 改名为 zynq-pynqz2.dts
在完成这些后,就可以编译 u-boot 了,运行命令:
1 | make distclean |
会在 u-boot-xlnx/ 目录下生成 u-boot 文件,将其改名为 u-boot.elf 待用,更新一些版本的 vivado 可以直接生成 u-boot.elf 文件。
同时在 arch/arm/dts/ 目录下生成 zynq-pynqz2.dtb 文件,即编译好的设备树文件,将其改名为 devicetree.dtb 待用。
设备树也可以单独编译,命令:
1 dtc -I dts -O dtb -o devicetree.dtb system-top.dtsdevicetree.dtb 为生成的 dtb 文件名字/路径,system-top.dts 为待编译的设备树文件
创建 BOOT.bin
Tools Required: vivado sdk
首先用 vivado sdk 生成 fsbl,即新建一个
Application Project
,选择 Zynq FSBL
模板:
然后在这个 Project 上右击 > Create Boot Image
依次添加 fsbl.elf, .bit, u-boot.elf,其中的
u-boot.elf 是在上一个步骤中生成的,最后点击
Create Image
,会在
编译内核
Tools Required: vivado sdk
Source Required: linux-xlnx
在目录 linux-xlnx 下,运行命令:
1 | make xilinx_zynq_defconfig |
编译完成后,在目录 linux-xlnx/arch/arm/boot/ 下生成未经压缩过的内核镜像 uImage 文件。
下载不同版本的 linux-xlnx,编译后可以得到不同内核版本
环境变量文件
创建一个名为 uEnv.txt 的文件,内容如下:
1 | bootargs=console=ttyPS0,115200n8 root=/dev/mmcblk0p2 rw noinitrd earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=0 |
这里面是系统启动时传给内核的参数,
console=ttyPS0,115200n8
表示控制台的串口号;
root=/dev/mmcblk0p2 rw noinitrd rootfstype=ext4
配合之前对 u-boot-xlnx/include/configs/zynq-common.h
的修改,实现了从 SD 的第二分区读取文件系统,该分区为 ext4 格式;
最后
Source Required: linaro 文件系统
将 SD 分两个区,第一个区 200M,为 FAT32 格式,剩余容量给第二个分区,为 ext4 格式。
将前几步得到的 BOOT.bin, devicetree.dtb, uImage, uEnv.txt 拷贝到 SD 卡的 FAT32 分区中;
将下载的 linaro 文件系统 解压,将 binary/boot/filesystem.dir 目录下的文件拷贝到 ext4 分区中:
1 | sudo rsync -a --progress ./ /media/fitz/sandisk |
最后,将 SD 卡插回板子上,板子上的条线帽选择 SD 卡启动,打开电源,连上串口线,可以看到串口输出如下信息,表示启动成功:
1 | U-Boot 2017.01-dirty (Mar 31 2020 - 17:21:17 +0800) |
坑
在 build device tree 时,报错:
1
devicetree.dtb: Warning (unit_address_vs_reg): Node /memory has a reg or ranges property, but no unit name
解决办法:
dtc 版本与 vivado sdk 版本不对应,换成对应版本的 dtc 就行修改 system-top.dts 中的
memory
:将:
1
2
3
4memory {
device_type = "memory";
reg = <0x0 0x20000000>;
};改为:
1
2
3
4memory {
device_type = "memory";
reg = <0x0 0x20000000>;
};
Linux 启动时内存分配失败:
启动时串口输出类似信息:
1
2
3
4
5
6
7
8U-Boot 2017.01-dirty (Mar 31 2020 - 17:21:17 +0800)
Model: Zynq pynq z2 Development Board
Board: Xilinx Zynq
I2C: ready
DRAM: ECC disabled 0 MiB
MMC: sdhci@e0100000: 0 (SD)
SF: Detected s25fl128s_64k with page size 256 Bytes, erase size 64 KiB, total 16 MiB可以看到 DRAM 为 0,应该是修改 u-boot-xlnx/include/configs/zynq-common.h 或 u-boot-xlnx/include/configs/zynq_pynqz2.h 的问题,DRAM 的大小应该在设备树文件里面的
memory
定义,不要在 zynq-common.h 里面用#define CONFIG_SYS_SDRAM_SIZE (512 * 1024 * 1024)
定义。