C code to relocate the vector table - c

I have following lines of code
#define SCB_VTOR SCB_VTOR_REG(SystemControl_BASE_PTR)
#define SCB_VTOR_REG(base) ((base)->VTOR)
/* VTOR Bit Fields */
#define SCB_VTOR_TBLOFF_MASK 0xFFFFFF80u
#define SCB_VTOR_TBLOFF_SHIFT 7
#define SCB_VTOR_TBLOFF(x) (((uint32_t)(((uint32_t)(x))<<SCB_VTOR_TBLOFF_SHIFT))&SCB_VTOR_TBLOFF_MASK)
extern uint32_t __vector_table[];
SCB_VTOR = (uint32_t)__vector_table;
in my main.c file.
and I have my __vector_table in the interrupt section of linker description file as follows:
MEMORY
{
m_interrupts (rx) : ORIGIN = 0x00002000, LENGTH = 0xC0 /*192 Bytes*/
....
....
....
....
.interrupts :
{
__vector_table = .;
. = ALIGN(4);
KEEP(*(.vectortable)) /* Startup code */
. = ALIGN(4);
} > m_interrupts
When an interrupt comes, since I loaded the register SCB_VTOR to the address of __vector_table, it will point to current location of vector table right?? What is the meaning of SCB_VTOR = (uint32_t)__vector_table; Is my SCB_VTOR pointing to the address 0x00002000 line of code? Thanks in advance

__vector_table contains the address when it was included in the linker script (__vector_table = .; means to get the value of the current address, that is, if the previous sections occupy 8kB, __vector_table will contain the next address).
Yes, SCB_VTOR contains the address 0x00002000, given that the section interrupts stars in 0x00002000, and according to the linker script, the first thing to do is get the address.
Bear in mind that some CPUs/MCUs have a fixed vector table, and it starts in a specific register.

Related

Stm32 relocating vector table placement in flash

I was trying to achieve this memory structure in my mcu flash
My Linker Script declares in the following order
Bootloader firmware
Main Firmware
Main Firmware Image Info (ie. crc, version number)
Main Firmware vector table
.
.everything else
But after the bootloader jumps to the Main Firmware Reset Handler an exception occours sometime when initalizing the .bss section (it correctly jumps to the reset handler and updates the VTOR)
Everything works if the Main firmware vector table is located before the Main Firmware Image Info, but when I try to swap the twos my firmware crashes during the .bss initialization of the main firmware after bootloader launches it.
Am i missing something? Is there any reason why I cannot seem to interpose a reserved section before the isr vector's?
In the system_stm32wlxx.c in the SystemInit function for the main firmware I have
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
Where
VECT_TAB_OFFSET = Size of Bootloader section, if the vector table is placed before the image infos.
or
VECT_TAB_OFFSET = Size of Bootloadersection +Size of Image info section, if the vector table is placed after the image infos.
To perform the jump in the bootloader I have
main_app_code = (uint32_t*) ((uint32_t)&__program1_start+(uint32_t)&__vect_start_offset); // main application base address
uint32_t main_app_stack_pointer = main_app_code[0]; // first word contains the address of the stack pointer
uint32_t main_app_reset_handler = main_app_code[1]; // second word contains the address of the reset handler
where __program1_start is defined in the linker script the address of base flash+bootloader size
and __vect_start_offset is also defined in the linker script as the size of the image info section ( or 0 if the isr table is placed before the image info section)
The code is then followed by
/** set the main stack pointer and then perform a jump to the main app reset handler*/
__set_MSP(main_app_stack_pointer);
/// Jump to application
((void(*)())main_app_reset_handler)();
Linker script of main firmware memory partitioning
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
RAM2 (xrw) : ORIGIN = 0x10000000, LENGTH = 32K
BOOT (rx) : ORIGIN = 0x08000000, LENGTH = __boot_size
FLASH (rx) : ORIGIN = 0x08000000+LENGTH(BOOT), LENGTH = __program_size
FLASH2 (rx) : ORIGIN = ORIGIN(FLASH)+LENGTH(FLASH), LENGTH = __program_size
DATA (rx) : ORIGIN = ORIGIN(FLASH2)+LENGTH(FLASH2), LENGTH = __data_size
}
/* Sections */
SECTIONS
{
/* The startup code into "FLASH" Rom type memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* burn specific firmware data into a section*/
.fw_infos :
{
. = ALIGN(4);
__fw_crc = ABSOLUTE(.); /* memory address*/
KEEP(*(.fw_infos)) /* Startup code */
. = ALIGN(4);
} >FLASH
etc etc...
I found the cause of the problem, when the image info section was placed before the isr vector section the expression:
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
should have been equal to 0x8000000|5820 = 0x8005820
But SCB->VTOR is only writable from bit7 - bit31
(https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/system-control-block/vector-table-offset-register?lang=en)
So SCB->VTOR was set to 0x8005800 instead of 0x8005820, this caused the main firmware to crash.
When instead isr section was placed first
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET
was equal to 0x8000000|5800 and said value is correctly assigned to the SCB->VTOR address and everything worked fine.
My solution was to change the main firmware linker script, altering the alignment of the Image Info section with the ALIGN(256) command (intead of the previous ALIGN(4)), in this way the following section ( the isr vector section) is placed onto an address whose Bit0-7 are zero and therefore there is no risk of corrupting the SCB->VTOR value.
SECTIONS
{
/* burn specific firmware data into a section*/
.fw_infos :
{
. = ALIGN(256);
__fw_crc = ABSOLUTE(.); /* memory address*/
KEEP(*(.fw_infos)) /* Startup code */
. = ALIGN(256);
} >FLASH
/* The startup code into "FLASH" Rom type memory */
.isr_vector :
{
. = ALIGN(256);
__vector_start = ABSOLUTE(.); /* memory address*/
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
In some cases Alig(128) would work, but in my case ALIGN(256) is necessary because I have 62 interrupt sources and according to Armv7 Architecture:
The Vector table must be naturally aligned to a power of two whose alignment value is greater than or equal to (Number of Exceptions supported x 4), with a minimum alignment of 128 bytes. On power-on or reset, the processor uses the entry at offset 0 as the initial value for SP_main, see The SP registers. All other entries must have bit [0] set to 1, because this bit defines the EPSR.T bit on exception entry. See Reset behavior and Exception entry behavior for more information.
from
https://developer.arm.com/documentation/ddi0403/d/System-Level-Architecture/System-Level-Programmers--Model/ARMv7-M-exception-model/The-vector-table
So 62*4 = 148-->256 alignment required

Adding a RAM section in linker file STM32

I am trying very hard to understand how to use a linker file, but my brain is apparently not getting it at all. I am using an STM32L476, which has two RAM regions, RAM and RAM2 (memory definition below). I would like to put a buffer into RAM2, but there is no section for RAM2 in the default linker script that is generated by Cube. Seems like a good exercise for me. I really thought that the following would do the trick, where all I've added is the .sensor_buffer section:
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
RAM2 (xrw) : ORIGIN = 0x10000000, LENGTH = 32K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
}
/* Sections */
SECTIONS
{
.sensor_buffer :
{
KEEP(*(.sensor_buffer))
} >RAM2
/* The startup code into "RAM" Ram type memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >RAM
/* The program code and other data into "RAM" Ram type memory */
.text :
{
...
} >RAM
// Other Cube-generated sections here
}
However, this adds a .sensor_buffer section to an address that is not in RAM2. From the .map file:
...
.igot.plt 0x0000000020000190 0x0 load address 0x000000000800f29c
.igot.plt 0x0000000020000190 0x0 c:/st/stm32cubeide_1.6.0/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.9-2020-q2-update.win32_1.5.0.202011040924/tools/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v7e-m+fp/hard/crtbegin.o
.sensor_buffer 0x0000000020000190 0x2000 load address 0x000000000800f29c
.sensor_buffer
0x0000000020000190 0x2000 Core/Src/main.o
0x0000000020002190 . = ALIGN (0x4)
.bss 0x0000000020002190 0x1fb0 load address 0x000000000801129c
0x0000000020002190 _sbss = .
0x0000000020002190 __bss_start__ = _sbss
...
Can someone point out either where I went wrong here, and/or, even better, somewhere that I can work through some "easy" examples to get used to LD? I really want to get this stuff, but the first steps are really brutal for me without any resources except a rather dense set of man pages.
EDIT Adding the code used to declare the buffer. In main.c, global scope:
static uint8_t data[DATA_BUFFERS][DATA_SIZE] __attribute__((section(".sensor_buffer")));
You have an error somewhere else. Maybe you simply do not use this linker script (you forgot to add or change the name in the command line)
I have compiled it and linked it without any problems with CubeIDE (I use 100 and 100 in the buffer declarations as I do not know the values of your macros [100x100 = 0x2710])
.sensor_buffer 0x0000000010000000 0x2710
*(.sensor_buffer)
.sensor_buffer
0x0000000010000000 0x2710 Core/Src/main.o
.isr_vector 0x0000000020000000 0x0
0x0000000020000000 . = ALIGN (0x4)

Reserve 16K sector in mcu flash with linkerscript at specific adress

I'm creating a bootloader for STM32F429 with the gnu toolchain (9.2.1) and trying to reserve some flash memory for user data, shared by bootloader and application. I want to reserve the second of its first four 16K flash sectors for this, since they're nice and small: all other sectors are 128K.
The memory layout should look look like this:
ISR vector table : 0x08000000 (428 bytes)
padding : 0x080001ac (15956 bytes)
-----------------------------
user data : 0x08004000 (16K)
-----------------------------
bootloader code : 0x08008000 (max 224K, for total of max. 256K)
I have modified ST's linker script to look like this:
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K /* max. bootloader binary size */
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 191K /* deduct 1K for NOINIT data */
NOINIT (rwx) : ORIGIN = 0x2002FC00, LENGTH = 1K /* NOINIT data will survive reset */
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* Then comes user flash sector, make sure to get one of the first 4 that are 16K in size
.user_flash :
{
. = ALIGN(0x4000)
KEEP(*(.user_flash))
. = ALIGN(0x4000)
} >FLASH
/* ... more sections below not shown here */
}
In my bootloader.cpp, I declare the user flash as follows:
__attribute__((__section__(".user_flash"))) const uint32_t user_flash[0x4000 / sizeof(uint32_t)] = {0};
However, in the debugger, the address of user_flash is not 0x08004000 as expected, but 0x0801b4e8.
Removing the __attribute__((__section__(".user_flash"))) from the declaration yields a different adress, proving the attribute has at least some effect.
Running arm-non-eabi-objdump -h on the .elf file confirms the (incorrect) adress of the user_flash section.
I have also tried declaring the section with . = 0x08004000, to no effect.
Another approach I tried is to place .isr_vector in its own 16K section, followed by a 16K section for the user flash and finally a (256-16-16) = 224K FLASH section. This produced a bogus binary that would hardfault.
What am I doing wrong here?
Edit:
I've tried inserting a .padding section after .isr_vector to try to align .user_flash that way, but no matter what I do, .text and .rodata sections seem to always come immediately after .padding, even though they are declared after the .user_flash section.
/* move the NVIC because we are running from 08004000 */
SCB->VTOR = (FLASH_BASE | 0x4000);

How to write/read to FLASH on STM32F4, Cortex M4

I want to write a variable, for example an integer with the number 5 to the FLASH and then after the power goes away and the device is turned on again read it.
I already know that in order to write something I first need to erase the page and then write.
In the manual it says:
Write OPTKEY1 = 0x0819 2A3B in the Flash option key register (FLASH_OPTKEYR)
Write OPTKEY2 = 0x4C5D 6E7F in the Flash option key register (FLASH_OPTKEYR)
How do I perform this tasks?
Sector 0 has a Block adress from 0x0800 0000 to 0x0800 3FFF, this is where I want to write.
Here the link to the manual, page 71: STM32 Manual
You can use following code for write data to flash with HAL library.
void Write_Flash(uint8_t data)
{
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR );
FLASH_Erase_Sector(FLASH_SECTOR_6, VOLTAGE_RANGE_3);
HAL_FLASH_Program(TYPEPROGRAM_WORD, FlashAddress, data);
HAL_FLASH_Lock();
}
You should update linker script as follows. Add DATA in MEMORY and add .user_data in SECTIONS.
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K
DATA (rwx) : ORIGIN = 0x08040000, LENGTH = 128k
}
/* Define output sections */
SECTIONS
{
.user_data :
{
. = ALIGN(4);
KEEP(*(.user_data))
. = ALIGN(4);
} > DATA
You should add following attribute on main code for reading data after power on
__attribute__((__section__(".user_data"))) const char userConfig[64];
After all these, you can read your flash data with calling userConfig[0].
I'm using STM32F407 and Atollic TrueSTUDIO® for STM32 Version 9.3.0.
When using above suggested code
attribute((section(".user_data"))) const char userConfig[64];
my compiler assumed userConfig to be constant zero. I had to remove the const from the declaration to make it work.
My complete solution consists of two parts (as already said above but with some further modifications):
Step 1 edit linker file:
In 'MEMORY'
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 896K /* origin size was 1024k, subtracted size of DATA */
DATA (rx) : ORIGIN = 0x080E0000, LENGTH = 128K
In 'SECTIONS'
/* User data section at the end of the flash to store calibration data etc. */
.user_data (NOLOAD):
{
. = ALIGN(4);
_user_data_start = .; /* create a global symbol at user_data start */
KEEP(*(.user_data))
. = ALIGN(4);
_user_data_end = .; /* create a global symbol at user_data end */
} >DATA
Step 2 write code:
uint8_t userConfig[64] __attribute__ ((section(".user_data")));
extern uint32_t _user_data_start;
extern uint32_t _user_data_end;
uint8_t ConfArray[16];
uint32_t TestArray[2];
// Copy a part from the userConfig to variable in RAM
for (i = 0; i < 16; i++)
{
ConfArray[i] = userConfig[i];
}
// get the address of start and end of user_data in flash
// the & is importand, else you would get the value at the address _user_data_start and _user_data_end points to
TestArray[0] = (uint32_t)&_user_data_start;
TestArray[1] = (uint32_t)&_user_data_end;

Reserve memory space in m_text memory region of FLASH on embedded target

I have a microcontroller with lots of flash divided in 1k flash sectors.
I want to flash prime numbers to a specific memory region during flashing, then during the first boot a crypto key will be generated, then the prime numbers will be erased and overwritten.
I would prefer to hide the prime numbers inside the m_text and not create a Read/Write memory area for the primes with the linker script.
The Firmware, prime numbers and bootloader are compiled separately, resulting in 3 .hex files that are used to flash.
Say that the FW code to start at 0x2000 and it is 0x2000 long, inside that region i want to allocate a 1024 byte sector that later can be erased and overwritten without bricking the FW.
With the following code i can read, erase and write data to the flash, but i have some questions:
With the following code will gcc allocate sector at 0x3000? if not, how?
Do you know any better way to do this?
Code:
...
typedef uint8_t sector[1024];
uint32_t* prime = (sector*)0x3000;
uint32_t data = *prime;
uint32_t dataToWrite = 0xdeadbeef;
flash_init();
flash_sector_erase(0x3000);
flash_block(0x3000,(uint8_t*)&dataToWrite,4);
data = *prime;
...
Linker script
MEMORY {
...
m_text(rx): ORIGIN = 0x00002000, LENGTH = 0x2000
...
}
UPDATE:
I have to reserve space at a given flash address with GCC, it seams that other compilers have a solution for this but with GCC i have to use linker scripts.
from here i read this:
The special linker variable dot `.' always contains the current output
location counter. Since the . always refers to a location in an output
section, it may only appear in an expression within a SECTIONS
command. The . symbol may appear anywhere that an ordinary symbol is
allowed in an expression.
Assigning a value to . will cause the location counter to be moved.
This may be used to create holes in the output section. The location
counter may never be moved backwards.
SECTIONS
{
output :
{
file1(.text)
. = . + 1000;
file2(.text)
. += 1000;
file3(.text)
} = 0x1234;
}
In the previous example, the .text' section fromfile1' is located
at the beginning of the output section output'. It is followed by a
1000 byte gap. Then the.text' section from file2' appears, also
with a 1000 byte gap following before the.text' section from
file3'. The notation= 0x1234' specifies what data to write in the
gaps (see section Output section fill).
My m_text section looks like this:
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
} > m_text
So what i can do is to change it to:
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
. = NEXT(0x400); /* move to start of next 1kb section*/
. += 0x400; /* jump 1k forward */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
} > m_text
Now we have reserved data on the flash, but the address will be dependent on the size of .text with is not ideal, but it might work if we change from direct addressing in the FW code to a section name and then take the size of .text from the FW project and padd to the next 1024 bytes sector to get the address that we will import to the Prime number project.
I am also not too happy about the padding before the sector, in an ideal world we would fill it with "junk code" or random data, i have seen that you can specify a pattern to fill with, but any consistent pattern will shine up as much as 0xffffff or 0x00000 to a reverse engineer ;)
any better ideas?
Not sure what compiler you are using but with GCC you can specify the section with a section attribute details here.
You could locate the array into the m_text section using gcc __attribute__((section("m_text") )).
A memory region is not a section. So you need to add this section to the linker script, placing it into this memory region (not sure, if both use different namespaces, but it would be better style to have different names for region and section). If that is not loaded with the programm, it has to be NOLOAD (much like .bss).
Or you only use C constucts and no linker facilities at all. However, this will prevent the data to load with the program, so it only works if you intend to program that area seperately from the program.

Resources