2026-03-31 2:48 PM - last edited on 2026-04-08 1:58 AM by mƎALLEm
Hi all,
I am trying to bring up a new custom board with an STM32H7R7 and external flash as well as external PSRAM. While I managed to get the external flash working after some try and error, the external PSRAM seems to be more stubborn.
The MX_XSPI1_Init() currently looks like this:
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_MACRONIX_RAM;
hxspi1.Init.MemorySize = HAL_XSPI_SIZE_64MB;
hxspi1.Init.ChipSelectHighTimeCycle = 3;
hxspi1.Init.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE;
hxspi1.Init.ClockMode = HAL_XSPI_CLOCK_MODE_0;
hxspi1.Init.WrapSize = HAL_XSPI_WRAP_32_BYTES;
hxspi1.Init.ClockPrescaler = 0;
hxspi1.Init.SampleShifting = HAL_XSPI_SAMPLE_SHIFT_NONE;
hxspi1.Init.ChipSelectBoundary = HAL_XSPI_BONDARYOF_8KB;
hxspi1.Init.MaxTran = 0;
hxspi1.Init.Refresh = 262;
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;
sXspiManagerCfg.Req2AckTime = 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 */
}
And my MX_EXTMEM_MANAGER_Init() like this:
void MX_EXTMEM_MANAGER_Init(void)
{
/* USER CODE BEGIN MX_EXTMEM_Init_PreTreatment */
/* USER CODE END MX_EXTMEM_Init_PreTreatment */
HAL_RCCEx_EnableClockProtection(RCC_CLOCKPROTECT_XSPI);
/* Initialization of the memory parameters */
memset(extmem_list_config, 0x0, sizeof(extmem_list_config));
/* EXTMEMORY_1 */
extmem_list_config[0].MemType = EXTMEM_NOR_SFDP;
extmem_list_config[0].Handle = (void*)&hxspi2;
extmem_list_config[0].ConfigType = EXTMEM_LINK_CONFIG_8LINES;
/* EXTMEMORY_2 */
extmem_list_config[1].MemType = EXTMEM_PSRAM;
extmem_list_config[1].Handle = (void*)&hxspi1;
extmem_list_config[1].ConfigType = EXTMEM_LINK_CONFIG_8LINES;
extmem_list_config[1].PsramObject.psram_public.MemorySize = HAL_XSPI_SIZE_64MB;
extmem_list_config[1].PsramObject.psram_public.FreqMax = 133 * 1000000u;
extmem_list_config[1].PsramObject.psram_public.NumberOfConfig = 1u;
/* Config */
extmem_list_config[1].PsramObject.psram_public.config[0].WriteMask = 0x40u;
extmem_list_config[1].PsramObject.psram_public.config[0].WriteValue = 0x28u;
extmem_list_config[1].PsramObject.psram_public.config[0].REGAddress = 0x00u;
/* Memory command configuration */
extmem_list_config[1].PsramObject.psram_public.ReadREG = 0xC0u;
extmem_list_config[1].PsramObject.psram_public.WriteREG = 0x40u;
extmem_list_config[1].PsramObject.psram_public.ReadREGSize = 2u;
extmem_list_config[1].PsramObject.psram_public.REG_DummyCycle = 4u;
extmem_list_config[1].PsramObject.psram_public.Write_command = 0x20u;
extmem_list_config[1].PsramObject.psram_public.Write_DummyCycle = 8u;//4u;
extmem_list_config[1].PsramObject.psram_public.Read_command = 0xA0u;
extmem_list_config[1].PsramObject.psram_public.WrapRead_command = 0x80u;
extmem_list_config[1].PsramObject.psram_public.Read_DummyCycle = 8u;
EXTMEM_Init(EXTMEMORY_1, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2));
EXTMEM_Init(EXTMEMORY_2, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1));
/* USER CODE BEGIN MX_EXTMEM_Init_PostTreatment */
/* USER CODE END MX_EXTMEM_Init_PostTreatment */
}
But in both cases I am not sure if the parameter are set correctly. I tried copying my STM32H7S78-DK board where it seemed reasonable and adjust the parameter I could find in the datasheet.
In order to test the correct function of the PSRAM I added some user code just after the memory is mapped in the BOOT_Application() function created by CubeMX:
BOOTStatus_TypeDef BOOT_Application(void)
{
BOOTStatus_TypeDef retr;
/* Mount the memory */
retr = MapMemory();
uint32_t ram_data0[8] = {0};
uint32_t ram_data1[8] = {0};
uint32_t ram_data2[8] = {0};
uint32_t ram_data3[8] = {0};
uint32_t *ext_ram = (uint32_t *)0x90000000;
// Ram test 0 - reading what is originally in the section
for (int32_t i = 0; i < 8; i++) {
ram_data0[i] = ext_ram[i];
}
// Ram test 1 - setting the section to zero and reading back
memset(ext_ram, 0x0, 8*sizeof(uint32_t));
for (int32_t i = 0; i < 8; i++) {
ram_data1[i] = ext_ram[i];
}
// Ram test 2 - setting the section to 0xAA and reading back
memset(ext_ram, 0xAA, 8*sizeof(uint32_t));
for (int32_t i = 0; i < 8; i++) {
ram_data2[i] = ext_ram[i];
}
// Ram test 3 - settinig the section to ascending numbers and reading back
uint8_t *ext_ram_bytes = (uint8_t*)0x90000000;
for (int i = 0; i < 32; i++) {
ext_ram_bytes[i] = (uint8_t)(i + 1);
}
// Then read back as uint32_t:
for (int i = 0; i < 8; i++) {
ram_data3[i] = ext_ram[i];
}
if (BOOT_OK == retr)
{
/* Jump on the application */
retr = JumpToApplication();
}
return retr;
}
Where the four test sections shall help to identify if the PSRAM is correctly mounted and working. The tests modify and then read 8 times 32bit sections. While I can see in my debugging sessions that the code is somewhat working I can never get it right so that all 8 x 32 bits are correctly written and read back. There is always a couple bits that are wrongly written or not written at all or perhaps wrongly read back ( I can currently not distinguish between write errors and read errors). Since I manage to write/read some of the bits correct I assume the hardware is correct and I am just missing the right settings in my boot software.
Can someone help me or point me further in the right direction as to how to find the right parameter in the two init functions above?
Any help is appreciated!
Regards
Sascha
2026-04-02 2:57 AM
Hello @SaschaK
Did you check if you are setting the right dummy cycle?
2026-04-02 5:15 AM
Hi @SaschaK
PSRAM driver inside External Memory Manager Middleware that is present in STM32H7RS Cube package (latest ExtMemManager version is V1.5.0 I think) is not able to send Configuration Register Read or Write commands that are 16 bit long, as described in ISSI datasheet you mentioned.
So I think that the Configuration Register update step you have in your configuration code will not work.
extmem_list_config[1].PsramObject.psram_public.NumberOfConfig = 1u;
/* Config */
extmem_list_config[1].PsramObject.psram_public.config[0].WriteMask = 0x40u;
extmem_list_config[1].PsramObject.psram_public.config[0].WriteValue = 0x28u;
extmem_list_config[1].PsramObject.psram_public.config[0].REGAddress = 0x00u;What was the purpose of this configuration step in your case ? (When applying WriteMask on WriteValue, you will only clear bit 6 in CR).
You might try to use ISSI memory using its default configuration, and adjust dummycycles nb accordingly in your configuration (Write_DummyCycle and Read_DummyCycle).
Other workaround is to build and send manually (i.e. outside of EMM APIs) a Config Register update with your expected value, after execution of EXTMEM_Init() and before entering in Memory Mapped mode.
PS : Support of 16 bit commands for Configuration registers updates has been added in EMM PSRAM driver and will be delivered in next EMM version in June'26 (V1.6.0).
2026-04-02 3:54 PM - edited 2026-04-02 4:10 PM
Hello @Saket_Om
I am not even sure what the right dummy cycles would be to be honest. Based on this diagram from the datasheet I would have assumed 3 dummy cycles
But changing the MX_EXTMEM_MANAGER_Init() to 3 dummy cycles didn't solve the issue. I brute force stepped through dummy cycles (left read and write at the same number of cycles) and found 7 to be the magic number. less than seven and more than seven results in several bits or bytes not being written or read correctly in my test sequences. How to derive the seven from the ISSI datasheet is however a mystery to me.
Thanks for the hint about the config register. The numbers currently written there are probably a left over from late night experiments. I did realize now that changing them does pretty much nothing. However, being conscious that the next EMM update will make them work, I would like to set them correct.
Based on the table below from the datasheet do I assume correct that the REGAddress would be 0x04?
Regards
Sascha
2026-04-02 5:44 PM
Hello
To add some context to your solution and generalize it a bit, we had similar difficulty solving this exact problem for the IS66WVO32M8DALL (the 256Mbit version of your device). In short, on this particular memory it is necessary to mismatch the memory's configured latency count and the MCU dummy cycles. The MCU setting must always be 1 less than what is configured in the memory's configuration register. See our findings summarized below:
At 1.8V, your default latency count (LC) (applied to the configuration register CR[7:4] of the IS66), is 8. This is shown in Table 6.5:
The reason that 7 dummy cycles set on the MCU side works in your case is indeed difficult to determine from the datasheet, but there is a key piece:
"4. Read access (LC) starts once RA [7:0] is captured (falling edge of 2nd command/address clock)"
For this device, the latency count begins before the address has been fully transmitted. In particular, one latency cycle overlaps with the address transfer (marked by the vertical red lines added to the diagram):
This isn't the case with other devices, and as a result the MCU dummy cycle setting must be one less than the setting applied to the IS66 configuration register to compensate. I'd be willing to bet that for your IS66 configuration, you're currently using the default 8 dummy cycles, and 7 dummy cycles works because it is one less than this setting.
Additionally and for what it's worth, it's very likely that other configurations will work in the same way. E.g. 7LC on the IS66 would work with 6 dummy cycles on the MCU side, 6 with 5, and so on, barring any other issues that limit you from using certain LC settings.
The same would go for fixed latency. Using the default 8LC and configuring Initial Access Latency as fixed (IS66 CR[3] = 1), the effective LC setting becomes 2LC (16 in this case) for memory read, memory write, and register read (per table 6.6 in the datasheet). Therefore a dummy cycle setting of 15 on the MCU side would produce a valid configuration in that case.
Hope this sheds more light on the issue and provides a bit more flexibility in your design.
Cheers!
2026-04-02 11:22 PM
Hi @SaschaK
I confirm @wwoods93 view on value of nb of dummy cycles to be used.
On STM32 side, value refers to nb of dummy cycles counted after end of Address, while it is started counting one cycle earlier on ISSI memory side. so in you configure the memory to use 7 dummy cycles, you should set 6 in STM32 EMM config.
In order to update Configuration register, you need to use Address = 0x00040000 with 16 bits INS 0x4000 for Write and 0xC000 for Read, if I'm right.
Regards
2026-04-06 1:31 PM
@wwoods93 thanks a lot for that explanation! This is very helpful in understanding why things work the way they work.
@Guenael Cadier thanks for the clarification. Sorry for the beginners question, but how would I go about to
if I would want to use anything but the default configuration on my RAM? Currently I am only utilizing the CubeMX generated code, which can't handle 16 bit commands as you mentioned. Can you give me a snippet of an example code with which I could configure my RAM?
2026-04-08 12:58 AM - edited 2026-04-08 12:59 AM
Hello @SaschaK
Upon request from @Guenael Cadier , here is a code snippet for the STM32H7R/S using an ISSI memory that reads memory registers (ID and configuration registers). CR content is expected to be 0xF022 at 3V and 0xF052 at 1.8V. Then code snippet writes to the configuration registers (to configure the memory latency, you only need to modify bits CR[7:4] according to Table 6.3 – Latency Counter in the ISSI memory datasheet), and then configures the external memory in memory-mapped mode to perform data write and read-back operations. In my setup, the CR register value is 0xF042, which corresponds to a latency value of 7; to achieve correct write and read behavior in memory-mapped mode, I configured the Dummy 'latency' to 6 on STM32 XSPI peripheral side.
Br,