Adding a RAM section in linker file STM32 - c

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)

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

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);

Reserve flash memory space in TI cc26xx

I am currently trying to reserve a sector of the flash memory in the linker file, to save some data to it (using the driverlib API). I first flash the script writing to the specific memory addresses, then I run my application which reads the saved data.
Unfortunately whenever I flash, the data is lost. Therefore I am trying to change the linker file and reserve some space but I am not sure if I am doing it correctly. I will appreciate any help or hints.
MEMORY
{
/* Flash Size 128 KB minus the CCA area below (88 bytes) */
/* OLD: FLASH (RX) : ORIGIN = 0x00000000, LENGTH = 0x0001FFA8 */
FLASH (RX) : ORIGIN = 0x00000000, LENGTH = 0x0001FF88
/*
* Custom reserved memory space with size of 32 bytes
*/
CNVM (RX) : ORIGIN = 0x0001FF88, LENGTH = 32
/*
* Customer Configuration Area and Bootloader Backdoor configuration
* in flash, up to 88 bytes
*/
FLASH_CCFG (RX) : ORIGIN = 0x0001FFA8, LENGTH = 88
/* RAM Size 20KB */
SRAM (RWX) : ORIGIN = 0x20000000, LENGTH = 0x00005000
/* Application can use GPRAM region as RAM if cache is disabled in CCFG */
GPRAM (RWX) : ORIGIN = 0x11000000, LENGTH = 0x00002000
}
/*. Highest address of the stack. Used in startup file .*/
_estack = ORIGIN(SRAM) + LENGTH(SRAM); /* End of SRAM */
/*. Generate a link error if heap and stack don’t fit into RAM .*/
_Min_Heap_Size = 0;
_Min_Stack_Size = 0x100;
SECTIONS
{
.text :
{
_text = .;
KEEP(*(.vectors))
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH = 0
.text:
{
*(.rodata*)
} > CNVM
.data :
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM AT > FLASH
.ARM.exidx :
{
*(.ARM.exidx*)
} > FLASH
.bss :
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > SRAM
.ccfg :
{
KEEP(*(.ccfg))
} > FLASH_CCFG
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(4);
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(4);
} > SRAM
.gpram :
{
} > GPRAM
}
Using the linker approach didn't provide the desired result in my case. What I ended up doing instead is editing the generated binary file from my code before flashing the sensortag.
Using the hexdump command on linux as follows hexdump generated_binary_file.bin one can notice that each line includes the start memory address (for a certain range of addresses) followed by the data written to this range.
And so using a small python script one can change the values in a certain line and generate its own modified binary file that will be later flashed to the sensortag. The memory address content can be read with a certain function that you include in your initial code (before bin generation & editing) such memcpy() for example.

Placing variables at specific address generates large binary file

I have to place array at specific address in memory. I'm using GCC.
I declare variable like this:
uint8_t __attribute__((section (".mySection"))) buffer[1234];
And in linker script I've got:
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 145K
MYSEC (x) : ORIGIN = 0x20025000, LENGTH = 155K
}
and later:
.mySection :
{
*(.mySection);
} > MYSEC
It's of course code for embedded system (ARM). Normally my program takes 22 KB, with this modification it takes 384 MB (!).
I don't understand why. If I remove __attribute__ it takes 22 KB again.
What am I missing?
Code used:
#inculde (...)
uint8_t __attribute__((section (".mySection"))) buffer = 5;
int main(void){
buffer = 10;
}
Full linker script (default, not written by me, parts are shortened:
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20050000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 145K
MYSEC (x) : ORIGIN = 0x20025000, LENGTH = 155K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
(...)
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
(...)
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
(...)
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
(...)
} >FLASH
.preinit_array :
{
(...)
} >FLASH
.init_array :
{
(...)
} >FLASH
.fini_array :
{
(...)
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
(...)
} >RAM AT> FLASH
.mySection :
{
*(.mySection);
} > MYSEC
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
(...)
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
(...)
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
(...)
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
I assume your output file is a pure binary format, not an ELF so you don't have ELF bootloader. In that case, placing initialized data at high address obviously will create a big output file. This is because in the binary format you don't have any mechanism to provide specific address, so the file is mapped 1:1 into memory. It means the whole address space up to your custom initialized variable needs to be included in the output file.
This is the same situation as with .data section and this is why the .data section is explicitly copied from Flash into RAM area at the startup.
p.s. You can find my article helpful (this issue is described there).

Linker script: align section to the next power of two

So, I have this linker script:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
SECTIONS
{
.kernel.text :
{
_kernel_text = .;
KEEP(kernel.a(.isr_vector))
KEEP(kernel.a(_sbrk))
kernel.a(.text*)
kernel.a(.rodata*)
_kernel_etext = .;
_kernel_flash_data = ALIGN(0x4);
} > FLASH
.kernel.data : /*AT(ADDR(.text) + SIZEOF(.text))*/ /*contains initialized data*/
{
_kernel_data = .;
kernel.a(vtable)
kernel.a(.data*)
_kernel_edata = .;
} > SRAM AT > FLASH
.kernel.bss :
{
_kernel_bss = .;
kernel.a(.bss*)
kernel.a(COMMON)
_kernel_ebss = .;
} > SRAM
.core.text : ALIGN(0x1000)
{
_core_text = .;
core.a(.text*)
core.a(.rodata*)
_core_etext = .;
_core_flash_data = ALIGN(0x4);
} > FLASH
/* There is lots more, but this is the interesting part */
}
And lets focus on the flash. The flash starts at 0x0 and first the entire kernel.text is written to it, then kernel.data is written to it. And I want a MPU to protect this memory region. This MPU can only work on memory sections with a size of a power of two. I want the entire kernel section in the flash (data + text) to be protected under a single region by the MPU, so I need its total size to be a power of two. The align command on .core.text will do this for me, for now. I know for a fact that .kernel.text + .kernel.data = 3960 bytes long, so I want to align to 2^12 = 4096 = 0x1000. Now I can protect from 0x0 to 0x1000 under a single region and protect the kernel under a single MPU region. Party on!
But the project is far from done and code will be added, so we might cross this boundry. The first time this is not a problem: the memory will align to the next 4096 barrier, which is 8192 and 2^13. But after that, the memory will align to 12288, which is not a power of two, so my MPU will spit at me and scoff me.
Is there a way to make this linker script always align to the next power of two?
Thankfully, GNU ld provides a handy-dandy log2ceil() function for instances just like this. Simply write your linker script as follows:
/* ... */
_flash_text_data_end_aligned = (2 << LOG2CEIL( _etext + SIZEOF(.data)));
/* ... */
.core.text: ALIGN(_flash_text_data_end_aligned)
/* ... */

Resources