2024-03-25 12:48 PM - edited 2024-03-25 12:51 PM
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
2024-05-15 08:15 AM
fyi, if any issue to source 128Mb OPI 3V BGA24 due to EOL, you can get APS12808L-3OBM-BA (also tested on STM32H72x/3x Disco, file attached)
Alex
Datasheet : http://www.apmemory.com/wp-content/uploads/APS12808L-3OBM-BA.pdf
Proto: https://www.mouser.com/ProductDetail/AP-Memory/APS12808L-3OBM-BA?qs=IS%252B4QmGtzzrXcGkbuYahqw%3D%3D
2024-06-07 09:02 AM
I got my custom PCB today, including H733 and S70KL1282.
At least the HyperRam worked instantly.
I check at power up by completely writing the RAM with i = 0, i++, then reading it back.
I know this test is not perfect, but on my test boards until now, when this test was working, so did the audio / SAI buffers I used in HyperRAM.
I think the only change compared to S70KL1281 was setting the latency to 7.
See init below.
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* OCTOSPIx basic init function
* for octal HyperBus / HyperRAM
* Infineon S70KL1281 / S70KL1282
* in memory-mapped mode
* linear burst read & write => no page wrap (only at end of memory)
* input clock is 1/2 HCLK (200 MHz) -> prescaler /2
*/
uint8_t OspiHypRam_Init(void)
{
uint8_t u8RetVal = 0;
/* use pointer to simplify OSPI 1 / 2 switching */
#if OSPI_CUSTOM_RAM_OSPI1 // custom: OSPI 1 HyperRAM; OSPI 2 Quad Flash
pOspiHypRam = OCTOSPI1;
#else // H735D: OSPI 1 Oct Flash; OSPI 2 HyperRAM
pOspiHypRam = OCTOSPI2;
#endif /* OSPI_CUSTOM_RAM_OSPI1 */
hOspiHypRam.Instance = pOspiHypRam;
/* clock & GPIO initialization */
OspiHypRamGpioDmaInit();
/* start OFF */
pOspiHypRam->CR = 0;
/* DEVICE configuration registers
* DCR1 .. DCR4
*/
/* DCR1:
* MTYP = 100 Hyperbus memory mode
* DEVSIZE
* CHST
* DLYBYP = 0 delay block is used
* FRCK = 0 no free running clock
* CKMODE = 0 clock low when idle
*/
pOspiHypRam->DCR1 = OCTOSPI_DCR1_MTYP_2 |
((OSPI_HYPERRAM_SIZE - 1) << OCTOSPI_DCR1_DEVSIZE_Pos) |
((OSPI_HYPERRAM_NCS_HITIME - 1) << OCTOSPI_DCR1_CSHT_Pos);
/* DCR2:
* WRAPSIZE = 0 wrapped read OFF
* PRESCALER = 1 divide by 2 -> 100 MHz = max at 3.3V
*/
pOspiHypRam->DCR2 = (OSPI_HYPERRAM_WRAP_ZERO << OCTOSPI_DCR2_WRAPSIZE_Pos) |
((OSPI_HYPERRAM_CLK_DIV - 1) << OCTOSPI_DCR2_PRESCALER_Pos);
/* DCR3:
* CSBOUND = 23 set to 2^n memory size
* MAXTRAN = 0 unused
*/
pOspiHypRam->DCR3 = (OSPI_HYPERRAM_CSBOUND << OCTOSPI_DCR3_CSBOUND_Pos);
/* DCR4:
* REFRESH = 400 NCS must be released for PSRAM for internal refresh
*/
pOspiHypRam->DCR4 = OSPI_HYPERRAM_REFRESH;
/* CR: control register
* no polling
* no interrupts
* FMODE = 0 indirect mode, set to memory-mapped mode later
* FTTHRES = 4 FIFO threshold
* FSEL = 0 nc in octal mode
* TCEN = 0 no timeout counter
* DMAEN = 0 no DMA
* ABORT = 0 no ABORT request
* EN = 0 ENable set later
*/
pOspiHypRam->CR = ((OSPI_HYPERRAM_FTHRES - 1) << OCTOSPI_CR_FTHRES_Pos);
/* TCR: timing config
* SSHIFT = 0 no read sample shifting
* DHQC = 1 delay hold quarter cycle ON
* DCYC = 0 dummy cycles between readd addr & data
*/
pOspiHypRam->TCR = OCTOSPI_TCR_DHQC |
(OSPI_HYPERRAM_DCYC << OCTOSPI_TCR_DCYC_Pos);
/* Hyperbus configuration
* memory-mapped mode configuration
* CR, DCR1, CCR, WCCR, DLR, AR
*/
/* 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;
/* CR: clear FMODE -> exit MM for now */
pOspiHypRam->CR &= ~OCTOSPI_CR_FMODE;
/* DCR1: Hyperbus from register access to memory */
pOspiHypRam->DCR1 &= ~OCTOSPI_DCR1_MTYP_0;
/* CCR & WCCR:
* DQS signal enabled (used as RWDS)
* DTR mode enabled on address and data
* address and data on 8 lines
*/
/* READ: CCR */
pOspiHypRam->CCR = OCTOSPI_CCR_DQSE |
OCTOSPI_CCR_DDTR | OCTOSPI_CCR_DMODE_2 |
OCTOSPI_CCR_ADSIZE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADMODE_2;
/* WRITE: WCCR */
pOspiHypRam->WCCR = OCTOSPI_CCR_DQSE |
OCTOSPI_WCCR_DDTR | OCTOSPI_WCCR_DMODE_2 |
OCTOSPI_WCCR_ADSIZE | OCTOSPI_WCCR_ADDTR | OCTOSPI_WCCR_ADMODE_2;
/* DLR: data length = 1 byte */
pOspiHypRam->DLR = 0;
/* AR: address reset */
pOspiHypRam->AR = 0;
/* update state */
hOspiHypRam.State = HAL_OSPI_STATE_CMD_CFG;
/* LPTR: lo-power timeout reset */
pOspiHypRam->LPTR = 0;
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* HW IO MUX setup */
/* MUX OCTOSPIM->PCR[n] */
pOspiHypRam->CR &= ~OCTOSPI_CR_EN;
/* NO multiplexed mode */
OCTOSPIM->CR = 0;
#if OSPI_CUSTOM_RAM_OSPI1 // custom: OSPI 1 HyperRAM; OSPI 2 Quad Flash
/* PORT 1:
* select signals & enable
* hi port 1 4..7: OCTOSPIM_PCR_IOHsrc=01 + IOHEN
* lo port 1 0..3: OCTOSPIM_PCR_IOLsrc=00 + IOLEN
* NCS 1: NCSsrc=0 + NCSEN
* DQS 1: DQSsrc=0 + DQSEN
* CLK 1: CLKsrc=0 + CLKEN
*/
OCTOSPIM->PCR[0] = ( OCTOSPIM_PCR_IOHSRC_0 | OCTOSPIM_PCR_IOHEN |
OCTOSPIM_PCR_IOLEN |
OCTOSPIM_PCR_NCSEN |
OCTOSPIM_PCR_DQSEN |
OCTOSPIM_PCR_CLKEN );
/* 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;
#else // H735-DK: OSPI 1 Oct Flash; OSPI 2 HyperRAM
/* PORT 2:
* select signals & enable
* hi port 2 4..7
* lo port 2 0..3
* NCS 2
* DQS 2
* clock 2
*/
OCTOSPIM->PCR[1] = ( OCTOSPIM_PCR_IOHSRC_1 | OCTOSPIM_PCR_IOHSRC_0 | OCTOSPIM_PCR_IOHEN |
OCTOSPIM_PCR_IOLSRC_1 | OCTOSPIM_PCR_IOLEN |
OCTOSPIM_PCR_NCSSRC | OCTOSPIM_PCR_NCSEN |
OCTOSPIM_PCR_DQSSRC | OCTOSPIM_PCR_DQSEN |
OCTOSPIM_PCR_CLKSRC | OCTOSPIM_PCR_CLKEN );
/* DELAY block */
/* disable the output clock and enable the access to the phase selection SEL[3:0] */
DLYB_OCTOSPI2->CR = DLYB_CR_SEN;
/* set delay */
DLYB_OCTOSPI2->CFGR = (0 & DLYB_CFGR_SEL_Msk);
/* disable the access to the phase selection, enable output */
DLYB_OCTOSPI2->CR = DLYB_CR_DEN;
#endif /* OSPI_CUSTOM_RAM_OSPI1 */
/* enable in peripheral -> clear bypass */
pOspiHypRam->DCR1 &= ~OCTOSPI_DCR1_DLYBYP;
/* clear all status flags */
pOspiHypRam->FCR = 0xFFFFFFFF;
/* CR: memory-mapped, no timeout */
pOspiHypRam->CR &= ~OCTOSPI_CR_TCEN;
pOspiHypRam->CR |= OCTOSPI_CR_FMODE;
/* CR: enable peripheral */
pOspiHypRam->CR |= OCTOSPI_CR_EN;
/* set state (for DMA & HAL stuff) */
hOspiHypRam.State = HAL_OSPI_STATE_BUSY_MEM_MAPPED;
return u8RetVal;
}
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* basic GPIO & DMA init */
void OspiHypRamGpioDmaInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct = { 0 };
#if OSPI_CUSTOM_RAM_OSPI1 // custom: OSPI 1 HyperRAM; OSPI 2 Quad Flash
/* OCTOSPI1 clock enable */
if( 0 == (RCC->AHB3ENR & RCC_AHB3ENR_IOMNGREN) ) __HAL_RCC_OCTOSPIM_CLK_ENABLE();
if( 0 == (RCC->AHB3ENR & RCC_AHB3ENR_OSPI1EN) ) __HAL_RCC_OSPI1_CLK_ENABLE();
/* reset & release */
__HAL_RCC_OSPI1_FORCE_RESET();
__HAL_RCC_OSPI1_RELEASE_RESET();
#else // H735-DK: OSPI 1 Oct Flash; OSPI 2 HyperRAM
/* OCTOSPI2 clock enable */
if( 0 == (RCC->AHB3ENR & RCC_AHB3ENR_IOMNGREN) ) __HAL_RCC_OCTOSPIM_CLK_ENABLE();
if( 0 == (RCC->AHB3ENR & RCC_AHB3ENR_OSPI2EN) ) __HAL_RCC_OSPI2_CLK_ENABLE();
/* reset */
__HAL_RCC_OSPI2_FORCE_RESET();
__HAL_RCC_OSPI2_RELEASE_RESET();
#endif /* OSPI_CUSTOM_RAM_OSPI1 */
/*
ATTENTION: very different ALTERNATE function numbers!
custom: HyperRAM OCTOSPI 1 GPIO Configuration
PA3 ------> OCTOSPIM_P1_CLK
PB2 ------> OCTOSPIM_P1_DQS
PE11 ------> OCTOSPIM_P1_NCS
PB1 ------> OCTOSPIM_P1_IO0
PB0 ------> OCTOSPIM_P1_IO1
PB13 ------> OCTOSPIM_P1_IO2
PA6 ------> OCTOSPIM_P1_IO3
PE7 ------> OCTOSPIM_P1_IO4
PE8 ------> OCTOSPIM_P1_IO5
PE9 ------> OCTOSPIM_P1_IO6
PE10 ------> OCTOSPIM_P1_IO7
H735-DK: HyperRAM OCTOSPI 2 GPIO Configuration
PF4 ------> OCTOSPIM_P2_CLK
PF12 ------> OCTOSPIM_P2_DQS
PG12 ------> OCTOSPIM_P2_NCS
PF0 ------> OCTOSPIM_P2_IO0
PF1 ------> OCTOSPIM_P2_IO1
PF2 ------> OCTOSPIM_P2_IO2
PF3 ------> OCTOSPIM_P2_IO3
PG0 ------> OCTOSPIM_P2_IO4
PG1 ------> OCTOSPIM_P2_IO5
PG10 ------> OCTOSPIM_P2_IO6
PG11 ------> OCTOSPIM_P2_IO7
*/
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
/* control signals */
GPIO_InitStruct.Pin = OSPIRAM_CLK_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_CLK_AF;
HAL_GPIO_Init(OSPIRAM_CLK_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = OSPIRAM_DQS_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_DQS_AF;
HAL_GPIO_Init(OSPIRAM_DQS_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = OSPIRAM_NCS_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_NCS_AF;
HAL_GPIO_Init(OSPIRAM_NCS_GPIO_Port, &GPIO_InitStruct);
/* data IOs */
GPIO_InitStruct.Pin = OSPIRAM_IO0_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_IO0_AF;
HAL_GPIO_Init(OSPIRAM_IO0_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = OSPIRAM_IO1_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_IO1_AF;
HAL_GPIO_Init(OSPIRAM_IO1_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = OSPIRAM_IO2_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_IO2_AF;
HAL_GPIO_Init(OSPIRAM_IO2_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = OSPIRAM_IO3_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_IO3_AF;
HAL_GPIO_Init(OSPIRAM_IO3_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = OSPIRAM_IO4_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_IO4_AF;
HAL_GPIO_Init(OSPIRAM_IO4_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = OSPIRAM_IO5_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_IO5_AF;
HAL_GPIO_Init(OSPIRAM_IO5_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = OSPIRAM_IO6_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_IO6_AF;
HAL_GPIO_Init(OSPIRAM_IO6_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = OSPIRAM_IO7_Pin;
GPIO_InitStruct.Alternate = OSPIRAM_IO7_AF;
HAL_GPIO_Init(OSPIRAM_IO7_GPIO_Port, &GPIO_InitStruct);
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* HyperRAM memory:
* S70KL1281 on H735-Discovery
* S70KL1282 on custom board
*/
/* size 2^x */
#define OSPI_HYPERRAM_SIZE 24
/* NCS high time between commands */
#define OSPI_HYPERRAM_NCS_HITIME 8
#define OSPI_HYPERRAM_WRAP_ZERO 0
/* clock prescaler */
#define OSPI_HYPERRAM_CLK_DIV 2
#define OSPI_HYPERRAM_CSBOUND (OSPI_HYPERRAM_SIZE - 1)
#define OSPI_HYPERRAM_REFRESH 400
#define OSPI_HYPERRAM_FTHRES 4
#define OSPI_HYPERRAM_DCYC 0
/* timing for register HLCR HyperBus latency configuration */
/* clock cycles for read / write recovery time */
#define OSPI_HYPERRAM_RW_REC_TIME 3
/* clock cycles for device access time */
#define OSPI_HYPERRAM_LATENCY_S70KL1281 6
#define OSPI_HYPERRAM_LATENCY_S70KL1282 7
#define OSPI_HYPERRAM_LATENCY OSPI_HYPERRAM_LATENCY_S70KL1282
/* end address */
#define OSPI_HYPERRAM_END_ADDR (1 << OSPI_HYPERRAM_SIZE)
/* ++++++++++++++++++++++++++++++++++++++++++++++++ */
/* DCR2 clock prescaler */
/* functional modes of peripheral */
#define OSPI_CR_FM_INDIRECT_WRITE (uint32_t)0 /*!< Indirect write mode */
#define OSPI_CR_FM_INDIRECT_READ (uint32_t)OCTOSPI_CR_FMODE_0 /*!< Indirect read mode */
#define OSPI_CR_FM_AUTO_POLLING (uint32_t)OCTOSPI_CR_FMODE_1 /*!< Automatic polling mode */
#define OSPI_CR_FM_MEMORY_MAPPED (uint32_t)OCTOSPI_CR_FMODE /*!< Memory-mapped mode */
2024-06-07 09:21 AM
Hi,
Good to hear, I can't comment much about specific for HyperRAM.
If competiveness is important, I can just advice to look at APMemory products, APS12808L-3OBM-BA, this is compatible device on your BGA24 PCB footprint.
Regards
Alex