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.