FPGA Artix7 Bootloader App Python升级

文章目录

    • 软硬环境
    • 复现官方 srec_spi_bootloader
      • 例子简介
      • Vivado硬件部分
      • 存储划分
      • Vitis 嵌入式 Boot
      • Vitis 嵌入式 App
      • elf转换srec
      • 合并boot和app得到mcs文件
      • 下载测试
      • 过程分析
    • 基础知识
      • BIT MCS HEX BIN
      • Bit Swapping
      • SREC 文件格式
      • Vivado约束
    • 串口Boot
      • 地址划分
      • 链接脚本修改
      • Github Link
      • App
      • Boot
      • 一条命令升级
    • 参考链接

本文在Artix7上复现了Xilinx官方的srec_spi_bootloader例子, 有详细的过程分析和图文说明, 然后动手实现了FPGA串口Boot的完整过程, 通过Python脚本一条命令升级, 自动把app的elf文件转bin文件,从app跳转boot,擦写flash, 写入app, 进行crc32校验后跳转到新的app.

本篇有详细的图文说明和源码链接, 欢迎大家评论, 点赞和收藏.

软硬环境

软件:

  • Vivado v2023.2.2
  • Vitis Unified IDE v2023.2.0

硬件:

  • FPGA: xc7a35tfgg484-2
  • 晶振: 3.3V, 50MHz 有源 单端, Y18
  • 复位: 低电平复位, F20
  • LED: F19, 低电平点亮
  • UART: RX G15, TX G16
  • SPI Flash, 3.3V, N25Q128:
    • CLK: CCLK0
    • CS: T19
    • IO 0~3: {P22 R22 P21 R21}

对于SPI Flash需要注意的地方:

  • 7 系列 FPGA 的 AXI Quad SPI 的 IP 配置里面需要勾选 Enable STARTUP Primitive 选项进行实例化, 让CLK能正常工作, CLK不体现在引脚配置里面
  • flash 型号选择 mt25ql128-spi-x1_x2_x4, 别名 n25q128-3.3v-spi-x1_x2_x4, NOR Flash, 128Mbit, 16MB, 后面的代码里扇区Sector统一按64KB擦除, 页Page大小256写入
  • 厂商选 Micron(Numonyx)
  • 未使用DDR3, 所幸实现功能的代码并不复杂, Boot和App全用片内RAM测试

复现官方 srec_spi_bootloader

例子简介

这个例子的默认路径是 C:\Xilinx\Vitis\2023.2\data\embeddedsw\lib\sw_apps\srec_spi_bootloader

简介:

简单的 SREC 引导加载程序

给定内存中镜像的位置,它能够启动 SREC 格式的镜像文件(Mototorola S-record 格式)。
特别是,该引导加载程序专为存储在可从处理器寻址的非易失性闪存中的镜像而设计。

请修改 blconfig.h 头文件中的定义“FLASH_IMAGE_BASEADDR”,以指向引导加载程序获取闪存镜像的位置。

您可以将这些源包含在您的软件应用程序项目中,并为您希望进行引导加载的处理器构建项目。
您还可以随后修改这些源,以使引导加载程序适应您可能需要的任何特定场景。

一般是Boot(Golden Image)运行在BRAM, 把 App(Multiboot Image) 搬到DDR3, 这里简化一下, 不使用DDR3, 只用FPGA自带的RAM来测试这个例子以简化流程.

Vivado硬件部分

Clocking Wizard:

  • 输入频率 50MHz, Single ended clock
  • 输出频率默认 100MHz
  • 复位类型: Active Low
  • 引出 clk_in1 引脚

Uartlite:

  • 波特率 115200
  • 引出 UART

Timer 不做更改, 用于验证Boot跳转后, App的中断是否能正常运行.

GPIO:

  • GPIO Width 设为 1
  • 引出 GPIO, 用于点灯.

Quad SPI:

  • Mode 选 Quad
  • Slave Device 选 Micron(Numonyx)

MicroBlaze:

  • 勾选 Enable Peripheral AXI Data Interface
  • Run Block Automation, Local Memory 128KB, 勾选 Interrupt Controller
  • 连接好 SPI, Timer 和 Uart 的中断

附上AXI Quad SPI的配置

在这里插入图片描述

自动连接后:

在这里插入图片描述

Generate Output Products

Create HDL Warapper

xdc 约束文件:

set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
# 注释掉暂时规避SPI驱动异常
# set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
# set_property CONFIG_MODE SPIx4 [current_design]

