這也是《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工作目錄-->應用程序目錄-->_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初始化時間:
原位置:
修改
/* *InitializetheSerialFlashLibrary. */ volatileintwait; do { Status=XIsf_Initialize(&Isf,&Spi,ISF_SPI_SELECT,IsfWriteBuffer); if(Status!=XST_SUCCESS){ xil_printf("XIsf_initializefailed!Status=%d ",Status); for(wait=0;wait100000;?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(容量大?。?28Mbyte)為例,根據芯片手冊中的描述:
該芯片讀時序如下:
對該芯片來說,該參數值就是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_33166886/article/details/112490916
這里要用到比較重要的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時候會很慢,好處就是可以避免很多因為文件錯誤導致功能異常。結構如下:
Record type | Byte count | Address | Data | Checksum |
---|
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)?!?0”/“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/SREC_(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/henrikbrixandersen/elf-bootloader?_ga=2.259888963.835186866.1691390458-2003547133.1691118205
在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區再進行升級。
總結
總結就下面的一張圖了:
關于這部分內容,大家有什么建議或者經驗,歡迎大家評論區留言討論~
-
應用程序
+關注
關注
37文章
3268瀏覽量
57704 -
軟核
+關注
關注
0文章
14瀏覽量
15851 -
固化
+關注
關注
0文章
167瀏覽量
10484
原文標題:一篇文章搞懂軟核(MicroBlaze)的固化和啟動
文章出處:【微信號:HXSLH1010101010,微信公眾號:FPGA技術江湖】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論