本文在Artix7上復(fù)現(xiàn)了Xilinx官方的srec_spi_bootloader例子, 有詳細的過程分析和圖文說明, 然后動手實現(xiàn)了FPGA串口Boot的完整過程, 通過Python腳本一條命令升級, 自動把app的elf文件轉(zhuǎn)bin文件,從app跳轉(zhuǎn)boot,擦寫flash, 寫入app, 進行crc32校驗后跳轉(zhuǎn)到新的app.
本篇有詳細的圖文說明和源碼鏈接, 歡迎大家評論, 點贊和收藏.
軟硬環(huán)境
軟件:
- Vivado v2023.2.2
- Vitis Unified IDE v2023.2.0
硬件:
- FPGA: xc7a35tfgg484-2
- 晶振: 3.3V, 50MHz 有源 單端, Y18
- 復(fù)位: 低電平復(fù)位, 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不體現(xiàn)在引腳配置里面 - flash 型號選擇
mt25ql128-spi-x1_x2_x4
, 別名n25q128-3.3v-spi-x1_x2_x4
, NOR Flash, 128Mbit, 16MB, 后面的代碼里扇區(qū)Sector統(tǒng)一按64KB擦除, 頁Page大小256寫入 - 廠商選
Micron(Numonyx)
- 未使用DDR3, 所幸實現(xiàn)功能的代碼并不復(fù)雜, Boot和App全用片內(nèi)RAM測試
復(fù)現(xiàn)官方 srec_spi_bootloader
例子簡介
這個例子的默認路徑是 C:\Xilinx\Vitis\2023.2\data\embeddedsw\lib\sw_apps\srec_spi_bootloader
簡介:
簡單的 SREC 引導(dǎo)加載程序
給定內(nèi)存中鏡像的位置,它能夠啟動 SREC 格式的鏡像文件(Mototorola S-record 格式)。
特別是,該引導(dǎo)加載程序?qū)榇鎯υ诳蓮奶幚砥鲗ぶ返姆且资蚤W存中的鏡像而設(shè)計。請修改 blconfig.h 頭文件中的定義“FLASH_IMAGE_BASEADDR”,以指向引導(dǎo)加載程序獲取閃存鏡像的位置。
您可以將這些源包含在您的軟件應(yīng)用程序項目中,并為您希望進行引導(dǎo)加載的處理器構(gòu)建項目。
您還可以隨后修改這些源,以使引導(dǎo)加載程序適應(yīng)您可能需要的任何特定場景。
一般是Boot(Golden Image)運行在BRAM, 把 App(Multiboot Image) 搬到DDR3, 這里簡化一下, 不使用DDR3, 只用FPGA自帶的RAM來測試這個例子以簡化流程.
Vivado硬件部分
Clocking Wizard:
- 輸入頻率 50MHz, Single ended clock
- 輸出頻率默認 100MHz
- 復(fù)位類型: Active Low
- 引出 clk_in1 引腳
Uartlite:
- 波特率 115200
- 引出 UART
Timer 不做更改, 用于驗證Boot跳轉(zhuǎn)后, App的中斷是否能正常運行.
GPIO:
- GPIO Width 設(shè)為 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]
# 注釋掉暫時規(guī)避SPI驅(qū)動異常
# 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 內(nèi)存配置文件 design_1_wrapper.mmi
導(dǎo)出硬件 XSA 文件, Include bitstream
資源消耗
存儲劃分
RAM: MicroBlaze 的 Local Memory 分配了 128KB, 把前32KB給Boot, 后96KB給App
Flash: FPGA位流文件大小 2.09MB, 和boot合并后也這么大, 壓縮后874KB, 把16MB SPI Flash的前4MB預(yù)留給Boot
Vitis 嵌入式 Boot
Open Workspace, 選擇一個文件夾作為工作空間.
Create Platform Component, 導(dǎo)入XSA文件, 創(chuàng)建硬件平臺, 編譯成庫.
File -> New Component -> From Examples, 從 Srec_spi_bootloader 例子創(chuàng)建 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位流文件, 內(nèi)存映射文件, elf文件 生成 download.bit 文件
對應(yīng)的 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轉(zhuǎn)換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位流文件, 內(nèi)存描述文件), 放到地址 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
此時如果不是斷電重啟, 而是按下復(fù)位按鍵, 會從App處重新開始執(zhí)行.
過程分析
先來看APP的SREC文件是怎么合并到MCS文件(可改后綴為HEX格式查看)的:
相當(dāng)于整個SREC文件被存入了Flash的0x00400000里面
bootloader的分析如下:
- 先是初始化 SPI, 用 FlashReadID() 讀ID, 這里連讀兩次后讀出來 0x20 0xBA 0x18, FlashID[3]字節(jié)含義為
- 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); ...}
, 當(dāng)然此處板子沒有超過16MB, 就沒進入4字節(jié)地址模式 - flash_get_srec_line() 函數(shù), 先從APP SREC文件存放的Flash地址(FLASH_IMAGE_BASEADDR 0x00400000)處讀4個字節(jié), 如
S017
, 得到長度len如 0x17 = 23, 然后再讀len * 2字節(jié)(因為一個字節(jié)轉(zhuǎn)成HEX文本是兩個), 這樣就讀出了一行SREC記錄存入sr_buf, 地址再加2個字符\r\n
就是下一個記錄的開始地址. - decode_srec_line() 函數(shù), 把sr_buf按照srec的記錄格式, 轉(zhuǎn)成對應(yīng)的 類型 地址 數(shù)據(jù) 數(shù)據(jù)長度, 同時也做了校驗, 因為checksum=0xFF-(sum&0xFF), 所以checksum+(sum&0xFF)=0xFF, 也就是cksum為0xFF表示校驗正確
- 判斷這一行的記錄類型, 如果是S1 S2 S3表示數(shù)據(jù), 就拷貝到內(nèi)存中去
memcpy ((void *)srinfo.addr, (void *)srinfo.sr_data, srinfo.dlen);
, 如果是S7 S8 S9表示起始地址和文件結(jié)束laddr = (void (*)())srinfo.addr;
- 最后跳轉(zhuǎn)到起始地址運行app:
(*laddr)();
, 初始定義是void (*laddr)();
基礎(chǔ)知識
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 | 二進制數(shù)據(jù)文件, 無標頭信息 |
MCS | YES(除SPI) | write_cfgmem -format MCS | ASCII PROM 文件, 含數(shù)據(jù) 地址 校驗和 |
HEX | 用戶定義 | write_cfgmem -format HEX | ASCII PROM 文件, 僅數(shù)據(jù) |
注: RBT 是 Rawbits 的簡寫
7 系列 FPGA 位流由三部分組成:
- 總線寬度自動檢測, 僅用于并行模式, SPI模式會忽略這些字節(jié)
- 同步字 0xAA995566
- FPGA配置
PROM 文件用于重新格式化位流文件以進行 PROM 編程(write_cfgmem -loadbit), PROM文件通常是位交換的, 除 SPI 配置模式外.
GUI方式生成BIT RBT BIN文件, 工程設(shè)置里面勾選, 下次Generate Bitstream就有了
生成的文件對比, Header不會被下到Flash里面, 總線寬度檢測會被SPI模式忽略, 但仍會放到0x00000000的位置, SPI模式會直接找到同步字 0xAA995566
GUI方式生成MCS BIN HEX文件, Vivado菜單欄Tools -> Generate Memory Configuration File
對應(yīng)的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接口不考慮這個. 圖略.
位交換是字節(jié)內(nèi)位的交換。 各種文件格式:
- MCS PROM 文件格式始終是位交換的,除非使用 SPI 配置模式的 write_cfgmem -interface spi1|spi2|spi4 選項。
- HEX 文件格式可以進行位交換或不進行位交換,具體取決于用戶選項。
- 位流文件(BIT、RBT、BIN)永遠不會進行位交換。
HEX 文件格式僅包含配置數(shù)據(jù)。其他 PROM 文件格式包括不應(yīng)發(fā)送到 FPGA 的地址和校驗和信息。地址和校驗和信息由某些第三方器件編程器使用,但不會編程到 PROM 中。
SREC 文件格式
Motorola S-record 以 ASCII 文本形式將二進制信息作為十六進制值傳達, 此文件格式也可以稱為 SRECORD、SREC、S19、S28、S37。
說明:
- S0 是 Header, 可以十六進制翻譯成ASCII字符串, 一般是文件名, 文件地址, 編譯日期, 版本等
- S1,S2,S3表示數(shù)據(jù), 分別有16bit,24bit,32bit地址
- S7,S8,S9表示結(jié)束, 對應(yīng)有32bit,24bit,16bit起始地址或零地址. 一般S1 S9搭配, S2 S8搭配, S3 S7搭配
- S5, S6表示計數(shù), 分別對應(yīng)16bit, 24bit計數(shù), 前者用于少于65536(0xFFFF), 后者少于1677215(0xFFFFFF), 可選的, 一般srec文件里面找不到這兩個記錄
- Count 表示記錄其余部分(地址 + 數(shù)據(jù) + 校驗和)后面的字節(jié)數(shù)(十六進制數(shù)字對), 一般至少2字節(jié)地址, 加上1字節(jié)校驗, 所以Count至少為0x03, 最大為0xFF
- Checksum 校驗和是字節(jié)計數(shù)、地址和數(shù)據(jù)字段的兩個十六進制數(shù)字對所表示的值之和的最小有效字節(jié)。在 C 編程語言中,總和通過以下方式轉(zhuǎn)換為校驗和:
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 原語, 無需嵌入式編程, 當(dāng)然 MicroBlaze 也有對應(yīng)的 HWICAP
- A B SWAP, 這在汽車中比較常見, 相互翻轉(zhuǎn), 可硬件支持或軟件模擬
- Boot A B, Boot 中可以加入救磚或安全啟動
- Boot App, 一般嵌入式中比較常見, 這里也給出一個這樣的例子, 僅僅是例子而已
寫好一個相對完善的Boot并不容易, 涉及 救磚 安全 是否需要相互升級 行業(yè)協(xié)議等等, 總有考慮不到的地方.
本節(jié)的Flash讀寫來自 C:\Xilinx\Vitis\2023.2\data\embeddedsw\XilinxProcessorIPLib\drivers\spi_v4_11\examples\xspi_numonyx_flash_quad_example.c
串口或SPI的讀寫盡量都改成了非阻塞或異步的.
地址劃分
本篇的區(qū)域劃分如下, 因為沒有使用Flash XIP, 需要把固件全部拷貝到RAM中, 性能更高, 但需要固件不要超過RAM的大小, 外部有DDR3時, RAM是足夠的, 本節(jié)使用的全部片內(nèi)RAM, 這里劃給APP編譯出來不超過64KB是能跑的:
鏈接腳本修改
boot 的鏈接腳本文件修改
app 的鏈接腳本文件修改
Github Link
源碼上傳到了: https://github.com/weifengdq/domain_controller_orin_x2_tc397/tree/main/fpga_artix7_boot_app
代碼是實驗性質(zhì)的, 功能上跑通, 未充分測試或整理優(yōu)化, 僅供參考.
文件說明:
-
ba_vivado.tcl
- 重建 Vivado 工程:
vivado -mode batch -source .\ba_vivado.tcl -nolog -nojournal
- 打開 Vivado 工程:
vivado .\ba_vivado\ba_vivado.xpr
- 重建 Vivado 工程:
-
design_1_wrapper.xsa
, Vivado 導(dǎo)出的硬件(含位流文件), Vitis 可以從這里創(chuàng)建 Platform -
boot
該文件夾是Vitis Embedded工程對應(yīng)的 boot 源碼, flash 驅(qū)動bsp_spi_flash.c
不同的Flash型號不一樣, 注意修改 -
app
該文件夾是Vitis Embedded工程對應(yīng)的 app 源碼 -
uptool.py
, 升級工具Python3
App
elf轉(zhuǎn)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 的基地址上面設(shè)置的 0x10000, 而中斷區(qū)在前 0x50 里面, 所以BIN文件的 0x50~0xFFFF 是無用的零值, BIN文件是地址連續(xù)的, 但app只要掐頭去尾, 這也是官方用srec的原因, 這里依然用BIN是因為PC上BIN文件哪怕幾MB也不算太大, 減少些HEX或SREC文件格式的解析, 腳本里面把這一段無用的零值忽略不傳送.
其它說明:
- info 指令判斷當(dāng)前是否是 app 文件, 這里只是隨便用的方法
xil_printf("current: %s\n", (*(uint32_t *)0x00000000) == 0xB0000001 ? "app" : "unknown");
, 不同的工程可能不同, 具體查看ELF轉(zhuǎn)出的BIN文件 - reset 指令可以出發(fā)cpu復(fù)位, 原理是跳到0地址全局中斷區(qū)
(*((void (*)())(0x00000000)))();
當(dāng)然這個不能在硬件中斷中用, 在主循環(huán)可以用, 有一個microblaze_disable_interrupts();
關(guān)中斷再復(fù)位, 但這樣只能復(fù)位一次 - jump 指令可以從 app 跳轉(zhuǎn)到 boot, 原理也不復(fù)雜, 因為每次重新上電先走的boot, 跳轉(zhuǎn)app的時候交換了上圖中的全局中斷區(qū)和中斷暫存區(qū), app里面只需要把這兩個區(qū)再交換一下, 跳到0地址執(zhí)行就可以從app進入boot了, boot的[0x50, 0xFFAF]的ram區(qū)還是boot的, 沒有人動它. 跳到boot如果沒有救磚操作, 超時后就又自動跳轉(zhuǎn)回boot
app測試截圖:
Boot
說明:
- 主要實現(xiàn)在
bsp_uart_boot.c
- 判斷當(dāng)前在 boot:
(*(uint32_t *)0x00000000) == 0xB0000000
根據(jù)實際情況而定 - 串口定長接收 16 字節(jié), 只有收APP數(shù)據(jù)的時候是定長 272( = 16 + 256) 字節(jié)
- 1s的救磚時間, 收到救磚命令就停留在boot
- 救磚超時開始把APP從Flash拷貝到RAM, 拷貝完后進行CRC32校驗, 失敗仍留在Boot
- CRC32校驗成功跳轉(zhuǎn)APP
boot 測試截圖
一條命令升級
app改動編譯后可通過 update 命令直接把 elf 文件刷進FPGA Flash里面
update 綜合命令說明:文章來源:http://www.zghlxwxcb.cn/news/detail-859977.html
- 每條指令前16字節(jié)是 CRC32 CMD ADDR SIZE, 收到的ACK前16字節(jié)是 CRC32 0xFFFFFFFF-CMD X X
- 腳本直接調(diào) mb-objcopy 把elf轉(zhuǎn)成bin文件
- 發(fā) info 指令 可以判斷當(dāng)前是 app 還是 boot
- 發(fā) jump 跳轉(zhuǎn)命令從app調(diào)到boot
- 發(fā) save brick 救磚命令禁止boot跳轉(zhuǎn)app
- 發(fā) erase 命令 擦除FLASH扇區(qū)(從bin文件大小自動計算出扇區(qū)數(shù))
- 發(fā) write 命令 256字節(jié)按頁對齊寫入FLASH(從bin文件摘出前0x50的APP_ISR和0x10000后的APP, 計算CRC32, 把地址長度和算出的CRC也存入Flash, 用于下次的安全啟動)
- 發(fā) check 命令 從Flash拷回到中斷暫存區(qū)和APP的RAM區(qū), 進行CRC32校驗, 和開機的安全啟動是一樣的
- 發(fā) jump 指令 從boot跳轉(zhuǎn)到app
執(zhí)行:文章來源地址http://www.zghlxwxcb.cn/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 用戶指南: 編程和調(diào)試 (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進制到文本字符串的轉(zhuǎn)換,16進制-BeJSON.com
到了這里,關(guān)于FPGA Artix7 Bootloader App Python升級的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!