cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 external loader

PFlor.2
Senior

I went through the ST External QSPI Loader how to and have verified my quadspi.c/h driver is good with the Winbond W25Q64 flash part we are using.  However after I create the .stldr and load it into the STM32 Programmer it will not read from the flash...get Error:data read failed.  I've attached the project if someone could help me out with what could be wrong?

20 REPLIES 20

the Loader_Src.c Init() is only called in the entry and resets and initializes the quadspi so I wouldn't think it would need QSPI Abort in there.  The loader does call QSPI Abort before write, erase is done.  Maybe I'm missing something in what you are suggesting.

PFlor2_0-1708048006797.png

 

Since the programmer successfully reads the flash when it initially connects, what does the programmer do when the READ button is pressed that it would fail?  I would think the loader is still in memory mapped mode since the HAL_QSPI_Abort() is only called at the beginning of the Write and Erase functions of the loader.

The loader example I'm using only enables memory mapped mode in the Init and Verify functions, should this be done somewhere else for the READ to work or does the READ just call the Init() of the loader?

PFlor.2
Senior

Found a resolution for this issue...

Apparently the enabling interrupts in the Init() function of the Loader_Src.c provided in the ST repository for the H7 external loader was hanging after it entered this function the first time.  Made the modifications below per ST suggestion...

PFlor2_0-1710859554743.png 

Once this was done however the HAL_Delay() in my QuadSPI drivers would hang (made sense since interrupts are disabled I guess) so I also had to include a local HAL_Delay() function that used NOP instead of ticks.

I added this in my quadspi.c file...

PFlor2_1-1710859743199.png

ST support is still looking into whey the enabling of interrupts is hanging in this case.

 

They need to move to a model that doesn't use interrupts.

If the board is running at 64 MHz, you can enable TIM2 / TIM5, with PSC=64000-1, ARR=0xFFFFFFFF, and you can pull the millisecond count from TIM->CNT

Using HAL_Delay() will make things slow. The HAL_GetTick() should be used to provide a bounding timeout, but functions should return as soon as the device is ready / finished.

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

Tesla I have a mostly working external loader I developed for the Windbond W25Q64JV but for some reason the "Erase Chip" function for the external flash fails with the STM32Programmer as seen below...

PFlor2_0-1733777190465.png

 

This function works fine with the loader you provided for me (CLIVEONE-W25Q64_STM32H7XX-PB2-PB6-PD11-PF9-PE2-PA1.stldr) back in May 2023.  Could you share your W25Q64 driver code with me so I can see what is wrong with mine?

If it returns immediately with an error, instrument to determine why.

For example the QSPI functions return an error due to being in Memory Mapped mode.

The device can complete immediately if protection bits are set, but this usually means it exits the Wait Complete early, but with no error returned.

Use Verbose Level 3 to see what the MassErase() call is returning to STM32 Cube Programmer

 

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

Thanks for the response....

I've tried to increment some counters in RAM to see if I'm getting any errors from the QSPI HAL functions but it doesn't seem to be telling me anything.

It does not return immediately as a failure, spends about 15sec or so trying to do the erase.

When I set the Verbosity Level to 3 the first time it successfully completed the mass erase...but now fails again.  I attached the log file

PFlor2_0-1733784139737.png  

PFlor2_1-1733784171747.png

 

 

The Sector Erase works fine and I could've sworn the mass erase worked fine too when I tested this back in April, but now that I have a new laptop and installed a newer version of the STM32Programmer it fails.

Here is my Loader_Src function for sector and Mass erase:

 

/**
 * @brief   Sector erase.
 *    EraseStartAddress :  erase start address
 *    EraseEndAddress   :  erase end address
 * @retval  LOADER_OK = 1       : Operation succeeded
 * @retval  LOADER_FAIL = 0 : Operation failed
 */
int SectorErase(uint32_t EraseStartAddress, uint32_t EraseEndAddress) {

  __set_PRIMASK(0); //enable interrupts

  if (HAL_QSPI_Abort(&hqspi) != HAL_OK) {
    __set_PRIMASK(1); //disable interrupts
    return LOADER_FAIL;
  }

  if (CSP_QSPI_EraseSector(EraseStartAddress, EraseEndAddress) != HAL_OK) {
    __set_PRIMASK(1); //disable interrupts
    return LOADER_FAIL;
  }

  __set_PRIMASK(1); //disable interrupts
  return LOADER_OK;
}

/**
 * Description :
 * Mass erase of external flash area
 * Optional command - delete in case usage of mass erase is not planed
 * Inputs    :
 *      none
 * outputs   :
 *     none
 * Note: Optional for all types of device
 */
int MassErase(void) {

  __set_PRIMASK(0); //enable interrupts

  if (HAL_QSPI_Abort(&hqspi) != HAL_OK) {
    *( (uint32_t *)ERASEMASSERR_ADDR) = ++ldrTest.errEraseMassAbort_cnt;
    __set_PRIMASK(1); //disable interrupts
    return LOADER_FAIL;
  }


  if (CSP_QSPI_Erase_Chip() != HAL_OK) {
    *( (uint32_t *)ERASEMASSERR_ADDR) = ++ldrTest.errEraseMassAbort_cnt;
    __set_PRIMASK(1); //disable interrupts
    return LOADER_FAIL;
  }

  *( (uint32_t *)ERASEMASS_ADDR) = ++ldrTest.errEraseMass_cnt;
  __set_PRIMASK(1); //disable interrupts
  return LOADER_OK;
}

 

