FPGA Artix7 Bootloader App Python升级

这篇具有很好参考价值的文章主要介绍了FPGA Artix7 Bootloader App Python升级。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


本文在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的配置

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

自动连接后:

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

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

资源消耗

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

存储划分

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处

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

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

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

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

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

编译得到 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 文件

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

对应的 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)

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

加上 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, 镜像文件选

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

编程完后, 断电重启, 因为是 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格式查看)的:

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

相当于整个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 命令 说明
BIT NO write_bitstream 含标头信息的二进制文件
RBT NO write_bitstream -raw_bitfile 含文本标头以及ASCII 1 0的BIT文件等效
BIN NO write_bitstream -bin_file 二进制数据文件, 无标头信息
MCS YES(除SPI) write_cfgmem -format MCS ASCII PROM 文件, 含数据 地址 校验和
HEX 用户定义 write_cfgmem -format HEX ASCII PROM 文件, 仅数据

注: RBT 是 Rawbits 的简写

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

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

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

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

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

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

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

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

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

对应的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。

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

说明:

  • 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), 每行有效的记录都要有校验和.

具体到文件

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

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是能跑的:

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

链接脚本修改

boot 的链接脚本文件修改

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

app 的链接脚本文件修改

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

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测试截图:

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

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 测试截图

FPGA Artix7 Bootloader App Python升级,域控制器开发 双Orin TC397 Artix-7,fpga开发,python,bootloader,github,artix7,microblaze

一条命令升级

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

执行:文章来源地址https://www.toymoban.com/news/detail-859977.html

> 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

到了这里,关于FPGA Artix7 Bootloader App Python升级的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Artix7 Microblaze下调试SDK官方lwIP RAW Mode TCP Server Application例程(详细说明)

    Vivado 2019.1 FPGA: Artix7 XC7A100T DDR3: MT41K256M16TW-107 PHY: RTL8211F(商业级) 为加快进入application,设置如下: 1.禁用autodetect, phy_link_speed设为100Mbps(此时autonegotiation被禁止,大大加快进入程序) 2.禁用dhcp, 使用默认IP地址设置(不用等待dhcp配置,加快速度) 先对SDK作简单的调整: 1.关闭

    2024年02月09日
    浏览(38)
  • stm32利用bootloader与app实现远程升级

    1、flash空间分配: 事先在flash开辟好空间,假设flash为128k,我这边给它分为五个区,分别为bootloader、标志位、application、application backup、标签ID。 (注:升级之前,app的代码烧录于application的flash存储区域内。用于app升级的 bin文件 大小一定要小于自己开辟的flash空间大小)

    2024年02月13日
    浏览(50)
  • 【STM32】IAP升级01 bootloader实现以及APP配置(主要)

    通过之前的了解 之前的了解,我们知道实现IAP升级需要两个条件: 1.APP程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始; 2.APP程序的中断向量表相应的移动,移动的偏移量为 x; 默认条件下的起始地址 默认的条件下,图中 IROM1 的起始地址(Start)一般为 0x08000000,大小

    2024年02月03日
    浏览(46)
  • 基于STM32单片机BOOTLOADER通过串口升级程序IAP——APP方案

                            此方法前提是你得有一个EEPROM         我用的单片机是STM32F103ZET6 , 此单片机FLASH容量为512KB; 在此单片机里面FLASH的起始地址是0X8000000,BOOT作为引导加载程序一般都是从这个地址开始,单片机一上点默认会从这个地址开始运行,所以将自己

    2024年02月04日
    浏览(63)
  • 基于FPGA 的SDRAM控制器

    4X16X4=256(Mbit),注意不是MByte sdram包含两个部分:sdram_ctrl、fifo_ctrl。 sdram_ctrl:其顶层为SDRAM的控制模块内部实例化了5个模块,有初始化、自刷新、写和读模块,还有一个仲裁模块对这四个不同操作进行分配; fifo_ctrl:其顶层为SDRAM的数据输入输出,内部实例化了两个用于连

    2024年02月08日
    浏览(64)
  • 基于FPGA的PID控制器设计

    PID控制应该算是应用非常广泛的控制算法了。常见的比如控制环境温度,控制无人机飞行高度速度等。PID我们将其分成三个参数,如下: P-比例控制,基本作用就是控制对象以线性的方式增加,在一个常量比例下,动态输出,缺点是会产生一个稳态误差。 I-积分控制,基本作

    2024年02月03日
    浏览(59)
  • 【进口控制器替代】基于Zynq-7020 FPGA的NI 8槽CompactRIO控制器

    667 MHz双核CPU,512 MB DRAM,1 GB存储容量,Zynq-7020 FPGA,更宽工作温度范围,8槽CompactRIO控制器 cRIO-9068是一款坚固耐用的无风扇嵌入式控制器,可用于高级控制和监测应用。这款软件设计控制器搭载FPGA、运行NI Linux Real-Time操作系统的实时处理器以及嵌入式用户界面功能。cRIO-906

    2024年01月25日
    浏览(62)
  • FPGA实验四:交通灯控制器设计

    目录 一、实验目的 二、设计要求 三、实验代码 1.design source文件代码

    2024年02月13日
    浏览(52)
  • FPGA十字路口红绿灯控制器

    课程设计:十字路口红绿灯控制器 1.要求双方向8个灯的时序控制 2.要求显示倒计时 verilog hdl代码模块 测试程序模块 注意: 选择比较好的器件 测试文件命名为testbench.vt,否则不行。 采用BCD倒计时,经测试时序无错,代码结构清晰。 EDA课设,感谢论坛其他大佬的代码,基于

    2024年02月11日
    浏览(56)
  • FPGA智能交通灯控制器系统系统设计

    把由5OM的有源晶振产生的现场可编程逻辑器件FPGA 的系统时钟输入到分频模块,经分频模块分频产生频率为1Hz的时钟脉冲,作为控制定时模块、控制模块、紧急模块、计数模块的时钟信号,然后再由定时模块来控制紧急模块和控制模块,按照交通管理规则控制交通工作状态的

    2024年02月04日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包