Author Topic: 32F417 - best way to program the FLASH from RAM based code  (Read 1466 times)

0 Members and 1 Guest are viewing this topic.

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
32F417 - best way to program the FLASH from RAM based code
« on: July 21, 2021, 08:36:08 pm »
After this
https://www.eevblog.com/forum/microcontrollers/how-to-create-elf-file-which-contains-the-normal-prog-plus-a-relocatable-block/msg3611627/#msg3611627
I am ready for the next challenge :)

Googling around, a lot of people get the RM and do it by going directly to the hardware. This bypasses the usually bloated ST libraries, but does it give you anything? The process will be totally time-limited by the FLASH writing speed. The only reason I can see to reinvent the wheel is to avoid bugs in the ST libs.

My loader will be extracting the data from an SPI serial FLASH. I have plenty of RAM; certainly enough to write a 16k block in one go.

Which ST function would people here recommend?

Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: 32F417 - best way to program the FLASH from RAM based code
« Reply #1 on: August 02, 2021, 02:37:39 pm »
I can see a number of functions provided by ST

Code: [Select]
/* Program operations */
static void   FLASH_Program_DoubleWord(uint32_t Address, uint64_t Data); // needs external VPP
static void   FLASH_Program_Word(uint32_t Address, uint32_t Data);   // this one is for normal 3.3V use
static void   FLASH_Program_HalfWord(uint32_t Address, uint16_t Data);  // for 2.1V to 3.6V
static void   FLASH_Program_Byte(uint32_t Address, uint8_t Data);  // for 1.8V to 3.6V.
static void   FLASH_SetErrorCode(void);

void FLASH_Erase_Sector(uint32_t Sector, uint8_t VoltageRange)

Fairly obviously you use FLASH_Erase_Sector to erase the block (size varies; 16k is the smallest) and then you write that block.

The program funcs appear to be non-blocking e.g.

Code: [Select]
static void FLASH_Program_Word(uint32_t Address, uint32_t Data)
{
  /* Check the parameters */
  assert_param(IS_FLASH_ADDRESS(Address));
 
  /* If the previous operation is completed, proceed to program the new data */
  CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);
  FLASH->CR |= FLASH_PSIZE_WORD;
  FLASH->CR |= FLASH_CR_PG;

  *(__IO uint32_t*)Address = Data;
}

so one needs to wait for each one to complete, with

Code: [Select]
HAL_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout)
{
  uint32_t tickstart = 0U;
 
  /* Clear Error Code */
  pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
 
  /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset.
     Even if the FLASH operation fails, the BUSY flag will be reset and an error
     flag will be set */
  /* Get tick */
  tickstart = HAL_GetTick();

  while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET)
  {
    if(Timeout != HAL_MAX_DELAY)
    {
      if((Timeout == 0U)||((HAL_GetTick() - tickstart ) > Timeout))
      {
        return HAL_TIMEOUT;
      }
    }
  }

  /* Check FLASH End of Operation flag  */
  if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP) != RESET)
  {
    /* Clear FLASH End of Operation pending bit */
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP);
  }
#if defined(FLASH_SR_RDERR) 
  if(__HAL_FLASH_GET_FLAG((FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | \
                           FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR | FLASH_FLAG_RDERR)) != RESET)
#else
  if(__HAL_FLASH_GET_FLAG((FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | \
                           FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR)) != RESET)
#endif /* FLASH_SR_RDERR */
  {
    /*Save the error code*/
    FLASH_SetErrorCode();
    return HAL_ERROR;
  }

  /* If there is no error flag set */
  return HAL_OK;
 

which needs to be heavily chopped down because in the boot loader I have no interrupt servicing so timeouts can't really be implemented. Can the programming really fail, without the device having a defective silicon?
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3694
  • Country: gb
  • Doing electronics since the 1960s...
Re: 32F417 - best way to program the FLASH from RAM based code
« Reply #2 on: August 08, 2021, 09:54:25 pm »
I have finished my boot loader and here is some code which somebody may find handy. It is of course based on the HAL code but is stripped down, because a lot of the error conditions are probably impossible unless the silicon is defective, and in any case there is no way to deal with most of them in a typical embedded system.

Some basic routines:

Code: [Select]
/**
  *
  * @param  Sector FLASH sector to erase 0-11
  * Device voltage range is 2.7V to 3.6V, and the operation will be done by word (32-bit)
  * @retval None
  * Waits for previous operation to complete.
  *
  */
static void L_FLASH_Erase_Sector(uint32_t Sector)
{

if (Sector<=11)
{
while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET);
CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE); // Clears bits 9,8
FLASH->CR |= FLASH_PSIZE_WORD; // sets them to 10
CLEAR_BIT(FLASH->CR, FLASH_CR_SNB); // Clears bits 6-3
FLASH->CR |= FLASH_CR_SER | (Sector << 3); // SER=1 & load sector # into 6-3
FLASH->CR |= FLASH_CR_STRT;
}

}



/**
  * @brief  Locks the FLASH control register access
  * @retval
  */
static void L_HAL_FLASH_Lock(void)
{
// Wait for any flash operation to finish
while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET);
// Set the LOCK Bit to lock the FLASH Registers access
FLASH->CR |= FLASH_CR_LOCK;
}



/**
  * @brief  Unlock the FLASH control register access
  * Issues the unlok codes
  */