...and also the sector and mass erase driver code...

 

uint8_t CSP_QSPI_EraseBlock(uint32_t flash_address) {
  QSPI_CommandTypeDef sCommand = { 0 };
  HAL_StatusTypeDef ret;

  sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
  sCommand.AddressSize = QSPI_ADDRESS_24_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;

  /* Enable write operations ------------------------------------------- */
  if ((ret = QSPI_WriteEnable()) != HAL_OK) {
    return ret;
  }

  /* Erasing Sequence -------------------------------------------------- */
  sCommand.Instruction = BLOCK_ERASE_CMD;
  sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
  sCommand.Address = flash_address;
  sCommand.DataMode = QSPI_DATA_NONE;
  sCommand.DummyCycles = 0;

  if ((ret = HAL_QSPI_Command(&hqspi, &sCommand,
      HAL_QPSI_TIMEOUT_DEFAULT_VALUE)) != HAL_OK) {
    return ret;
  }

  /* Configure automatic polling mode to wait for end of erase */
  if ((ret = QSPI_AutoPollingMemReady()) != HAL_OK)
  {
    return ret;
  }

  return HAL_OK;
}


uint8_t CSP_QSPI_Erase_Chip(void) {

  QSPI_CommandTypeDef sCommand = { 0 };

  if (QSPI_WriteEnable() != HAL_OK) {
    return HAL_ERROR;
  }

  /* Erasing Sequence --------------------------------- */
  sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
  sCommand.Instruction = CHIP_ERASE_CMD;
  sCommand.AddressMode = QSPI_ADDRESS_NONE;
  sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  sCommand.DataMode = QSPI_DATA_NONE;
  sCommand.DummyCycles = 0;
  sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
  sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
  sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;

  if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_MAX_DELAY) != HAL_OK) {
    return HAL_ERROR;
  }

  if (QSPI_AutoPollingMemReady() != HAL_OK) {
    return HAL_ERROR;
  }

  return HAL_OK;

}

 

I see no reason why it shouldn't be working.

 

What's the timeout for QSPI_AutoPollingMemReady() ?

Chip Erase time is quoted at between 20 and 100 seconds (on W25Q64 docs to hand), 64KB Sector at 150 ms to 2 seconds

If you're getting an error after 15 seconds I'd assume that it's from a Time Out count that's either too short, or running too fast. I'd avoid SW delay loops, and instead use something that ticks with the MCU, say DWT CYCCNT or TIM2/TIM5 geared to count milliseconds.

Different size and process geometry devices have different speeds, I think the Winbond code I have 200 seconds for Chip Erase, with a 10% margin, so 220 seconds. Devices taking a minute or two would not surprise me.

Some of the Micron's can go much quicker, especially if the sectors within the device are already erased.

Complete almost instantly if block protection triggers

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

I think I found it...my System Timebase source was set to use Tim6 but the interrupt priority level for that timer was 15.  I had noticed that the HAL_Delay() didn't work and I had to override it to just use a loop of NOP.  But then when checking the AutoPolling timeout I saw that the HAL_QSPI_AutoPolling() was using HAL_GetTick() and I thought maybe that wasn't working right either.  Sure enough once I changed the Tim6 interrupt priority to 0 the chip erase is now working.  Thanks for all the discussion to help me track it down

/**********************************************
 * roughly waints for "Delay" ms before return
 * set uC clock speed. Tested on M4, maybe has
 *  to change using other uC.
 * ********************************************/
#if 1
void HAL_Delay(uint32_t Delay) {
  const uint32_t clock = 400;     // uC MHz
  volatile uint32_t delay1;     //
  volatile uint32_t multiplier = 35;  // that's "converting" Delay value into time spent in the below loop
  volatile uint32_t k;

  delay1=Delay;

  for (k=0; (k<(delay1*clock*multiplier)); k++)
  {
    __NOP();
  }
}
#endif

 

I'm not sure what's going on, starting to wonder if it is something peculiar with the STM32CubeProgrammer.  I know I did several successful "chip erases" on Monday before I left and now this morning the same code is failing again.  Even though the programmer reports "Error: Mass erase operation failed.Please verify flash protection" when I read the contents it actually seems to be erased.  With the Erase Chip function the CHIP_ERASE_CMD is used with no addressing so I don't see how it could be trying to access memory out of range or anything.

Again all other operations seem to work fine (read, write, sector erase)

***************UPDATE***********************

Tesla was correct above, was totally related to the timeout for QSPI_AutoPollingMemReady().  Needed to pass a timeout into this function to allow for different timeout values depending on the operation.  This function just always used a standard default value of 20000 when in fact the chip erase needed longer.