2024-08-18 12:33 PM
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();
}
}
2024-08-19 01:21 AM
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.
2024-08-19 12:13 PM - edited 2024-08-19 12:54 PM
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(); }
}
Are these settings coorect?
Btw: when executing the commands, we don't have any signal of the QSPI bus
2024-08-20 02:28 AM
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.
2024-08-20 12:26 PM
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)
2024-08-21 12:56 AM
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:
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.
2024-08-21 12:21 PM - edited 2024-08-21 12:49 PM
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.
2024-08-22 12:39 AM - edited 2024-08-22 01:30 AM
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".
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.
2024-08-22 03:20 AM
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?
2024-08-22 11:28 AM
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);
}
}