單片機-XIP-外部閃存就地執行代碼


聲明:此博文所述我未實踐,目的是知識整理。

 

1.  常說的 "單片機的norflash上可以執行代碼 “ 這句話該如何理解?

CPU做取指、譯碼、執行。

常說的哪些介質可以執行程序,實際上指的是CPU可以從那里取指,以供后續譯碼和執行。

 

2.  單片機一般在內部norfalsh上執行代碼

Soc讀取內部NorFlash時可以總線式訪問,這對SoC來說更方便,由SoC直接以地址方式來訪問,而且,NorFlash不需要初始化,時序簡單。

PS:nandflash接口管理復雜,需要專門的控制器。 

 

3. 單片機內部norflash上執行代碼面臨的困局

用ST做GUI界面,面臨最大的問題就是芯片內部flash最大才2M。在現在這個時代,2M其實也放不了幾張圖片。

如果嫌棄你單片機的norflash不夠大呢?你換再大的單片機norflash也大不了多少了,所以:

方案1. 使用ST在M7內核芯片上增加的QSPI控制器,讓用戶把代碼放在外部存儲就地執行。

方案2. 使用sdram,將代碼搬運到sdram內執行。

方案1 對應的方式稱為XIP 。 

方案2 對應的方式稱為 BootROM,顧名思義,從ROM搬運代碼到RAM去執行,所以叫BootROM。

 

4. norflash不夠的解決方案1 -- XIP

(1). 在外部閃存(外部QSPI或FMC-NOR閃存)“就地執行”。

(2). 用戶應用程序代碼應鏈接到目標執行存儲器地址(外部QSPI或FMC-NOR閃存)。

(3). 需要內存映射支持

上面 (1)意思是:XiP是可以在外部閃存直接執行代碼的,就像芯片在內部flash 的地址0x0800 0000直接執行一樣,稱為“就地執行”。

上面(2) 意思是:用戶應用程序代碼編譯的時候鏈接地址要改成外部閃存的地址,如STM32 H7系統給QSPI Flash在系統總線分配的地址是0x9000 0000,那么代碼的地址就要改成0x9000 0000。

 

XIP模型操作流程

XiP啟動方式實際是借助一個bootloader,這個bootloader做了一件很與眾不同的事情,就是把QSPI FLASH映射到了系統總線0x9000 0000 這個地址上。

映射上去以后,只要我們訪問0x9000 0000這個地址,系統總線就會自動去讀QSPI FLASH 0地址的數據。此時是自動去讀!

比如:uint32_t temp = *((uint32_t *)0x90000000)就能直接拿到QSPI FLASH第0~3地址數據。

當映射完成后,先關閉全部中斷和cache,然后跳轉到用戶程序執行。

 

借助STM32H7的QSPI、XIP的方式,我們就可以實現讓程序在W25QXX系列芯片內跑起來了。

 

5 . norflash不夠的解決方案2 -- BootROM

(1). 從閃存啟動,配置外部RAM存儲器(SDRAM或SRAM),

    先從閃存復制用戶應用程序二進制文件(SDCARD或SPI-Flash存儲器)到外部SDRAM或外部SRAM,然后跳轉執行用戶應用程序。

(2). 用戶應用程序代碼應鏈接到目標執行存儲器地址(外部SDRAM或SRAM)。

這種模型就很通用了,適用於凡是有FMC能驅動外部RAM的任意一款ST芯片。當然鏈接地址肯定是外部RAM存儲器的地址了。至於閃存,隨意都可以,什么SPI FLASH、TF卡、I2C存儲器都可以。

比如:STM32F407ZG + SRAM(32MB) + TF卡,把程序編譯成二進制放到TF卡里面,每當我把新的程序更新到TF卡,運行的程序就更新了,就有點像玩Linux TF卡啟動的感覺了。

 

6. XiP模型bootloader編寫

參考博文:https://blog.csdn.net/sudaroot/article/details/110128506?utm_medium=distribute.pc_relevant_download.none-task-blog-baidujs-1.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-baidujs-1.nonecase

#include <stdio.h> #include "w25qx_qspi.h" typedef void (*pFunction)(void); #define APPLICATION_ADDRESS            QSPI_BASE
 
static uint32_t QSPI_EnableMemoryMappedMode(QSPI_HandleTypeDef *QSPIHandle); pFunction JumpToApplication; int main(void) { /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* Enable I-Cache---------------------------------------------------------*/ SCB_EnableICache(); /* Enable D-Cache---------------------------------------------------------*/ SCB_EnableDCache(); /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_QUADSPI_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ printf("Sudaroot XiP BootLoader\r\n"); /* 1.W25Qx Init */ W25Qx_QSPI_Init(); /* 2.Enable MemoryMapped mode */ QSPI_EnableMemoryMappedMode(&hqspi); /* 3.Disable CPU L1 cache before jumping to the QSPI code execution */
  /* Disable I-Cache */ SCB_DisableICache(); /* Disable D-Cache */ SCB_DisableDCache(); /* 4.Disable Systick interrupt */ HAL_SuspendTick(); /* 5.Initialize user application's Stack Pointer & Jump to user application */ JumpToApplication = (pFunction) (*(__IO uint32_t*) (APPLICATION_ADDRESS + 4)); __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); JumpToApplication(); /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1) { /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief Configure the QSPI in memory-mapped mode * @retval QSPI memory status */
static uint32_t QSPI_EnableMemoryMappedMode(QSPI_HandleTypeDef *QSPIHandle) { QSPI_CommandTypeDef s_command; QSPI_MemoryMappedTypeDef s_mem_mapped_cfg; /* Configure the command for the read instruction */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = QUAD_INOUT_FAST_READ_CMD; s_command.AddressMode = QSPI_ADDRESS_4_LINES; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_4_LINES; s_command.DummyCycles = 6; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_HALF_CLK_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Configure the memory mapped mode */ s_mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE; s_mem_mapped_cfg.TimeOutPeriod = 0; return HAL_QSPI_MemoryMapped(QSPIHandle, &s_command, &s_mem_mapped_cfg); } int __io_putchar(int ch) { HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 1000); return ch; }

 

參考資料:

安富萊_STM32-V7開發板_用戶手冊,含BSP驅動包設計(V3.1).pdf

 

STM32CubeIDE QSPI間接模式和內存映射模式 讀寫W25Q64

https://blog.csdn.net/sudaroot/article/details/109097135

 

.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM