2024-07-16 09:16 AM
Hello community,
I'm looking for some direction on how to setup the OSPI bus to communitcate with an external PSRAM part from ISSI - IS66WVO8M8 (datasheet attached). I've read through application notes and a handful of example projects but am having a hard time getting the logic to sync.
A few things I've done:
If you look in the MX_OCTOSPI2_Init function, I am trying to read from the ID register to confirm the correct part, run a quick and dirty write/read check, and then use the part as a GUI framebuffer.
static void MX_OCTOSPI2_Init(void)
{
/* USER CODE BEGIN OCTOSPI2_Init 0 */
/* USER CODE END OCTOSPI2_Init 0 */
OSPIM_CfgTypeDef sOspiManagerCfg = {0};
/* USER CODE BEGIN OCTOSPI2_Init 1 */
/* USER CODE END OCTOSPI2_Init 1 */
/* OCTOSPI2 parameter configuration*/
hospi2.Instance = OCTOSPI2;
hospi2.Init.FifoThreshold = 1;
hospi2.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
hospi2.Init.MemoryType = HAL_OSPI_MEMTYPE_APMEMORY;
hospi2.Init.DeviceSize = 26;
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_32_BYTES;
hospi2.Init.ClockPrescaler = 1;
hospi2.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
hospi2.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE;
hospi2.Init.ChipSelectBoundary = 0;
hospi2.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED;
hospi2.Init.MaxTran = 0;
hospi2.Init.Refresh = 0;
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();
}
/* USER CODE BEGIN OCTOSPI2_Init 2 */
OSPI_RegularCmdTypeDef command = { 0 };
// Read the ID register
command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
command.FlashId = HAL_OSPI_FLASH_ID_1;
command.Instruction = 0xC000;
command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;
command.Address = 0;
command.AddressMode = HAL_OSPI_ADDRESS_8_LINES;
command.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;
command.AlternateBytes = HAL_OSPI_ALTERNATE_BYTES_NONE;
command.DummyCycles = 2;
command.DataMode = HAL_OSPI_DATA_8_LINES;
command.NbData = 4;
command.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;
command.DQSMode = HAL_OSPI_DQS_ENABLE;
if (HAL_OSPI_Command(&hospi2, &command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
//
uint32_t id = 0;
if (HAL_OSPI_Receive(&hospi2, (uint8_t*) &id, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (id == 0)
{
Error_Handler();
}
// Read the config register
command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
command.Instruction = 0xC000;
command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;
command.Address = 0x00040000;
command.AddressMode = HAL_OSPI_ADDRESS_8_LINES;
command.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;
command.AlternateBytes = HAL_OSPI_ALTERNATE_BYTES_NONE;
command.DataMode = HAL_OSPI_DATA_8_LINES;
command.NbData = 4;
command.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;
command.DummyCycles = 2;
command.DQSMode = HAL_OSPI_DQS_ENABLE;
if (HAL_OSPI_Command(&hospi2, &command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
uint32_t config = 0;
if (HAL_OSPI_Receive(&hospi2, (uint8_t*) &config, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (id == 0)
{
Error_Handler();
}
OSPI_MemoryMappedTypeDef config = {
.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_ENABLE,
.TimeOutPeriod = 0,
};
if (HAL_OSPI_MemoryMapped(&hospi2, &config) != HAL_OK)
{
Error_Handler();
}
uint8_t* src;
uint32_t errorCount = 0;
// write test
src=(uint8_t*) OCTOSPI2_BASE;
for (uint32_t i = 0; i < 0x0FFFFFFF; i++)
*src++ = i & 0xFF;
// Read test
src=(uint8_t*) OCTOSPI2_BASE;
for (uint32_t i = 0; i < 0x0FFFFFFF; i++)
if (*src++ != (i & 0xFF))
errorCount++;
if (errorCount != 0)
while(1);
/* USER CODE END OCTOSPI2_Init 2 */
}
As it is now the read operation will stall if the DQSM is enabled. If I disable DQSM then the read returns `0x88888888` instead of an expected value.
Any feedback and help on this is appreciated. Thanks in advance!
- Taylor
2024-07-16 10:10 AM
Does the ID read properly, as what?
All the code beyond that is unworkable, for RAM you'd need to properly create Read and Write command templates.
With the right commands, and configuration operation type
2024-07-16 12:08 PM
In my attached code I am performing a read operation in lines 46 to 78. The goal is to read the ID register, should be a 16-bit value with data encoded into it representing the number of rows and columns in the part. Should be something like 0x2883 or something similar. The datasheet says it will read out the same 16-bit value twice, so it should be 0x28832883, assuming that is the exact value.
The Read/Write command is shown in the same lines of code, 46-78, to read the ID reg. Then, I had understood that by putting the part into memory-mapped mode that the peripheral would handle the formatting such that you could access the part using pointers instead of directly using the peripheral. All of the example projects I have seen follow this methodology after putting the memory into memory-mapped mode.