作者:Ivy Guo,AMD 赛灵思开发者
MultiBoot是FPGA远程更新配置文件时一种非常普遍的应用 ——为了确保安全,我们通常需要安排一个Golden Image,升级失败后FPGA能回跳(Fallback)到此配置,从而使FPGA始终处于可被检测的工作状态。
很多客户有同样的问题:我在升级Update Image一半时突然掉电了,为什么回跳机制不起作用了呢?FPGA怎么挂死了呢?其实这个现象是‘符合预期’的,回跳机制不能应付这种异常。如果配置文件写到一半突然中断,嵌在配置数据流里面的指令序列同样也没有了,并且有可能中断在任意位置。FPGA的控制逻辑此时就失去了工作方向,不知道下一步该做什么。https://docs.xilinx.com/v/u/en-US/xapp1247-multiboot-spi
Xapp1247, Appendix A提供了一个很好的解决方案。利用两个timer或者称之为barrier的小image,嵌在Golden和Update之间或附在Update之后,通过合理的给两个timer赋值,可以解决Update Image刷新时同步字丢失或者半程掉电的情况。
但是同时又有客户提出了问题:我的应用对回跳时间要求很高,Xapp1247,Appendix A的方案对于半程掉电的场景,只有搜索完整个Update Image区域,看到Timer#2的设置才能完成回跳。有没有办法缩短这段时间呢?
MultiBoot的跳转实际上是非常灵活的,我们这里就尝试提供一种思路。
1. 去掉Timer#2,只保留Timer#1作为Golden和Update Image之间的barrier Image。
2. Update Image采取从后往前倒着烧录的办法。(在实际应用中,烧写flash都是用CPU/MCU/FPGA控制或者第三方编程器实现的,所以这一点也很容易实现)。
3. 精确设定Timer#1的值,使其看到Update中的TIMER指令及赋值但不需更多。
工作原理如下:
Timer寄存器的值只有在Power-Cycle或者PROG_B过程中才能被清除,或者被新的Timer值覆盖,或者在整个配置数据加载完毕后自动失效。
我们通过精确设定Timer#1的值,使FPGA控制逻辑有足够时间看到Update Image中新的Timer值。TIMER指令位于Image的头部。因为Update Image是倒着写入的,能看到新的Timer值,说明Update Image基本已经更新好了。由于新的timer值是足够控制逻辑加载完整的配置数据的,Timer寄存器被新值更新后,Timer#1相当于失效了。FPGA有充足时间可以顺利读入完整配置数据,开启正常工作。
如果由于断电等原因,Update更新到一半就停止了,此时会缺失Update的同步字,TIMER指令等等位于头部的信息。Timer#1在一个有限的时间内搜索Update Timer但是没有看到,timeout之后就会直接触发回跳。因此不用等待搜索整个Update存储空间完毕,依靠尾部的Timer#2才能触发回跳了。
整个解决方案的重点就在于设定Timer#1的值。
这个其实很简单,根据你自己生成的Timer#1和Update的MCS文件 (方法参考Xapp1247),计算一下Timer#1的指令到Update的TIMER之间的字节数即可。
以KU040的bit为例,观察Update Image头部的命令/数据序列,可以看到有3002 2001,这就是设置Timer寄存器的命令TIMER。我们想Timer#1的时间足够看到Update中的3002 2001命令以及赋值,其他不需要了,随意添加几个cycle或者几个字的裕量即可。
![5ee50e61d00e9d9aba69f5111ebc9703.png](https://i-blog.csdnimg.cn/blog_migrate/b547b4f82ae005d7d50b1fdb3c46bd9d.png)
比如我们设到3000 8001,多3个字的余量。
Timer的格式如下:
![e32a90963060d0219a7f90df1c9a0c94.png](https://i-blog.csdnimg.cn/blog_migrate/90b14baf732e013987ac7eb9ac07c1b9.png)
Barrier/Timer#1里从TIMER及赋值开始,后面有两个NOOP 字,加上后续Update里从FFFFFFFF开始直到3000 8001有28个字,一共30个字,那么就是30*32=960 bit。SPIx1串行配置中,一个CCLK读取一个bit,所以Timer#1的值设置为h‘3C0.
注意根据自己实际的Image来计算。假设说你的Timer#1和Update之间还有一些其他的Padding,那么这些字节也需要计算进去。
如果使用了SPI x4, x8或BPI并行配置,同样对应计算一下:
![f9b35d8f41384c09b25ef2c1ade8e36e.png](https://i-blog.csdnimg.cn/blog_migrate/944f5047a3c631c357a25fa033306ee0.png)
如上图,30 03 E0 01是把bus width从默认的x1切到x4的命令。如果在读入Timer#1之前中执行了该命令(比如Golden里面),那么Timer#1的值需要按照一个CCLK cycle,读取4个bit来计算。和Timer命令类似,在FPGA控制逻辑读取数据的过程中,如果没有碰到新的30 03 E0 01设定新的数据宽度,那么将一直按照之前设定的bus width来读入数据或者指令。
假设Timer#1以及Update都是以x4读取的,那么Timer#1需设为h’F0.
综上,通过合理设定barrier#1中的Timer#1数值,我们可以极大地缩短升级掉电这种MultiBoot失效场景的回跳时间。