cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H723 - Problem with Serial RAM in memory mapped mode

MDell.1
Associate II

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

14 REPLIES 14
ChahinezC
Lead

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.

RF_Tom
Associate

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

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

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Alex - APMemory
Senior II

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

@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

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Alex - APMemory
Senior II

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

Alex - APMemory
Senior II

another way to show the data

0693W00000LwLiEQAV.png

LCE
Principal

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

Alex - APMemory
Senior II

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