cancel
Showing results for 
Search instead for 
Did you mean: 

Unconventional use of the FLASH memory of a STM32F4xx MCU. Possible?

Digimorf
Associate III
Posted on June 26, 2017 at 08:34

Hello, 

I am developing an entertainment firmware for a custom board that mounts an STM32F469 microcontroller. This has 2Megs of FLASH and I would like to do something 'unusual' with the memory use. My idea comes from the old retro video games consoles. Many of them had an embedded firmware ROM inside, that was available for user programs in ROM cartridges or programs written with an embedded BASIC interpreter.

I was wondering if it is possible to simulate a similar situation in the internal FLASH of the MCU I am using. Currently, I am using ATOLLIC TrueStudio. Let's look at, for example, a FLASH memory range of this type:

FLASH memory space

+---------------------------------------------------------+------------------------+---------+

| User program                                            | Locked Code            | Address |

| The user can build his own code from within TrueStudio  | Pre-uploaded functions |  Table  |

+---------------------------------------------------------+------------------------+---------+

|<---------------------- 1,792KB ------------------------>|<------------ 256KB ------------->|

Locked code: Permanent firmware (Maybe factory burned?).

Address Table: Indexed table of pointers to firmware functions.

User programs: Memory space that can be used be the user.

The locked code is a library of functions stored at specific addresses in the FLASH memory. I thought to follow the example in:

http://blog.atollic.com/using-gnu-gcc-on-arm-cortex-devices-placing-code-and-data-on-special-memory-addresses-using-the-gnu-ld-linker

So, somehow it's possible to force GCC to place of all functions at specific addresses. Then, all addresses could be stored in a [/url]indexed table of pointers located at the end of the FLASH memory. This is known by the user of course. At this point, the FLASH memory range from 0x08000000 up to the 'Locked code' is available to the user for his own, and he could 'invoke' a specific memory location in the table to call a function of the firmware.

Something like the example shown here:

https://stackoverflow.com/questions/8915797/calling-a-function-through-its-address-in-memory-in-c-c

typedef void 

func(void);

func* f = (func*)0xdeadbeef;

f();

 

So the user could use or not this code in his programs and take advantage of the 'pre-uploaded firmware' that drives the board. When the user develops his code, the compiler should restrict the upload memory range available to the 'User program area' only, so that the pre-uploaded firmware is not overwritten. In your opinion is this a good idea, is it possible to do it?

Thanks 

#stm32f4xx #flash #jump-tables
1 ACCEPTED SOLUTION

Accepted Solutions
Posted on June 27, 2017 at 12:29

    ((func)0x08080000) ();

And if you try 0x08080001 instead ?

View solution in original post

6 REPLIES 6
Digimorf
Associate III
Posted on June 27, 2017 at 12:23

Hi, so, I am trying to implement this situation on an STM32F407 discovery board for a test. 0) I created a simple project divided into a couple of source files. One *.c file contains a simple function that initializes the onboard LEDs. >This example works correctly. The LED initialization function is called by the main(), the LEDs are turned ON and then the programs enters an infinite loop:

int main (void)

 

{   

 

  Status_LEDS_Init ();   

 

  while (1)   

 

  {   

 

  }

 

}

1) After that, I have modified the linker script, and I added a new section to the memory map, like the example:

/* Specify the memory areas */

 

MEMORY {

 

   FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 512K

 

   FW (rx)         : ORIGIN = 0x08080000, LENGTH = 512K

 

   RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K

 

   MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K

 

   CCMRAM (rw)     : ORIGIN = 0x10000000, LENGTH = 64K

 

}

2) I placed the section .firmware descriptor between .text and .rodata:

...

 

   /* The program code and other data goes into FLASH */

 

   .text :

 

   {

 

     . = ALIGN(4);

 

     *(.text)           /* .text sections (code) */

 

     *(.text*)          /* .text* sections (code) */

 

     *(.glue_7)         /* glue arm to thumb code */

 

     *(.glue_7t)        /* glue thumb to arm code */

 

     *(.eh_frame)

 

      KEEP (*(.init))

 

      KEEP (*(.fini))

 

      . = ALIGN(4);

 

     _etext = .;        /* define a global symbols at end of code */

 

   } >FLASH

 

 

  .firmware :

 

   {

 

       *(.firmware*)

 

   } >FW 

 

 

    /* Constant data goes into FLASH */

 

   .rodata :

 

   {

 

...

3) The project compiled correctly, so I forced the function that initializes the LEDs to be placed in .firmware section.

