cancel
Showing results for 
Search instead for 
Did you mean: 

[SOLVED] Issue with custom External Loader for IS25LP256D on STM32F746 - update to V2.5.0 & deinit qspi in Init()

KShim.1738
Associate III

I've made a custom external loader for IS25LP256D on STM32F746. It works well in general except for one problem. Verify doesn't stop when "Verify Programming" is checked. Memory-mapped mode works well, which means reading is good. Erase() and Write() work well too.

Could someone please help me with this issue? I've attached the STM32 CubeProgrammer log.

#include "Loader_Src.h"
#include "quadspi.h"
#include "qflash.h"
#include <string.h>
 
 
/* Private variables ---------------------------------------------------------*/
extern QSPI_HandleTypeDef hqspi;
 
 
void Error_Handler(void) {
}
 
int Init (uint8_t configureMemoryMappedMode)
{
	SystemInit();
        HAL_Init();
	SystemClock_Config();
  
	/* Initialize QuadSPI ------------------------------------------------------ */
	MX_QUADSPI_Init();
 
    spiResetMemory();
    spiEnableQEOnFlash();
    spiSetDummyCyclesOnFlash();
 
 
	if(!configureMemoryMappedMode)
	{
		if(spiEnterMemoryMappedMode() != 0)
		{
			return 0;
		}
	}
 
	return 1;
}
 
KeepInCompilation int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
{      
  uint32_t BlockAddr;
  EraseStartAddress = EraseStartAddress - (EraseStartAddress % 0x10000);
 
  spiEnterIndirectMode();
 
  while(EraseEndAddress >= EraseStartAddress)
  {      
    BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    if(spiErase64k(BlockAddr) != 0)
    {
      return 0;
    }
    EraseStartAddress += 0x10000;
  }
  
  return 1;	
}
 
KeepInCompilation int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
{
  uint32_t offset;
 
  offset = Address & 0x0FFFFFFF;
 
  spiEnterIndirectMode();
 
	if(spiWrite(offset, buffer, Size) == 0)
		return 1;
	else
		return 0;
}
 
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  return HAL_OK;
}
 
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
 
  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 216;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Activate the Over-Drive mode
  */
  if (HAL_PWREx_EnableOverDrive() != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART5;
  PeriphClkInitStruct.Uart5ClockSelection = RCC_UART5CLKSOURCE_PCLK1;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}
 
uint32_t CheckSum(uint32_t StartAddress, uint32_t Size, uint32_t InitVal)
{
  uint8_t missalignementAddress = StartAddress%4;
  uint8_t missalignementSize = Size ;
  int cnt;
  uint32_t Val;
	
  StartAddress-=StartAddress%4;
  Size += (Size%4==0)?0:4-(Size%4);
  
  for(cnt=0; cnt<Size ; cnt+=4)
  {
    Val = *(uint32_t*)StartAddress;
    if(missalignementAddress)
    {
      switch (missalignementAddress & 0xF)
      {
        case 1:
          InitVal += (uint8_t) (Val>>8 & 0xff);
          InitVal += (uint8_t) (Val>>16 & 0xff);
          InitVal += (uint8_t) (Val>>24 & 0xff);
          missalignementAddress-=1;
          break;
        case 2:
          InitVal += (uint8_t) (Val>>16 & 0xff);
          InitVal += (uint8_t) (Val>>24 & 0xff);
          missalignementAddress-=2;
          break;
        case 3:   
          InitVal += (uint8_t) (Val>>24 & 0xff);
          missalignementAddress-=3;
          break;
      }  
    }
    else if((Size-missalignementSize)%4 && (Size-cnt) <=4)
    {
      switch ((Size-missalignementSize) & 0xF)
      {
        case 1:
          InitVal += (uint8_t) Val;
          InitVal += (uint8_t) (Val>>8 & 0xff);
          InitVal += (uint8_t) (Val>>16 & 0xff);
          missalignementSize-=1;
          break;
        case 2:
          InitVal += (uint8_t) Val;
          InitVal += (uint8_t) (Val>>8 & 0xff);
          missalignementSize-=2;
          break;
        case 3:   
          InitVal += (uint8_t) Val;
          missalignementSize-=3;
          break;
      } 
    }
    else
    {
      InitVal += (uint8_t) Val;
      InitVal += (uint8_t) (Val>>8 & 0xff);
      InitVal += (uint8_t) (Val>>16 & 0xff);
      InitVal += (uint8_t) (Val>>24 & 0xff);
    }
    StartAddress+=4;
  }
  
  return (InitVal);
}
 
