2025-12-13 10:27 PM - edited 2025-12-15 7:01 PM
Hello,
I am working with the STM32U585AI using OCTOSPI2 to interface with the IS66WVO32M8 32 Mbyte ISSI OctalRAM. Currently I am facing an issue of read operations failing for both register and memory access, both in and out of OPI DTR mode. Register reads return a value of 0x8080 or 0x8888. Memory reads (using both HAL_OSPI_Receive() and HAL_OSPI_Receive_DMA() return some sequence of 0x80s and 0x88s in the amount of data requested (32 byte read returns 32 bytes of 0x80, 0x88, or some combination).
EDIT: Dummy cycles in code snippets corrected to both be 16 (based on config being applied to IS66WVO32M8 configuration register).
Here is my OCTOSPI2 initialization:
void MX_OCTOSPI2_Init(void)
{
/* USER CODE BEGIN OCTOSPI2_Init 0 */
/* USER CODE END OCTOSPI2_Init 0 */
OSPIM_CfgTypeDef sOspiManagerCfg = {0};
HAL_OSPI_DLYB_CfgTypeDef HAL_OSPI_DLYB_Cfg_Struct = {0};
/* USER CODE BEGIN OCTOSPI2_Init 1 */
/* USER CODE END OCTOSPI2_Init 1 */
hospi2.Instance = OCTOSPI2;
hospi2.Init.FifoThreshold = 1;
hospi2.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
hospi2.Init.MemoryType = HAL_OSPI_MEMTYPE_MACRONIX_RAM;
hospi2.Init.DeviceSize = 25;
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 = 4;
hospi2.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
hospi2.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE;
hospi2.Init.ChipSelectBoundary = 4;
hospi2.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED;
hospi2.Init.MaxTran = 0;
hospi2.Init.Refresh = 320;
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();
}
HAL_OSPI_DLYB_Cfg_Struct.Units = 0;
HAL_OSPI_DLYB_Cfg_Struct.PhaseSel = 0;
if (HAL_OSPI_DLYB_SetConfig(&hospi2, &HAL_OSPI_DLYB_Cfg_Struct) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN OCTOSPI2_Init 2 */
/* USER CODE END OCTOSPI2_Init 2 */
}
Register read:
Status Driver::readRegister(uint32_t reg_address, uint16_t* value) {
if (!value) {
return Status::INVALID_PARAM;
}
if (currentMode_ != OperatingMode::COMMAND_MODE) {
if (disableMemoryMapped() != Status::OK) {
return Status::ERROR;
}
}
OSPI_RegularCmdTypeDef cmd = {};
cmd.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
cmd.FlashId = HAL_OSPI_FLASH_ID_1;
cmd.Instruction = OCTALRAM_READ_REG_CMD; // 0xC000
cmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
cmd.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
cmd.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
cmd.Address = reg_address;
cmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
cmd.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
cmd.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE;
cmd.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
cmd.DataMode = HAL_OSPI_DATA_1_LINE;
cmd.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE;
cmd.NbData = 2U;
cmd.DummyCycles = 16U;
cmd.DQSMode = HAL_OSPI_DQS_DISABLE;
cmd.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
if (HAL_OSPI_Command(ospiHandle_, &cmd, config_.timeoutMs) != HAL_OK) {
return Status::ERROR;
}
uint8_t reg_data[2U];
if (HAL_OSPI_Receive(ospiHandle_, reg_data, config_.timeoutMs) != HAL_OK) {
return Status::ERROR;
}
*value = (reg_data[0U] << 8U) | reg_data[1U];
return Status::OK;
}
And I am primarily focused on the DMA memory read (seeing same behavior with and without DMA):
Status Driver::readDMA(uint32_t address, uint8_t* data, uint32_t size, DMACallback callback) {
if (!data || size == 0 || address + size > OCTALRAM_SIZE) {
return Status::INVALID_PARAM;
}
if (dmaBusy_) {
return Status::BUSY;
}
if (currentMode_ != OperatingMode::COMMAND_MODE) {
if (disableMemoryMapped() != Status::OK) {
return Status::ERROR;
}
}
// memset(&dmaRxBuffer_, '\0', sizeof(dmaRxBuffer_));
for (uint16_t i = 0; i < 512; ++i) {
dmaRxBuffer_[i] = 0U;
}
dmaCallback_ = callback;
dmaBusy_ = true;
// read command
OSPI_RegularCmdTypeDef cmd = {};
cmd.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
cmd.FlashId = HAL_OSPI_FLASH_ID_1;
cmd.Instruction = OCTALRAM_READ_CMD; // 0x8000
cmd.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
cmd.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
cmd.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;
cmd.Address = address;
cmd.AddressMode = HAL_OSPI_ADDRESS_8_LINES;
cmd.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
cmd.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;
cmd.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
cmd.DataMode = HAL_OSPI_DATA_8_LINES;
cmd.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;
cmd.NbData = size;
cmd.DummyCycles = 16U;
cmd.DQSMode = HAL_OSPI_DQS_DISABLE;
cmd.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
if (HAL_OSPI_Command(ospiHandle_, &cmd, config_.timeoutMs) != HAL_OK) {
dmaBusy_ = false;
return Status::ERROR;
}
if (HAL_OSPI_Receive_DMA(ospiHandle_, dmaRxBuffer_) != HAL_OK) {
dmaBusy_ = false;
return Status::ERROR;
}
while (dmaBusy_); // set in callback
for (uint16_t i = 0; i < size; ++i) {
data[i] = dmaRxBuffer_[i];
}
return Status::OK;
}I have referenced several other posts that have experienced similar issues with octospi and/or provide instruction on the same/similar memory device.
This post seems to indicate the same issue appearing on STM32H7:
And I have been unable to replicate even the partial success found by this user (again on STM32H7, not STM32U5 but using the IS66WVO32):
DMA functions and interrupts are all working properly (and non-DMA/polling show the same behavior). I have experimented with using AP memory type setting for the conventional SPI commands as described in the second link above to no avail, and have tried a wide variety of configurations of the ISSI device itself. Unfortunately, I have limited ability to probe the device and cannot confirm definitively that the settings are being applied without being able to read the registers.
I believe at this point I am doing everything correctly. It seems that this is potentially an unresolved problem across other similar devices as well (based on other reports of similar OSPI memories reading back 0x88 under similar conditions), although none of the STM32U575xx/STM32U585xx OCTOSPI-related entries seem to cover this failure mode.
What could be causing the junk read-back values? Is there a solution for the IS66WVO32M8 to work with the STM32U585 in any capacity? Changing memory chips effectively isn't an option for us with current timelines, but any feedback on the current approach or suggestions of different avenues to pursue would be greatly appreciated.
Thanks very much for your time.
Solved! Go to Solution.
2025-12-16 1:08 AM
Hello @wwoods93 ,
The delay block is mainly analog. When “UNIT” field is changed the LNGF (bit 31) flag must be set before to read the LNG field (bit 27:16). The LNG field is continuously sampled and the LNG value is updated at each CLKIN (Delay input clock) rising edge.
For better synchronization to transfer the LNG value to AHB domain, could you please try to add some delays (NOP) after the LNGF flag before to read LNG and to get several LNG values before to change "UNIT".
The idea is to wait for LNGF flag and then read LNG field several times until LNG !=0. It can be said LNG value is either correct or zero.
I hope this help you.
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.
2025-12-15 5:36 AM
Hello @wwoods93 and welcome to the community;
I recommend you referring to this How to calibrate the delay block with the OCTOSPI ... - STMicroelectronics Community article and check the delay block configuration. When the delay block enabled, you need to calibrate it according to the phase synchronization to be introduced in the data strobe detection.
->Register reads return a value of 0x8080 or 0x8888
Could you please verify the dummy cycle.
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.
2025-12-15 6:59 PM
@KDJEM.1 thanks for your reply.
I followed the calibration procedure from the link provided and was able to run the exhaustive calibration example code, but was unable to find valid settings. I noticed during this process that the DLYB_CFGR bit 31 LNGF (length valid flag) was in many cases being set to zero when when the delay value is updated within the calibration loop. As I understand it, this would result in the delay not being applied. Not sure if this is relevant, but it stuck out during debugging.
For the dummy cycles, I updated the original post code snippets to show that I am using 16 (2 x 8 latency) dummy cycles for both memory read and memory write. I have the IS66WVO32M8 configuration register latency set to 8, and according to the datasheet read and write should be a 2 latency count operation, so I believe this makes sense, but I have tried this and other values with corresponding latency settings applied to the IS66WVO32M8 configuration register without success.
I have since set up memory-mapped mode, and found that the same behavior occurs, although I did observe that if I decrease the OCTOSPI2 clock divider from 4 to 2 (in either mode), I see 0x0C instead of 0x80/0x88.
A few resulting questions:
- Is the behavior of the LNGF flag described above normal? From the RM it appears that LNGF should be set to 1 after a new value is populated in LNG (resulting from a change to the UNIT bits), and this does not seem to happen consistently.
- More generally, is there a path forward if I am unable to calibrate the delay block? I can disable it to no evident ill effects, but am unclear as to whether that will lead to problems down the line.
Thanks for your time!
2025-12-16 1:08 AM
Hello @wwoods93 ,
The delay block is mainly analog. When “UNIT” field is changed the LNGF (bit 31) flag must be set before to read the LNG field (bit 27:16). The LNG field is continuously sampled and the LNG value is updated at each CLKIN (Delay input clock) rising edge.
For better synchronization to transfer the LNG value to AHB domain, could you please try to add some delays (NOP) after the LNGF flag before to read LNG and to get several LNG values before to change "UNIT".
The idea is to wait for LNGF flag and then read LNG field several times until LNG !=0. It can be said LNG value is either correct or zero.
I hope this help you.
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.
2025-12-18 11:08 PM
@KDJEM.1 Thanks for this. The delay block information was a big help. I now have the delay block calibrated and enabled.
Ultimately, we learned of a power issue on our board that has proven to be the source of our problem. The IS66WVO32M8 is now successfully being read from and written to in both blocking and DMA modes. Thanks again for all your help.