cancel
Showing results for 
Search instead for 
Did you mean: 

Setting up HyperRAM S70KL1282 with STM32H735 issues

MartinDQ
Associate III

Hi,

I'm trying to setup this new RAM chips on STM32H735 in OSPI/HyperRAM mode. The HW connection is the same as on ST STM32H735 devkit. I found a driver for similar chip S70KL1281 but there are some differences, e.g. in latencies, but generally S70KL1282 is faster. The used driver file identification from header:

******************************************************************************
* @file s70kl1281.c
* @modify MCD Application Team
* @brief This file provides the S70KL1281 OSPI drivers.

...

* Copyright (c) 2020 STMicroelectronics.
******************************************************************************

I configured the Hyperbus in STM32CubeMX but there are no hints how to setup all the options for this memory. So after some messing and trial-error I ended with this partially working init code:

 

 

static void MX_OCTOSPI2_Init(void)
{
  OSPIM_CfgTypeDef sOspiManagerCfg = {0};
  OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};

  /* OCTOSPI2 parameter configuration*/
  hospi2.Instance = OCTOSPI2;
  hospi2.Init.FifoThreshold = 1;
  hospi2.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
  hospi2.Init.MemoryType = HAL_OSPI_MEMTYPE_HYPERBUS;
  hospi2.Init.DeviceSize = 24;
  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 = 2; // for testing use prescaler 2 at 133MHz, prescaler 1 gets garbage
  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 = 500;
  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 = 3;
  sHyperBusCfg.AccessTime = 7;
  sHyperBusCfg.WriteZeroLatency = HAL_OSPI_NO_LATENCY_ON_WRITE; // someone suggested HAL_OSPI_LATENCY_ON_WRITE but doesn't have effect
  sHyperBusCfg.LatencyMode = HAL_OSPI_FIXED_LATENCY;
  if (HAL_OSPI_HyperbusCfg(&hospi2, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    Error_Handler();
  }
}

 

 

First time I had a problem that reading from memory was unrealiable and I sometimes getting a different results on every run. Enabling the magic option hospi2.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED; solved it but still not at the full clock speed I configured - I have both OSPI clocked at 133MHz. The FlashROM at OSPI1 works fine at 133MHz but for this HyperRAM I have to set prescaler 2. Then I got some consistent result.

But there's another problem that test data pattern I wrote into memory are readed by 8 Bytes shifted further than it should be. Currently for testing I don't use mapping yet but I use driver functions S70KL1281_Write and S70KL1281_Read. A simple piece of code for test write and readbeck:

 

 

{
uint8_t buff[64]={0};
int i;
buff[0]=0xab; buff[1]=0xcd; buff[2]=0xef; buff[3]=0xaa;
if (S70KL1281_Write(&XRAM, buff, 0, 16)!=S70KL1281_OK)
  printf("Failed to write to HyperRAM\n");
for (i=0; i<32; i++)
  printf("%02X ", buff[i]);
printf("\n");
if (S70KL1281_Read(&XRAM, buff, 0, 32)!=S70KL1281_OK)
  printf("Failed to read from HyperRAM\n");
for (i=0; i<32; i++)
  printf("%02X ", buff[i]);
printf("\n");
if (S70KL1281_Read(&XRAM, buff, 0, 32)!=S70KL1281_OK)
  printf("Failed to read from HyperRAM\n");
for (i=0; i<32; i++)
  printf("%02X ", buff[i]);
printf("\n");
}

 

 

The memory address is 0 but I get this:

Ext. OctoSPI HyperRAM ID: 0C81 0001
Manufacturer: Infineon, HyperRAM 2.0, rowbits: 13, colbits: 9
AB CD EF AA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
57 47 57 5D D5 D7 8C 15 AB CD EF AA 00 00 00 00 00 00 00 00 00 00 00 00 D5 D5 57 5F D5 D5 5F 75
57 47 57 5D D5 D7 8C 15 AB CD EF AA 00 00 00 00 00 00 00 00 00 00 00 00 D5 D5 57 5F D5 D5 5F 75