KeepInCompilation uint64_t Verify (uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement)
{
    uint32_t InitVal = 0;
    uint32_t VerifiedData = 0;
    uint64_t checksum;
 
    Size*=4;
 
#if 0
    spiResetMemory();
    spiEnableQEOnFlash();
    spiSetDummyCyclesOnFlash();
#endif
 
  if(spiEnterMemoryMappedMode() != 0)
    return 0;
 
	checksum = CheckSum((uint32_t)MemoryAddr + (missalignement & 0xf), Size - ((missalignement >> 16) & 0xF), InitVal);  
	
  while (Size>VerifiedData)
  {
		if ( *(uint8_t*)MemoryAddr++ != *((uint8_t*)RAMBufferAddr + VerifiedData))
			return ((checksum<<32) + (MemoryAddr + VerifiedData));  
		
		VerifiedData++;  
  }
        
  return (checksum<<32);
}
 
int MassErase(void)
{
  spiEnterIndirectMode();
  spiEraseChip();
 
  return 1;
}

After programming, the contents look good. I've run TouchGFX app with IS25LP256D, which contains images, fonts and texts. It is about 11MB in total. If the external image is not written in place, TouchGFX will cause a hard fault. But after programming external flash, it runs well.

Log generated the following messages until I pressed the stop button. Without verification my external loader has no problem.

20:50:01:397 : Init flashloader...
20:50:01:406 : halt ap 0
20:50:01:406 : w ap 0 reg 0 0x00000000
20:50:01:406 : w ap 0 reg 1 0x00000000
20:50:01:406 : w ap 0 reg 2 0x00000000
20:50:01:406 : w ap 0 reg 3 0x00000000
20:50:01:406 : w ap 0 reg 4 0x00000000
20:50:01:406 : w ap 0 reg 5 0x00000000
20:50:01:406 : w ap 0 reg 6 0x00000000
20:50:01:406 : w ap 0 reg 7 0x00000000
20:50:01:406 : w ap 0 reg 8 0x00000000
20:50:01:406 : w ap 0 reg 9 0x00000000
20:50:01:406 : w ap 0 reg 10 0x00000000
20:50:01:406 : w ap 0 reg 11 0x00000000
20:50:01:406 : w ap 0 reg 12 0x00000000
20:50:01:406 : w ap 0 reg 13 0x00000000
20:50:01:406 : w ap 0 reg 14 0x20000001
20:50:01:406 : w ap 0 reg 15 0x2000063B
20:50:01:406 : w ap 0 reg 16 0x01000000
20:50:01:406 : w ap 0 reg 17 0x2000E7D8
20:50:01:406 : w ap 0 reg 18 0x00000000
20:50:01:407 : run ap 0
20:50:01:407 : halt ap 0
20:50:01:407 : r ap 0 reg 0 0x00000001
20:50:01:475 : r ap 0 @0x90000000 0x00000400 bytes
20:50:01:476 : Reading data...
20:50:01:476 : reset ap 0
20:50:01:476 : run ap 0
20:50:01:476 : halt ap 0 Status = 0
20:50:01:476 : w ap 0 reg 15 (0x20000000)
20:50:01:476 : w ap 0 reg 17 (0x20000500)
20:50:01:476 : w ap 0 reg 16 (0x01000000)
20:50:01:476 : w ap 0 @0x2000E800 0x00000200 bytes
20:50:01:476 : w ap 0 @0x20000000 0x00000004 bytes
20:50:01:768 : w ap 0 @0x20000004 0x0000E3D8 bytes
20:50:01:768 : Init flashloader...
20:50:01:775 : halt ap 0
20:50:01:775 : w ap 0 reg 0 0x00000000
20:50:01:775 : w ap 0 reg 1 0x00000000
20:50:01:775 : w ap 0 reg 2 0x00000000
20:50:01:775 : w ap 0 reg 3 0x00000000
20:50:01:775 : w ap 0 reg 4 0x00000000
20:50:01:775 : w ap 0 reg 5 0x00000000
20:50:01:775 : w ap 0 reg 6 0x00000000
20:50:01:775 : w ap 0 reg 7 0x00000000
20:50:01:775 : w ap 0 reg 8 0x00000000
20:50:01:775 : w ap 0 reg 9 0x00000000
20:50:01:775 : w ap 0 reg 10 0x00000000
20:50:01:775 : w ap 0 reg 11 0x00000000
20:50:01:775 : w ap 0 reg 12 0x00000000
20:50:01:775 : w ap 0 reg 13 0x00000000
20:50:01:775 : w ap 0 reg 14 0x20000001
20:50:01:775 : w ap 0 reg 15 0x2000063B
20:50:01:775 : w ap 0 reg 16 0x01000000
20:50:01:775 : w ap 0 reg 17 0x2000E7D8
20:50:01:775 : w ap 0 reg 18 0x00000000
20:50:01:775 : run ap 0
20:50:01:835 : halt ap 0
20:50:01:835 : r ap 0 reg 0 0x00000001
20:50:01:835 : r ap 0 @0x90000000 0x00000400 bytes
20:50:01:835 : Reading data...
20:50:01:835 : reset ap 0
20:50:01:835 : run ap 0
20:50:01:852 : halt ap 0 Status = 0
20:50:01:852 : w ap 0 reg 15 (0x20000000)
20:50:01:852 : w ap 0 reg 17 (0x20000500)
20:50:01:852 : w ap 0 reg 16 (0x01000000)
20:50:01:853 : w ap 0 @0x2000E800 0x00000200 bytes
20:50:01:853 : w ap 0 @0x20000000 0x00000004 bytes
20:50:02:031 : aborting on going operation...
20:50:02:146 : w ap 0 @0x20000004 0x0000E3D8 bytes
20:50:02:146 : Operation canceled
20:50:02:165 : Error: Failed to read memory at address 0x90000000 during verification
20:50:02:165 : Error: Download verification failed
20:50:02:216 : RUNNING Program ...
20:50:02:216 : Address: : 0x08020000
20:50:02:267 : Application is running
20:50:02:274 : Start operation achieved successfully

