2017-06-25 11:34 PM
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:
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-tablesSolved! Go to Solution.
2017-06-27 05:29 AM
((func)0x08080000) ();
And if you try 0x08080001 instead ?
2017-06-27 03:23 AM
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?2017-06-27 05:29 AM
((func)0x08080000) ();
And if you try 0x08080001 instead ?
2017-06-27 06:14 AM
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?
2017-06-27 06:41 AM
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 ...
2017-06-27 08:14 AM
Yes, but I didn't get why the blx instruction behaves in two different ways. But anyway this is very helpful.
2017-06-27 08:24 AM