2021-04-12 11:07 PM
Hi everyone,
I’m testing a 32Mb serial QUAD SPI RAM (ISSI – ISS66WVS4M8) with the nucleo – H723ZG development board.
It works fine if I use all the QUAD SPI commands but my need is to use it in memory mapped mode.
When i use the memory mapped mode configuration I notice a problem in the write phase.
The test that I’m doing consists in writing a sector of the memory and then reading it, checking that both operations are fine.
So, after having set the memory mapped configuration, I try to write with a for cycle 128 values inside the memory.
Here is the strange thing: if these 128 values that I try to write are a “uint64_t�? type the write operation works fine.
But if I use “uint8_t�? or uint32_t�? type, write operation don’t work and some of the values read in the memory are not correct.
I also have noticed that with the 64bit variable, the “CHIP SELECT�? signal is correctly set low at the beginning of the operation and set high just at the end of the whole operation, while with the 32 o 8 bit variable CS goes low and high several times.
Do you have any suggesions? I attach the code
Best Regards,
Mattia
2021-04-16 07:05 AM
Dear @MDell.1,
hospi1.Init.ChipSelectBoundary = 10;
hospi1.Init.Refresh = 0; // OCTOSPI Clock period x Refresh = tCEM
hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED; // HAL_OSPI_DELAY_BLOCK_USED;
Chahinez.
2022-03-30 08:40 AM
are there any update on this issue? I've also run into the same problem using the STM32H723 (Rev. Z) OSPI in QSPI memory mapped I/O mode in combination with an AP Memory APS6404L-3SQN PSRAM.
All 64 bit memory mapped write and read accesses (u/int64_t, double) work totally fine, confirmed by both using memory test patterns and a logic analyzer on the QSPI interface. Readbacks are performed flawlessly even after several hours of continuous reading from the PSRAM, which seems to imply, that the ChipSelectBoundary and Refresh / tCEM timings are suitable for my device and no corruption due to missed DRAM refreshes via /CS over-extension occur.
Smaller requested I/O transfer sizes (i.e. u/int8_t ... u/int32_t, float) are always corrupted in the write process, as the OSPI interfaces seems to add padding zeros (confirmed via readback and logic analyzer) to achieve a 64 bit total transfer size.
When two consecutive uint8_t = 0xAA writes are performed, the actual data transmitted to the PSRAM is 0xAA00000000000000AA00000000000000, i.e. 128 bits, instead of 16 bits. Since the CLK is running normally during the "zero" phase, the address counter in the PSRAM keeps updating and the zeros get written, corrupting the original data structure.
This was verified by using both own code and using the source code and configuration procedures presented in AN5050 (Rev. 7) "Octo-SPI interface on STM32 microcontrollers" using the QSPI-PSRAM section. Since the code in AN5050 uses byte-wise R/W access via an __io uint8_t * pointer, the verification read back fails due to padding-zeros added during the write process.
As the PSRAM I'm using does not support DQS signaling I'm applying the hard-fault mitigation technique outlined the errata sheet (Rev. 7, March 2022, 2.7.6 Memory-mapped write error response when DQS output is disabled), i.e. the DQSE bit is set for writes, despite DQS not being physically connected and not being mapped via the GPIO matrix to a pin.
In the last paragraph of 2.7.6. the following statement is made regarding the workaround:
"If the DQS output is asserted on memory-mapped writes while the AXI bus transfer has some byte-enable bits deasserted, the bytes that should be masked get written to the memory."
This seems awfully close to the effect MDell.1 and I am experiencing, as the transfer is always the width of the AXI bus (64 bit) and the data width signaled by the AXI bus interface (byte-enable bits) is ignored.
Is there any updated mitigation technique available to avoid this effect?
Otherwise this would effectively result in the conclusion, that memory-mapped writes via OSPI in QSPI mode are only possible without data (structure) corruption, when the data structures are aligned to the 64 bit width of the AXI bus data interface...
Best Regards
Tom
2022-03-30 01:00 PM
You really should work with the AP Memory people, they are probably best equipped to understand the mechanics, limitations and expectations here, and probably understand the IP ST is using better. @Alex - APMemory
Related
https://community.st.com/s/question/0D53W00000NVsy1SAD/esppsram64h-and-stm32h743-qspi-mode
2022-03-30 01:30 PM
Hi,
We have studied this case and concluded it's not possible to get full functionnality of STM32H7A (also H72x/3x) with QSPI SDR. But it works well with QSPI DDR (7pins, 128Mb in WLCSP) and OPI (11pins, 64Mb~512Mb BGA24/WLCSP). Other STM32 works well also with all device, including QSPI SDR (6 pins, 16Mb~128Mb, SOP8/USON/WLCSP).
Regards
Alex
2022-03-30 01:54 PM
@Alex - APMemory Thanks Alex for your follow up here, I appreciate your diligence.
Do you have a single-sheet or matrix of parts/modes for your parts vs STM32 parts?
I think having a clear break-down of working combos, and perhaps github BSP code, would do a lot to increase successful design-in of parts.
Yes, I think the OCTOSPI IP for the H7Ax,H7Bx,H72x,H73x are common
2022-03-30 03:01 PM
Here is an overview of STM32/APMemory IoT RAM device supported
covering HPI, OPI, QSPI SDR, QSPI SDR per STM32 family
Hope this help
For proto: https://www2.mouser.com/c/?m=AP%20Memory%20Technology
2022-03-30 03:17 PM
another way to show the data
2023-02-06 03:02 AM
On a H723, after playing with the timings (clock rate, delay block) and enabling DQS in WCCR also for SDR Quad SPI (errata: turn DQS on even when not using it), it looks for now that I can do memory mapped writes to a quad SPI PSRAM (APS6404L-3SQN).
But ONLY when using 4- or 8-byte access.
Can anybody confirm that?
It's behaving a little odd, though, concerning
a) speed (after some compilations I get only 120 Mbit/s at 50 MHz when writing)
b) page size (1 kB of APS6404L) wrap, sometimes it wraps, sometimes it doesn't...
I've attached the octospi source, and here's my test code:
/* MEMORY MAPPED mode READ test */
case UCMND_QSPIR_TST_MM:
{
uint32_t i = 0;
uint32_t u32MemAddr = 0;
uint32_t u32RdLen = 0;
uint32_t u32CycStart = 0;
uint32_t u32CycStop = 0;
float flClockMHz = (float)HAL_RCC_GetSysClockFreq() / 1E6;
#define QSPI_MEM_TEST_RD_LEN 1504
uint8_t u8MemBuf[QSPI_MEM_TEST_RD_LEN];
uint8_t *pu8MemAddr = NULL;
/* get start address and read length */
sscanf((char *)u8Uart3RxBuf, "%*s %*s %lX %lX", &u32MemAddr, &u32RdLen);
if( u32RdLen == 0 ) u32RdLen = QSPI_MEM_TEST_RD_LEN;
if( u32RdLen > QSPI_MEM_TEST_RD_LEN ) u32RdLen = QSPI_MEM_TEST_RD_LEN;
if( u32MemAddr > (QSPIMEM_ADDR_MAX - u32RdLen) ) u32MemAddr = QSPIMEM_ADDR_MAX - u32RdLen;
uart_printf("u32MemAddr = %08lX\n\r", u32MemAddr);
uart_printf("u32RdLen = %08lX = %lu\n\r", u32RdLen, u32RdLen);
if( u8QSpiMode != QSPI_MODE_QUAD_MM )
{
/* QUAD mode */
if( OctoSpi1CmndQuadOn() != 0 ) uart_printf("\n\r# ERR: OctoSpi1CmndQuadOn()\n\r");
else uart_printf("OctoSpi1CmndQuadOn() okay\n\r");
/* memory mapped mode */
if( OctoSpi1QuadMemMapOn() != 0 ) uart_printf("\n\r# ERR: OctoSpi1QuadMemMapOn()\n\r");
else uart_printf("OctoSpi1QuadMemMapOn() okay\n\r");
}
/* reset buffer */
for( i = 0; i < QSPI_MEM_TEST_RD_LEN; i++ ) u8MemBuf[i] = 0xA5;
/* set pointer */
pu8MemAddr = (uint8_t *)(OCTOSPI1_BASE + u32MemAddr);
uart_printf("pu8MemAddr = %08lX\n\r", (uint32_t)pu8MemAddr);
__disable_irq();
/* save cycle counter */
u32CycStart = DWT->CYCCNT;
/* read quad SPI memory */
for( i = 0; i < u32RdLen; i++ )
{
u8MemBuf[i] = *pu8MemAddr;
pu8MemAddr++;
}
/* save cycle counter */
u32CycStop = DWT->CYCCNT;
__DSB();
__ISB();
__enable_irq();
/* display */
uint32_t j = 0;
for( i = 0; i < u32RdLen; i++ )
{
if( i % 16 == 0 )
{
uart_printf("\n\r%06lX: ", (i + u32MemAddr));
for( j = 0; j < 16; j++ ) uart_printf("%02X ", u8MemBuf[i + j]);
for( j = 0; j < 16; j++ )
if( u8MemBuf[i + j] >= ' ' && u8MemBuf[i + j] <= '~' )
uart_printf("%c", (char)u8MemBuf[i + j]);
else
uart_printf(".");
}
}
uart_printf("\n\r\n\r");
u32Val = u32CycStop - u32CycStart;
flVal = (float)u32Val / flClockMHz;
uart_printf("pu8MemAddr = %08lX\n\r", (uint32_t)pu8MemAddr);
uart_printf("%lu bytes read\n\r", u32RdLen);
uart_printf("%lu CPU cycles -> %.1f us\n\r", u32Val, flVal);
uart_printf("-> %.2f Mbit/s\n\r", (8.0f * (float)u32RdLen / flVal));
break;
}
/* MEMORY MAPPED mode WRITE test */
case UCMND_QSPIR_TST_MM:
{
uint32_t i = 0;
uint32_t u32MemAddr = 0;
uint32_t u32WrLen = 0;
uint32_t u32CycStart = 0;
uint32_t u32CycStop = 0;
float flClockMHz = (float)HAL_RCC_GetSysClockFreq() / 1E6;
#define QSPI_MEM_TEST_WR_LEN 1504
uint8_t u8MemBuf[QSPI_MEM_TEST_WR_LEN];
uint8_t *pu8MemAddr = NULL;
uint32_t *pu32MemAddr = NULL;
uint64_t *pu64MemAddr = NULL;
/* get start address and write length */
sscanf((char *)u8Uart3RxBuf, "%*s %*s %lX %lX", &u32MemAddr, &u32WrLen);
if( u32WrLen == 0 ) u32WrLen = QSPI_MEM_TEST_WR_LEN;
if( u32WrLen > QSPI_MEM_TEST_WR_LEN ) u32WrLen = QSPI_MEM_TEST_WR_LEN;
if( u32MemAddr > (QSPIMEM_ADDR_MAX - u32WrLen) ) u32MemAddr = QSPIMEM_ADDR_MAX - u32WrLen;
uart_printf("u32WrLen = %08lX = %lu\n\r", u32WrLen, u32WrLen);
uart_printf("u32MemAddr = %08lX\n\r", u32MemAddr);
if( u8QSpiMode != QSPI_MODE_QUAD_MM )
{
/* QUAD mode */
if( OctoSpi1CmndQuadOn() != 0 ) uart_printf("\n\r# ERR: OctoSpi1CmndQuadOn()\n\r");
else uart_printf("\n\rOctoSpi1CmndQuadOn() okay\n\r");
/* memory mapped mode */
if( OctoSpi1QuadMemMapOn() != 0 ) uart_printf("\n\r# ERR: OctoSpi1QuadMemMapOn()\n\r");
else uart_printf("\n\rOctoSpi1QuadMemMapOn() okay\n\r");
}
/* fill buffer */
if( u8Uart3RxBuf[UART_CMND_BYTE_REGVAL1] == '0' )
{
for( i = 0; i < u32WrLen; i++ ) u8MemBuf[i] = 0;
uart_printf("buffer reset to all 0\n\r");
}
else
{
/* hex counter display */
#if( 0 )
for( i = 0; i < u32WrLen; i++ ) u8MemBuf[i] = (uint8_t)(i & 0x000000FF);
/* display buffer */
uart_printf("buffer to write is:\n\r");
for( i = 0; i < u32WrLen; i++ )
{
if( i % 16 == 0 ) uart_printf("\n\r%04lX: ", i);
uart_printf("%02X ", u8MemBuf[i]);
}
/* text display */
#else
char szTest[] = "MemMapWrite_16_B";
uint32_t j = 0;
for( i = 0; i < u32WrLen; i++ )
{
u8MemBuf[i] = (uint8_t)szTest[j++];
if( j == 16 ) j = 0;
}
/* display buffer */
uart_printf("buffer to write is:\n\r");
for( i = 0; i < u32WrLen; i++ )
{
if( i % 16 == 0 ) uart_printf("\n\r%04lX: ", i);
uart_printf("%c", (char)u8MemBuf[i]);
}
#endif
uart_printf("\n\r\n\r");
}
/* set pointer */
pu8MemAddr = (uint8_t *)(OCTOSPI1_BASE + u32MemAddr);
pu32MemAddr = (uint32_t *)(OCTOSPI1_BASE + u32MemAddr);
pu64MemAddr = (uint64_t *)(OCTOSPI1_BASE + u32MemAddr);
uart_printf("pu8MemAddr = %08lX\n\r", (uint32_t)pu8MemAddr);
uart_printf("pu32MemAddr = %08lX\n\r", (uint32_t)pu32MemAddr);
uart_printf("pu64MemAddr = %08lX\n\r", (uint32_t)pu64MemAddr);
uart_printf("u32WrLen = %ld\n\r", u32WrLen);
/* WRITE quad SPI memory */
#if( 0 )
/* byte */
uart_printf("### using pu_8_MemAddr ###\n\r");
__disable_irq();
/* save cycle counter */
u32CycStart = DWT->CYCCNT;
for( i = 0; i < u32WrLen; i++ )
{
*pu8MemAddr = u8MemBuf[i];
pu8MemAddr++;
}
#else
#if( 1 )
/* 32 bit words */
uart_printf("### using pu_32_MemAddr ###\n\r");
__disable_irq();
/* save cycle counter */
u32CycStart = DWT->CYCCNT;
u32WrLen /= 4;
for( i = 0; i < u32WrLen; i++ )
{
pu32MemAddr[i] = *((uint32_t *)&u8MemBuf[i * 4]);
}
#else
/* 64 bit words */
uart_printf("### using pu_64_MemAddr ###\n\r");
__disable_irq();
/* save cycle counter */
u32CycStart = DWT->CYCCNT;
u32WrLen /= 8;
for( i = 0; i < u32WrLen; i++ )
{
pu64MemAddr[i] = *((uint64_t *)&u8MemBuf[i * 8]);
//*pu64MemAddr = *((uint64_t *)&u8MemBuf[i * 8]);
//pu64MemAddr++;
}
#endif
#endif
/* save cycle counter */
u32CycStop = DWT->CYCCNT;
__DSB();
__ISB();
__enable_irq();
u32Val = u32CycStop - u32CycStart;
flVal = (float)u32Val / flClockMHz;
uart_printf("%lu words written\n\r", u32WrLen);
uart_printf("%lu CPU cycles -> %.1f us\n\r", u32Val, flVal);
//uart_printf("-> %.2f Mbit/s\n\r", (8.0f * (float)u32WrLen / flVal));
uart_printf("-> %.2f Mbit/s\n\r", (64.0f * (float)u32WrLen / flVal));
/* analysis */
uart_printf("check QSPI memory with 'cr Qm'\n\r");
break;
}
2023-02-06 03:59 AM
Hi
From our understanding, STM32H723 doesn't support Memory mapped write for QSPI SDR (no issue for QSPI DDR and OPI)
Does the issue seen are only for Write or also Read ?
Regards
Alex