简介:
感谢社区提供的评测机会,本次评测的板卡为 STM32H7S78-DK先来张上电运行图片。
该板卡有着丰富的外设资源,主要特性如下:
-这款开发板基于 Arm® Cortex®-M7 的 STM32H7S7L8H6H 微控制器,采用 TFBGA225 封装,具有 64KB 闪存和 620KB SRAM。
- 带电容式触摸屏的 5 英寸 LCD 模块
- 带 USB 2.0 HS 接口的 USB Type-C®
- USB Type-C®,带 USB 2.0 FS 接口
- 以太网符合 IEEE-802.3-2002 标准
- I2S 音频编
- 一个 ST-MEMS 数字麦克风
- 1-Gbit Octo-SPI NOR FLASH
- 256-Mbit Hexadeca-SPI PSRAM
- 附带 Wi-Fi® 模块(符合 802.11 b/g/n 标准)
- 四个用户 LED 指示灯
- 用户和复位按钮
- 两个 USB Type-C
- 以太网 RJ45
- 摄像头接口: FPC 连接器
- microSD™ 卡
- 立体声耳机插孔,包括模拟麦克风输入
- 音频 MEMS 子板扩展连接器
- ARDUINO® Uno V3 扩展连接器
- STMod+ 扩展连接器
- Pmod™ 扩展连接器
芯片的主要外设资源说明如下:
芯片内部只有64KB 的flash 资源,该芯片可以用来开发大型的MCU 程序,显然如果程序都放在片内flash 运行是放不下的,板卡上通过XSPI 模块分别挂载了1-Gbit Octo-SPI NOR FLASH(128MB 的nor flash), 256-Mbit Hexadeca-SPI PSRAM (32MB)的RAM资源,这下才是运行大型嵌入式程序及GUI 该有的资源配置, 芯片是支持XIP模式运行在nor flash 上的,本次的主题就是检讨如何将程序存储在NOR FLASH 上运行。
XIP 配置
对于XIP的 运行方式,ST 的HAL 软件包 STM32Cube_FW_H7RS_V1.1.0\Projects\STM32H7S78-DK\Templates\Template_XIP 路径下提供了IAR/MDK/CUBEIDE 的工程模板来方便我们直接XIP方式运行程序,在开始探讨XIP运行方式前我们先看下XIP 像款的user flash/xspi1/xspi2 这三段外设的地址映射空间。
user flash 映射到芯片的-0x08000000~0x0800ffff
xspi1(RAM)映射到芯片 -0x90000000~0x9fffffff
xspi2(NOR flash)映射到芯片-0x70000000~0x7fffffff
STM32Cube_FW_H7RS_V1.1.0\Projects\STM32H7S78-DK\Templates\Template_XIP 模板路径下可以看到一个boot工程和Appli工程,boot 工程会编译出一个运行在user flash的镜像该镜像会完成完成XSPI1 和 XSPI2 的初始化将app 工程使用的RAM及ROM映射到上述的地址空间,然后跳转到appi 运行,本次试用IAR环境运行XIP工程,我们查看boot 工程代码来验证上述的XIP运行方式。
以下是boot 工程对应的IAR 化境的linkfile(stm32h7s7xx_flash.icf)
从上述link 文件的layout 可知boot 程序运行于user flash,堆栈空间存放在Dtcm。
从boot 工程的如下代码可以看出boot 工程会配置对应的pin 脚为xspi 功能,并对其进行初始化。
/** * @brief XSPI MSP Initialization * This function configures the hardware resources used in this example * @param hxspi: XSPI handle pointer * @retval None */ void HAL_XSPI_MspInit(XSPI_HandleTypeDef* hxspi) { GPIO_InitTypeDef GPIO_InitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; if(hxspi->Instance==XSPI1) { /* USER CODE BEGIN XSPI1_MspInit 0 */ /* USER CODE END XSPI1_MspInit 0 */ /** Initializes the peripherals clock */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_XSPI1; PeriphClkInit.Xspi1ClockSelection = RCC_XSPI1CLKSOURCE_PLL2S; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } /* Peripheral clock enable */ HAL_RCC_XSPIM_CLK_ENABLED++; if(HAL_RCC_XSPIM_CLK_ENABLED==1){ __HAL_RCC_XSPIM_CLK_ENABLE(); } __HAL_RCC_XSPI1_CLK_ENABLE(); __HAL_RCC_GPIOO_CLK_ENABLE(); __HAL_RCC_GPIOP_CLK_ENABLE(); /**XSPI1 GPIO Configuration PO3 ------> XSPIM_P1_DQS1 PP10 ------> XSPIM_P1_IO10 PP12 ------> XSPIM_P1_IO12 PP14 ------> XSPIM_P1_IO14 PP2 ------> XSPIM_P1_IO2 PP5 ------> XSPIM_P1_IO5 PO2 ------> XSPIM_P1_DQS0 PP1 ------> XSPIM_P1_IO1 PP11 ------> XSPIM_P1_IO11 PP15 ------> XSPIM_P1_IO15 PP3 ------> XSPIM_P1_IO3 PP0 ------> XSPIM_P1_IO0 PP7 ------> XSPIM_P1_IO7 PP8 ------> XSPIM_P1_IO8 PP13 ------> XSPIM_P1_IO13 PP4 ------> XSPIM_P1_IO4 PO4 ------> XSPIM_P1_CLK PP6 ------> XSPIM_P1_IO6 PO0 ------> XSPIM_P1_NCS1 PP9 ------> XSPIM_P1_IO9 */ GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_XSPIM_P1; HAL_GPIO_Init(GPIOO, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_12|GPIO_PIN_14|GPIO_PIN_2 |GPIO_PIN_5|GPIO_PIN_1|GPIO_PIN_11|GPIO_PIN_15 |GPIO_PIN_3|GPIO_PIN_0|GPIO_PIN_7|GPIO_PIN_8 |GPIO_PIN_13|GPIO_PIN_4|GPIO_PIN_6|GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_XSPIM_P1; HAL_GPIO_Init(GPIOP, &GPIO_InitStruct); /* USER CODE BEGIN XSPI1_MspInit 1 */ /* USER CODE END XSPI1_MspInit 1 */ } else if(hxspi->Instance==XSPI2) { /* USER CODE BEGIN XSPI2_MspInit 0 */ /* USER CODE END XSPI2_MspInit 0 */ /** Initializes the peripherals clock */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_XSPI2; PeriphClkInit.Xspi2ClockSelection = RCC_XSPI2CLKSOURCE_PLL2S; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } /* Peripheral clock enable */ HAL_RCC_XSPIM_CLK_ENABLED++; if(HAL_RCC_XSPIM_CLK_ENABLED==1){ __HAL_RCC_XSPIM_CLK_ENABLE(); } __HAL_RCC_XSPI2_CLK_ENABLE(); __HAL_RCC_GPION_CLK_ENABLE(); /**XSPI2 GPIO Configuration PN1 ------> XSPIM_P2_NCS1 PN3 ------> XSPIM_P2_IO1 PN0 ------> XSPIM_P2_DQS0 PN11 ------> XSPIM_P2_IO7 PN10 ------> XSPIM_P2_IO6 PN9 ------> XSPIM_P2_IO5 PN2 ------> XSPIM_P2_IO0 PN6 ------> XSPIM_P2_CLK PN8 ------> XSPIM_P2_IO4 PN4 ------> XSPIM_P2_IO2 PN5 ------> XSPIM_P2_IO3 */ GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_3|GPIO_PIN_0|GPIO_PIN_11 |GPIO_PIN_10|GPIO_PIN_9|GPIO_PIN_2|GPIO_PIN_6 |GPIO_PIN_8|GPIO_PIN_4|GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_XSPIM_P2; HAL_GPIO_Init(GPION, &GPIO_InitStruct); /* USER CODE BEGIN XSPI2_MspInit 1 */ /* USER CODE END XSPI2_MspInit 1 */ } } /** * @brief XSPI MSP De-Initialization * This function freeze the hardware resources used in this example * @param hxspi: XSPI handle pointer * @retval None */ void HAL_XSPI_MspDeInit(XSPI_HandleTypeDef* hxspi) { if(hxspi->Instance==XSPI1) { /* USER CODE BEGIN XSPI1_MspDeInit 0 */ /* USER CODE END XSPI1_MspDeInit 0 */ /* Peripheral clock disable */ HAL_RCC_XSPIM_CLK_ENABLED--; if(HAL_RCC_XSPIM_CLK_ENABLED==0){ __HAL_RCC_XSPIM_CLK_DISABLE(); } __HAL_RCC_XSPI1_CLK_DISABLE(); /**XSPI1 GPIO Configuration PO3 ------> XSPIM_P1_DQS1 PP10 ------> XSPIM_P1_IO10 PP12 ------> XSPIM_P1_IO12 PP14 ------> XSPIM_P1_IO14 PP2 ------> XSPIM_P1_IO2 PP5 ------> XSPIM_P1_IO5 PO2 ------> XSPIM_P1_DQS0 PP1 ------> XSPIM_P1_IO1 PP11 ------> XSPIM_P1_IO11 PP15 ------> XSPIM_P1_IO15 PP3 ------> XSPIM_P1_IO3 PP0 ------> XSPIM_P1_IO0 PP7 ------> XSPIM_P1_IO7 PP8 ------> XSPIM_P1_IO8 PP13 ------> XSPIM_P1_IO13 PP4 ------> XSPIM_P1_IO4 PO4 ------> XSPIM_P1_CLK PP6 ------> XSPIM_P1_IO6 PO0 ------> XSPIM_P1_NCS1 PP9 ------> XSPIM_P1_IO9 */ HAL_GPIO_DeInit(GPIOO, GPIO_PIN_3|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_0); HAL_GPIO_DeInit(GPIOP, GPIO_PIN_10|GPIO_PIN_12|GPIO_PIN_14|GPIO_PIN_2 |GPIO_PIN_5|GPIO_PIN_1|GPIO_PIN_11|GPIO_PIN_15 |GPIO_PIN_3|GPIO_PIN_0|GPIO_PIN_7|GPIO_PIN_8 |GPIO_PIN_13|GPIO_PIN_4|GPIO_PIN_6|GPIO_PIN_9); /* USER CODE BEGIN XSPI1_MspDeInit 1 */ /* USER CODE END XSPI1_MspDeInit 1 */ } else if(hxspi->Instance==XSPI2) { /* USER CODE BEGIN XSPI2_MspDeInit 0 */ /* USER CODE END XSPI2_MspDeInit 0 */ /* Peripheral clock disable */ HAL_RCC_XSPIM_CLK_ENABLED--; if(HAL_RCC_XSPIM_CLK_ENABLED==0){ __HAL_RCC_XSPIM_CLK_DISABLE(); } __HAL_RCC_XSPI2_CLK_DISABLE(); /**XSPI2 GPIO Configuration PN1 ------> XSPIM_P2_NCS1 PN3 ------> XSPIM_P2_IO1 PN0 ------> XSPIM_P2_DQS0 PN11 ------> XSPIM_P2_IO7 PN10 ------> XSPIM_P2_IO6 PN9 ------> XSPIM_P2_IO5 PN2 ------> XSPIM_P2_IO0 PN6 ------> XSPIM_P2_CLK PN8 ------> XSPIM_P2_IO4 PN4 ------> XSPIM_P2_IO2 PN5 ------> XSPIM_P2_IO3 */ HAL_GPIO_DeInit(GPION, GPIO_PIN_1|GPIO_PIN_3|GPIO_PIN_0|GPIO_PIN_11 |GPIO_PIN_10|GPIO_PIN_9|GPIO_PIN_2|GPIO_PIN_6 |GPIO_PIN_8|GPIO_PIN_4|GPIO_PIN_5); /* USER CODE BEGIN XSPI2_MspDeInit 1 */ /* USER CODE END XSPI2_MspDeInit 1 */ } }
以下代码段会对xspi 进行初始化配置。
/** * @brief XSPI1 Initialization Function * @param None * @retval None */ static void MX_XSPI1_Init(void) { /* USER CODE BEGIN XSPI1_Init 0 */ /* USER CODE END XSPI1_Init 0 */ XSPIM_CfgTypeDef sXspiManagerCfg = {0}; /* USER CODE BEGIN XSPI1_Init 1 */ /* USER CODE END XSPI1_Init 1 */ /* XSPI1 parameter configuration*/ hxspi1.Instance = XSPI1; hxspi1.Init.FifoThresholdByte = 2; hxspi1.Init.MemoryMode = HAL_XSPI_SINGLE_MEM; hxspi1.Init.MemoryType = HAL_XSPI_MEMTYPE_APMEM_16BITS; hxspi1.Init.MemorySize = HAL_XSPI_SIZE_32GB; hxspi1.Init.ChipSelectHighTimeCycle = 1; hxspi1.Init.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE; hxspi1.Init.ClockMode = HAL_XSPI_CLOCK_MODE_0; hxspi1.Init.WrapSize = HAL_XSPI_WRAP_NOT_SUPPORTED; hxspi1.Init.ClockPrescaler = 0; hxspi1.Init.SampleShifting = HAL_XSPI_SAMPLE_SHIFT_NONE; hxspi1.Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_ENABLE; hxspi1.Init.ChipSelectBoundary = HAL_XSPI_BONDARYOF_8KB; hxspi1.Init.MaxTran = 0; hxspi1.Init.Refresh = 0; hxspi1.Init.MemorySelect = HAL_XSPI_CSSEL_NCS1; if (HAL_XSPI_Init(&hxspi1) != HAL_OK) { Error_Handler(); } sXspiManagerCfg.nCSOverride = HAL_XSPI_CSSEL_OVR_NCS1; sXspiManagerCfg.IOPort = HAL_XSPIM_IOPORT_1; if (HAL_XSPIM_Config(&hxspi1, &sXspiManagerCfg, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN XSPI1_Init 2 */ /* USER CODE END XSPI1_Init 2 */ } /** * @brief XSPI2 Initialization Function * @param None * @retval None */ static void MX_XSPI2_Init(void) { /* USER CODE BEGIN XSPI2_Init 0 */ /* USER CODE END XSPI2_Init 0 */ XSPIM_CfgTypeDef sXspiManagerCfg = {0}; /* USER CODE BEGIN XSPI2_Init 1 */ /* USER CODE END XSPI2_Init 1 */ /* XSPI2 parameter configuration*/ hxspi2.Instance = XSPI2; hxspi2.Init.FifoThresholdByte = 4; hxspi2.Init.MemoryMode = HAL_XSPI_SINGLE_MEM; hxspi2.Init.MemoryType = HAL_XSPI_MEMTYPE_MACRONIX; hxspi2.Init.MemorySize = HAL_XSPI_SIZE_32GB; hxspi2.Init.ChipSelectHighTimeCycle = 2; hxspi2.Init.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE; hxspi2.Init.ClockMode = HAL_XSPI_CLOCK_MODE_0; hxspi2.Init.WrapSize = HAL_XSPI_WRAP_NOT_SUPPORTED; hxspi2.Init.ClockPrescaler = 0; hxspi2.Init.SampleShifting = HAL_XSPI_SAMPLE_SHIFT_NONE; hxspi2.Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_ENABLE; hxspi2.Init.ChipSelectBoundary = HAL_XSPI_BONDARYOF_NONE; hxspi2.Init.MaxTran = 0; hxspi2.Init.Refresh = 0; hxspi2.Init.MemorySelect = HAL_XSPI_CSSEL_NCS1; if (HAL_XSPI_Init(&hxspi2) != HAL_OK) { Error_Handler(); } sXspiManagerCfg.nCSOverride = HAL_XSPI_CSSEL_OVR_NCS1; sXspiManagerCfg.IOPort = HAL_XSPIM_IOPORT_2; if (HAL_XSPIM_Config(&hxspi2, &sXspiManagerCfg, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN XSPI2_Init 2 */ /* USER CODE END XSPI2_Init 2 */ } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPION_CLK_ENABLE(); __HAL_RCC_GPIOO_CLK_ENABLE(); __HAL_RCC_GPIOP_CLK_ENABLE(); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ }
对xspi初始化完成后,调用该接口跳转到app 程序运行,跳转前配置跳转后的运行环境,然后跳转到XIP地址运行。
/** * @addtogroup BOOT_XIP_Exported_Functions Boot XIP exported functions * @{ */ BOOTStatus_TypeDef BOOT_Application(void) { BOOTStatus_TypeDef retr; /* mount the memory */ retr = MapMemory(); if (BOOT_OK == retr) { /* jump on the application */ retr = JumpToApplication(); } return retr; } BOOTStatus_TypeDef JumpToApplication(void) { uint32_t primask_bit; typedef void (*pFunction)(void); pFunction JumpToApp; uint32_t Application_vector; if (EXTMEM_OK != EXTMEM_GetMapAddress(EXTMEM_MEMORY_BOOTXIP, &Application_vector)) { return BOOT_ERROR_INCOMPATIBLEMEMORY; } /* Suspend SysTick */ HAL_SuspendTick(); /* Disable I-Cache---------------------------------------------------------*/ SCB_DisableICache(); /* Disable D-Cache---------------------------------------------------------*/ SCB_DisableDCache(); /* Initialize user application's Stack Pointer & Jump to user application */ primask_bit = __get_PRIMASK(); __disable_irq(); /* Apply offsets for image location and vector table offset */ Application_vector += EXTMEM_XIP_IMAGE_OFFSET + EXTMEM_HEADER_OFFSET; SCB->VTOR = (uint32_t)Application_vector; JumpToApp = (pFunction) (*(__IO uint32_t *)(Application_vector + 4u)); #if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) || \ (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) /* on ARM v8m, set MSPLIM before setting MSP to avoid unwanted stack overflow faults */ __set_MSPLIM(0x00000000); #endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */ __set_MSP(*(__IO uint32_t*) Application_vector); /* Re-enable the interrupts */ __set_PRIMASK(primask_bit); JumpToApp(); return BOOT_OK; }
app 程序使用的linkfile 的链接地址为xspi1和xspi2 对应的空间,程序需要加载在nor flash上运行。
/*###ICF### Section handled by ICF editor, don't touch! ****/ /*-Editor annotation file-*/ /* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */ /*-Specials-*/ define symbol __ICFEDIT_intvec_start__ = 0x70000000; /*-Memory Regions-*/ define symbol __ICFEDIT_region_ROM_start__ = 0x70000000; define symbol __ICFEDIT_region_ROM_end__ = 0x77FFFFFF; define symbol NONCACHEABLEBUFFER_size = 0x400; define symbol __ICFEDIT_region_RAM_start__ = 0x24000000; define symbol __ICFEDIT_region_RAM_end__ = 0x24071FFF - NONCACHEABLEBUFFER_size; define symbol NONCACHEABLEBUFFER_start = __ICFEDIT_region_RAM_end__ + 1; define symbol NONCACHEABLEBUFFER_end = __ICFEDIT_region_RAM_end__ + NONCACHEABLEBUFFER_size; /*-Sizes-*/ define symbol __ICFEDIT_size_cstack__ = 0x400; define symbol __ICFEDIT_size_heap__ = 0x200; /**** End of ICF editor section. ###ICF###*/ define symbol __region_ITCM_start__ = 0x00000000; define symbol __region_ITCM_end__ = 0x0000FFFF; define symbol __region_DTCM_start__ = 0x20000000; define symbol __region_DTCM_end__ = 0x2000FFFF; define symbol __region_SRAMAHB_start__ = 0x30000000; define symbol __region_SRAMAHB_end__ = 0x30007FFF; define symbol __region_BKPSRAM_start__ = 0x38800000; define symbol __region_BKPSRAM_end__ = 0x38800FFF; define symbol __region_EXTRAM_start__ = 0x90000000; define symbol __region_EXTRAM_end__ = 0x91FFFFFF; export symbol NONCACHEABLEBUFFER_start; export symbol NONCACHEABLEBUFFER_size; export symbol __ICFEDIT_region_ROM_start__; export symbol __ICFEDIT_region_ROM_end__; export symbol __region_EXTRAM_start__; export symbol __region_EXTRAM_end__; define memory mem with size = 4G; define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__]; define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__]; define region EXTRAM_region = mem:[from __region_EXTRAM_start__ to __region_EXTRAM_end__]; define region NONCACHEABLE_region = mem:[from NONCACHEABLEBUFFER_start to NONCACHEABLEBUFFER_end]; define region ITCM_region = mem:[from __region_ITCM_start__ to __region_ITCM_end__]; define region DTCM_region = mem:[from __region_DTCM_start__ to __region_DTCM_end__]; define region SRAMAHB_region = mem:[from __region_SRAMAHB_start__ to __region_SRAMAHB_end__]; define region BKPSRAM_region = mem:[from __region_BKPSRAM_start__ to __region_BKPSRAM_end__]; define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { }; define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { }; initialize by copy { readwrite }; do not initialize { section .noinit, section noncacheable_buffer }; place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; place in ROM_region { readonly }; place in RAM_region { readwrite }; place in NONCACHEABLE_region { section noncacheable_buffer }; place in DTCM_region { block CSTACK, block HEAP };
IAR app 工程在debug时会同时将boot 工程的image 下载到user flash ,从而在上电时先运行user flash 的boot程序准备xip运行环境然后跳转到xip 运行。
IAR debug 运行appI 程序发现程序运行的地址为0x70000000 的地址空间,外挂的RAM 也可以正常访问。
从debug log 窗口也可以看出,IAR 在现在app 程序后会把boot 程序下载到user flash
后续在XIP工程基础上继续验证其他功能。