__attribute__((section('.firmware'))) int Status_LEDS_Init (void)

 

{

 

   /*

 

    * DESCRIPTION: This function is used to initialize the hardware that drives

 

    *              the GPIOs connected to the two status LEDs.

 

    * PARAMETERS:  See Above.

 

    * RETURNS:     Nothing.

 

    */

 

    // Local GPIO structure used to setup lines using the Standard Peripheral

 

   // Library.

 

    GPIO_InitTypeDef lGPIO_InitStructure;

 

    // Turn on the MCU peripheral by enabling its Clock.

 

    RCC_AHB1PeriphClockCmd (STATUS_LEDS_RCC, ENABLE);

 

    // Configure the output lines connected to the Status LEDs as output, LOW

 

   // level as default.  

 

    lGPIO_InitStructure.GPIO_Pin   = STATUS_LED1 | STATUS_LED2 | STATUS_LED3 | STATUS_LED4;

 

   lGPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;

 

   lGPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

 

   lGPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

 

   lGPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;

 

   GPIO_Init (STATUS_LEDS_PERIPHERAL, &lGPIO_InitStructure);

 

    // Put LEDs line HIGH.

 

   STATUS_LEDS_PERIPHERAL->ODR |= (STATUS_LED1 | STATUS_LED2 | STATUS_LED3 | STATUS_LED4);

 

 

    return 1;

 

  } // End Status_LEDS_Init.

4) The project compiles, and from the debugger, I can see clearly that this function is placed correctly at 0x0808000, where the .firmware section starts. Of course, the program works perfectly. Now I know that this function is placed at the absolute address 0x08080000. 5) I tried to change the way of calling the function, by using this address:

...

 

typedef int (*func)(void);

 

  int main (void)

 

{

 

   ((func)0x08080000) ();

 

    while (1)

 

     {

 

     }

 

}

6) The code is compiled perfectlòy but the program goes to 'Hardfault error'. So I checked the ASM code, tried to trace the execution step by step, and I wasn't able to understand where the problem is since the program jumps to that location in both cases, but when the function is called with the symbol name it works. When it's called with the address location it doesn't work.

7) I tried to put both calls in the main to see the differences in the ASM:

typedef int (*func)(void);

 

  int main (void)

 

  {

 

    Status_LEDS_Init ();

 

    ((func)0x08080000) ();

 

    while (1)

 

     {

 

     }

 

  }

7) Compiled and got this ASM:

0800f0c4:   ldr     r3, [pc, #8]    ; (0x800f0d0 <main+16>)

 

0800f0c6:   blx     r3

 

0800f0c8:   ldr     r3, [pc, #8]    ; (0x800f0d4 <main+20>)

 

0800f0ca:   blx     r3

 

0800f0cc:   b.n     0x800f0cc <main+12>

 

0800f0ce:   nop   

8) The two calls are absolutely the same, but the first works, the second, after jumping the PC to 0x0808000, goes to Hardfault. This is the end of the story

:(

I am going crazy. Do you have an idea of what this problem is caused by?
Posted on June 27, 2017 at 12:29

    ((func)0x08080000) ();

And if you try 0x08080001 instead ?

Posted on June 27, 2017 at 13:14

Hey AvaTar, thank you, that worked!

I have read about the instruction

blx

that changes the bit 0 of the location held by the register, and the instructions set, but here, in the case the code is called explicitly with the address, it doesn't. So, doing 

0x0808000 | 1 

has that function? But do you know why this happens?

Posted on June 27, 2017 at 13:41

It is ARM instruction mode vs. Thumb mode.

Cortex M only supports Thumb mode, and ARM long ago decided to put this flag in the address LSB ...

Posted on June 27, 2017 at 15:14

Yes, but I didn't get why the blx instruction behaves in two different ways. But anyway this is very helpful.    

161169CGIL6