本文从最简单的petalinux需求的vivado工程开始,建立一个能跑起来linux的vivado工程。同时将linux kernel、根文件系统部署在接到SD1接口上的emmc中,qspi-flash中放置BOOT.BIN,uboot唤起emmc中的image.ub。并填坑关于petalinux在SD0为空时,配置从SD1启动的bug
目录
1 - VIVADO工程建立
1.1 - PS配置
1.2 - 加个AXI-GPIO
1.3 - 引脚约束,bitstream生成与压缩
1.4 - 生成bitstream,导出hdf
2 - Petalinux工程
2.1 - 配置BOOT.BIN从QSPI FLASH唤起emmc中的image.ub
2.2 - petalinux从跳过空SD0从SD1启动的bug修复
2.3 - 编译,打包
3 - 启动
这里我们从建立一个petalinux所要求的最小linux系统开始,从这个工程搭建我们后面所需的PL框架。
依照UG1144中介绍,在zynq-7000系列上启动linux所需要的必备器件有以下三个:
新建一个vivado工程,创建框图设计(Block Design),添加ZYNQ7 Processing System
MIO配置,添加QSPI Flash,根据你的板子来设置引脚,注意Bank0和Bank1的电平设置
MIO配置,添加外设,这里添加了一个以太网ENET0,一个USB0,SD0 和SD1分别是连接到SD卡和eMMC,UART1被选中,它将被作为linux系统基本串口登录
Clock配置,修改IO上的外设时钟
DDR配置,修改为PS上接入的内存
添加一个AXI GPIO IP核,用来控制板上的4个LED。将其GPIO设置为4bit地址宽度,设置GPIO为外部引脚
点击“Run Connection Automation”和“Run Block Automation”,自动连接部分信号线和自动添加缺少的ip
完成后我们可以点击Diagram框内的Optimiz Routing,优化一下布局
可以看到,整个框图比较简单,PS核DDR和部分FIXED_IO引出,AXI GPIO通过一个AXI桥连入到PS核上的AXI_GP通用总线上
创建HDL wrapper
在生成的 design_1_wrapper.v 文件中,我们可以看到我们外连的led引脚(名称会因为你外连后是否修改名称而定)
添加一个xdc约束文件
这里名称我们取“myLinuxBase”,添加完毕后在其内加入下述代码:
set_property PACKAGE_PIN F13 [get_ports led_0_tri_o[0]]
set_property PACKAGE_PIN E13 [get_ports led_0_tri_o[1]]
set_property PACKAGE_PIN G12 [get_ports led_0_tri_o[2]]
set_property PACKAGE_PIN G11 [get_ports led_0_tri_o[3]]
set_property IOSTANDARD LVCMOS18 [get_ports led_0_tri_o[*]]
#bit compress
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
可以看到,前4行代码都是led引脚管脚约束,第6行是约束这4个管脚的电平
后面3行则是对生成的bitstream进行压缩,否则bitstream生成后会有13MB之大。
生成bitstream,因为之前没有进行综合,这里会提示进行综合,无错误后会成功生成bitstream文件
生成bitstream后,我们导出hdf硬件描述文件 File -> Export -> Export Hardware,一定勾选包括bitstream!!
bitstream是vivado中生成的,导出为hdf文件时需要include bitstream。
hdf实际只是个压缩文件,其中包括硬件信息、bitstream等等,petalinux需要解包hdf然后将其中的bitstream用于后面进行petalinux-package
这样,我们就得到petalinux所需的包含bitstream的hdf文件了,接下来开始petalinux生成
petalinux工程的建立、导入hdf就不赘述了。
我们板上现在的情况是,SD0和SD1口都被使用,其中SD0口是SD卡使用,SD1口是接到了板上的eMMC。
eMMC被分区为两个部分,mmcblock1p1是FAT分区,里面存放image.ub,mmcblock1p2是EXT4分区,里面是根文件系统。
eMMC的分区和根文件系统的部署可以用以下的方法:
先使用一个从SD启动的linux,在板上启动SD-Linux后,使用fdisk对emmc进行分区,使用mount进行挂载,挂载EXT4分区后将已经制作好的根文件系统放入eMMC上的EXT4分区
也可以使用uboot对eMMC进行读写,我这里只是取巧而已
我们期望的是板子上电后,从QSPI-FLASH读取BOOT.BIN,执行到uboot后,uboot从eMMC的FAT分区读取image.ub,并将内核释放到内存中运行。
ZYNQ-7000系列只支持SD启动和QSPI启动,不支持直接的从eMMC启动BOOT.BIN,但是UltraScale的支持
参考:
- https://www.xilinx.com/support/answers/50991.html
- 《UG585- Zynq-7000 SoC Technical Reference Manual 》中 11.2.1 Boot Device
主要有以下几点需要配置
这里暂时不做别的配置了,保存退出
petalinux2018.3中会有一个这样的bug(实际上之前也存在,官方修复还是有漏洞),当像我们这样SD0接入SD卡,SD1接入eMMC时,当SD卡没有插入时启动,尽管配置petalinux的主sd为ps7_sd_1,但是其uboot启动时会报错找不到mmc 0
“no mmc device at slot 0”
"card did not respond to voltage select"
这个问题实际上官方之前已经发现,并且在petalinux2017.4之后的版本进行了修复(AR#69780),但是修复并没有完全解决问题。在版本修复之前,问题的来源是在petalinux-config设置主SD为1后,并没有能够成功修改uboot的启动指令,故给出了AR69780中的解决方案,但是解决方案并不完美。
在版本修复之后,我们打开
看到光标高亮的cp_kernel2ram变量,其在后面会被执行
"default_bootcmd=run uenvboot; run cp_kernel2ram && bootm ${netstart}\0" \
""
/* BOOTCOMMAND */
#define CONFIG_BOOTCOMMAND "run default_bootcmd"
但是这里,如果当板上使用了两个SD/SDIO,但是用户将SD0作为SD卡使用,SD1作为eMMC使用,则会有一个问题。那就是虽然在petalinux中配置使用eMMC作为主启动SD(也的确修改了sdbootdev变量),但是当SD0,即SD卡槽没有插入SD卡时,uboot在执行mmcinfo时会报错提示mmc0 卡槽未检测成功,导致后面的命令全部终止。
这里介绍简单的解决方法:
在 project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h 文件的末尾加入下面的代码
/* fix codes */
#ifdef CONFIG_BOOTCOMMAND
#undef CONFIG_BOOTCOMMAND
#define CONFIG_BOOTCOMMAND "mmc dev ${sdbootdev}; run default_bootcmd"
#endif
实际上是在执行默认启动环境变量前先将mmc切换到sdbootdev所表示的SD设备(可能是0,也可能是1,这取决于用户在petalinux中设置primary SD是哪一个)
也可以直接看我在xilinx论坛中关于这个问题的解答(https://forums.xilinx.com/t5/Embedded-Linux/BOOting-image-ub-from-eMMC/m-p/979363/highlight/false#M33948)
$ petalinux-build
$ petalinux-package --boot --fsbl --fpga --u-boot --force
然后将BOOT.BIN烧录到QSPI-FLASH中,image.ub写入emmc的FAT分区中
还记得我们前面的SD-Linux吗,我们依旧可以用这个作为媒介进行写入
首先擦除QSPI-FLASH:flash_erashall /dev/mtd0
使用 dd 或者 flash_write 指令将 BOOT.BIN 写入flash:dd if=BOOT.BIN of=/dev/mtdblock0
使用mount挂载eMMC的FAT分区:mount /mnt/mmcFat /dev/mmcblk1p1
直接将image.ub使用cp指令拷贝进 /mnt/mmcFat即可
上电启动,拨码设置从QSPI启动,我们可以看到下面的uboot输出
U-Boot 2018.01-00083-gd8fc4b3b70 (Jun 01 2019 - 09:37:43 +0000) Xilinx Zynq ZC702
Board: Xilinx Zynq
Silicon: v3.1
DRAM: ECC disabled 1 GiB
MMC: Card did not respond to voltage select!
mmc_init: -95, time 24
mmc@e0100000 - probe failed: -95
sdhci_transfer_data: Error detected in status(0x208000)!
Card did not respond to voltage select!
mmc_init: -95, time 25
SF: Detected n25q256 with page size 256 Bytes, erase size 4 KiB, total 32 MiB
*** Warning - bad CRC, using default environment
In: serial@e0001000
Out: serial@e0001000
Err: serial@e0001000
Board: Xilinx Zynq
Silicon: v3.1
Net: ZYNQ GEM: e000b000, phyaddr ffffffff, interface rgmii-id
eth0: ethernet@e000b000
U-BOOT for osrc_myLinuxBase
ethernet@e000b000 Waiting for PHY auto negotiation to complete...... done
BOOTP broadcast 1
DHCP client bound to address 192.168.2.54 (8 ms)
Hit any key to stop autoboot: 0
sdhci_transfer_data: Error detected in status(0x208000)!
switch to partitions #0, OK
mmc1(part 0) is current device
Device: mmc@e0101000
可以看到,在最开始,MMC报错还是Card did not respond to voltage select
但是在读秒过后,执行我们修改了的BOOTCOMMAND,在上面的启动代码片段的末尾我们可以看到,mmc切换到了1为主启动mmc,然后升高的从mmc中读取了image.ub,解压了linux到内存,启动了系统