cancel
Showing results for 
Search instead for 
Did you mean: 

Timing issues while storing data to flash

Mr_M_from_G
Senior II

Hello,

I tried to store data to flash on a STM32H742 (running at 480MHz) and I have some timing issues. Meanwhile I found myself a workaround that I also want to share here. Still I would like to ask some questions to better understand whats going on, or maybe get a better solution if someone knows one.

I have only ten int32-values, I want to store in flash. To keep the impact on timing as small as possible I defined an array "logging_array" where I store data while my code is running and when it is finished I send them over UART to a terminal. Mostly I store FLASH->SR2 because I store in bank2 and want to see which bits are set.

I use these defines:

#define SetBitMask(Var,BitMask) (Var|=(BitMask))
#define ClearBitMask(Var,BitMask) (Var&=~(BitMask))
#define BitMaskIsSet(Var,BitMask) ((Var&(BitMask))==(BitMask))
#define BitMaskIsClear(Var,BitMask) ((Var&(BitMask))==0)

Here is my code:

int32_t* var_ptr;
int32_t* flash_ptr;
int32_t n;
int32_t k;
uint32_t logging_array [50];
uint32_t* array_ptr = &logging_array [0];
  if (BitMaskIsSet (FLASH->SR2, FLASH_SR_BSY))
  { // return error, when write or erase is still ongoing
    return (error_case);
  }
  // sector erase of sector 0 in bank 2:
  // clear all error bits in case one is set:
  SetBitMask (FLASH->CCR2,   FLASH_CCR_CLR_WRPERR | FLASH_CCR_CLR_PGSERR | FLASH_CCR_CLR_STRBERR | FLASH_CCR_CLR_INCERR
                           | FLASH_CCR_CLR_OPERR| FLASH_CCR_CLR_RDPERR | FLASH_CCR_CLR_RDSERR | FLASH_CCR_CLR_SNECCERR
                           | FLASH_CCR_CLR_DBECCERR | FLASH_CCR_CLR_CRCEND | FLASH_CCR_CLR_CRCRDERR);
  // check if FLASH-CR2 is locked and unlock it
  if (BitMaskIsSet (FLASH->CR2, FLASH_CR_LOCK))
  { // then unlock it:
    FLASH->KEYR2 = 0x45670123;
    FLASH->KEYR2 = 0xCDEF89AB;
  }
  // select an erase of sector 0 in bank 2 and start it
  FLASH->CR2 = FLASH_CR_PSIZE; // select max parallelism, clear all other bits, = esp select sector 0
  SetBitMask (FLASH->CR2, FLASH_CR_SER); // select sector erase
  SetBitMask (FLASH->CR2, FLASH_CR_START);
  while (BitMaskIsSet (FLASH->SR2, FLASH_SR_BSY))
  { // sector erase takes 1.8 to 4 sec
    vTaskDelay (pdMS_TO_TICKS (50)); // using FreeRTOS
  }
  FLASH->CR2 = FLASH_CR_PSIZE; // select max parallelism, clear all other bits
  SetBitMask (FLASH->CR2, FLASH_CR_PG); // select programming
  flash_ptr = (int32_t*)(data_flash_start_address); // data_flash_start_address = 0x08100000  // start address of sector 0 of flash bank 2
 
  for (n = 0; n < Task_IF_Rx_Flash_data_size; )
  {
    *array_ptr++ = FLASH->SR2; // #1
    var_ptr = Task_IF_Rx_Flash_data [n].var_adr;
    *flash_ptr = *var_ptr;
    *array_ptr++ = FLASH->SR2; // #2
    flash_ptr++;
    n++;
    if ((n % 8) == 0)
    { // write buffer is full => programming starts automatically, check QW is set, then wait till it is cleared
      *array_ptr++ = FLASH->SR2; // #3
      k = 0;
      while (BitMaskIsClear (FLASH->SR2, FLASH_SR_QW))
      {
        k++;
        if (k >= 1000000) break;
      }
      *array_ptr++ = k; // #4
      *array_ptr++ = FLASH->SR2; // #5
      k = 0;
      while (BitMaskIsSet (FLASH->SR2, FLASH_SR_QW))
      {
        k++;
        if (k >= 1000000) break;
      }
      *array_ptr++ = k; // #6
      *array_ptr++ = FLASH->SR2; // #7
      if (check_program_success (n - 8, 8) == -1)
      {
        SetBitMask (FLASH->CR2, FLASH_CR_LOCK);
        return (error_case);
      }
    }
  }
  *array_ptr++ = FLASH->SR2; // #8
  if ((n % 8) != 0)
  { // then less than 8 int32 have been given => fill the rest with 0xFFFFFFFF
  int32_t m = n;
    while ((m % 8) != 0)
    {
      *array_ptr++ = FLASH->SR2; // #9
      *flash_ptr = 0xFFFFFFFF;
      flash_ptr++;
      m++;
      *array_ptr++ = FLASH->SR2; // #10
    }
    // here: (n % 8) == 0 ie.write buffer is full => programming starts automatically, check QW is set, then wait till it is cleared
    *array_ptr++ = FLASH->SR2; // #11
    k = 0;
    while (BitMaskIsClear (FLASH->SR2, FLASH_SR_QW))
    {
      k++;
      if (k >= 1000000) break;
    }
    *array_ptr++ = k; // #12
    *array_ptr++ = FLASH->SR2; // #13
    k = 0;
    while (BitMaskIsSet (FLASH->SR2, FLASH_SR_QW))
    {
      k++;
      if (k >= 1000000) break;
    }
    *array_ptr++ = k; // #14
    *array_ptr++ = FLASH->SR2; // #15
    if (check_program_success (n - (n % 8), n % 8) == -1)
    {
      SetBitMask (FLASH->CR2, FLASH_CR_LOCK);
      return (error_case);
    }
  }
  *array_ptr++ = FLASH->SR2; // #16
  SetBitMask (FLASH->CR2, FLASH_CR_LOCK);
  *array_ptr++ = FLASH->SR2; // #17
  array_ptr = &logging_array [0];
  vTaskDelay (pdMS_TO_TICKS (10));
  while (array_ptr < &logging_array [50])
  {
    Sende_uint32_Hex_to_UART (UART_DBG, *array_ptr++, output_with_0x);
    Sende_Zeichen_to_UART (UART_DBG, '\r');
    Sende_Zeichen_to_UART (UART_DBG, '\n');
    vTaskDelay (pdMS_TO_TICKS (1));
  }

