cancel
Showing results for 
Search instead for 
Did you mean: 

QSPI migration from STM32F7 to STM32H7

Bob Shankle
Associate III

Hi Folks,

I've been asked to look at a problem we are having migrating from the STM32F7 to STM32H7. We are seeing a problem when communicating with the QUAD SPI chip (Micron MT25QL256ABA).

(I've very familiar with SPI communications and microcontrollers, but new to STM32.)

I looked at section 2.4 of the Errata file for the H7 chip: https://www.st.com/resource/en/errata_sheet/dm00368411-stm32h742xi-g-and-stm32h743xi-g-device-limitations-stmicroelectronics.pdf but honestly I'm still going up the learning curve for this chip.

We have a sample program that writes data to the QSPI and then reads it back in MemoryMappedMode. Strangely the program runs through the tests 5 times and then fails to read the data back for comparison. Here is a section of the code that consistently fails:

	// +++++++ START MULTI-SECTOR TEST ++++++++++
	// Erase a sector then write a sector
	// Notes: EraseSector command may be size dependent.
	//        EndAddress parameter is ignored.
 
	// Need to re-init to exit Memory Mapped Mode from previous test
	CSP_QUADSPI_Init();
 
	for (var = 0; var < SECTOR_COUNT; var++)
	{
		if (CSP_QSPI_EraseSector(var * MEMORY_SECTOR_SIZE,
				(var + 1) * MEMORY_SECTOR_SIZE - 1) != HAL_OK)
		{
			while (1);  //breakpoint - error detected
		}
		Wait_For_QUADSPI();
 
		if (CSP_QSPI_WriteMemory(upCountBuffer, var * MEMORY_SECTOR_SIZE,
				sizeof(upCountBuffer)) != HAL_OK)
		{
			while (1);  //breakpoint - error detected
		}
 
		if (CSP_QSPI_EraseSector(0x1000000 + (var * MEMORY_SECTOR_SIZE),
				0x1000000 + (var * MEMORY_SECTOR_SIZE) + (MEMORY_SECTOR_SIZE - 1)) != HAL_OK)
		{
			while (1);  //breakpoint - error detected
		}
		Wait_For_QUADSPI();
 
		if (CSP_QSPI_WriteMemory(downCountBuffer, 0x1000000 + (var * MEMORY_SECTOR_SIZE),
				sizeof(downCountBuffer)) != HAL_OK)
		{
			while (1);  //breakpoint - error detected
		}
 
	 }
 
	if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK)
	{
		while (1); //breakpoint - error detected
	}
	HAL_Delay(10);
	//Wait_For_QUADSPI();
 
	// xxCountBuffer[] (size of one sector) is bulk compared against a written sector.
	// This comparison occurs, sector by sector for SECTOR_COUNT number of times.
	for (var = 0; var < SECTOR_COUNT; var++)
	{
		memcpy( upCountBuffer_read,
				(uint8_t*) (0x90000000 + (var * MEMORY_SECTOR_SIZE)), MEMORY_SECTOR_SIZE);
		if (memcmp(upCountBuffer, upCountBuffer_read, MEMORY_SECTOR_SIZE) != HAL_OK)
		{
			while (1);  //breakpoint - error detected - otherwise QSPI works properly
		}
 
		if (memcmp(upCountBuffer,
				(uint8_t*) (0x90000000 + (var * MEMORY_SECTOR_SIZE)),
				MEMORY_SECTOR_SIZE) != HAL_OK)
		{
			while (1);  //breakpoint - error detected - otherwise QSPI works properly
		}
 
		memcpy( downCountBuffer_read,
				(uint8_t*) (0x90000000 + 0x1000000 + (var * MEMORY_SECTOR_SIZE)),
				MEMORY_SECTOR_SIZE);
		if (memcmp(downCountBuffer, downCountBuffer_read, MEMORY_SECTOR_SIZE) != HAL_OK)
		{
			while (1);  //breakpoint - error detected - otherwise QSPI works properly
		}
 
		if (memcmp(downCountBuffer,
				(uint8_t*) (0x90000000 + 0x1000000 + (var * MEMORY_SECTOR_SIZE)),
				MEMORY_SECTOR_SIZE) != HAL_OK)
		{
			while (1);  //breakpoint - error detected - otherwise QSPI works properly
		}
	}

Note the Wait_For_QUADSPI() is as follows:

void Wait_For_QUADSPI(void)
{
	HAL_Delay(10);
	int count = 0;
	HAL_QSPI_StateTypeDef iState;
//return;
 
	while (1)
	{
		iState = HAL_QSPI_GetState(&hqspi);
		switch (iState)
		{
		case HAL_QSPI_STATE_RESET:
			break;
		case HAL_QSPI_STATE_READY:
			return;
		case HAL_QSPI_STATE_BUSY: 
			break;
		case HAL_QSPI_STATE_BUSY_INDIRECT_TX:
			break;
		case HAL_QSPI_STATE_BUSY_INDIRECT_RX:
			break;
		case HAL_QSPI_STATE_BUSY_AUTO_POLLING:
			break;
		case HAL_QSPI_STATE_BUSY_MEM_MAPPED:
			count++;
			if (count>10)
			{
				//HAL_QSPI_Abort(&hqspi);/// do I need to reinit?
				return;
			}
			break;
		case HAL_QSPI_STATE_ABORT:
			break;
		case HAL_QSPI_STATE_ERROR:
			break;
		default:
			break;
		}
		HAL_Delay(10);
	}
}

