Writing to non-volatile memory without disrupting UART interrupts execution on STM32F4XX

2

I have several OVERRUN errors on UART peripheral because I keep receiving UART data while my code is stall because I'm executing a write operation on flash.

I'm using interrupts for UART and has it is explained on Application Note AN3969 :

EEPROM emulation firmware runs from the internal Flash, thus access to the Flash will be stalled during operations requiring Flash erase or programming (EEPROM initialization, variable update or page erase). As a consequence, the application code is not executed and the interrupt can not be served.

This behavior may be acceptable for many applications, however for applications with realtime constraints, you need to run the critical processes from the internal RAM.

In this case:

  1. Relocate the vector table in the internal RAM.
  2. Execute all critical processes and interrupt service routines from the internal RAM. The compiler provides a keyword to declare functions as a RAM function; the function is copied from the Flash to the RAM at system startup just like any initialized variable. It is important to note that for a RAM function, all used variable(s) and called function(s) should be within the RAM.

So I've search on the internet and found AN4808 which provides examples on how to keep the interrupts running while flash operations.

I went ahead and modified my code :

Linker script : Added vector table to SRAM and define a .ramfunc section

/* stm32f417.dld */
ENTRY(Reset_Handler)

MEMORY
{
    ccmram(xrw)       : ORIGIN = 0x10000000, LENGTH = 64k
    sram              : ORIGIN = 0x20000000, LENGTH = 112k
    eeprom_default    : ORIGIN = 0x08004008, LENGTH = 16376
    eeprom_s1         : ORIGIN = 0x08008000, LENGTH = 16k
    eeprom_s2         : ORIGIN = 0x0800C000, LENGTH = 16k
    flash_unused      : ORIGIN = 0x08010000, LENGTH = 64k
    flash             : ORIGIN = 0x08020000, LENGTH = 896k
}

_end_stack = 0x2001BFF0;

SECTIONS 
{
  . = ORIGIN(eeprom_default);
  .eeprom_data :
  {
    *(.eeprom_data)
  } >eeprom_default

  . = ORIGIN(flash);

  .vectors :
    {
        _load_vector = LOADADDR(.vectors);
        _start_vector = .;
        *(.vectors)
        _end_vector = .;
    } >sram AT >flash

    .text :
    {
        *(.text)
        *(.rodata)
        *(.rodata*)
        _end_text = .;
    } >flash

    .data : 
    {
        _load_data = LOADADDR(.data);
        . = ALIGN(4);
        _start_data = .;
        *(.data)
    } >sram AT >flash


    .ramfunc :
    {
        . = ALIGN(4);
        *(.ramfunc)
        *(.ramfunc.*)
        . = ALIGN(4);
        _end_data = .;
    } >sram AT >flash

    .ccmram :
    {
        _load_ccmram = LOADADDR(.ccmram);
        . = ALIGN(4);
        _start_ccmram = .;
        *(.ccmram)
        *(.ccmram*)
        . = ALIGN(4);
        _end_ccmram = .;
    } > ccmram AT >flash

    .bss :
    {
        _start_bss = .;
        *(.bss)
        _end_bss = .;
    } >sram

    . = ALIGN(4);

    _start_stack = .;


}

_end = .;
PROVIDE(end = .);

Reset Handler : Added vector table copy SRAM and define a .ramfunc section

void Reset_Handler(void)
{
  unsigned int *src, *dst;

  /* Copy vector table from flash to RAM */
  src = &_load_vector;
  dst = &_start_vector;
  while (dst < &_end_vector)
    *dst++ = *src++;

  /* Copy data section from flash to RAM */
  src = &_load_data;
  dst = &_start_data;
  while (dst < &_end_data)
    *dst++ = *src++;

  /* Copy data section from flash to CCRAM */
  src = &_load_ccmram;
  dst = &_start_ccmram;
  while (dst < &_end_ccmram)
    *dst++ = *src++;

  /* Clear the bss section */
  dst = &_start_bss;
  while (dst < &_end_bss)
    *dst++ = 0;

  SystemInit();
  SystemCoreClockUpdate();

  RCC->AHB1ENR = 0xFFFFFFFF;
  RCC->AHB2ENR = 0xFFFFFFFF;
  RCC->AHB3ENR = 0xFFFFFFFF;
  RCC->APB1ENR = 0xFFFFFFFF;
  RCC->APB2ENR = 0xFFFFFFFF;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOIEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_CCMDATARAMEN;

  main();

  while(1);
}

system_stm32f4xxx.c : Uncommented VECT_TAB_SRAM define

/*!< Uncomment the following line if you need to relocate your vector Table in
     Internal SRAM. */
#define VECT_TAB_SRAM
#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field. 
                                   This value must be a multiple of 0x200. */

Added a definition of RAMFUNC to set section attributes :

#define RAMFUNC __attribute__ ((section (".ramfunc")))

Addded RAMFUNC before UART related function and prototypes so it gets run from RAM.

RAMFUNC void USART1_IRQHandler(void)
{
  uint32_t sr = USART1->SR;
  USART1->SR & USART_SR_ORE ? GPIO_SET(LED_ERROR_PORT, LED_ERROR_PIN_bp):GPIO_CLR(LED_ERROR_PORT, LED_ERROR_PIN_bp);
  if(sr & USART_SR_TXE)
  {

    if(uart_1_send_write_pos != uart_1_send_read_pos)
    {
      USART1->DR = uart_1_send_buffer[uart_1_send_read_pos];
      uart_1_send_read_pos = (uart_1_send_read_pos + 1) % USART_1_SEND_BUF_SIZE;
    }
    else
    {
      USART1->CR1 &= ~USART_CR1_TXEIE;
    }
  }

  if(sr & (USART_SR_RXNE | USART_SR_ORE)) 
  {
    USART1->SR &= ~(USART_SR_RXNE | USART_SR_ORE);
    uint8_t byte = USART1->DR;
    uart_1_recv_buffer[uart_1_recv_write_pos] = byte;
    uart_1_recv_write_pos = (uart_1_recv_write_pos + 1) % USART_1_RECV_BUF_SIZE;
  }
}

My target runs properly with vector table and UART function in RAM but I still I get an overrun on USART. I'm also not disabling interrupts when performing the flash write operation.

I also tried to run code from CCM RAM instead of SRAM but has I saw on this post code can't be executed on CCM RAM on STMF32F4XX...

Any idea ? Thanks.

interrupt
stm32
ram
uart
stm32f4
asked on Stack Overflow Feb 27, 2019 by Julian Poidevin

1 Answer

4

Any attempt to read from flash while a write operation is ongoing causes the bus to stall.

In order to not be blocked by flash writes, I think not only the the interrupt code, but the interrupted function has to run from RAM too, otherwise the core cannot proceed to a state when interrupts are possible.

Try relocating the flash handling code to RAM.

If it's possible, I'd advise switching to an MCU with two independent banks of flash memory, like the pin- and software-compatible 427/429/437/439 series. You can dedicate one bank to program code and the other to EEPROM-like data storage, then writing the second bank won't disturb code running from the first bank.


User contributions licensed under CC BY-SA 3.0