2025-07-02 1:17 AM
I'm working with an STM32H7 board and trying to interface with some external RAM using OCTOSPI over the HyperBus protocol. I'm attempting to run the RAM in memory-mapped mode and have mostly followed the STM32 application note provided by STM.
Most of the code was adapted directly from the application note. During the delay calibration step, the code gets stuck when trying to access the external memory. Specifically, the fault happens here:
*mem_addr = Cal_buffer[index]; // IT HAPPENS HERE
This results in the program entering MemManage_Handler(void), and execution halts. I think i might have some misconfigurations.
I had to make a small modification to the DelayBlock_Calibration() function. The original line:
HAL_RCCEx_OCTOSPIDelayConfig(delay, 0);
was replaced with:
__HAL_RCC_OSPI_CONFIG(delay);
because the original function didn't compile for me. That said, the code never reaches this point, so it’s likely not the root cause, but I mention it in case it's relevant.
As for my clock setup:
The OCTspi is running at 120Mhz, but I have a clock prescaler of 2, giving me 60 MHz to start.
How do I debug or fix this? Could there be something wrong with my configuration that causes the memory access to fault?
Does my substitution of HAL_RCCEx_OCTOSPIDelayConfig with __HAL_RCC_OSPI_CONFIG make sense? What's the correct function to use in this context?
Thanks in advance.
macros
main
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* 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_USART3_UART_Init();
MX_UART8_Init();
MX_RTC_Init();
MX_OCTOSPI1_Init();
/* USER CODE BEGIN 2 */
//app_main();
MAXTRAN_Configuration();
EnableMemMapped();
DelayBlock_Calibration();
uint32_t *hyperbus_ptr = (uint32_t *)0x90000000; // QUADSPI_BK1_R_BASE
uint32_t test_value = 0xA5A5A5A5;
uint32_t read_value = 0;
*hyperbus_ptr = test_value; // Write to the memory THIS LINE
read_value = *hyperbus_ptr; // Read it back
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
if (read_value == test_value){
HAL_GPIO_TogglePin(RGB_LED0_GPIO_Port, RGB_LED0_Pin);
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
memory-related code
static void MX_OCTOSPI1_Init(void)
{
/* USER CODE BEGIN OCTOSPI1_Init 0 */
/* USER CODE END OCTOSPI1_Init 0 */
OSPIM_CfgTypeDef sOspiManagerCfg = {0};
OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};
/* USER CODE BEGIN OCTOSPI1_Init 1 */
/* USER CODE END OCTOSPI1_Init 1 */
/* OCTOSPI1 parameter configuration*/
hospi1.Instance = OCTOSPI1;
hospi1.Init.FifoThreshold = 1;
hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_HYPERBUS;
hospi1.Init.DeviceSize = 26;
hospi1.Init.ChipSelectHighTime = 1;
hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
hospi1.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;
hospi1.Init.ClockPrescaler = 2;
hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE;
hospi1.Init.ChipSelectBoundary = 0;
hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED;
hospi1.Init.MaxTran = 0;
hospi1.Init.Refresh = 241;
if (HAL_OSPI_Init(&hospi1) != HAL_OK)
{
Error_Handler();
}
sOspiManagerCfg.ClkPort = 1;
sOspiManagerCfg.DQSPort = 1;
sOspiManagerCfg.NCSPort = 1;
sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW;
sOspiManagerCfg.IOHighPort = HAL_OSPIM_IOPORT_1_HIGH;
if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
sHyperBusCfg.RWRecoveryTime = 7;
sHyperBusCfg.AccessTime = 7;
sHyperBusCfg.WriteZeroLatency = HAL_OSPI_LATENCY_ON_WRITE;
sHyperBusCfg.LatencyMode = HAL_OSPI_FIXED_LATENCY;
if (HAL_OSPI_HyperbusCfg(&hospi1, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN OCTOSPI1_Init 2 */
/* USER CODE END OCTOSPI1_Init 2 */
}
void EnableMemMapped(void)
{
OSPI_HyperbusCmdTypeDef sCommand;
OSPI_MemoryMappedTypeDef sMemMappedCfg;
sCommand.AddressSpace = HAL_OSPI_MEMORY_ADDRESS_SPACE; //0x90000000;
sCommand.Address = 0;
sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
sCommand.NbData = 1;
sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;
if (HAL_OSPI_HyperbusCmd(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK)
{
Error_Handler();
}
/*Disable timeout counter for memory mapped mode*/
sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
sMemMappedCfg.TimeOutPeriod = 0x1;
/*Enable memory mapped mode*/
if (HAL_OSPI_MemoryMapped(&hospi1, &sMemMappedCfg) != HAL_OK)
{
Error_Handler();
}
}
void DelayBlock_Calibration()
{
/*buffer used for calibration*/
uint8_t Cal_buffer[] = " ****Delay Block Calibration Buffer**** ****Delay Block Calibration Buffer**** ****Delay Block Calibration Buffer**** ****Delay Block Calibration Buffer**** ****Delay Block Calibration Buffer**** ****Delay Block Calibration Buffer**** ";
uint16_t index;
__IO uint8_t *mem_addr;
uint8_t test_failed;
uint8_t delay = 0x0;
uint8_t Min_found = 0;
uint8_t Max_found = 0;
uint8_t Min_Window = 0x0;
uint8_t Max_Window = 0xF;
uint8_t Mid_window = 0;
uint8_t calibration_ongoing = 1;
/* Write the Cal_buffer to the memory*/
mem_addr = (__IO uint8_t *)(OCTOSPI1_BASE);
for (index = 0; index < DLYB_BUFFERSIZE; index++)
{
*mem_addr = Cal_buffer[index]; // IT HAPPENS HERE
mem_addr++;
}
while (calibration_ongoing)
{
/* update the Delayblock calibration */
__HAL_RCC_OSPI_CONFIG(delay);
test_failed = 0;
mem_addr = (__IO uint8_t *)(OCTOSPI1_BASE);
for (index = 0; index < DLYB_BUFFERSIZE; index++)
{
/* Read the Cal_buffer from the memory*/
if (*mem_addr != Cal_buffer[index])
{
/*incorrect data read*/
test_failed = 1;
}
mem_addr++;
}
if (Min_found!=1)
{
if (test_failed == 1)
{
if (delay < 15)
{
delay++;
}
else
{
/* If delay set to maximum and error still detected: can't use external
Memory*/
Error_Handler();
}
}
else
{
Min_Window = delay;
Min_found=1;
delay = 0xF;
}
}
/* search for the Max window */
else if (Max_found!=1)
{
if (test_failed == 1)
{
if (delay > 0)
{
delay--;
}
else
{
/* If delay set to minimum and error still detected: can't use external
Memory */
Error_Handler();
}
}
else
{
Max_Window = delay;
Max_found=1;
}
}
/* min and max delay window found , configure the delay block with the
middle window value and exit calibration */
else
{
Mid_window = (Max_Window+Min_Window)/2;
__HAL_RCC_OSPI_CONFIG(Min_Window);
/* Exit calibration */
calibration_ongoing = 0;
}
}
}
void MAXTRAN_Configuration(void)
{
/*Maximum transaction configured for 4us*/
MODIFY_REG(hospi1.Instance->DCR3, OCTOSPI_DCR3_MAXTRAN, 0x000000F1);
}
2025-07-03 7:02 AM
Hello @_summer_intern_,
Could you please precisely which STM32H7 device are you using?
The delay block in STM32L4+ is different from the one in STM32H7.
In STM32L4+, the delay block is a feature provided to the OCTOSPI interface, where unitary delays can be configured using the OCTOSPIx_DLY field in the RCC_DLYCFGR register.
In STM32H7, the delay block is an independent peripheral that can be configured for the Octo-SPI interface.
For more information about the delay block peripheral, I recommend you to refer to RM0468 section 27 Delay block (DLYB).
Also, I advise you to refer to OSPI_HyperRAM_MemoryMapped and get inspired to create your project.
This example describes how to write and read data in memory-mapped mode in the OCTOSPI HyperRAM memory and compare the result with an intensive access. Note that, this example runs on STM32H735G discovery device.
I hope this help you.
Thank you.
Kaouthar
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2025-07-04 3:29 AM
Hello Kaothar,
I'm using the STM32H723ZG. Although I haven't reviewed this specific repository before, my code is largely based on the same example. I’ve made some modifications. Specifically, I removed the calibration routine, as it wasn't used in the GitHub repository you linked, and I don't require it at the moment. However, the code still gets stuck in the MemManage_Handler.
mem_addr = (__IO uint32_t *)(OCTOSPI2_BASE + address);
I mentioned the delay block feature because calibration is necessary when using higher frequencies, as noted in AN5050 on page 36. I believe the issue may be related to the configuration of hospi1 or the setup of my OSPI_HyperbusCmdTypeDef.
What do you think? Any ideas on how I can debug this issue? The code below returns HAL_OK each time.
void EnableMemMapped(void)
{
OSPI_HyperbusCmdTypeDef sCommand;
OSPI_MemoryMappedTypeDef sMemMappedCfg;
sCommand.AddressSpace = HAL_OSPI_MEMORY_ADDRESS_SPACE; //0x90000000?;
sCommand.Address = 0;
sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
sCommand.NbData = 1;
sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;
if (HAL_OSPI_HyperbusCmd(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK)
{
Error_Handler();
}
/*Disable timeout counter for memory mapped mode*/
sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
sMemMappedCfg.TimeOutPeriod = 0x1;
/*Enable memory mapped mode*/
if (HAL_OSPI_MemoryMapped(&hospi1, &sMemMappedCfg) != HAL_OK)
{
Error_Handler();
}
}
2025-07-04 3:46 AM
Hello @_summer_intern_ ,
I think that you connecting the external RAM to the OCTOSPI1 interface.
In this case, this code line
mem_addr = (__IO uint32_t *)(OCTOSPI2_BASE + address);
should be
mem_addr = (__IO uint32_t *)(OCTOSPI1_BASE + address);
To achieve a high OCTOSPI frequency, it is recommended to use the delay block (DLYB), if it is available in STM32 device when in DTR mode.
Please look at this post may help you Solved: Re: Help needed for tuning 64Mb HYPERRAM (S27KL064... - Page 2 - STMicroelectronics Community
Thank you.
Kaouthar
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2025-07-04 6:30 AM
I am sorry I misspelled the line. Here is my current attempt
volatile uint32_t *hyperbus_ptr = (volatile uint32_t *)0x90000000;
uint32_t test_value = 0xA5A5A5A5;
uint32_t read_value;
*hyperbus_ptr = test_value; // Write to the memory THIS LINE GIVES ME AN 'ERROR'
read_value = *hyperbus_ptr; // Read it back
When in debug mode, I get placed here
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
/* USER CODE END MemoryManagement_IRQn 0 */
while (1)<----- stuck here
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
/* USER CODE END W1_MemoryManagement_IRQn 0 */
}
}
2025-07-09 3:49 AM
Hello @_summer_intern_
MemManage faults can be caused by violations of the access rules defined by the MPU configuration.
Please verify your MPU (Memory Protection Unit) configuration to ensure that the external RAM address region is correctly set up with appropriate access permissions.
Could you temporarily disable the MPU entirely and then test the memory access again?
Br,