There's some garbage in first 8 Bytes (57 47 57 5D D5 D7 CC 15 - uninitialized memory?) followed by correct pattern AB CD EF AA and trailing zeros from buffer

22 REPLIES 22
NesrynELMK
ST Employee

Hello @martin,

I checked your OCTOSPI configuration, and it seems correct!

I propose testing this example:STM32CubeH7 OSPI HyperRAM MemoryMapped, and changing the OSPI_HYPERRAM_LATENCY from 6 to 7 in the main.h file (as per the memory datasheet, the default value of the Initial Latency is 7). This example demonstrates how to write to and read data from the OCTOSPI HyperRAM memory in memory-mapped mode and to compare the results with those from intensive access.

The HyperRAM memory used is an S70KL1281.

 

Kind regards,

Nissrine

I tried to use S70KL1281_EnableMemoryMappedMode() function from the driver to enable mem.mapping but I'm still having 8B offset. I also tried to use OSPI2 initilaization function parametyers but result was the same. I suspect it must be there some difference between S70KL1281 and S70KL1282...

{
__IO uint32_t *mem_addr = (__IO uint32_t *)(OCTOSPI2_BASE);
int i;

if (S70KL1281_EnableMemoryMappedMode(&XRAM)!=S70KL1281_OK)
  printf("Failed to Enable Memory Mapped Mode\n");
else
  printf("Memory Mapped Mode Enabled\n");

mem_addr[0]=0xABCDEFAA;
mem_addr[1]=0x12345678;
mem_addr[2]=0;
mem_addr[3]=0;
for (i=0; i<48; i++); // a shot delay, 46 read ok, 47 read garbage

printf("%08lX\n", mem_addr[0]);
printf("%08lX\n", mem_addr[0]);
printf("%08lX\n", mem_addr[0]); // 57 45 57 5D = "WEW]"

for (i=0; i<32/4; i++)
  printf("%08lX ", mem_addr[i]);
printf("\n");

for (i=0; i<32; i++)
  printf("%02X ", ((uint8_t *)mem_addr)[i]);
printf("\n");
}

Output is:

Ext. OctoSPI HyperRAM ID: 0C81 0001
Manufacturer: Infineon, HyperRAM 2.0, rowbits: 13, colbits: 9
Memory Mapped Mode Enabled
ABCDEFAA - this read changes according to for loop short delay, for shorter times it reads ABCDEFAA, for longer times it reads 5D574557
5D574557
5D574557
5D574557 158CD7D5 ABCDEFAA 12345678 00000000 00000000 5F57D5D5 755FF5D5
57 45 57 5D D5 D7 8C 15 AA EF CD AB 78 56 34 12 00 00 00 00 00 00 00 00 D5 D5 57 5F D5 F5 5F 75

LCE
Principal

Hi Martin,

I'm still using the H735-DK with the S70KL1281, planning to use S70KL1282 on my custom PCB.

So for now I can compare your code only to mine with direct register setup, working with S70KL1281 at 100 MHz.

Differences to your code:

- setup: FIFO threshold = 4 (maybe that's worth a try, as some other STM32 Q/O-SPIs suffer from alignment problems)

- delay block, setting the registers:

/* DELAY block */
	/* disable the output clock and enable the access to the phase selection SEL[3:0] */
	DLYB_OCTOSPI1->CR 	= DLYB_CR_SEN;
	/* set delay */
	DLYB_OCTOSPI1->CFGR = (0 & DLYB_CFGR_SEL_Msk);
	/* disable the access to the phase selection, enable output */
	DLYB_OCTOSPI1->CR 	= DLYB_CR_DEN;

- and another good guess: I start "MspInit" with a peripheral reset after the clock enable:

void OctoSpiGpioDmaInit(OSPI_HandleTypeDef *phOspi)
{
	GPIO_InitTypeDef GPIO_InitStruct = { 0 };

	if( OCTOSPI1 == phOspi->Instance )
	{
		/* OCTOSPI1 clock enable */
		__HAL_RCC_OCTOSPIM_CLK_ENABLE();
		__HAL_RCC_OSPI1_CLK_ENABLE();

		/* reset & release */
		__HAL_RCC_OSPI1_FORCE_RESET();
		__HAL_RCC_OSPI1_RELEASE_RESET();

		/** OCTOSPI 1 GPIO Configuration
...

 

 

 

 

OK, can you tell me that R/W works as expected for you on S70KL1281? When do you get S70KL1282? I would use S70KL1281 in our project but it was out of stock so I have to choose some newer replacement...

I already tried FIFO threshold = 4 and all options from main.c of example code (begisn with /* Initialize OctoSPI ----------------------------------------------------- */) but any difference. I don't use DMA. I may play with delay block a bit but it seems to be a mystery box. From what I have found it seems that it needs to be tuned differently for various chips, probably by trial error...

LCE
Principal

OK, can you tell me that R/W works as expected for you on S70KL1281?

Yes, using it in memory mapped mode only, it works for hours / days.

Maximum speed depends on compilation (I have to find the reasons for that), it's always above 400 Mbit/s, best write rate was something about 1500 Mbit/s.

I would use S70KL1281 in our project but it was out of stock...

Same here, it's EOL.

Infineon even has a migration datasheet, and the only (seemingly) relevant difference is the latency of 7 instead of 6 cycles.

LCE
Principal

When do you get S70KL1282?

I just ordered the PCB incl. assembly, that will take at least 8 weeks from now on. :(

 

MartinDQ
Associate III

BTW what clock speed do you use for OSPI and what divider?

I routed it to some PLL output that I set at 133MHz. Before enabling the delay block I got some data consistency issues did you tried also without delay block and how it performed?

I don't have fast-enugh scope to check waveforms at full speed.

I went through the datesheet and already seen the initial latency difference so I set it in my code. Also there's a mention that this memory is duble-die (stacked) chip but I don't know if it imply some different behavior, I think it's just internal architecture that should be transparent for the user who see just a single 32-bit address from the outside...

LCE
Principal

Clock source is RCC_OSPICLKSOURCE_D1HCLK, which is 1/2 system clock, in my case clock sourcing OSPI is 200 MHz, prescaler halves that, so it's running with 100 MHz. Same scope problems here... :D

I played with the delay block in the beginning, then found that best setting was to activate it, but keep all values at 0. It seemed that the propagation delay alone by activation was good enough.

Edit: find my HyperRam setup in attached files, including test function with quite exact time measurement with DWT->CYCCNT
Works on H735-DK with the S70KL1281 at 100 MHz.

In case you never used DWT->CYCCNT, here's the setup:

/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* CPU cycle count activation for debugging */
#if( 1 ) 	//	DEBUG_CPU_TIMING
	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
	DWT->LAR = 0xC5ACCE55;
	DWT->CYCCNT = 0;
	DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
	DWT->CTRL |= DWT_CTRL_PCSAMPLENA_Msk;
#endif	/* DEBUG_CPU_TIMING */

Hello @MartinDQ 

 

I have run a test with an STM32H735G-DK that integrates the HyperRam S70KL1281, using your OSPI2 configuration and the S70KL1281_Write and S70KL1281_Read functions from the S70KL1281 OSPI drivers. With the exception that the access time in my case should be set to 6 for the S70KL1281 memory, I found that the issue is related to the refresh parameter. According to the memory datasheet, this parameter should be 4µs (see section 10.5.2 Write transactions, Table 34 Write timing parameters, page 50).

For this reason, I propose using the following formula to determine the correct value:

 

uint32_t Ospi_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_OSPI);
OSPIHandle.Init.Refresh = ((2U * (Ospi_clk / (OSPIHandle.Init.ClockPrescaler + 1U))) / 1000000U) + 4U; // The chip select should be released every 4µs

 

 

Kind regards,

Nissrine