In previous tests it would hang if I called the above function after EnableMemoryMappedMode so I tried to abort but that didn't help. So I commented out that part and now use a hardcoded delay of 10mSec after Enableing Memory Mapped mode.

Here is the EnableMemoryMappedMode code. Note I tried with and without timeout enabled.

uint8_t CSP_QSPI_EnableMemoryMappedMode(void) {
 
	QSPI_CommandTypeDef sCommand;
	QSPI_MemoryMappedTypeDef sMemMappedCfg;
 
	/* Enable Memory-Mapped mode-------------------------------------------------- */
	/***** Assuming QUAD MODE is enabled *****/
	/***** Assuming 4-byte address mode ******/
	sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES;
	//sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_4_LINES;
	sCommand.DataMode = QSPI_DATA_4_LINES;
	sCommand.NbData = 1;
	sCommand.Address = 0;
	//sCommand.Instruction = QUAD_OUT_FAST_READ_CMD;
	sCommand.Instruction = QUAD_4BYTE_OUT_FAST_READ_CMD;
	sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_QUAD;
 
	sMemMappedCfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_ENABLE; ///QSPI_TIMEOUT_COUNTER_DISABLE;  RRS
 
	if (HAL_QSPI_MemoryMapped(&hqspi, &sCommand, &sMemMappedCfg) != HAL_OK) {
		return HAL_ERROR;
	}
	return HAL_OK;
}

Anyway, I wanted to reach out and see if there were others who had a similar issue and can share some code with how you got around the problem (ie how you implemented the suggestions in the Errata pdf.)

thanks much!

Bob

1 ACCEPTED SOLUTION

Accepted Solutions
Bob Shankle
Associate III

Hi All. Thanks for the help. For this specific case my problem was I needed to add 10 dummycycles. (The documentation for the QSPI chip said 0 dummy cycles to use, but the H7 needed the cycles for some reason.)

thanks again,

Bob

View solution in original post

9 REPLIES 9
Bob Shankle
Associate III

Hi,

I forgot to mention it fails on line 11 in the first code snippet and stays at the while(1):

	for (var = 0; var < SECTOR_COUNT; var++)
	{
		memcpy( upCountBuffer_read,
				(uint8_t*) (0x90000000 + (var * MEMORY_SECTOR_SIZE)), MEMORY_SECTOR_SIZE);
		if (memcmp(upCountBuffer, upCountBuffer_read, MEMORY_SECTOR_SIZE) != HAL_OK)
		{
			while (1);   here is where it usually fails the 5th time through the main loop (outside var=....
		}

SofLit
ST Employee

Dear @Bob Shankle​ 

Please double check if you're not in the case of the errata "2.4.4 Memory-mapped read of last memory byte fails". If yes, please configure FSIZE the double size of the real size of your memory.

SofLit

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.
Bob Shankle
Associate III

Hi SofLit,

The original value was 24, which would map to 2^(24+1) = 32MB, so I upped the value to 25, but didn't see an improvement. Thanks for suggestion.

I'll keep looking.

Bob

First rule of fight club would be to clear the auto/local variables to ensure you don't have anything wonky in there.

The wrap errata, I've seen 32-bytes out, but you look to have pinned that down.

Watch for excessive slew-rate and ringing. You should be able to edge off the speed settings on the pins for short traces.

Watch that the IO Compensation has been enabled.

Watch for caching.

The direct and memory-mapped modes can't exist concurrently. Make sure the writes/erases complete properly (dual banked devices can complete at different times), and watch that resetting out of memory-mapped doesn't clear mode/bit width settings, etc.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Bob Shankle
Associate III

thanks for suggestions.

Bob

No problem.

Might also recommend soak testing in Direct mode to assure that the QSPI FLASH is behaving as expected, and that the issue is more on the transitional/memory-mapped side.

Not got a strong opinion either way, but the QSPI FLASH don't have an async reset, and the internal state machines are less tolerant of being end-run or glitched. And most of the transitional work is about walking the part into the final state you want it, and not always starting from the state you expect it.

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

Hi @Bob Shankle​ ,

Also don't forget to disable access to the added size and leave the access to the exact size of your memory using MPU. This avoid CM7 to access (by speculation) this non-valid region.

This is a thread related to CM7 speculative access avoidance.

You can refer also to the AN4861 / section "4.6 Special recommendations for Cortex-M7 (STM32F7/H7) "

SofLit

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.
Bob Shankle
Associate III

Hi Folks,

Thanks for the suggestions. I'll check into them. (Feel free to send others, we are near the production phase for this product so feedback is much appreciated.)

Bob

Bob Shankle
Associate III

Hi All. Thanks for the help. For this specific case my problem was I needed to add 10 dummycycles. (The documentation for the QSPI chip said 0 dummy cycles to use, but the H7 needed the cycles for some reason.)

thanks again,

Bob