2024-03-06 09:46 AM - edited 2024-03-18 07:16 AM
I need help with configuring HYPERRAM for my STM32 on a custom PCB. The HYPERRAM pins are about 32mm away from the MCU pins.
The HYPERRAM works, but I get strange flickering on the display even with static data in RAM which I think comes memory errors of the external RAM. My memory passes some basic memory tests, but I think things go wrong when the LTDC peripheral reads from the memory at high speeds. I could be wrong about this, but I need to tune the configuration of the memory anyway. I want to exclude this possibility.
There are so many settings and contradictory advice that I'm overwhelmed.
Turning on D-cache for the HYPERRAM doesn't help.
If I calibrate the delay block the memory tests fail. If I don't calibrate it, it doesn't fail. If I disable it it also fails.
I've implemented calibration according to the following forum post: https://community.st.com/t5/stm32-mcus-products/stm32h7-octospi-mode-hyperbus-hyperram-access-and-delay-block/m-p/143244/highlight/true#M27659
I can read the registers from the memory chip (ID0, ID1, CR0, CR1) and the values match the default values from the datasheet. But I'm not able to successfully write to a register. I want to set the output driver impedance to 46 Ohmin CR0 (instead of the default 34 Ohm) since our traces have an impedance of 50 Ohm. I don't know if this is going to improve the situation, but it would be good to know how I can set this value.
RAM is connected to OSPI2. Pins are configured with highest drive strength since I want to run the memory at 200MHz eventually. I'm now running it lower to make testing with a logic analyzer easier.
For now I clock the OSPI2 peripheral at 332MHz (333 is the max frequency for peripheral).
This allows me to use a prescaler of 2 to get 166MHz or a prescaler of 4 to get 83MHz. These are the frequencies I want to get working first (with delay block calibration). You need at least a prescaler of 2 (register value of 1) to enable DHQC so I won't be able to run the OSPI at 200MHz with DHQC.
Here is my configuration:
This configuration works, but not if I calibrate the delay block or try to write to CR0 (these blocks have been defined off).
/* OCTOSPI2 init function */
void MX_OCTOSPI2_Init(void)
{
/* USER CODE BEGIN OCTOSPI2_Init 0 */
HAL_Delay(1); // 1-2ms delay after power on
HAL_GPIO_WritePin(MCU_HYPERBUS_NRESET_GPIO_Port, MCU_HYPERBUS_NRESET_Pin, 1); //de-assert reset
HAL_Delay(1); // 1-2ms delay after reset
/* USER CODE END OCTOSPI2_Init 0 */
OSPIM_CfgTypeDef sOspiManagerCfg = {0};
OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};
/* USER CODE BEGIN OCTOSPI2_Init 1 */
// calibrate delay block:
#if 0
//same config as below
hospi2.Instance = OCTOSPI2;
hospi2.Init.FifoThreshold = 1;
hospi2.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
hospi2.Init.MemoryType = HAL_OSPI_MEMTYPE_HYPERBUS;
hospi2.Init.DeviceSize = 23;
hospi2.Init.ChipSelectHighTime = 1;
hospi2.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
hospi2.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
hospi2.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;
hospi2.Init.ClockPrescaler = 4;
hospi2.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
hospi2.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE;
hospi2.Init.ChipSelectBoundary = 0;
hospi2.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED;
hospi2.Init.MaxTran = 0;
hospi2.Init.Refresh = 83;
// override clock setting:
hospi2.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_ENABLE;
hospi2.Init.Refresh = 0;
if (HAL_OSPI_Init(&hospi2) != HAL_OK)
{
Error_Handler() ;
}
if (DelayBlock_Enable(DLYB_OCTOSPI2) != HAL_OK)
{
Error_Handler();
}
HAL_OSPI_DeInit(&hospi2);
#endif
/* USER CODE END OCTOSPI2_Init 1 */
hospi2.Instance = OCTOSPI2;
hospi2.Init.FifoThreshold = 1;
hospi2.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
hospi2.Init.MemoryType = HAL_OSPI_MEMTYPE_HYPERBUS;
hospi2.Init.DeviceSize = 23;
hospi2.Init.ChipSelectHighTime = 1;
hospi2.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
hospi2.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
hospi2.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;
hospi2.Init.ClockPrescaler = 4;
hospi2.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
hospi2.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE;
hospi2.Init.ChipSelectBoundary = 0;
hospi2.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED;
hospi2.Init.MaxTran = 0;
hospi2.Init.Refresh = 83;
if (HAL_OSPI_Init(&hospi2) != HAL_OK)
{
Error_Handler();
}
sOspiManagerCfg.ClkPort = 2;
sOspiManagerCfg.DQSPort = 2;
sOspiManagerCfg.NCSPort = 2;
sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_2_LOW;
sOspiManagerCfg.IOHighPort = HAL_OSPIM_IOPORT_2_HIGH;
if (HAL_OSPIM_Config(&hospi2, &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(&hospi2, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN OCTOSPI2_Init 2 */
OSPI_HyperbusCmdTypeDef sCommand = {0};
OSPI_MemoryMappedTypeDef sMemMappedCfg = {0};
//
volatile HAL_StatusTypeDef status;
volatile uint16_t ID0=0;
volatile uint16_t ID1=0;
volatile uint16_t CR0=0;
volatile uint16_t CR1=0;
sCommand.AddressSpace = HAL_OSPI_REGISTER_ADDRESS_SPACE;
sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS; // HAL_OSPI_ADDRESS_32_BITS
sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;
#if 0
///write to CR0
volatile uint16_t CR0_new = 0x8f2f | (3<<12);
sCommand.Address = 0x800*2; //CR0
sCommand.NbData = 2;
if (HAL_OSPI_HyperbusCmd(&hospi2, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
status = HAL_OSPI_Transmit(&hospi2, (uint8_t*)&CR0_new, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
#endif
// read registers
sCommand.Address = 0*2; //ID0
sCommand.NbData = 2;
if (HAL_OSPI_HyperbusCmd(&hospi2, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
status = HAL_OSPI_Receive(&hospi2, (uint8_t*)&ID0, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
sCommand.Address = 1*2; //ID1
sCommand.NbData = 2;
if (HAL_OSPI_HyperbusCmd(&hospi2, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
status = HAL_OSPI_Receive(&hospi2, (uint8_t*)&ID1, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
sCommand.Address = 0x800*2; //CR0
sCommand.NbData = 2;
if (HAL_OSPI_HyperbusCmd(&hospi2, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
status = HAL_OSPI_Receive(&hospi2, (uint8_t*)&CR0, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
sCommand.Address = 0x801*2; //CR1
sCommand.NbData = 2;
if (HAL_OSPI_HyperbusCmd(&hospi2, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
status = HAL_OSPI_Receive(&hospi2, (uint8_t*)&CR1, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
sCommand.AddressSpace = HAL_OSPI_MEMORY_ADDRESS_SPACE;
sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;
sCommand.Address = 0;
sCommand.NbData = 1;
if (HAL_OSPI_HyperbusCmd(&hospi2, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
if (HAL_OSPI_MemoryMapped(&hospi2, &sMemMappedCfg) != HAL_OK)
{
Error_Handler();
}
//memory tests
externalRamValid = memoryTests16BitTestAll((void*)EXTERNAL_RAM_START_ADDRESS, EXTERNAL_RAM_SIZE_BYTES, hospi2.Init.DeviceSize-1, 9, &memoryTestResult);
if (!externalRamValid)
{
Error_Handler();
}
/* USER CODE END OCTOSPI2_Init 2 */
}
Solved! Go to Solution.
2024-03-18 07:12 AM - edited 2024-03-18 07:25 AM
(I still haven't got the LYB_OSPI_NOR_FastTuning/OSPI_PSRAM_MemoryMapped algorithm working. But I'll park that for now. If anyone knows how to get that working let me know.)
Here is the summary of what I needed to do to get everything to work for the s27kl0642dpbhi020 HyperRAM with the STM32H735IG with a 1024x600 display: