cancel
Showing results for 
Search instead for 
Did you mean: 

OSPI skipping bytes

BDoon.1
Associate II
We have been using an STM32 U585 with additional external FLASH connected to OSPI1 and external PSRAM connected to OSPI2.  This is not on a dev kit, it is our own hardware.  The PSRAM is memory-mapped and is using the HyperBus protocol.  We have noticed that every once in awhile the PSRAM will skip two bytes while reading from the memory.  As this is a memory mapped peripheral, we are reading it via memcpy, and we don't see HAL_OSPI_ErrorCallback or HAL_OSPI_AbortCpltCallback hit.   It appears to always skip 2 bytes after the first 8 bytes of the memcpy, and it still transfers the full number of bytes, walking 2 bytes past the end of the desired read.  Even stranger, if we immediately issue the same memcpy after detecting this issue, it works correctly on the second attempt.
 
I have walked through the errata and the datasheets and cannot find a rhyme or reason as to why we would be deterministically dropping bytes like this.  It is always the same 2 bytes from the same 20 byte memcpy.  It is always corrected on a second read, and there is no indication from the HAL or the hardware that anything has gone wrong.
 
Does anyone have any insight into what could cause something like that?  Any ideas on debugging efforts I could try to further test this, or OSPI settings that might be worth manipulating?
7 REPLIES 7
KDJEM.1
ST Employee

Hello @BDoon.1 ,

Could you please give more details about your OCTOSPI configuration?

Which PSRAM memory reference are you using?

I recommend you to refer to the memory datasheet and AN5050 precisely section "6.2.4 OCTOSPI configuration and parameter settings" and check the OCTOSPI configuration Sample shifting, Delay hold quarter cycle, Chip select boundary, Refresh rate ......

May be this example can help you OSPI_PSRAM_MemoryMapped.

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.

Unfortunately the code is not something I can share in full.  I've included our OSPI2 setup code where we memory map it.  We are running the memory at 80MHz, slower than its max 166MHz.  This issue is reproducible on multiple devices, so much so that two units running side by side will generally both experience the issue at the same time.  It happens at the same exact memory address every time too.  This is OSPI2, so the memory mapped addressing starts at 0x70000000.  We have a buffer reserved for data starting at 0x7020B930.  We're allocating chunks out of this buffer that have a 20 byte header on them, so our actual data starts at 0x7020B944.  So what happens is we read that 20 byte header, it fails its CRC, then we read it again immediately and it passes.  Dumping each of those reads reveals that the first one has skipped over the 9th and 10th byte of the transfer, but still transferred a full 20 bytes (so it read past the end of the range by 2 bytes as well) . When I dump the OSPI2 registers, I can see that the AR register (address register) always has 2144608  in it, which is address 0x7020B960.  This is in the data region of the allocation failing its header read.

 

 

 

  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_OSPI;
  PeriphClkInit.OspiClockSelection = RCC_OSPICLKSOURCE_SYSCLK;
  HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
  __HAL_RCC_OSPI2_CLK_ENABLE();

  OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};

  /* These settings chosen for HyperBus clock freq = 80MHz */
  /* This driver is to support HyperRAM only, meaning that several of these options are fixed */
  OSPI_HandleTypeDef hospi;
  memset(&hospi, 0, sizeof(OSPI_HandleTypeDef));
  self->hospi.Instance = OCTOSPI2;
  self->hospi.Init.FifoThreshold = 1;
  self->hospi.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
  self->hospi.Init.MemoryType = HAL_OSPI_MEMTYPE_HYPERBUS;
  self->hospi.Init.DeviceSize = 23;
  self->hospi.Init.ChipSelectHighTime = 1;
  self->hospi.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
  self->hospi.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
  self->hospi.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;

  // Find the closest prescaler for the desired clock frequency
  uint32_t sourceClockFrequency = HAL_RCC_GetHCLKFreq();
  self->hospi.Init.ClockPrescaler = 1;

  for (uint32_t prescaler = 1; prescaler < PRESCALER_MAX; prescaler++)
  {
     uint32_t ospiClockFrequency = sourceClockFrequency / prescaler;
     if (ospiClockFrequency <= self->config.ClockFrequency)
     {
        self->hospi.Init.ClockPrescaler = prescaler;
        break;
     }
  }

  self->hospi.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
  self->hospi.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE;
  self->hospi.Init.ChipSelectBoundary = 0;
  self->hospi.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED;
  self->hospi.Init.MaxTran = 0;
  self->hospi.Init.Refresh = 160; // set a tCSM of 4us, the worst case at temperature for any of our parts

  if (HAL_OSPI_Init(&hospi) != HAL_OK)
     return false;

  sHyperBusCfg.RWRecoveryTime = 6;
  sHyperBusCfg.AccessTime = 6;
  sHyperBusCfg.WriteZeroLatency = HAL_OSPI_LATENCY_ON_WRITE;
  sHyperBusCfg.LatencyMode = HAL_OSPI_FIXED_LATENCY;    // Can use variable if config reg 0 is modified

  if (HAL_OSPI_HyperbusCfg(&hospi, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
     return false;

  // Find the closest DLYB offset
  HAL_OSPI_DLYB_CfgTypeDef delayBlockConfig;
  if(HAL_OSPI_DLYB_GetClockPeriod(&hospi, &delayBlockConfig) == HAL_OK)
  {
     /* The ST example project divides this down. Essentially, we want the phase shift to
     * be just enough that we guarantee the data is valid but not so much as to reduce
     * Thold too much.  See section 7 of app note.*/
     self->delayBlockConfig.PhaseSel /= 4;
  }
  else
     return false;

  // Configure the delay block after having discovered the settings
  HAL_OSPI_DLYB_CfgTypeDef dlyb_cfg_test;
  dlyb_cfg_test = delayBlockConfig;

  HAL_OSPI_DLYB_SetConfig(&hospi, &dlyb_cfg_test);
  HAL_OSPI_DLYB_GetConfig(&hospi, &dlyb_cfg_test);
 
  if(delayBlockConfig.PhaseSel != dlyb_cfg_test.PhaseSel || self->delayBlockConfig.Units != dlyb_cfg_test.Units)
     return false;

  DelayMilliseconds(100); // without this delay, certain boards fail the subsequent call to ConfigureOSPILatency
 
  // Configure latency after having discovered the settings
  if(!ConfigureOSPILatency())
     return false;

  DelayMilliseconds(100);

  if(!ConfigOSPIMemoryMap(self))
     return false;

 

 

LCE
Principal

For displaying code, pleas use the </> button...

LCE
Principal

Almost readable...

Anyway, which RAM are you actually using?

sHyperBusCfg.AccessTime = 6;

There are a few parameters depending on the actual IC used.

I will be using an Infineon S70KL1282 soon, switching from H735-Disco's S70KL1281.

And the S70KL1282 needs one more clock cycle, 7 instead of 6.

I don't know if the above is that HAL setting, with direct register access, it's the TACC bits in HLCR:

	/* HLCR: HyperBus latency configuration register
	 *	TRWR[7:0]	= x		read write recovery time
	 *	TACC[7:0]	= y		access time
	 *	WZL			= 0		latency on write access
	 *	LM			= 1		fixed latency
	 */
	pOspiHypRam->HLCR = (OSPI_HYPERRAM_RW_REC_TIME << OCTOSPI_HLCR_TRWR_Pos) |
						(OSPI_HYPERRAM_LATENCY << OCTOSPI_HLCR_TACC_Pos) |
						OCTOSPI_HLCR_LM;

 

The ram we're using is an Infineon S27KL0642.  We've scrutinized the settings for our application and clock setup.  I should mention that the code will run correctly for days on end.  We've just found a specific test case that produces this skipped read when left to run overnight.

Do you think an incorrect AccessTime value could cause it to skip 2 bytes but still clock the full transfer like we're seeing?

HAL_OSPI_HyperbusCfg does set HLCR.  I've also updated the code in my post again, hopefully you'll find it more readable now!

LCE
Principal

Do you think an incorrect AccessTime value could cause it to skip 2 bytes but still clock the full transfer like we're seeing?

Yes and no, strange that it is running for hours otherwise.

Guessing wildly: 1 latency clock cycle missing might lead to 2 missing bytes at double data rate.

So better check the datasheet again, I just had a quick glance at it and ... that was not enough to understand it, looks like latency count is not constant.
Maybe LatencyMode = HAL_OSPI_FIXED_LATENCY is not the right way, but again just guessing.

 

I just compared to my code for the S70KL1281 + H7 (IDK if the OCTOSPI peripheral is different from U5), as far as I can understand the HAL stuff...

- I use a FIFO threshold of 4, and because I read about some STM32 having trouble with mem mapped mode and PSRAMs and alignment, all the buffers I use have the __attribute__((aligned(4))) .

- OSPI_HYPERRAM_NCS_HITIME in DCR1 is set to 8 (yours: ChipSelectHighTime = 1)

- Delay Block is enabled, but delay set to 0, I don't know what the HAL stuff does

- I think you refresh every 2 µs with refresh rate of 160 at 80 MHz, but I might be wrong

 

Also check again the code before the read that fails. That any different from other reads?

Alex - APMemory
Senior II

Hi,

In case of not finding issue, you have reference of using OPSI (APS6408L) on STM32U585 disco / Cube

Attached is example for 64Mb OPI 3V BGA24 APS6408L-3OBM-BA code, but basically same for 128Mb version APS12808L-3OBM-BA

regards

Alex