7 REPLIES 7
MM..1
Chief II

Hi , i see in log only one possible trouble read

r ap 0 @0x90000000 0x00000400 bytes

but write only

w ap 0 @0x2000E800 0x00000200 bytes

Maybe an trouble with block sizes.

KShim.1738
Associate III

Here is the Dev_Info.c

I tested the loader with the device type of NOR_FLASH. But the same.

struct StorageInfo const StorageInfo  =  {
#endif
	"IS25LP256D_F7SEALER", // Device Name + EVAL Borad name
	SPI_FLASH,                  // Device Type
	0x90000000,                 // Device Start Address
	0x2000000,                  // Device Size in Bytes (32MBytes)
        /* Max page Size for writing per command 256Bytes - data sheet 8.11 */
	0x100,
	0xFF,                       						        // Initial Content of Erased Memory
	// Specify Size and Address of Sectors (view example below)
        {
            /* (Sector Num) * (Sector Size) = (Device Size in Bytes) */
            { 0x00000200, 0x00010000, },
            // Sector Num : 512 ,Sector Size: 64KBytes
            { 0x00000000, 0x00000000, }
        },
};

Thank you for your advice. But I found out that there is nothing wrong with my external loader. It just takes too long Cube Programmer to read the external flash memory.

KShim.1738
Associate III

I tested with much short bin files.

  1. 1392 bytes (actually text file. but I changed the file extension to bin. And specify the address at 0x91000000)
  2. 382316 byts(actually intflash.bin. programmed at 0x91000000)

for the first file, it finishes very quick without any errors, which is expected because the file size is just about 1KB.

for the second file, it takes 3 mins. The size is about 380KB.

Actual size of my external binary is 11,180,648 Bytes i.e. ~11MB. It would take 87 mins, or 1 hour and 27 mins.

It looks like Cube Programmer calls Init() every time it reads 0x400 bytes from the external flash.

KShim.1738
Associate III

I updated the STM32 CubeProgrammer to 2.5.0 from 2.1.0 with the hope it's been improved. CubeProgrammer doesn't call Init() every time it reads 0x400 bytes. But it stops working while reading the external flash for verification.

One more issue added with v2.5.0. I cannot read the external flash twice. To read the flash again I have to press "System Reset" button on CPU Tab.

KShim.1738
Associate III

I've got the answer to the problems of my loader with Cube Programmer v2.5.0 from one of my colleagues.

insert HAL_QSPI_DeInit(&hqspi) into Init().

int Init (uint8_t configureMemoryMappedMode)
{
    extern volatile uint32_t gfQPImode;
    uint32_t i;
    uint8_t *ptr;
 
    gfQPImode = QSPI_QPI_MODE_DISABLED;
 
    ptr = (uint8_t *)&hqspi;
    for(i=0; i<sizeof(hqspi); i++) {
        *(ptr + i) = 0x00;
    }
 
  /* Enable the CPU Cache */
    SystemInit();
    SystemClock_Config();
  
    hqspi.Instance = QUADSPI;
    HAL_QSPI_DeInit(&hqspi);
    MX_QUADSPI_Init();
 
    spiResetMemory();
    spiEnter32bitAddressMode();
    spiEnableQEOnFlash();
    spiSetDummyCyclesOnFlash();
 
    if(!configureMemoryMappedMode)
    {
        if(spiEnterMemoryMappedMode() != 0)
        {
            return 0;
        }
    }
 
    return 1;
}

ST does try and switch back-n-forth between memory-mapped mode, they use init/reset, but strikes me they just need a better method. Overall the whole loader/programmer model is very poorly conceived and executed, with a lot of unwritten rules and undisclosed logic.

The QSPI memories typically don't have an async-reset, so carry over settings/history regardless of what the STM32 side peripheral is up to.

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