这也是《FPGA 实现串口升级及 MultiBoot》系列中的一篇文章,作为一个专题单独出来说明。
本篇文章分为三个主题:固化、启动和 MultiBoot 实现。
固化分为 SPI 和 BPI FLASH 两种情况;启动分为 SREC 解析及加快启动模式的 ELF 直读;最后就是 MultiBoot 实现的时候应该注意什么。
固化
软核的固化和外部 FLASH 及应用程序大小有很大关系。小应用程序,使用 BRAM即可运行,固化的时候和逻辑一起固化即可运行,这时候不管外部是什么类型 FLASH,和逻辑固化一样。大应用程序,需要用到外部 DDR,就需要两个启动程序,一个小的 Bootloader,和逻辑一起启动,起来后从外部 FLASH 读取相应的应用程序放到DDR里运行,这时候就要根据不同的 FLASH,设计相应的读取逻辑及应用。下面我们根据不同的 FLASH 类型进行相应的说明。
SPI FLASH
小应用程序
小应用程序,比如串口通信,IIC、SPI等配置通信,不占用多少 STACK_SIZE 和 HEAP_SIZE,用内部 BRAM 就可运行,这时候将应用程序的 LD 设置为运行到BRAM 即可,生成 elf 后就可以将其和逻辑的 BIT 融合在一起生成新的BIT,正常生成 MCS 即可烧写。
elf 和 bit 融合成新的 BIT,有两种方式,下面详细介绍:
调试阶段
调试阶段通过 Vitis 或者 SDK 一步即可生成新的 BIT:
即在 vitis 工作目录-->应用程序目录-->_ide-->bitstream-->download.bit
该 bit 就是融合后的 BIT,接下来可以使用 Vitis/SDK 或者 Vivado 进行下载。
稳定阶段
应用程序一般比较简单,所以调试稳定后,就可以将 elf 交给逻辑端,让他们继续折腾。
Vivado 中在生成 bit 前,通过下面步骤将 elf 和软核关联起来,后面Vivado 生成 bit 后,就是包含了软核程序的 BIT,继续后续的固化工作即可。
大应用程序
大应用程序就会复杂很多,主要是建立软核读取外部 FLASH 的通道。
大的应用程序需要运行到 SDRAM 中,节省内部 BRAM,这里的“大”并不是我们说的 elf 文件大,而是需要的运行空间大。
其实可以完全不需要内部 BRAM 也可以运行大应用程序程序,不过制作过程比较复杂,我们这次还是需要一点内部 BRAM 启动 bootloader。
整个流程框图如下:
首先这种方式需要三个文件:1、FPGA 的 bit 文件(需要包含 FLASH 的读写控制器);2、bootloader 生成的 elf 文件; 3、大应用应用程序的 ELF 文件。
这里要用到比较重要的 IP-axi_quad_spi(可以自己按照需求写 IP),将 IP 按照下面设置进行设置:
主要是使能 STARTUP 原语。
PS:7 系列的在 IP 内部使能 STARTUP 即可,对于 U+ 系列由于 FLASH 的 IO 位于 BANK0,需要在顶层再使用 STARTUP3 原语进行 IO 引出(IP 内部虽然也使用了原语,但是我调试的时候只有在顶层添加 STARTUP3 原语才能进行通信,不清楚是 BUG 还是没设置好,按照官方说明使用方式也是要在顶层再添加原语)。
逻辑端添加上诉 IP 即可,接下来是应用程序设计。
在 SDK 或者 Vitis 中添加下面 BootLoader 程序:
接下来要修改第一个地方:
就是这个起始地址,其中 0x44A00000 就是 Vivado 中 IP 的基地址,0x80000是从 FLASH 读取的地址,即应用存放在 FLASH 中的地址,该地址要和生成 MCS 时候地址相匹配。
第二个需要注意的地方就是 BootLoader 要放到 BRAM 里运行,查看以下 LD:
这样就可以和上面小应用程序一样,随着逻辑端一起启动。
接下来就是固化,固化也是分为两个阶段。
调试阶段
调试阶段首先将大应用程序放到 FLASH 相应的偏移地址上,通过以下工具可以直接烧写:
这里先选择大应用程序生成的 elf,然后选择烧写的偏移地址(按照 BootLoader 里设置的偏移地址进行设置),然后选择对应的 FLASH,最后一定要勾选 Convert ELF to bootloadable SREC format and program,这是目前官方驱动程序能读取的二进制结构-SREC。
关于怎么减少启动时间我们后面会单独讨论。
- 注意点 1:修改驱动
如果不能读取 FLASH,那么需要修改驱动文件。Board support Package setting,修改 xilisf,设置 serial_flash_family(根据 FLASH 厂家修改)serial_flash_interface 根据需求修改。
- 注意点 2:读取超时
增加等待 flash 初始化时间:
原位置:
修改
/*
* Initialize the Serial Flash Library.
*/
volatile int wait;
do
{
Status = XIsf_Initialize(&Isf, &Spi, ISF_SPI_SELECT, IsfWriteBuffer);
if(Status != XST_SUCCESS) {
xil_printf("XIsf_initialize failed! Status=%d\r\n", Status);
for (wait=0;wait < 100000; wait++);
for (wait=0;wait < 100000; wait++);
}
}while(Status != XST_SUCCESS );
- 注意点 3:读取速度
某些国产 FLASH 的速度远低于进口 FALSH,所以注意看下数据手册,控制好 ext_spi_clk,同时下图还有一个分频系数:
稳定阶段
如果 BootLoader 能够引到启动后,可以将 BootLoader 的 ELF 添加到 Vivado 工程中,这样在大应用程序需要重建后无需每次都建立 BootLoader 工程进行调试。同时 axi_quad_spi 不更改(基地址不变)情况下,BootLoader 都不需要修改。
BPI FLASH
小应用程序
小应用程序和 SPI 的一样,不随着外围 FLASH 不同而改变,就不赘述了。
大应用程序
大应用程序就更复杂一些,也是需要建立软核读取外部 FLASH 的通道。
接下来我们按照另一个思路讲解 BPI FLASH 大应用程序的固化。上面我们说过大应用程序的固化需要三个文件:1、FPGA 的 bit 文件(需要包含 FLASH 的读写控制器);2、bootloader 生成的 elf 文件; 3、大应用程序的 ELF 文件。我们按照顺序讲解每个文件生成需要的必要条件:
FPGA 的 bit 文件
FPGA 设计中需要增加对 FLASH 控制,对于 BPI FLASH,官方推荐 AXI-EMC IP,通过 AXI-MEM 映射外部 FLASH,这个 IP 可以控制常见的 BPI FLASH(可配置参数很低),对于不能控制的 FLASH,需要自己写控制器+控制驱动。
AXI_EMC IP 概述
AXI_EMC 是 FPGA 的一个 ip core,axi 外部存储控制器,支持 sram,nor flash,psram,cellularRAM,IP 核使用 AXI4 接口,支持 32bit 和 64bit 的数据位宽,支持以下 memory type, 即:
- 异步 SRAM
- 同步 SRAM
- 串行 flash or 并行 nor flash
- 伪静态随机存储器
每个 emc 控制器支持挂接 4 个存储设备
来源:pg100-figure1-1
信号连接
我们这次使用的 BPI FLASH 为 S29GL01GSXXXX,可以通过 AXI-EMC 控制,具体在 FPGA 中的连线如下:
主要将这个 IP 连到 MB 上,外接引脚比较重要,因为 IP 要兼容的东西比较多,所以引出来的引脚比较多,对于不同的外设需要连接不同的引脚,对于本次设计的引脚连接如下表所示:
图中红线部分需根据表进行连接
IP 界面时序参数配置
(1) 第一页
默认配置即可,如果使用比较大的内存则可选总线位宽为 64 位,我们控制 FLASH,默认 32 位即可:
这里注意以下,如果使用 U+ 系列,这个 IP 会在下面位置有个使能 STARTUP3 原语的选项:
使能后和 SPI FLASH 一样,需要在顶层添加 STARTUP 原语引出 BPI 的DQ0~DQ3。这里说下原因,因为 7 系列 FPGA 的 DQ0~DQ3 是在 BANK14,而 U+ 是在 BANK0 上,同时官方 IP 可能有 BUG,导致需要 2 次使用原语。
(2) 第二页
这一页的设置是核心,需要根据 FLASH 数据手册进行配置:
- Memory Type
支持 Sync SRAM, Async SRAM, Linear Flash, Page Mode Flash, PSRAM, or Micron Flash
- Data Width
Memory 数据位宽,也就是接的存储设备的数据位宽,支持 8,16,32,64 位位宽
- Parity
可以设置为 No parity,Odd Parity,or Even Parity,只有 Memory Type 为 Sync SRAM 时才能设置
- Delay Mode
可以设置为 Flow-Through model or Pipeline Model,只有 Memory Type 为 Sync SRAM 时才能设置
- Read CE Low to Data Valid Period
根据描述,该参数在不同的 memory type 下含义不同,如果是 flash,则和 tELQV 的值相等,这里以 Page Mode Flash 为例说明,基本上也就是片选拉低的时间,其他类型的存储设备没有验证,暂不清楚,以 flash 型号为S29GL01GSXXX(容量大小:128Mbyte)为例,根据芯片手册中的描述:
该芯片读时序如下:
对该芯片来说,该参数值就是 tCE,即 100ns = 100000ps
- Read Address Valid to Data Valid Period
这里的意思为读地址在数据有效前的保持时间,不是片选保持的时间,对于 S29GL01GSXX 来说,就是 tAVQV 的值,和描述的一样,即为 100ns = 100000ps,如上图所示。
- Page Access Period
根据描述的含义,对于 Page Mode Flash 来说就是访问一页需要的时间,以 S29GL01GSXX 为例,也就是 tPACC,为 25ns,如下:
- Read CE High to Data Bus HZ Period
该参数的含义是指片选(CE)从有效变为无效后,至少要保持多长时间,即从低变高后,要保持多长时间的高电平,以 S29GL01GSXX 为例,就是图中的 tDF 也就是 tEHQZ,为 20ns
- Read OE High to Data Bus HZ Period
和上边参数一样,指 OE 高电平保持的时间,也就是上图中的 tDF,为 20ns
- AXI Read Timing
根据 AXI EMC IP 核手册的 page47 页得知以上读时序图,结合“IP 核参数默认值”查看
- AXI Write Timing
根据 AXI EMC IP 核手册的 page47 页得知以上写时序图,结合“IP 核参数默认值”查看
IP 核参数默认值:
- Write Cycle Period
根据描述,AXI EMC core 根据该参数去保持 CE 为低的时间,但不是指 CE 保持的时间,以 S29GL01GSXX 为例,值等于 tWC,即 60ns,如下
- Write Enable Minimum Pulse Width
该参数指片选(WE)保持的最小时间,以 S29GL01GSXX 为例,值等于 tWP,即最小 25ns
- Write Phase Period
该参数表示两个 WE 之间的最小间隔,以 S29GL01GSXX 为例,值等于 tWPH,即最小 20ns
- Write WE High to Data Bus LZ Period
该参数表示在一个写周期里,写使能(WE)到数据总线低阻的时间,或者写到读的恢复时间(可能理解的不正确,关于此参数配置时请谨慎),以 S29GL01GSXX 为例,使用的默认值,即 0ps。
- Write Recovery Period for Flash Memory
这个参数应该指的是数据写完后,WE 还需要保持多少个时钟周期的时间,这里使用默认值,根据下图,此参数小于等于 twrr,具体多少不知道,个人理解大于等于 tWPH 应该就可以了。
IP 其他界面默认即可,地址分配界面将 IP 寻址空间大小设置成实际 FLASH 大小,如下:
编译后导出 bit 到 SDK 或者 Vitis,就可以制作 BootLoader.
以上参数解释,参考下面的博文:
https://blog.csdn.net/qq_3316...
这里要用到比较重要的 IP-axi_quad_spi(可以自己按照需求写 IP),将 IP 按照下面设置进行设置:
BootLoader制作
打开 sdk 或者 Vitis,新建一个 SREC 的工程。 这个就是传说中启动文件。
其中 1 是用来制作 BPI FALSH 的启动文件,2 是用来制作 SPI FLASH 启动文件的。
修改 FLASH 的镜像地址。地址不是乱写的。是根据你的 mcs 文件大小,文件大小在 runs/imp/ 文件夹下的 prm 文件上有告知。后面接上的。然后生成文件。注意这个地址也是你后面真正运行的 elf 地址写入。
也可以按照 prm 文件后续接上 elf 文件:
bootloader 工程,注意选择 ld 看链接表设定。不要把代码和数据放在 DDR 上面。
然后编译工程。生成 elf 文件。然后按照之前的方式将 BootLoader elf 和 vivado 生成的 bit 合成一个新的 bit。
剩下的步骤和之前 SPI FLASH 一样了,就不赘述了。
启动
固化完成了,接下来就是启动了。小应用程序和 FPGA 启动一起,所以就不说明了,重点介绍大应用程序。
大应用程序如果使用官方的 BootLoader,在读取大应用到 DDR 中时,是读取的 SREC,那么为什么要读取 SREC,SREC 文件有什么特点?我们接下来开始揭开面纱。
SREC 文件
SREC 文件是带有程序的地址信息和数据校验功能,所以在读取 SREC 文件时是连读连校验并且要转译,所以在读取 SREC 时候会很慢,好处就是可以避免很多因为文件错误导致功能异常。结构如下:
SREC 格式文件由一系列 ASCII 文本记录组成。这些记录从左到右具有以下结构:
a.Record type——2个字节 ASCII 字符,第一个字符为‘S’(ASCII 0x53),第二个字符为 ASCII 数字的‘0’~‘9’(ASCII 0x30 到 0x39)
b.Byte count——两个十六进制数字(“00”至“FF”),表示记录其余部分(地址 + 数据 + 校验和)后面的字节数(十六进制数字对)。此字段的最小值为 3(16 位地址字段加 1 个校验和字节时为 2),最大值为 255(0xFF)。“00”/“01”/“02”为非法值。
c.Address——大端地址——4/6/8 个 16 进制的 ASCII 数字,取决于 Record type 的类型
d.Data——数据 ——2*n 个 16 进制的 ASCII 数字(n 字节数据)
e.Checksum ——2 个 16 进制的 ASCII 数字,即字节数、地址和数据字段的两个十六进制数字对所表示的值之和的补码的最低有效字节。在 C 编程语言中,总和通过以下方式转换为校验和:0xFF - (sum & 0xFF)
图片来源:https://en.wikipedia.org/wiki...(file_format)
ELF 和 SREC 对比:
ELF 转换成 SREC
如果使用官方的 BootLoader,那么 SREC 格式是必不可少的,而 SDK 或者Vitis 只能生成 ELF 文件,下面介绍几种方式将 ELF 转换成 SREC。
方式一:通过 XSCT Console
将 elf 转换成 SREC format,打开 Xilinx—> XSCT Console。
通过 cd 命令进入 elf 所在的目录:
输入:mb-objcopy -O srec app.elf app.srec
就会在 ELF 所在目录生成 SREC 文件。
方式二:自动转换
在应用上右击,属性,打开属性窗口。在图中位置添加命令:
mb-objcopy -O srec {ProjName}.srec
这样每次在 APP 编译完成后就会将 ELF 自动转换成 SREC(在 ELF 目录里)。
方式二:使用命令手动设置
使用下图中两个脚本,cd 到 elf 文件位置后,使用 mb-object 命令完成转换:
最后这种方式和方式一类似,好处就是可以使用 Win 下的脚本调用,然后生成一键脚本。
加快启动
从上面的分析可知,在读取 SREC 时候会进行很多无关操作,导致启动时间大大增加,尤其是大应用程序生成的 ELF 比较大(SREC 也会比较大)的时候,这个时间肯定不能忍受的。
方式一:减少打印
在进行调试的时候,BootLoader 会有读取进程通过串口打印,这一操作方便调试的时候快速进行问题定位,完成调试的时候,可以将打印去掉,注释掉下图位置语句即可:
更改读取方式
我们可以减少读取过程中的转译和校验等过程,直接引导搬运 FLASH 的 elf 文件至 DDR,减少“中间商赚差价”的时间,可大大提高启动速度,相关的工程可以参考下面的链接。
https://github.com/henrikbrix...
在 eb-config.h 文件中更改实际用到的 FLASH 地址以及匹配 FLASH 信息,主要就是修改对应 FLASH 的 opration 指令以及 dummy cycle,可以根据你使用的 FLASH 类型查阅数据手册。
MultiBoot 实现注意点
在小应用程序时候其实和纯 FPGA 应用一样,没什么大的区别,没什么注意的。主要在大应用程序:
大应用程序主要包含上面几个文件组成(根据自己需求可能有所不同)。考虑到升级过程中传输完成擦除 FLASH 后及突然断电等特殊情况,所以需要考虑 CRC error(突然断电)、IDCODE error(升级包制作错误)、Watchdog timer time-out error(擦除 FLASH 后未升级、突然断电)三种情况。
Golden 区无需变动,主要在 M 区的程序,在升级 APP 时候面对上面的情况怎么操作?因为目前的 MultiBoot 机制只能在 FPGA 逻辑层进行操作,如果 APP 程序错误可能触发不了回退机制。
这里提供一个思路,就是对 M 区 APP 增加 CRC 校验,BootLoader 在读取APP的ELF或者 SREC 时如果 CRC 校验错误,那么就破坏 M 区的 FPGA 程序(FLASH),让 FPGA 程序启动时候触发四种错误的任何一种,就可以回退到 Golden 区再进行升级。
总结
总结就下面的一张图了:
END
作者:碎碎思
来源:OpenFPGA
相关文章推荐
- 很能体现 FPGA 硬件思维的一道面试题
- 基于 FPGA 及深度学习的人脸检测系统设计
- 多平台 FPGA 工程快速移植与构建
- 开源 ISP(Infinite-ISP)介绍
- FPGA/ASIC 在 AI 推理加速中的研究
更多 FPGA 干货请关注 FPGA 的逻辑技术专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。