Skip to main content
Visitor II
April 13, 2021
Question

STM32H723 - Problem with Serial RAM in memory mapped mode

  • April 13, 2021
  • 8 replies
  • 12092 views

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

    This topic has been closed for replies.

    8 replies

    Visitor II
    April 16, 2021

    Dear @MDell.1​,

    • Referring to the memory datasheet, the ChipSelectBoundary should be set to 10 and the Refresh parameter has to be configured depending on OCTOSPI frequency and memory's tCEM parameter respecting the formula as follows: 

    hospi1.Init.ChipSelectBoundary = 10;

    hospi1.Init.Refresh = 0; // OCTOSPI Clock period x Refresh = tCEM

    • For the DelayBlockBypass I recommend you testing the two following configurations and control their impact:

    hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED; // HAL_OSPI_DELAY_BLOCK_USED;

    • Can you also try to test using a reduced buffer size to see if there is any change.

    Chahinez.

    Visitor II
    March 30, 2022

    Dear ChahinezC, dear MDell.1,

    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

    Graduate II
    March 30, 2022

    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

    Explorer
    March 30, 2022

    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

    Graduate II
    March 30, 2022

    @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

    Explorer
    March 30, 2022

    Here is an overview of STM32/APMemory IoT RAM device supported

    covering HPI, OPI, QSPI SDR, QSPI SDR per STM32 family

    Hope this help

    0693W00000LwLa0QAF.pngFor proto: https://www2.mouser.com/c/?m=AP%20Memory%20Technology

    Explorer
    April 17, 2024

    Updated AP Memory IoT RAM (QSPI, OPI & HPI PSRAM) overview with latest STM32 MCU

    AlexAPMemory_1-1713378085336.png

     

    Explorer
    March 30, 2022

    another way to show the data

    0693W00000LwLiEQAV.png

    Explorer
    April 17, 2024

    Just updating the STM32 - IoT RAM (QSPI, Octal and  Hexa RAM) support overview

    AlexAPMemory_0-1713374986714.png

    AP MEMORY - STMicroelectronics

    Graduate II
    February 6, 2023

    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;
    	}

    Explorer
    February 6, 2023

    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

    Graduate II
    February 8, 2023

    Good question... I don't have time for bit-by-bit scope debugging of the quad signal.

    So sometimes I can't tell if the writing or the reading is the problem.

    So I will switch to HyperBus / Octo RAM anyway.

    Explorer
    February 8, 2023

    Ok that's seems good to move to OPI.

    You have a wide range of option from 64Mb up to 512Mb 1.8V or 3V

    OPI equivalent product of the QSPI product you have tested: APS6408L-OBM-BA

    0693W00000YAGuCQAX.pngFor reference and proto:

    https://eu.mouser.com/c/semiconductors/memory-ics/dram/?package%20%2F%20case=BGA-24&tradename=APMemory

    Alex

    Graduate
    April 16, 2024

    I have a question regarding the AP memory, APS6408L, I am trying to interface it with STM32U585 (actually using a B-U585I-IOT02A dev kit). I am using memory mapped mode for both read and write.

    According to the datasheet, page size is 1Kb and I saw this additional information in the datasheet,

    "7.2 Burst Type & Length
    Read and write operations are default Hybrid Wrap 32 mode. Other burst lengths of 16, 32, 64 or 1K bytes in
    standard or Hybrid wrap modes are register configurable(see Table 20). The device also includes command for
    Linear Bursting. Bursts can start on any even address. Write burst length has a minimum of 2 bytes. Read has no
    minimum length. Both write and read have no restriction on maximum burst length as long as tCEM is met."

     

    Does this mean, it is possible to write datalength of more than 1Kb at a time in memory-mapped mode? Does the page size restrict the maximum size of data that can be written at once? I am using memcpy command to copy the data.

     

    Thanks in advance.

     

    Rikesh