2021-07-13 02:25 AM
Hi,
I'm trying to develop a program on a NUCLEO-L4R5ZI-P to write/read data from a S27K0641 (HYPERRAM) and S26K128 (HYPERFLASH).
First I choosed to use only Indirect mode to access the memory. Is it the best option?
Then, there are a lot of options available on HyperBus (such as Delay block bypass, Delay Hold-Quarter, ...) and I was wondering which one are critical in order to access the memories?
Here are the OCTOSPI configuration function generated by the IDE:
You will also find r/w short function which use Indirect mode.
static void MX_OCTOSPI1_Init(void)
{
/* USER CODE BEGIN OCTOSPI1_Init 0 */
/* USER CODE END OCTOSPI1_Init 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 = 22;
hospi1.Init.ChipSelectHighTime = 1;
hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
hospi1.Init.ClockPrescaler = 1;
hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE;
hospi1.Init.ChipSelectBoundary = 0;
hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED;
if (HAL_OSPI_Init(&hospi1) != HAL_OK)
{
Error_Handler();
}
sHyperBusCfg.RWRecoveryTime = 0;
sHyperBusCfg.AccessTime = 0;
sHyperBusCfg.WriteZeroLatency = HAL_OSPI_NO_LATENCY_ON_WRITE;
sHyperBusCfg.LatencyMode = HAL_OSPI_VARIABLE_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 */
}
Here is a function that configures the memory :
void ospi1_initialize(void)
{
OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};
OSPI_HyperbusCmdTypeDef scommand = {0};
sHyperBusCfg.RWRecoveryTime = 4;
sHyperBusCfg.AccessTime = 4;
sHyperBusCfg.WriteZeroLatency = HAL_OSPI_NO_LATENCY_ON_WRITE;
sHyperBusCfg.LatencyMode = HAL_OSPI_VARIABLE_LATENCY;
if (HAL_OSPI_HyperbusCfg(&hospi1, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
HAL_Delay(1);
/*
* CONFIGURATION REGISTER 0
* 0b1000 1111 0001 0111
*/
uint8_t CR0[2];
CR0[0] = 0b00010111;
CR0[1] = 0b10001111;
scommand.AddressSpace = HAL_OSPI_REGISTER_ADDRESS_SPACE ;
scommand.AddressSize = HAL_OSPI_ADDRESS_16_BITS;
scommand.DQSMode = HAL_OSPI_DQS_ENABLE;
scommand.Address = 0x00010000;
scommand.NbData = 2;
if (HAL_OSPI_HyperbusCmd(&hospi1,&scommand,HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if(HAL_OSPI_Transmit(&hospi1,CR0,HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
HAL_Delay(10);
/*
* CONFIGURATION REGISTER 1
* 0b0000 0000 0000 0010
*/
uint8_t CR1[2];
CR1[0] = 0;
CR1[1] = 2;
scommand.AddressSpace = HAL_OSPI_REGISTER_ADDRESS_SPACE ;
scommand.AddressSize = HAL_OSPI_ADDRESS_16_BITS;
scommand.DQSMode = HAL_OSPI_DQS_ENABLE;
scommand.Address = 0x000100001;
scommand.NbData = 2;
if (HAL_OSPI_HyperbusCmd(&hospi1,&scommand,HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if(HAL_OSPI_Transmit(&hospi1,CR1,HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
HAL_Delay(10);
}
And here are my functions to write/read to/from the memory:
void ospi1_write(uint32_t add, uint8_t *data)
{
OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};
OSPI_HyperbusCmdTypeDef scommand = {0};
sHyperBusCfg.RWRecoveryTime = 4;
sHyperBusCfg.AccessTime = 4;
sHyperBusCfg.WriteZeroLatency = HAL_OSPI_NO_LATENCY_ON_WRITE;
sHyperBusCfg.LatencyMode = HAL_OSPI_VARIABLE_LATENCY;
if (HAL_OSPI_HyperbusCfg(&hospi1, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
HAL_Delay(1);
scommand.AddressSpace = HAL_OSPI_MEMORY_ADDRESS_SPACE ;
scommand.AddressSize = HAL_OSPI_ADDRESS_16_BITS;
scommand.DQSMode = HAL_OSPI_DQS_ENABLE;
scommand.Address = add;
scommand.NbData = sizeof(data);
if (HAL_OSPI_HyperbusCmd(&hospi1,&scommand,HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if(HAL_OSPI_Transmit(&hospi1,data,HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
HAL_Delay(10);
}
void ospi1_read(uint32_t add)
{
OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};
OSPI_HyperbusCmdTypeDef scommand = {0};
sHyperBusCfg.RWRecoveryTime = 4;
sHyperBusCfg.AccessTime = 4;
sHyperBusCfg.WriteZeroLatency = HAL_OSPI_NO_LATENCY_ON_WRITE;
sHyperBusCfg.LatencyMode = HAL_OSPI_VARIABLE_LATENCY;
if (HAL_OSPI_HyperbusCfg(&hospi1, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
HAL_Delay(1);
scommand.AddressSpace = HAL_OSPI_MEMORY_ADDRESS_SPACE ;
scommand.AddressSize = HAL_OSPI_ADDRESS_16_BITS;
scommand.DQSMode = HAL_OSPI_DQS_ENABLE;
scommand.Address = add;
scommand.NbData = 2;
if (HAL_OSPI_HyperbusCmd(&hospi1,&scommand,HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if(HAL_OSPI_Receive(&hospi1,pdata,HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
HAL_Delay(10);
}
2021-07-14 06:14 AM
For a HyperRAM that appears almost identical to yours (based on the Data Sheets) the following is how I configured memory-mapped mode. I have no experience with HyperFlash so that's left as an exercise for the diligent student. Nor can I speak to your access functions. Good luck.
void MX_OCTOSPI1_Init(void)
{
OSPIM_CfgTypeDef sOspiManagerCfg = {0};
OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};
hospi1.Instance = OCTOSPI1;
hospi1.Init.FifoThreshold = 4; /* works, haven't experimented. */
hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_HYPERBUS;
hospi1.Init.DeviceSize = 23; /* Your S27KL0641 device is 8 MB, so 23 address bits are needed. */
hospi1.Init.ChipSelectHighTime = 1; /* tCSHI */
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; /* My OCTOSPI1 module is clocked at 200 MHz ==> 100 MHz HyperRAM clock. */
hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE;
hospi1.Init.ChipSelectBoundary = 0;
hospi1.Init.ClkChipSelectHighTime = 0;
hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED;
hospi1.Init.MaxTran = 0;
hospi1.Init.Refresh = 100; /* THIS IS IMPORTANT IF YOU'RE NOT A FAN OF DRAM FADE. (1 uS will work for both temperature grades.) */
if (HAL_OSPI_Init(&hospi1) != HAL_OK)
{
Error_Handler();
}
/* I had to use OSPIM to get the signals routed properly. YMMV. */
sOspiManagerCfg.ClkPort = 2;
sOspiManagerCfg.DQSPort = 2;
sOspiManagerCfg.NCSPort = 1;
sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_2_LOW;
sOspiManagerCfg.IOHighPort = HAL_OSPIM_IOPORT_1_HIGH;
/* ??? should be set by CubeMX (the 0 from struct decl above will assert in
* HAL_OSPIM_Config() if USE_FULL_ASSERT is enabled. */
sOspiManagerCfg.Req2AckTime = 1;
if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
sHyperBusCfg.RWRecoveryTime = 4; /* tRWR */
sHyperBusCfg.AccessTime = 6; /* tACC (Data Sheet implies 4 should work but doesn't for me) */
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();
}
/* Memory-mapped mode setup */
{
OSPI_HyperbusCmdTypeDef sCommand = { 0 };
OSPI_MemoryMappedTypeDef sMemMappedCfg = { 0 };
/* Memory-mapped mode configuration --------------------------------------- */
sCommand.AddressSpace = HAL_OSPI_MEMORY_ADDRESS_SPACE;
sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;
sCommand.Address = 0;
sCommand.NbData = 1;
if( HAL_OSPI_HyperbusCmd( &hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE ) != HAL_OK )
{
Error_Handler();
}
sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
if( HAL_OSPI_MemoryMapped( &hospi1, &sMemMappedCfg ) != HAL_OK )
{
Error_Handler();
}
}
}
2021-07-14 08:11 AM
Ok thanks!
But in this configuration you shared, how do you access the memory?
2021-07-14 09:59 AM
As memory with the base address OCTOSPI1_BASE.
2021-07-16 04:10 AM
So it would be something like that?
uint32_t RxHyperRAM[BUFFERSIZE];
/* For reading memory*/
uint16_t ospi1_read(uint32_t add)
{
uint16_t res;
ram_add = (__IO uint32_t *)(OCTOSPI1_BASE + add);
res = *ram_add;
return res;
}
/* For writing memory*/
void ospi1_write(uint32_t add,uint8_t dat)
{
ram_add = (__IO uint32_t *)(OCTOSPI1_BASE + add);
*ram_add = dat;
}
2021-07-16 06:10 AM
If you wish.
One additional thing to be aware of: HyperRAM, just like (S)DRAM, etc., really, really, really cares about matching trace lengths. Prototyping on a Nucleo may not work well, if at all (especially at 100 MHz).
2021-07-16 07:05 AM
The writing functions seems to work properly, but the reading functions is blocking the program like it was waiting for a data to come but I don't understand why because the memory drives RWDS. With a logic analyser I obtain that frame:
2021-07-16 07:23 AM
The OCTOSPI driver on my nucleoboard is limited to 64 MHz. Is it a problem?
2021-07-16 10:36 AM
Not sure but possibly adjusting some of the timing parameters might help. If not then I don't have any other ideas than to try to get some help from Cypress and/or ST.
2021-07-19 01:11 AM
Ok thank you for your help!