cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7B0 OCTA SPI not working for APS6404 QSPI RAM

OGhis
Senior

Hi,

We try to send an receive data to the external APS6404 RAM.
Therefore we read the application note AN5050 and copy the function from page 55 (7.2.5).

At first the function HAL_RCCEx_OCTOSPIDelayConfig() we cannot find in the library.
What is correct function what we must us than or what we must change in the source code?

Without executing the function DelayBlock_Calibration() the MCU blocks when we read some data.

Here is our code:

#define LINEAR_BURST_READ 0x20
#define LINEAR_BURST_WRITE 0xA0
#define DUMMY_CLOCK_CYCLES_SRAM_READ 5
#define DUMMY_CLOCK_CYCLES_SRAM_WRITE 4
/* Exported macro -----------------------------------------------------*/
#define BUFFERSIZE (COUNTOF(aTxBuffer) - 1)
#define COUNTOF(__BUFFER__) (sizeof(__BUFFER__) / sizeof(*(__BUFFER__)))
#define DLYB_BUFFERSIZE (COUNTOF(Cal_buffer) - 1)
#define EXTENDEDBUFFERSIZE (1048576)

uint8_t aTxBuffer[] = " **OCTOSPI/Octal-spi PSRAM Memory-mapped communication example** " \
                      " **OCTOSPI/Octal-spi PSRAM Memory-mapped communication example** " \
                      " **OCTOSPI/Octal-spi PSRAM Memory-mapped communication example** ";
__IO uint8_t *mem_addr;
uint32_t address = 0;
uint16_t index1;   /* index1 counter of bytes used when reading/
                      writing 256 bytes buffer */
uint16_t index2;   /* index2 counter of 256 bytes buffer used when reading/
                      writing the 1Mbytes extended buffer */

void user_main(void) {
  EnableMemMapped();
  DelayBlock_Calibration();
  test_memory();
}


void test_memory(void) {

  mem_addr = (__IO uint8_t*) (OCTOSPI1_BASE + address);
  /*Writing 1Mbyte (256Byte BUFFERSIZE x 4096 times) */
  for (index2 = 0; index2 < EXTENDEDBUFFERSIZE / BUFFERSIZE; index2++) {
    for (index1 = 0; index1 < BUFFERSIZE; index1++) {
      *mem_addr = aTxBuffer[index1];
      mem_addr++;
    }
  }
  /*----------------------------------------------------------------------*/
  /* Reading Sequence of 1Mbyte */
  mem_addr = (__IO uint8_t*) (OCTOSPI1_BASE + address);
  /*Reading 1Mbyte (256Byte BUFFERSIZE x 4096 times)*/
  for (index2 = 0; index2 < EXTENDEDBUFFERSIZE/BUFFERSIZE; index2++) {
    for (index1 = 0; index1 < BUFFERSIZE; index1++) {
      if (*mem_addr != aTxBuffer[index1]) {   // <<== blocks here
        /*if data read is corrupted we can toggle a led here: example blue led*/
        __NOP();   
      }
      mem_addr++;
    }
  }
}

#if 1
void DelayBlock_Calibration(void) {
  /*buffer used for calibration*/
  uint8_t Cal_buffer[] = "****Delay Block Calibration Buffer ****" \
                         "****Delay Block Calibration Buffer ****" \
                         "****Delay Block Calibration Buffer ****" \
                         "****Delay Block Calibration Buffer ****" \
                         "****Delay Block Calibration Buffer ****" \
                         "****Delay Block Calibration Buffer ****";
  uint16_t index;
  __IO uint8_t *mem_addr;
  uint8_t test_failed;
  uint8_t delay = 0x0;
  uint8_t Min_found = 0;
  uint8_t Max_found = 0;
  uint8_t Min_Window = 0x0;
  uint8_t Max_Window = 0xF;
  uint8_t Mid_window = 0;
  uint8_t calibration_ongoing = 1;

  /* Write the Cal_buffer to the memory*/
  mem_addr = (__IO uint8_t*) (OCTOSPI1_BASE);

  for (index = 0; index < DLYB_BUFFERSIZE; index++) {
    *mem_addr = Cal_buffer[index];
    mem_addr++;
  }

  while (calibration_ongoing) {
    /* update the Delayblock calibration */
    HAL_RCCEx_OCTOSPIDelayConfig(delay, 0);   // <<== not found
    test_failed = 0;
    mem_addr = (__IO uint8_t*) (OCTOSPI1_BASE);
    for (index = 0; index < DLYB_BUFFERSIZE; index++) {
      /* Read the Cal_buffer from the memory*/
      if (*mem_addr != Cal_buffer[index]) {
        /*incorrect data read*/
        test_failed = 1;
      }
      mem_addr++;
    }
    /* search for the Min window */
    if (Min_found != 1) {
      if (test_failed == 1) {
        if (delay < 15) {
          delay++;
        } else {
          /* If delay set to maximum and error still detected: can't use external
           PSRAM */
          Error_Handler();
        }
      } else {
        Min_Window = delay;
        Min_found = 1;
        delay = 0xF;
      }
    }
    /* search for the Max window */
    else if (Max_found != 1) {
      if (test_failed == 1) {
        if (delay > 0) {
          delay--;
        } else {
          /* If delay set to minimum and error still detected: can't use external
           PSRAM */
          Error_Handler();
        }
      } else {
        Max_Window = delay;
        Max_found = 1;
      }
    }
    /* min and max delay window found, configure the delay block with the middle
     window value and exit calibration */
    else {
      Mid_window = (Max_Window + Min_Window) / 2;
      HAL_RCCEx_OCTOSPIDelayConfig(Mid_window, 0);     // <<== not found
      /* exit calibration */
      calibration_ongoing = 0;
    }
  }
}
#endif


void EnableMemMapped(void) {

  OSPI_RegularCmdTypeDef sCommand;
  OSPI_MemoryMappedTypeDef sMemMappedCfg;

  sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
  sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
  sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
  sCommand.AddressMode = HAL_OSPI_ADDRESS_8_LINES;
  sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
  sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;
  sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
  sCommand.DataMode = HAL_OSPI_DATA_8_LINES;
  sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;
  sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;
  sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
  sCommand.Address = 0;
  sCommand.NbData = 1;
  /* Memory-mapped mode configuration for Linear burst write operations */
  sCommand.OperationType = HAL_OSPI_OPTYPE_WRITE_CFG;
  sCommand.Instruction = LINEAR_BURST_WRITE;
  sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_SRAM_WRITE;
  if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
      != HAL_OK) {
    Error_Handler();
  }
  /* Memory-mapped mode configuration for Linear burst read operations */
  sCommand.OperationType = HAL_OSPI_OPTYPE_READ_CFG;
  sCommand.Instruction = LINEAR_BURST_READ;
  sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_SRAM_READ;

  if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
      != HAL_OK) {
    Error_Handler();
  }
  /*Disable timeout counter for memory mapped mode*/
  sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
  /*Enable memory mapped mode*/
  if (HAL_OSPI_MemoryMapped(&hospi1, &sMemMappedCfg) != HAL_OK) {
    Error_Handler();
  }
}

 

17 REPLIES 17
KDJEM.1
ST Employee

Hello @OGhis ,

 

The AN5050 shared some examples with STM32L4P5G-DK board. For STM32H7 delay block calibration and PSRAM, I recommend you to refer to DLYB_OSPI_PSRAM_ExhaustiveTuning example and get inspired to configure your project. This example describes how to tune delay block with OSPI PSRAM in indirect Read/Write mode with STM32U5.

Also I advise you to take a look at this discussion and at OSPI examples with STM32H7 may 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.

Hi,

None of your suggestions meet our expectations.
- The  DLYB_OSPI_PSRAM_ExhaustiveTuning example, our STM32H7B0VBTx has no DLYB option.
- The project use an APS6408L but we use an APS6404L-3SQR-SN has only 4 data bits.
OSPI examples with STM32H7 examples is for HyperRam chips and not QSPI
 

What we must change in the sCommand?  (we want to use it in memory mapped mode)
See code below:

 

 

