单片机-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

 

.

/************* 社会的有色眼光是:博士生、研究生、本科生、车间工人; 重点大学高材生、普通院校、二流院校、野鸡大学; 年薪百万、五十万、五万; 这些都只是帽子,可以失败千百次,但我和社会都觉得,人只要成功一次,就能换一顶帽子,只是社会看不见你之前的失败的帽子。 当然,换帽子决不是最终目的,走好自己的路就行。 杭州.大话西游 *******/
posted @ 2021-02-05 16:52  一匹夫  阅读(497)  评论(0编辑  收藏  举报