set_property IOSTANDARD LVCMOS33 [get_ports clk_in1_0]
set_property IOSTANDARD LVCMOS33 [get_ports reset_rtl_0]
set_property IOSTANDARD LVCMOS33 [get_ports {GPIO_0_tri_io[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports UART_0_rxd]
set_property IOSTANDARD LVCMOS33 [get_ports UART_0_txd]
set_property IOSTANDARD LVCMOS33 [get_ports spi_rtl_0_io0_io]
set_property IOSTANDARD LVCMOS33 [get_ports spi_rtl_0_io1_io]
set_property IOSTANDARD LVCMOS33 [get_ports spi_rtl_0_io2_io]
set_property IOSTANDARD LVCMOS33 [get_ports spi_rtl_0_io3_io]
set_property IOSTANDARD LVCMOS33 [get_ports {spi_rtl_0_ss_io[0]}]

set_property PACKAGE_PIN Y18 [get_ports clk_in1_0]
set_property PACKAGE_PIN F20 [get_ports reset_rtl_0]
set_property PACKAGE_PIN F19 [get_ports {GPIO_0_tri_io[0]}]
set_property PACKAGE_PIN G15 [get_ports UART_0_rxd]
set_property PACKAGE_PIN G16 [get_ports UART_0_txd]
set_property PACKAGE_PIN T19 [get_ports {spi_rtl_0_ss_io[0]}]
set_property PACKAGE_PIN P22 [get_ports spi_rtl_0_io0_io]
set_property PACKAGE_PIN R22 [get_ports spi_rtl_0_io1_io]
set_property PACKAGE_PIN P21 [get_ports spi_rtl_0_io2_io]
set_property PACKAGE_PIN R21 [get_ports spi_rtl_0_io3_io]

Generate Bitstream, 得到:

  • FPGA 位流文件 design_1_wrapper.bit, 大小约 2.09MB
  • FPGA 内存配置文件 design_1_wrapper.mmi

导出硬件 XSA 文件, Include bitstream

资源消耗

在这里插入图片描述

存储划分

RAM: MicroBlaze 的 Local Memory 分配了 128KB, 把前32KB给Boot, 后96KB给App

Flash: FPGA位流文件大小 2.09MB, 和boot合并后也这么大, 压缩后874KB, 把16MB SPI Flash的前4MB预留给Boot

Vitis 嵌入式 Boot

Open Workspace, 选择一个文件夹作为工作空间.

Create Platform Component, 导入XSA文件, 创建硬件平台, 编译成库.

File -> New Component -> From Examples, 从 Srec_spi_bootloader 例子创建 Boot 程序

修改 App 的偏移地址为4MB处

在这里插入图片描述

修改链接文件中的SIZE为(32KB-0x50 = 0x7FB0), 下面的Section也都是用的BRAM, 这里没有用DDR3

在这里插入图片描述

FlashReadID这里读两遍以防止第一次读出错

在这里插入图片描述

编译得到 srec_spi_bootloader.elf

 mb-size --format=berkeley srec_spi_bootloader.elf
    text	   data	    bss	    dec	    hex	filename
   15004	    360	   2092	  17456	   4430	srec_spi_bootloader.elf

菜单栏 Vitis -> Program Device, 点Generate从 FPGA位流文件, 内存映射文件, elf文件 生成 download.bit 文件

在这里插入图片描述

对应的 tcl 命令为

update_mem -meminfo C:/z/ws_vivado/fpga_boot_app/ba_vitis/srec_spi_bootloader/_ide/bitstream/design_1_wrapper.mmi -data {C:\z\ws_vivado\fpga_boot_app\ba_vitis\srec_spi_bootloader\build\srec_spi_bootloader.elf} -proc design_1_i/microblaze_0 -bit C:/z/ws_vivado/fpga_boot_app/ba_vitis/srec_spi_bootloader/_ide/bitstream/design_1_wrapper.bit -out C:/z/ws_vivado/fpga_boot_app/ba_vitis/srec_spi_bootloader/_ide/bitstream/download.bit -force
 Loading bitfile C:/z/ws_vivado/fpga_boot_app/ba_vitis/srec_spi_bootloader/_ide/bitstream/design_1_wrapper.bit

因为Vivado中约束文件没有位流压缩, 这里出来的download.bit大小也是2.09MB, 但其实和先前的 design_1_wrapper.bit 不一样了.

Vitis 嵌入式 App

新建 app_component

修改链接文件中的基地址为32KB(0x8000), SIZE 96KB(0x18000)

在这里插入图片描述

加上 gpio 和 timer 的代码

#include "xgpio.h"
#include "xil_exception.h"
#include "xil_printf.h"
#include "xinterrupt_wrap.h"
#include "xparameters.h"
#include "xtmrctr.h"
#include <stdbool.h>

volatile bool timer_isr_flag = false;

void TimerCounterHandler(void *CallBackRef, u8 TmrCtrNumber) {
  XTmrCtr *InstancePtr = (XTmrCtr *)CallBackRef;
  if (XTmrCtr_IsExpired(InstancePtr, TmrCtrNumber)) {
    timer_isr_flag = true;
  }
}

int timer_init(XTmrCtr *TmrCtrInstancePtr, UINTPTR BaseAddr, u32 ReloadValue) {
  int Status = XTmrCtr_Initialize(TmrCtrInstancePtr, BaseAddr);
  if (Status != XST_SUCCESS) {
    return -1;
  }
  Status = XSetupInterruptSystem(
      TmrCtrInstancePtr, (XInterruptHandler)XTmrCtr_InterruptHandler,
      TmrCtrInstancePtr->Config.IntrId, TmrCtrInstancePtr->Config.IntrParent,
      XINTERRUPT_DEFAULT_PRIORITY);
  if (Status != XST_SUCCESS) {
    return -2;
  }
  XTmrCtr_SetHandler(TmrCtrInstancePtr, TimerCounterHandler, TmrCtrInstancePtr);
  u8 TmrCtrNumber = 0;
  XTmrCtr_SetOptions(TmrCtrInstancePtr, TmrCtrNumber,
                     XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION |
                         XTC_DOWN_COUNT_OPTION);
  XTmrCtr_SetResetValue(TmrCtrInstancePtr, TmrCtrNumber, ReloadValue);
  XTmrCtr_Start(TmrCtrInstancePtr, TmrCtrNumber);

  return 0;
}

int gpio_init(XGpio *Gpio, UINTPTR BaseAddr) {
  int Status = XGpio_Initialize(Gpio, BaseAddr);
  if (Status != XST_SUCCESS) {
    return -1;
  }
  XGpio_SetDataDirection(Gpio, 1, ~0x01);
  XGpio_DiscreteWrite(Gpio, 1, 0x01);
  return 0;
}

int main(void) {
  xil_printf("\r\n###################################\r\n");

  XGpio led0;
  int ret = gpio_init(&led0, XPAR_XGPIO_0_BASEADDR);
  if (ret != 0) {
    xil_printf("gpio_init failed: %d\r\n", ret);
    return -1;
  }

  XTmrCtr timer0;
  ret = timer_init(&timer0, XPAR_XTMRCTR_0_BASEADDR, 100000000);
  if (ret != 0) {
    xil_printf("timer_init failed: %d\r\n", ret);
    return -2;
  }
  bool led_status = true;
  while (1) {
    if (timer_isr_flag) {
      timer_isr_flag = false;
      if (led_status) {
        led_status = false;
        xil_printf("led0 on\r\n");
        XGpio_DiscreteClear(&led0, 1, 0x01);
      } else {
        led_status = true;
        xil_printf("led0 off\r\n");
        XGpio_DiscreteWrite(&led0, 1, 0x01);
      }
    }
  }

  return 0;
}

编译得到 app_component.elf 文件

elf转换srec

# mb-objcopy 位置
# C:\Xilinx\Vitis\2023.2\gnu\microblaze\nt\bin\mb-objcopy.exe
# C:\Xilinx\Vivado\2023.2\gnu\microblaze\nt\bin\mb-objcopy.exe
mb-objcopy -O srec app_component.elf app_component.srec

合并boot和app得到mcs文件

需要的文件:

  • boot文件: download.bit(这里面也包含了FPGA位流文件, 内存描述文件), 放到地址 0
  • app文件: app_component.srec, 放到地址 0x00400000

写tcl脚本文件

# make_mcs.tcl
write_cfgmem -force -format MCS -size 16 -interface SPIx1 -loadbit " up 0 ./download.bit" -loaddata " up 0x00400000 app_component.srec " merge.mcs

运行 tcl 脚本, 生成 merge.mcs

vivado -mode batch -source  make_mcs.tcl

如果是 SPIx4 会得到如下错误, 改约束文件没啥用, 无奈 -interface SPIx1

ERROR: [Writecfgmem 68-20] SPI_BUSWIDTH property is set to "1" on bitfile ./download.bit. This property has to be set to "4" to generate a configuration memory file for the SPIX4 interface. Please ensure that a valid value has been set for the property BITSTREAM.Config.SPI_buswidth and rerun this command.

下载测试

菜单栏 Vitis -> Program Flash, 镜像文件选

在这里插入图片描述

编程完后, 断电重启, 因为是 SPIx1, 启动会比较慢, 6s后, 串口日志:

SREC SPI Bootloader
FlashID=0xFF 0xFF 0xFF

FlashID=0x20 0xBA 0x18

Loading SREC image from flash @ address: 00400000

Bootloader: Processed (0x)00000001 S-records
Bootloader: Processed (0x)00000002 S-records
Bootloader: Processed (0x)00000003 S-records
Bootloader: Processed (0x)00000004 S-records
...
Bootloader: Processed (0x)000004be S-records
Bootloader: Processed (0x)000004bf S-records
Bootloader: Processed (0x)000004c0 S-records
Bootloader: Processed (0x)000004c1 S-records
Executing program starting at address: 00000000

###################################
led0 on
led0 off
led0 on
led0 off

此时如果不是断电重启, 而是按下复位按键, 会从App处重新开始执行.

过程分析

先来看APP的SREC文件是怎么合并到MCS文件(可改后缀为HEX格式查看)的:

在这里插入图片描述

相当于整个SREC文件被存入了Flash的0x00400000里面

bootloader的分析如下:

  • 先是初始化 SPI, 用 FlashReadID() 读ID, 这里连读两次后读出来 0x20 0xBA 0x18, FlashID[3]字节含义为
    • 0, Man.ID, 如 0x20 Micron(Numonyx) 或 ST?
    • 1, ID Code, 如 0xBA 和 0x18连起来 0xBA18 指代 N25Q128
    • 2, 容量, 0x15 2MB; 0x16 4MB, 0x17, 8MB; 0x18 16MB; 0x19 32MB; 0x20 64MB; 0x21 128MB; 0x22 256MB
  • 判断容量大于16MB(2^24), 从24bit地址进入32bit地址, if (FlashID[FLASH_SIZE] > FLASH_16_MB) { Status = FlashEnterExit4BAddMode(&Spi, ENTER_4B); ...}, 当然此处板子没有超过16MB, 就没进入4字节地址模式
  • flash_get_srec_line() 函数, 先从APP SREC文件存放的Flash地址(FLASH_IMAGE_BASEADDR 0x00400000)处读4个字节, 如S017, 得到长度len如 0x17 = 23, 然后再读len * 2字节(因为一个字节转成HEX文本是两个), 这样就读出了一行SREC记录存入sr_buf, 地址再加2个字符\r\n就是下一个记录的开始地址.
  • decode_srec_line() 函数, 把sr_buf按照srec的记录格式, 转成对应的 类型 地址 数据 数据长度, 同时也做了校验, 因为checksum=0xFF-(sum&0xFF), 所以checksum+(sum&0xFF)=0xFF, 也就是cksum为0xFF表示校验正确
  • 判断这一行的记录类型, 如果是S1 S2 S3表示数据, 就拷贝到内存中去 memcpy ((void *)srinfo.addr, (void *)srinfo.sr_data, srinfo.dlen);, 如果是S7 S8 S9表示起始地址和文件结束 laddr = (void (*)())srinfo.addr;
  • 最后跳转到起始地址运行app: (*laddr)();, 初始定义是 void (*laddr)();

基础知识

BIT MCS HEX BIN

AMD配置文件格式:

文件扩展位交换AMD Vivado TCL 命令说明
BITNOwrite_bitstream含标头信息的二进制文件
RBTNOwrite_bitstream -raw_bitfile含文本标头以及ASCII 1 0的BIT文件等效
BINNOwrite_bitstream -bin_file二进制数据文件, 无标头信息
MCSYES(除SPI)write_cfgmem -format MCSASCII PROM 文件, 含数据 地址 校验和
HEX用户定义write_cfgmem -format HEXASCII PROM 文件, 仅数据

注: RBT 是 Rawbits 的简写

7 系列 FPGA 位流由三部分组成:

  • 总线宽度自动检测, 仅用于并行模式, SPI模式会忽略这些字节
  • 同步字 0xAA995566
  • FPGA配置

PROM 文件用于重新格式化位流文件以进行 PROM 编程(write_cfgmem -loadbit), PROM文件通常是位交换的, 除 SPI 配置模式外.

GUI方式生成BIT RBT BIN文件, 工程设置里面勾选, 下次Generate Bitstream就有了

在这里插入图片描述

生成的文件对比, Header不会被下到Flash里面, 总线宽度检测会被SPI模式忽略, 但仍会放到0x00000000的位置, SPI模式会直接找到同步字 0xAA995566

在这里插入图片描述

GUI方式生成MCS BIN HEX文件, Vivado菜单栏Tools -> Generate Memory Configuration File

在这里插入图片描述

对应的Command为

write_cfgmem  -format mcs -size 16 -interface SPIx4 -loadbit {up 0x00000000 "C:/z/ws_vivado/fpga_boot_app/ba_vitis/srec_spi_bootloader/_ide/bitstream/download.bit" } -loaddata {up 0x00400000 "C:/z/ws_vivado/fpga_boot_app/ba_vitis/app_component/build/app_component.srec" } -force -file "C:/z/ws_vivado/fpga_boot_app/merge/merge.mcs"

Bit Swapping

位交换适用于串行、SelectMAP 或 BPI PROM 文件以及 ICAPE2 接口。SPI接口不考虑这个. 图略.

位交换是字节内位的交换。 各种文件格式:

  • MCS PROM 文件格式始终是位交换的,除非使用 SPI 配置模式的 write_cfgmem -interface spi1|spi2|spi4 选项。
  • HEX 文件格式可以进行位交换或不进行位交换,具体取决于用户选项。
  • 位流文件(BIT、RBT、BIN)永远不会进行位交换。

HEX 文件格式仅包含配置数据。其他 PROM 文件格式包括不应发送到 FPGA 的地址和校验和信息。地址和校验和信息由某些第三方器件编程器使用,但不会编程到 PROM 中。

SREC 文件格式

Motorola S-record 以 ASCII 文本形式将二进制信息作为十六进制值传达, 此文件格式也可以称为 SRECORD、SREC、S19、S28、S37。

在这里插入图片描述

说明:

  • S0 是 Header, 可以十六进制翻译成ASCII字符串, 一般是文件名, 文件地址, 编译日期, 版本等
  • S1,S2,S3表示数据, 分别有16bit,24bit,32bit地址
  • S7,S8,S9表示结束, 对应有32bit,24bit,16bit起始地址或零地址. 一般S1 S9搭配, S2 S8搭配, S3 S7搭配
  • S5, S6表示计数, 分别对应16bit, 24bit计数, 前者用于少于65536(0xFFFF), 后者少于1677215(0xFFFFFF), 可选的, 一般srec文件里面找不到这两个记录
  • Count 表示记录其余部分(地址 + 数据 + 校验和)后面的字节数(十六进制数字对), 一般至少2字节地址, 加上1字节校验, 所以Count至少为0x03, 最大为0xFF
  • Checksum 校验和是字节计数、地址和数据字段的两个十六进制数字对所表示的值之和的最小有效字节。在 C 编程语言中,总和通过以下方式转换为校验和: 0xFF - (sum & 0xFF), 每行有效的记录都要有校验和.

具体到文件

在这里插入图片描述

Vivado约束

添加上位压缩, 让bitstream文件更小一些

set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]

编译后从原来的 2141KB 缩为 874KB, 可减少一部分的Flash占用

串口Boot

常见的Boot的方式:

  • Golden + Multiboot, 依靠 ICAPE2 原语, 无需嵌入式编程, 当然 MicroBlaze 也有对应的 HWICAP
  • A B SWAP, 这在汽车中比较常见, 相互翻转, 可硬件支持或软件模拟
  • Boot A B, Boot 中可以加入救砖或安全启动
  • Boot App, 一般嵌入式中比较常见, 这里也给出一个这样的例子, 仅仅是例子而已

写好一个相对完善的Boot并不容易, 涉及 救砖 安全 是否需要相互升级 行业协议等等, 总有考虑不到的地方.

本节的Flash读写来自 C:\Xilinx\Vitis\2023.2\data\embeddedsw\XilinxProcessorIPLib\drivers\spi_v4_11\examples\xspi_numonyx_flash_quad_example.c

串口或SPI的读写尽量都改成了非阻塞或异步的.

地址划分

本篇的区域划分如下, 因为没有使用Flash XIP, 需要把固件全部拷贝到RAM中, 性能更高, 但需要固件不要超过RAM的大小, 外部有DDR3时, RAM是足够的, 本节使用的全部片内RAM, 这里划给APP编译出来不超过64KB是能跑的:

在这里插入图片描述

链接脚本修改

boot 的链接脚本文件修改

在这里插入图片描述

app 的链接脚本文件修改

在这里插入图片描述

Github Link

源码上传到了: https://github.com/weifengdq/domain_controller_orin_x2_tc397/tree/main/fpga_artix7_boot_app

代码是实验性质的, 功能上跑通, 未充分测试或整理优化, 仅供参考.

文件说明:

  • ba_vivado.tcl
    • 重建 Vivado 工程: vivado -mode batch -source .\ba_vivado.tcl -nolog -nojournal
    • 打开 Vivado 工程: vivado .\ba_vivado\ba_vivado.xpr
  • design_1_wrapper.xsa, Vivado 导出的硬件(含位流文件), Vitis 可以从这里创建 Platform
  • boot 该文件夹是Vitis Embedded工程对应的 boot 源码, flash 驱动 bsp_spi_flash.c 不同的Flash型号不一样, 注意修改
  • app 该文件夹是Vitis Embedded工程对应的 app 源码
  • uptool.py, 升级工具Python3

App

elf转bin

# 注意修改升级工具 `uptool.py` 中的路径
# objcopy = r'C:\Xilinx\Vitis\2023.2\gnu\microblaze\nt\bin\mb-objcopy.exe'
# elf = r'C:\z\ws_vivado\fpga_boot_app\bs_vitis_embedded\app\build\app.elf'

mb-objcopy -o binary app.elf app.bin

因为 app 的基地址上面设置的 0x10000, 而中断区在前 0x50 里面, 所以BIN文件的 0x50~0xFFFF 是无用的零值, BIN文件是地址连续的, 但app只要掐头去尾, 这也是官方用srec的原因, 这里依然用BIN是因为PC上BIN文件哪怕几MB也不算太大, 减少些HEX或SREC文件格式的解析, 脚本里面把这一段无用的零值忽略不传送.

其它说明:

  • info 指令判断当前是否是 app 文件, 这里只是随便用的方法 xil_printf("current: %s\n", (*(uint32_t *)0x00000000) == 0xB0000001 ? "app" : "unknown");, 不同的工程可能不同, 具体查看ELF转出的BIN文件
  • reset 指令可以出发cpu复位, 原理是跳到0地址全局中断区 (*((void (*)())(0x00000000)))(); 当然这个不能在硬件中断中用, 在主循环可以用, 有一个 microblaze_disable_interrupts(); 关中断再复位, 但这样只能复位一次
  • jump 指令可以从 app 跳转到 boot, 原理也不复杂, 因为每次重新上电先走的boot, 跳转app的时候交换了上图中的全局中断区和中断暂存区, app里面只需要把这两个区再交换一下, 跳到0地址执行就可以从app进入boot了, boot的[0x50, 0xFFAF]的ram区还是boot的, 没有人动它. 跳到boot如果没有救砖操作, 超时后就又自动跳转回boot

app测试截图:

在这里插入图片描述

Boot

说明:

  • 主要实现在bsp_uart_boot.c
  • 判断当前在 boot: (*(uint32_t *)0x00000000) == 0xB0000000 根据实际情况而定
  • 串口定长接收 16 字节, 只有收APP数据的时候是定长 272( = 16 + 256) 字节
  • 1s的救砖时间, 收到救砖命令就停留在boot
  • 救砖超时开始把APP从Flash拷贝到RAM, 拷贝完后进行CRC32校验, 失败仍留在Boot
  • CRC32校验成功跳转APP

boot 测试截图

在这里插入图片描述

一条命令升级

app改动编译后可通过 update 命令直接把 elf 文件刷进FPGA Flash里面

update 综合命令说明:

  • 每条指令前16字节是 CRC32 CMD ADDR SIZE, 收到的ACK前16字节是 CRC32 0xFFFFFFFF-CMD X X
  • 脚本直接调 mb-objcopy 把elf转成bin文件
  • 发 info 指令 可以判断当前是 app 还是 boot
  • 发 jump 跳转命令从app调到boot
  • 发 save brick 救砖命令禁止boot跳转app
  • 发 erase 命令 擦除FLASH扇区(从bin文件大小自动计算出扇区数)
  • 发 write 命令 256字节按页对齐写入FLASH(从bin文件摘出前0x50的APP_ISR和0x10000后的APP, 计算CRC32, 把地址长度和算出的CRC也存入Flash, 用于下次的安全启动)
  • 发 check 命令 从Flash拷回到中断暂存区和APP的RAM区, 进行CRC32校验, 和开机的安全启动是一样的
  • 发 jump 指令 从boot跳转到app

执行:

> python .\uptool.py -s COM32 -c update
convert C:\z\ws_vivado\fpga_boot_app\bs_vitis_embedded\app\build\app.elf to app.bin
7E B2 F5 FA F3 FF FF FF 00 00 00 00 00 00 00 00   ~...............
63 75 72 72 65 6E 74 3A 20 61 70 70 0A            current:.app.
■ 17:20:49.013958
✌️: current: app
current    : app
63 75 72 72 65 6E 74 3A 20 62 6F 6F 74 0A         current:.boot.
■ 17:20:49.028623

0F 2D 61 88 FC FF FF FF 00 00 00 00 00 00 00 00   .-a.............
73 61 76 65 20 62 72 69 63 6B 20 67 65 74 0A      save.brick.get.
■ 17:20:49.211294
...
...
✌️: current: boot
save brick ok
app size 42254, isr size 80, erase size 131072, sectors 2
38 0D C4 15 F7 FF FF FF 00 00 3F 00 00 00 01 00   8.........?.....
65 72 61 73 65 20 64 6F 6E 65 3A 20 30 78 30 30   erase.done:.0x00
33 46 30 30 30 30 2C 20 6C 65 6E 3A 20 36 35 35   3F0000,.len:.655
33 36 0A                                          36.
■ 17:20:51.221235
2C B2 B0 EF F7 FF FF FF 00 00 40 00 00 00 01 00   ,.........@.....
65 72 61 73 65 20 64 6F 6E 65 3A 20 30 78 30 30   erase.done:.0x00
34 30 30 30 30 30 2C 20 6C 65 6E 3A 20 36 35 35   400000,.len:.655
33 36 0A                                          36.
■ 17:20:51.739222
D8 2B BC DA F8 FF FF FF 00 00 00 00 10 01 00 00   .+..............
6E 65 78 74 5F 6C 65 6E 3A 20 32 37 32 0A         next_len:.272.
■ 17:20:52.233010
write 1/166
AA F2 D5 3C F6 FF FF FF 00 00 40 00 10 01 00 00   ...<......@.....
77 72 69 74 65 20 64 6F 6E 65 3A 20 30 78 30 30   write.done:.0x00
34 30 30 30 30 30 2C 20 6C 65 6E 3A 20 32 37 32   400000,.len:.272
0A                                                .17:20:52.279141
write 2/166
1E F9 A2 9A F6 FF FF FF 00 01 40 00 10 01 00 00   ..........@.....
77 72 69 74 65 20 64 6F 6E 65 3A 20 30 78 30 30   write.done:.0x00
34 30 30 31 30 30 2C 20 6C 65 6E 3A 20 32 37 32   400100,.len:.272
0A 
...
...
■ 17:20:59.682305
write app_info, app_isr_info and isr, addr 4194304
BE 4D A1 C6 F6 FF FF FF 00 00 3F 00 10 01 00 00   .M........?.....
77 72 69 74 65 20 64 6F 6E 65 3A 20 30 78 30 30   write.done:.0x00
33 46 30 30 30 30 2C 20 6C 65 6E 3A 20 32 37 32   3F0000,.len:.272
0A                                                .17:20:59.728140
write app_info, app_isr_info and isr end
EF 41 7E DB F8 FF FF FF 00 00 00 00 10 00 00 00   .A~.............
6E 65 78 74 5F 6C 65 6E 3A 20 31 36 0A            next_len:.16.
■ 17:20:59.772271
90 B3 5A 64 FE FF FF FF 00 00 00 00 00 00 00 00   ..Zd............17:20:59.787298
61 70 70 5F 63 72 63 20 63 68 65 63 6B 20 6F 6B   app_crc.check.ok
2C 20 65 6E 74 65 72 20 61 70 70 0A               ,.enter.app.
■ 17:20:59.857745
read data size: 28
app_crc check ok, enter app

check ok, jump to app
F1 5B 6D 8E F4 FF FF FF 00 00 00 00 00 00 00 00   .[m.............
6A 75 6D 70 20 74 6F 20 61 70 70 0A 63 75 72 72   jump.to.app.curr
65 6E 74 3A 20 61 70 70 0A                        ent:.app.
■ 17:21:01.383697
jump to app
current: app

参考链接

  • Vivado Design Suite 用户指南: 编程和调试 (UG908)
  • 7 Series FPGAs Configuration User Guide(UG470)
  • MultiBoot with 7 Series FPGAs and SPI
  • SPI SREC Bootloader Example Design for the Arty Evaluation Board (avnet.com)
  • FPGA Bootloader Part 1 - MicroBlaze SREC SPI Bootloader Hardware Step-by-step | Shadowcode
  • FPGA Bootloader Part 2 - Vitis SREC SPI Bootloader Software Step-by-Step | Shadowcode
  • SPI (Serial Peripheral Interface) - 杰哥的知识库 (jia.je)
  • SREC (file format) - Wikipedia
  • Intel HEX - Wikipedia
  • 16进制到文本字符串的转换,16进制-BeJSON.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/499801.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1.Netty介绍及NIO三大组件

Netty网络编程Netty的底层是NIO&#xff08;非阻塞IO&#xff09;&#xff0c;常用的多线程和线程池使用的是阻塞IO&#xff0c;其效率并不高。支持高并发&#xff0c;性能好高性能的服务端程序、客户端程序 NIO三大组件 一、Channel 读写数据的双向传输通道 常见的传输通道…

Taskflow 简单使用

Hello World #include <taskflow/taskflow.hpp>int main() {tf::Executor executor; tf::Taskflow taskflow;// 返回一个std::tuple<tf::Task, tf::Task, tf::Task, tf::Task> auto [A, B, C, D] taskflow.emplace([](){std::cout<<"A"<<s…

金三银四面试题(六):对象大小知多少

对象和数组在JVM如何在堆中布局&#xff1f;更常见地问法就是对象头都包含哪些信息 在JVM中对象和数组尽管都是连续的内存块。但在堆内存中的布局方式有些不同。 对象的组成 对象在JVM中可以分为三个部分&#xff0c;对象头&#xff08;Header&#xff09;&#xff0c;实例数…

SoC芯片的DVFS技术详解

​A72训练营很多同学问DVFS技术怎么实现的&#xff0c;这里小编就和大家掰扯掰扯SoC芯片的DVFS技术吧。 1. DVFS技术介绍 DVFS&#xff08;Dynamic Voltage and Frequency Scaling&#xff09;即动态电压频率调节技术&#xff0c;是一种高效的低功耗技术&#xff0c;它通过动态…

初始化脚手架

说明: 1 --- Vue脚手架是Vue官方提供的标准化开发工具&#xff08;开发平台&#xff09; 2 --- 最新的版本是 4.x 3 --- 文档 Vue CLI 具体步骤: 1 --- 如果下载缓慢请配置npm淘宝镜像npm config set registry http://registry.npm.taobao.org 2 --- 全局安装 vue/cli npm ins…

Apache Kafka + 矢量数据库 + LLM = 实时 GenAI

公众号&#xff1a;Halo咯咯 生成式人工智能 (GenAI) 支持先进的人工智能用例和创新&#xff0c;但也改变了企业架构的外观。大型语言模型 (LLM)、向量数据库和检索增强生成 (RAG) 需要新的数据集成模式和数据工程最佳实践。 Apache Kafka 和 Apache Flink 的数据流在大规模实时…

CIM搭建实现发送消息的效果

目录 背景过程1、下载代码2、进行配置3、直接启动项目4、打开管理界面5、启动web客户端实例项目6、发送消息 项目使用总结 背景 公司项目有许多需要发送即时消息的场景&#xff0c;之前一直采用的是传统的websocket连接&#xff0c;它会存在掉线严重&#xff0c;不可重连&…

推荐算法策略需求-rank model优化

1.pred_oobe (base) [rusxx]$ pwd /home/disk2/data/xx/icode/baidu/oxygen/rus-pipeline/pipeline-migrate/UserBaseActiveStatPipeline/his_session (base) [rusxx]$ sh test.sh 2. user_skill_history_dict_expt2包含userid [workxx]$ vim /home/work/xx/du-rus/du_rus_o…

2.2.1.3-移动平均线

跳转到根目录&#xff1a;知行合一&#xff1a;投资篇 已完成&#xff1a; 1、投资&技术   1.1.1 投资-编程基础-numpy   1.1.2 投资-编程基础-pandas   1.2 金融数据处理   1.3 金融数据可视化 2、投资方法论   2.1.1 预期年化收益率   2.1.2 一个关于yaxb的…

Docker命令及部署Java项目

文章目录 简介Docker镜像镜像列表查找镜像拉取镜像删除镜像镜像标签 Docker容器容器启动容器查看容器停止和重启后台模式和进入强制停止容器清理停止的容器容器错误日志容器别名及操作 Docker部署Java项目 简介 Docker是一种容器化技术&#xff0c;可以帮助开发者轻松打包应用…

python练习五

1. 给定一个包含n1个整数的数组nums&#xff0c;其数字在1到n之间&#xff08;包含1和n&#xff09;&#xff0c;可知至少存在一个重复的整数&#xff0c;假设只有一个重复的整数&#xff0c;请找出这个重复的数 def find_difnumber(ls):for index in range(0, len(ls)):for n…

如何使用命令行对RK开发板进行OpenHarmony版本烧录?

问题 在 OpenHarmony 自动化测试环境中&#xff0c;需要对流水线上的 RK 设备进行烧录&#xff0c;图形工具只能人工操作&#xff0c;那么有什么方法可以纯命令行进行自动化烧录呢&#xff1f; 思路 我们发现 RK 开发板实际是使用 upgrade_tool 的执行文件进行烧录的&#x…

力扣Lc24--- 434. 字符串中的单词数(java版)-2024年3月29日

1.题目描述 2.知识点 注1&#xff1a; \\s: 匹配一个或多个空格字符。|: 表示逻辑或&#xff0c;用于分隔不同的正则表达式部分。(?[\\p{Punct}]): 正向前瞻&#xff0c;匹配任何标点符号之前的位置。(?<[\\p{Punct}]): 正向后顾&#xff0c;匹配任何标点符号之后的位置…

工厂能耗管控物联网解决方案

工厂能耗管控物联网解决方案 工厂能耗管控物联网解决方案是一种创新的、基于先进技术手段的能源管理系统&#xff0c;它深度融合了物联网&#xff08;IoT&#xff09;、云计算、大数据分析以及人工智能等前沿科技&#xff0c;以实现对工业生产过程中能源消耗的实时监测、精确计…

github项目名称变更sourcetree如何同步

github项目名称变更sourcetree如何同步 方法1:删除本地仓库 重新从URL克隆 方法2:修改远程地址链接 1.打开项目所在文件夹的终端 2.删除本地关联的这个远程仓库origin git remote rm origin 3.关联修改名字后的远程仓库地址 git remote add origin <新的远程仓库地址&…

前端超分辨率技术应用:图像质量提升与场景实践探索-设计篇

超分辨率&#xff01; 引言 在数字化时代&#xff0c;图像质量对于用户体验的重要性不言而喻。随着显示技术的飞速发展&#xff0c;尤其是移动终端视网膜屏幕的广泛应用&#xff0c;用户对高分辨率、高质量图像的需求日益增长。然而&#xff0c;受限于网络流量、存储空间和图像…

代码随想录算法训练营第35天| 435. 无重叠区间、763.划分字母区间、56. 合并区间

435. 无重叠区间 题目链接&#xff1a;无重叠区间 题目描述&#xff1a;给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互不重叠 。 解题思想&#xff1a; 这道题目和射气球很像。 *“需…

深入浅出MySQL主从复制与读写分离原理及其实践

目录 一、主从复制 &#xff08;一&#xff09;主从复制简介 1.基本概述 2.复制类型 &#xff08;二&#xff09;主从复制流程与影响因素 1.主从复制的流程 2.影响因素 &#xff08;三&#xff09;实现主从复制 1.搭建时间同步 2.配置master服务器 2.1 开启二进制日…

C++11入门手册第一节,学完直接上手Qt(共两节)

入门 hello.cpp #include <iostream>int main() { std::cout << "Hello Quick Reference\n"<<endl; return 0;} 编译运行 $ g hello.cpp -o hello$ ./hello​Hello Quick Reference 变量 int number 5; // 整数float f 0.95; //…

分享vue3+OpenTiny UI+cesium.js实现三维地球

效果图 使用vue3 OpenTiny UI cesium 实现三维地球 node.js > v16.0 opentiny vue3 ui安装指南 https://opentiny.design/tiny-vue/zh-CN/os-theme/docs/installation yarn add opentiny/vue3 项目依赖 "dependencies": {"opentiny/vue": "3…