void EnableMemMapped(void) {

  OSPI_RegularCmdTypeDef sCommand;
  OSPI_MemoryMappedTypeDef sMemMappedCfg;

  sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
  sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_4_LINES;  
  sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;  ==> 4 of 8 bits
  sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
  sCommand.AddressMode = HAL_OSPI_ADDRESS_8_LINES;  ==> 4 or 8 lines
  sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;  ==> 24 or 32 bits
  sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;  ==> ??
  sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
  sCommand.DataMode = HAL_OSPI_DATA_8_LINES;   ==> 4 or 8 lines
  sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;  ==> enable or disable
  sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
  sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
  sCommand.Address = 0;
  sCommand.NbData = 1;
  /* Memory-mapped mode configuration for Linear burst write operations */
  sCommand.OperationType = HAL_OSPI_OPTYPE_WRITE_CFG;   
  sCommand.Instruction = LINEAR_BURST_WRITE;     ==> ==> (Value was 0x20 AP6408, but what for the AP6404??
  sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_SRAM_WRITE;
  if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {   Error_Handler(); }
  /* Memory-mapped mode configuration for Linear burst read operations */
  sCommand.OperationType = HAL_OSPI_OPTYPE_READ_CFG;
  sCommand.Instruction = LINEAR_BURST_READ;  ==> 0xA0 for the AP6408 but what using for the AP6404 ??
  sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_SRAM_READ;

  if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {  Error_Handler(); }
  /*Disable timeout counter for memory mapped mode*/
  sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
  /*Enable memory mapped mode*/
  if (HAL_OSPI_MemoryMapped(&hospi1, &sMemMappedCfg) != HAL_OK) { Error_Handler(); }
}

 

 

 

OGhis_1-1724094765515.png

Are these settings coorect?

OGhis_0-1724094125923.png

Btw: when executing the commands, we don't have any signal of the QSPI bus

 

KDJEM.1
ST Employee

Hi @OGhis ,

 

Thank you for coming back to the community and I apologize that my suggestions did not meet your expectations.

About the settings: Device Size defines the memory size in number of bytes = 2^(DEVSIZE+1).

For 8MBytes memory the Device Size must be configure 23 as mentioned in AN5050 table8.STM32CubeMX - Configuration of OCTOSPI parameters.

Could you please try with this sCommand:

/*Function to Enable Memory mapped mode in Quad mode 4-4-4*/
void EnableMemMappedQuadMode(void)
{
  OSPI_RegularCmdTypeDef sCommand;
  OSPI_MemoryMappedTypeDef sMemMappedCfg;
  sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
  sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_4_LINES;
  sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
  sCommand.AddressMode = HAL_OSPI_ADDRESS_4_LINES;
  sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE;
  sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
  sCommand.DataMode = HAL_OSPI_DATA_4_LINES;
  sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE;
  sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
  sCommand.Address = 0;
  sCommand.NbData = 1;
  /* Memory-mapped mode configuration for Quad Read mode 4-4-4*/
  sCommand.OperationType = HAL_OSPI_OPTYPE_READ_CFG;
  sCommand.Instruction = FAST_READ_QUAD;
  sCommand.DummyCycles = FAST_READ_QUAD_DUMMY_CYCLES;
  if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    Error_Handler();
  }
  /* Memory-mapped mode configuration for Quad Write mode -4-4*/
  sCommand.OperationType = HAL_OSPI_OPTYPE_WRITE_CFG;
  sCommand.Instruction = QUAD_WRITE;
  sCommand.DummyCycles = WRITE_QUAD_DUMMY_CYCLES;
  sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;
  if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    Error_Handler();
  }
  /*Disable timeout counter for memory mapped mode*/
  sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
  /*Enable memory mapped mode*/
  if (HAL_OSPI_MemoryMapped(&hospi1, &sMemMappedCfg) != HAL_OK)
  {
    Error_Handler();
  }
}

 

Please don't forgot to check the dummy cycle in main.h

#define FAST_READ_QUAD      0xEB
#define QUAD_WRITE          0x38

#define FAST_READ_QUAD_DUMMY_CYCLES                 6
#define WRITE_QUAD_DUMMY_CYCLES                     0

I hope this help you.

Please take a look at How to configure OctoSPI and QuadSPI with an STM32, I think it is very helpful.

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.

Hi,

Thanks for the information

We seen now signals on all the I/O pins of the AP6404, but when we write an read back the data we have always the same data 0x88. (same data over the hole memory size, started from address 0x90000000)

What else can we verify and what could still be misconfigured?

btw: the AP6404 has a command to put the chip in quad spi mode.
Why is this missing in your proposed source code?  => AP6404_ENTER_QUAD_MODE (0x35)

Hi @OGhis ,

I only provided some configuration for read and write operation in memory mapped mode like that you shared, not all the code.

The SPI Quad mode enable operation command (0x35) is available only in SPI mode to switch the device into quad IO mode before enter in memory mapped:

KDJEM1_0-1724226608516.png

 

void EnterQuadMode(void)
{
  OSPI_RegularCmdTypeDef sCommand;
  sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
  sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
  sCommand.Instruction = ENTER_QUAD_MODE;
  sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
  sCommand.AddressMode = HAL_OSPI_ADDRESS_NONE;
  sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
  sCommand.DataMode = HAL_OSPI_DATA_NONE;
  sCommand.DummyCycles = ENTER_QUAD_DUMMY_CYCLES;
  sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
  sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
  /*Enter QUAD mode*/
  if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    Error_Handler();
  }
}

 

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.

Hi,

Thanks for the information.

We can now write and read to the external AP6404 memory, but not write byte by byte.
Here the test code we used:

 

#define EXTENDEDBUFFERSIZE (1048576)
#define OCTOSPI1_BASE      (0x90000000UL)