static void L_HAL_FLASH_Unlock(void)
{
// Wait for any flash operation to finish
while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET);
// Write the two unlock codes
WRITE_REG(FLASH->KEYR, FLASH_KEY1);
WRITE_REG(FLASH->KEYR, FLASH_KEY2);
}



/**
  * @brief  Program word (32-bit) at a specified address.
  * @note   This function must be used when the device voltage range is from
  *         2.7V to 3.6V.
  *
  * @note   If an erase and a program operations are requested simultaneously,
  *         the erase operation is performed before the program one.
  *
  * @param  Address specifies the address to be programmed.
  * @param  Data specifies the data to be programmed.
  * @retval None
  * Waits for previous operation to finish
  *
  */

static void L_FLASH_Program_Word(uint32_t Address, uint32_t Data)
{
// wait for any previous op to finish
while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET);
// clear program size bits
CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);
// reload program size bits
FLASH->CR |= FLASH_PSIZE_WORD;
// enable programming
FLASH->CR |= FLASH_CR_PG;
// write the data in
*(volatile uint32_t*)Address = Data;
}



I start the boot loader with a bit which tests the uppermost flash sector (sector 11, size 128k) by erasing it, verifying the erasure, programming it with varying data, and verifying that, before proceeding with the rest

Code: [Select]
// ===== Make sure we can erase and program sector 11 - the top one =====
// If that works, the rest should work :)

uint32_t error=0;
uint32_t data;
uint32_t address;

// Erase sector
L_HAL_FLASH_Unlock();
L_FLASH_Erase_Sector(11);
L_HAL_FLASH_Lock();

// Check it is all FFs
for (address=0x080e0000; address<0x080fffff; address+=4)
{
data=*(volatile uint32_t*)address;
if ( data != 0xffffffff )
error++;
}

// Erase sector again
L_HAL_FLASH_Unlock();
L_FLASH_Erase_Sector(11);

// Fill it with data
for (uint32_t i=0; i<(128*1024); i+=4)
{
L_FLASH_Program_Word(i+0x080e0000, i);
}

// Probably always best to lock the flash again before reading it
L_HAL_FLASH_Lock();

// Check the data we have just written
data=0;
for (address=0x080e0000; address<0x080fffff; address+=4)
{
if ( (*(volatile uint32_t*)address) != data )
error++;
data+=4;
}

// If any errors, set up a code on LEDs and allow override with a button
if (error!=0)

and here is another piece of code

Code: [Select]
// Erase whole 1MB except boot block
  // This is done in one go, rather than inside the loop below, to avoid complicated
  // coding around the variable size sectors (16k to 128k). Exec time 9 seconds.

  L_HAL_FLASH_Unlock();
  for (uint32_t i=2;i<=11;i++)
  {
  L_FLASH_Erase_Sector(i);
  }
  L_HAL_FLASH_Lock();

  // Check it is all FFs
  error=0;
  for (address=0x08080000; address<0x080fffff; address+=4)
  {
  data=*(volatile uint32_t*)address;
  if ( data != 0xffffffff )
  error++;
  }


  pagebase=4100+64; // 4100-5119 factory image in serial FLASH pages
  cpubase=0x08008000; // base of CPU FLASH, after boot block

  // Program entire image in one go
  // Loop which always does the whole 512k (16 32k blocks) except block 0

  for (uint32_t block32k=1; block32k<16; block32k++) // 1-15
  {

  // Read each 32k block into buffer1
  buffer1idx=0;
  for ( uint32_t page=pagebase; page<(pagebase+64); page++ )
  {
  AT45dbxx_ReadPage(&buffer1[buffer1idx],512,page*512);   // reads 512 bytes at a time, from serial FLASH address of page*512
  buffer1idx+=512;
  }

  // Program 32k block
  L_HAL_FLASH_Unlock();
  for (uint32_t i=0; i<(32*1024); i+=4)
  {
  uint32_t data=buffer1[i]|(buffer1[i+1]<<8)|(buffer1[i+2]<<16)|(buffer1[i+3]<<24);
  L_FLASH_Program_Word(i+cpubase, data);
  }
  L_HAL_FLASH_Lock();

  // Verify 32k block against buffer1
  for (uint32_t i=0; i<(32*1024); i+=4)
  {
  uint32_t data=buffer1[i]|(buffer1[i+1]<<8)|(buffer1[i+2]<<16)|(buffer1[i+3]<<24);
  if ( (*(volatile uint32_t*) (i+cpubase)) != data ) error++;

}

  cpubase+=(32*1024);
  pagebase+=64;
  block32k++;

  }

  attempt++;

  }
  while ( (attempt<NUM_ATTEMPTS) && (error!=0) );

I am allowing up to 5 attempts but in reality I never saw any errors.

You can see above that erasing most of the 1MB FLASH takes 9 seconds. Erasing and programming the two bottom blocks (2x16k) takes 0.6 second (I measured that carefully) which extrapolates to 15-20 seconds for erasing and writing the whole 1MB. That includes reading the data from a serial FLASH over SPI running at 21mbps, so the raw time would be nearer to 15s. One could perhaps speed this up a little by interleaving data fetching with the FLASH programming cycle (the above code is blocking so it waits).
« Last Edit: August 09, 2021, 01:03:24 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf