1、背景介绍
当前因为国产化的需要,项目中几乎都用国产芯片,但国产芯片有时候不太稳定,就拿国产flash芯片来说,小概率会出现flash内存储的数据发生异常,导致系统无法启动。在这种背景下,引入了国产zynq的multiboot功能。
2、multiboot描述
下面是国产ZYNQ厂家自己的介绍
在FPGA实际开发过程中,我们一般都有升级系统程序的需求。一般我们用下载器下载就行了,可是,这个在实验室还好说,如果在正式产品中比如已经装机了,可能插下载器就不是那么方便了,于是产生了"在线升级"的需求。由于在线升级是靠程序做的,那么有可能在升级过程中出现意外导致升级失败,这样程序就自己把自己给弄死了,也就是我们经常听到的变砖了。出现这种情况咋办?如果不做保护措施,那就只能开盖用下载器烧程序了。
为了解决上述问题,于是multiboot 出现了,字面意思就是多重引导。就是用冗余的方式来保证在线升级的可靠性。 多重引导,就是在flash 中放两个或多个版本,一般放两个就行了,我们一般称呼这个两个版本:一个叫golden版本,一个叫update版本。golden 版本我们一般就只在生产时烧写一次,后面都不动这个版本了,所以出错的概率相对就低了。我们要更新功能时,就不断更新update 即可,如果 update 更新失败了, golden就是拿来救命的,从而保证不变砖。
从上面介绍能看到,这个multiboot主要是针对后面更新来用的,但其实针对flash异常情况也是可以用的,因为存放了多个版本,如果flash中某段数据异常了,存放在其中的这个版本自然无法使用,于是可以从另一个版本加载,这样就有了双保险。
3、启动过程描述
国产zynq启动这块和进口xilinx zynq一样(进口zynq启动过程),首先也是启动Bootrom,但是这里Bootrom已经自带了一个跳转功能,机制是这样的(进口xilinx的zynq也有该功能)。

如图所示,启动过程中国产ZYNQ会自动按照32K地址递增的方式去找寻正确的BOOT.bin,那么可以在flash中32K地址对齐的地方,烧写两个版本 BOOT.bin 即可实现多版本加载。
4、操作步骤
首先根据flash容量制定好空间划分情况,两个版本的boot.bin偏移要为32K的整数倍,比如下面这个例子
flash_total:0x2000000
uboot.bin_size:4.6MB ( 实际预留 0x500000 Byte)
image.ub_size :10.1MB( 实际预留 0xaf0000 Byte)
|-------------|-----------|-----------|--------------------------------------------------------------------------------------|
| 名称 | Start | End | 说明 |
| uboot.bin_1 | 0x000000 | 0x500000 | |
| uboot.bin_2 | 0x500000 | 0xA00000 | |
| Image校验值 | 0xA00000 | 0xA10000 | 0xA01000为起始地址写四个字节的image.ub_1的CRC32校验值 如果存放的校验值与image.ub_1计算出的校验值不一致,则直接启动image.ub_2 |
| image.ub_1 | 0xA10000 | 0x1500000 | |
| image.ub_2 | 0x1500000 | 0x1FF0000 | |
| 环境变量 | 0x1FF0000 | 0x2000000 | 预留1*64KB 环境变量 |
上面例子中flash总容量是32MB,Bootrom搜寻范围只能是前16MB,于是这里将uboot.bin和image.ub分开来了,两个uboot.bin存放在前16MB。
同时由于uboot.bin和image.ub分开存放,除了针对uboot.bin能够进行multiboot,针对image.ub也可以进行multiboot, 即将image.ub也保存两份,第一份计算出校验值存放在一个地方,如果校验值与第一份不匹配才加载第二份image.ub。流程图如下:

Uboot中配置的环境变量如下:
bash
"spiboot=echo Copying FIT from SPI flash to RAM... && " \
"sf probe && sf read 0x2000000 0xA10000 0xaf0000 && crc32 0x2000000 0xaf0000 0x3000000 && sf read 0x3100000 0xA01000 4 && " \
"if cmp.b 0x3000000 0x3100000 4;then echo compare correct...;else echo compare incorrect!!! && sf probe && sf read 0x2000000 0x1500000 0xaf0000;fi && " \
"bootm 0x2000000\0" \
第一次烧写只能用下载器烧写,制作镜像采用下面工具

在step4 Add中添加fsbl bit uboot.elf的校验值 chechsum选择sha2
注:如果checksum为none,则Bootrom不会进行校验

后面只要uboot能够启动,都可以在uboot下面操作。
第1步:擦除flash
bashsf probe && sf erase 0 0x2000000第2步:按照上面表格往指定地址写入uboot.bin和image.ub
注:flash首次烧写还是用jtag,后面如果有uboot就可以不用jtag了。
第3步:重启
第4步:crc32 校验指定位置
第1处:
bashsf probe && sf read 0x2000000 0xA10000 0xaf0000 && crc32 0x2000000 0xaf0000第2处:
bashsf probe && sf read 0x2000000 0x1500000 0xaf0000 && crc32 0x2000000 0xaf0000第5步:对比两次的校验值
第6步:将校验值写入 0xA01000 地址,写入4Byte
bashsf probe && sf read 0x2000000 0xA10000 0xaf0000 && crc32 0x2000000 0xaf0000 0x3000000 && md.b 0x3000000 4 && sf probe && sf update 0x3000000 0xA01000 4第7步:将flash数据读到内存,并读出内存数据,确认是否写入crc32校验值正确
bashsf probe && sf read 0x3100000 0xA01000 4 && md.b 0x3100000 4第8步:比较计算出的和写入flash的数据是否相同
bashsf probe && sf read 0x2000000 0xA10000 0xaf0000 && crc32 0x2000000 0xaf0000 0x3000000 && sf probe && sf read 0x3100000 0xA01000 4 && if cmp.b 0x3000000 0x3100000 4;then echo compare correct...;else echo compare incorrect!!!;fi
备注:
1、擦除image.ub的CRC32校验值:
bash
sf probe && sf erase 0xA01000 0x1000
2、擦除完读出确认是否擦除成功:
bash
sf probe && sf read 0x3000000 0xA01000 0x10 && md.b 0x3000000 0x10
说明:上面例子中因为flash容量够大,因此实现了uboot和image各存放两份。当然,也可以uboot保存两份,image只保留一份。或者将uboot和image.ub打包在一起组成一个完整的启动镜像boot.bin,然后针对这个完整的boot.bin存放两份。总之,只要确保存放引导文件的起始位置在FLASH的前16MB即可。