volatile uint8_t *pAddr;
uint8_t counter = 0;

for (pAddr = (volatile uint8_t *) OCTOSPI1_BASE;
     pAddr < (volatile uint8_t *)(OCTOSPI1_BASE + EXTENDEDBUFFERSIZE);
     pAddr++) {
  *pAddr = counter++;
}

 

The results are:

counter     Addr                   Read at           value                 expected value
0x00         0x90000000       0x90000004   0x00000000      0x00000000 change the value at 0x90000004    
0x01         0x90000001       0x90000000   0x00000100      0x00000100  
0x02         0x90000002       0x90000000   0x00020000      0x00020100  
0x03         0x90000003       0x90000000   0x03000000      0x03020100  
0x04         0x90000004       0x90000004   0x00000004      0x00000004  also writes 0x00000000 at 0x90000008       
0x05         0x90000005       0x90000004   0x00000500      0x00000504  
0x06         0x90000006       0x90000004   0x00060000      0x00060504  
0x07         0x90000007       0x90000004   0x07000000      0x07060504  
0x08         0x90000008       0x90000005   0x00000008      0x00000008  also writes 0x00000000 at 0x9000000B

We try also to write with 32bit data, but same results.
Only when we try wrting 64bits long data, than he write correct the data to the RAM memory.
So it looks that he has only 64bit write acces to the external RAM and 8 bit read access.

What can be wrong with the settings or .....  here?
We would write the data to the memory with 8 bit access.

OGhis_0-1724267514639.png

 

 

KDJEM.1
ST Employee

Hello @OGhis ,

 

Glad to know that you are now able to write and read.

Please take a look at these discussions, I think you encountering the same issue:

Solved: Re: STM32H7B0RBT6 with QSPI PSRAM APS6404L-3SQR - STMicroelectronics Community

Solved: Re: [STM32H7B0][APS6404L-3SQR] Issue with Write in... - STMicroelectronics Community

Concerning the 64 bit writes , there is an errata "Memory-mapped write error response when DQS output is disabled".

KDJEM1_0-1724311931154.png

So with this errata we need to enable the DQS when doing memory mapped writes even if the memory do not support DQS. This is the workaround.

Now concerning products with AXI bus (OCTOSPI memory mapped region on AXI not AHB) the memory mapped write are always done in multiple of the AXI bus size (64-bit).

So when you do a 8-16-32 bit write , the OCTOSPI will output 64-bit and mask the rest using DQS pin.

The problem is if the memory do not support DQS (which is in our case), the rest of the AXI data will be written to the memory. 

Please let me know if your request in answered or not?

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.

Hi,

If I understand correctly, is there no other option than to write data to the AP6404 in 64 bit in my case due missing DQS pin?

Can we resolve the issue as follow
1 DMA in halfword mode for my ADC which stores the samples in the internal RAM memory buffer.
At the end of the tranfert we start a second DMA.
We have seen that the MDMA has a 64bit option.
With this MDMA we store the internal ADC RAM buffer to 0x900000000 in 64 bit mode and that writes the QSPI it to the AP6404.

To read back the samples, we start another MDMA in 64 bit mode to an internal RAM buffer. This buffer can then be used to send the samples via the USB interface.

Is this a good workarround solution for us?

Hi,

We also did a very simple memory test and we see that it misses a few writes.
There are between 20 and 90 locations that has an incorrect value.
The location is random and the value in memory is always 0x0000000000000000uLL.

The problem is always during the write cyclus.
What can be wrong here?

We did the memory test like this;

void test_memory(void) {

  volatile uint64_t *pAddr;
  uint64_t counter = 0x0001020304050607uLL;

  for (pAddr = (volatile uint64_t *) OCTOSPI1_BASE;
       pAddr < (volatile uint64_t *)(OCTOSPI1_BASE + EXTENDEDBUFFERSIZE);
       pAddr++) {
    *pAddr = counter;
    counter += 0x0101010101010101uLL;
  }

  uint32_t errCnt = 0;
  uint32_t cnt = 0;
  counter = 0x0001020304050607uLL;
  volatile uint64_t value;
  for (pAddr = (volatile uint64_t *) OCTOSPI1_BASE;
       pAddr < (volatile uint64_t *)(OCTOSPI1_BASE + EXTENDEDBUFFERSIZE);
       pAddr++) {
    value = *pAddr;
    if (value != counter) {
      errCnt++;
    }
    cnt++;
    counter += 0x0101010101010101uLL;
  }

  if (errCnt != 0) {
    DEBUG_LOG(DBG_Error, "RAM check errors: %d/%d", errCnt, cnt);
  }
}