Here are the results from logging_array:

position   current data stored    variable    value   bits set in SR2
    #1         1                 FLASH->SR2  0x010000  EOP
    #2         1                 FLASH->SR2  0x010000  EOP
    #1         2                 FLASH->SR2  0x010000  EOP
    #2         2                 FLASH->SR2  0x010000  EOP
    #1         3                 FLASH->SR2  0x010000  EOP
    #2         3                 FLASH->SR2  0x010002  EOP, WBNE
    #1         4                 FLASH->SR2  0x010002  EOP, WBNE
    #2         4                 FLASH->SR2  0x010002  EOP, WBNE
    #1         5                 FLASH->SR2  0x010002  EOP, WBNE
    #2         5                 FLASH->SR2  0x010002  EOP, WBNE
    #1         6                 FLASH->SR2  0x010002  EOP, WBNE
    #2         6                 FLASH->SR2  0x010002  EOP, WBNE
    #1         7                 FLASH->SR2  0x010002  EOP, WBNE
    #2         7                 FLASH->SR2  0x010002  EOP, WBNE
    #1         8                 FLASH->SR2  0x010002  EOP, WBNE
    #2         8                 FLASH->SR2  0x010002  EOP, WBNE
    #3                           FLASH->SR2  0x010002  EOP, WBNE
    #4                               k        0x04 = 4d
    #5                           FLASH->SR2  0x010005  EOP, QW, BSY
    #6                               k        0x0B0F = 2831d
    #7                           FLASH->SR2  0x010000  EOP
    #1         9                 FLASH->SR2  0x010000  EOP
    #2         9                 FLASH->SR2  0x010000  EOP
    #1         10                FLASH->SR2  0x010000  EOP
    #2         10                FLASH->SR2  0x010000  EOP
    #8                           FLASH->SR2  0x010000  EOP
    #9         11                FLASH->SR2  0x010000  EOP
    #10        11                FLASH->SR2  0x010000  EOP
    #9         12                FLASH->SR2  0x010002  EOP, WBNE
    #10        12                FLASH->SR2  0x010002  EOP, WBNE
    #9         13                FLASH->SR2  0x010002  EOP, WBNE
    #10        13                FLASH->SR2  0x010002  EOP, WBNE
    #9         14                FLASH->SR2  0x010002  EOP, WBNE
    #10        14                FLASH->SR2  0x010002  EOP, WBNE
    #9         15                FLASH->SR2  0x010002  EOP, WBNE
    #10        15                FLASH->SR2  0x010002  EOP, WBNE
    #9         16                FLASH->SR2  0x010002  EOP, WBNE
    #10        16                FLASH->SR2  0x010002  EOP, WBNE
    #11                          FLASH->SR2  0x010005  EOP, QW, BSY
    #12                              k        0x00 
    #13                          FLASH->SR2  0x010005  EOP, QW, BSY
    #14                              k        0x0463 = 1123d
    #15                          FLASH->SR2  0x010000  EOP
    #16                          FLASH->SR2  0x010000  EOP
    #17                          FLASH->SR2  0x010000  EOP

You can see two timing issues:

  1. WBNE gets set pretty long time after one word has been copied to the write buffer.It is not neccessary to copy 3 words as it may seem. If you wait longer after one word is filled into write buffer WBNE gets set also. First I tried to check if WBNE is set after I copied all my data to find out if I have a rest smaller than 32 bytes not yet stored but that failed, WBNE was not set when I checked it and I always had the last two words not stored. So the only way it surely works for me is to fill up to 32 bytes at the end. Trying to use force write seems not to be a safe way.
  2. QW also gets set a little late, it is not there right after the 8th word is filled into write buffer but some instructions later so I need a loop to wait till it is set. Before I did not have the k counter in the QW while loops and sometimes it stuck there but I could not find out exactly why.

Questions:

Are there any reasons for these latencies? What can I do to surely see WBNE set when it should be there.

Hope this helps and thanks for any answers

Martin

2 REPLIES 2
Mr_M_from_G
Senior II

kindly pushing this up again

Martin

TDK
Guru

What is WBNE and how is it calculated?

It's possible things are held in the cache and only flushed to flash when necessary.

If you feel a post has answered your question, please click "Accept as Solution".