2024-11-25 04:30 AM - edited 2024-11-26 04:33 AM
The primary objective of this article is to provide a comprehensive step-by-step guide on creating an external flash loader to interface with external memories. This demonstration uses the STM32H7S3L8H6 microcontroller and XSPI to interface with the external memory, but it can be easily tailored for other microcontrollers.
To create an external flash loader, follow the steps outlined below. The required tools for this process include STM32CubeProgrammer, STM32CubeMX, and STM32CubeIDE.
If the microcontroller operates under 2.5 V, High-Speed Low Voltage (HSLV) must be enabled. In this case, according to the datasheet, the flash memory operates at 1.8 V. Consequently, HSLV on I/O port 2 needs to be enabled, where the memory is interfaced. You can determine which port your memory uses by consulting the alternate functions section in your microcontroller's DS14360 Rev 2.
We developed this article using the Nucleo-H7S3L8, but to make it more generic started from the MCU itself. That’s why you should:
To enable the created external flash loader to operate on the microcontroller, you need to select the proper power supply. In this case, the Nucleo-H7S3L8 features a Low-dropout regulator (LDO) and doesn’t include a switched-mode power supply (SMPS) according to UM3276 - Rev 1. To configure it, you need to perform the steps below:
In this scenario, the external flash memory is interfaced with XSPI1 in [Octo SPI] mode on the second port, with the chip select pin set to [NCS1]. Refer to board schematics MB1737-B02.
Next, you need to select the memory manufacturer (memory type), memory size, chip select high time cycle, and delay hold quarter cycle from the memory datasheet and application note AN5050. In this case:
Memory type: Macronix
Memory size: 256 Mbit/s (maximum size is 256 Mbit/s according to the memory datasheet)
Chip select high time: 2 (found in the memory datasheet under "AC CHARACTERISTICS”)
Delay hold quarter cycle: Enabled (as the memory is used in DTR mode, per application note AN5050)
To prevent speculative reads from undesired external memory regions, the memory protection unit (MPU) needs to be enabled to allow access only from specific regions. In this case there are two MPU instances, one for the boot subproject, and the other for the application subproject. To configure the one relative to boot context, (CORTEX_M7_BOOT) the steps are as follows:
For further information about MPU attributes, refer to AN4838
As for the application context (CORTEX_M7_APPLI):
As mentioned above (section 2.1) HSLV needs to be used in this case. To activate it, you need to configure option bytes and SBS (secure boot system). In this case, I/O HSLV FOR XSPIM2 needs to be enabled.
External memory manager is a middleware that generates necessary drivers to interface with external memories and generates boot code. It needs to be configured as follows:
External memory loader is a middleware that makes generating custom external memory loaders more user-friendly. That’s why it needs to be configured.
*Refer to memory datasheet under “ERASE AND PROGRAMMING PERFORMANCE”
The operating frequency is up to 600 MHz for the STM32H7Rx/Sx family. For maximum performance, set the CPUCLK value to the mentioned frequency.
Before generating your custom external flash loader, you need to access the [Project Manager] and configure the following settings:
Using STM32CubeIDE:
After generating your project using STM32CubeMX, the next step is selecting the external memory loader subproject and building it. This step will generate your external flash loader.
Note: If your STM32CubePogrammer is installed in a privileged location, open STM32CubeIDE as administrator to authorize the copying of your generated folder under STM32CubeProgrammer path.
Note: If a "cp: can’t create 'C:\XX\XX' warning: File copy failed" error occurs, make sure that STM32CubeProgrammer is installed in an unprivileged directory, and that your project path doesn’t contain spaces.
A message should appear in the console, indicating that your loader has been successfully built. This occurs because the "Selected Toolchain" option is chosen in section 2.8.
If you’re using STM32CubeIDE, your loader should be added automatically. You can verify this in the [Debug Configurations] under [Debugger].
Then, an extra step should be done before debugging, which is adding the boot context image to be built and executed. Proceed with the following steps in debug configurations under startup:
Using STM32CubeProgrammer:
Under [External Loaders] select your loader to interact with your external memory by its address.
Hey Mohamed_Aziz_REZGUI,
This is the most detailed and correct description of this topic. I got my project running by following the AN5050 application note, but that document is very general and flawed. However, your post helped me to optimize my project.
Do you know a description of using an APS256XXN-OBRx with an STM32H7RS series MCU?
I made a board for my project, which is based on an STM32H7R3L8H6H MCU, an APS256XXN-OBR-BG 256MBit PSRAM on HEXASPI, an MX25UW25645GXDI00-T 256MBit flash on OCTOSPI, and an EMMC08G-MV28-01J10 SDMMC, among other peripherals.
Here are some pictures of it:
Thanks to your post, the MX25UW25645GXDI00-T on the OCTOSPI is now optimized. The APS256XXN-OBR-BG on the HEXASPI is working, but it's probably not optimal. The example code for the STM32H7S78-DK that I found in the firmware package "STM32Cube_FW_H7RS_V1.1.0" contains several bugs and flaws.
I searched the internet for countless hours to find something useful, but unfortunately, I wasn't able to find anything.
Here is one example of what I think can't be correct:
static void Configure_APMemory(void)
{
/* MR0 register for read and write */
uint8_t regW_MR0[2]={0x24,0x8D}; /* To configure AP memory Latency Type and drive Strength */
uint8_t regR_MR0[2]={0};
/* MR8 register for read and write */
uint8_t regW_MR8[2]={0x4B,0x08}; /* To configure AP memory Burst Type */
uint8_t regR_MR8[2]={0};
/*Read Latency */
uint8_t latency=6;
/* Configure Read Latency and drive Strength */
if (APS256_WriteReg(&hxspi1, MR0, regW_MR0) != HAL_OK)
{
Error_Handler();
}
/* Check MR0 configuration */
if (APS256_ReadReg(&hxspi1, MR0, regR_MR0, latency ) != HAL_OK)
{
Error_Handler();
}
/* Check MR0 configuration */
if (regR_MR0 [0] != regW_MR0 [0])
{
Error_Handler() ;
}
/* Configure Burst Length */
if (APS256_WriteReg(&hxspi1, MR8, regW_MR8) != HAL_OK)
{
Error_Handler();
}
/* Check MR8 configuration */
if (APS256_ReadReg(&hxspi1, MR8, regR_MR8, 6) != HAL_OK)
{
Error_Handler();
}
if (regR_MR8[0] != regW_MR8[0])
{
Error_Handler() ;
}
}
This routine is used to configure and initialize the PSRAM chip.
/* MR0 register for read and write */
uint8_t regW_MR0[2]={0x24,0x8D}; /* To configure AP memory Latency Type and drive Strength */
uint8_t regR_MR0[2]={0};
/* MR8 register for read and write */
uint8_t regW_MR8[2]={0x4B,0x08}; /* To configure AP memory Burst Type */
uint8_t regR_MR8[2]={0};
The data sent to the MR0 and MR8 registers don't match the description in the data sheet of the APS256XXN-OBR-BG.
APM_PSRAM_OPI_Xccela (APS256XXN-OBRx v1.0 PKG)
Please let me know if you know of any good documentation about this.
thank you