Linker script: align section to the next power of two - linker

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)
/* ... */

Related

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

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.

How can I duplicate the Interrupt Vector Table using the linker script on a Cortex M0+?

First of all, I hope I'm not asking something that has already been asked before. I've searched as much as I can but I haven't found an answer to my specific problem or something useful.
I'm working on a FRDM-KL82Z board which runs a Cortex M0+ core. I'm using MCUXpresso IDE v10.0.2 and a Segger J-Link programmer, although I think this is not relevant for this question.
This project will need a custom bootloader and app coded by different developers, each block with its own flash memory space: 8K for the bootloader and 120K for the app (this may change in the future but it's no big deal at the moment).
Once the bootloader is completed, it will manage the jump to App space and the app will change de Vector Table Offset Register (VTOR) so that the Interrupt Vector Table changes form the Boot IVT to the App IVT. This has already been tested successfully.
My aim is to set up the linker script file so that the app developers can build and debug their project on the board before the bootloader is completed, as they will be developed at the same time. The reason for this is that they can work with the App space as it will be in the final version.
I think the Reset vector and the Config bits must be at their default position because the hardware will go to the same position every time it needs to read them.
My first idea consist in disabling the automatic linker script generation and modifying the MyProject_Debug.ld file.
What the script automatically generates:
INCLUDE "LEDTest_Debug_library.ld"
INCLUDE "LEDTest_Debug_memory.ld"
ENTRY(ResetISR)
SECTIONS
{
/* MAIN TEXT SECTION */
.text : ALIGN(4)
{
FILL(0xff)
__vectors_start__ = ABSOLUTE(.) ;
KEEP(*(.isr_vector))
/* Global Section Table */
. = ALIGN(4) ;
__section_table_start = .;
__data_section_table = .;
LONG(LOADADDR(.data));
LONG( ADDR(.data));
LONG( SIZEOF(.data));
LONG(LOADADDR(.data_RAM2));
LONG( ADDR(.data_RAM2));
LONG( SIZEOF(.data_RAM2));
__data_section_table_end = .;
__bss_section_table = .;
LONG( ADDR(.bss));
LONG( SIZEOF(.bss));
LONG( ADDR(.bss_RAM2));
LONG( SIZEOF(.bss_RAM2));
__bss_section_table_end = .;
__section_table_end = . ;
/* End of Global Section Table */
*(.after_vectors*)
/* Kinetis Flash Configuration data */
. = 0x400 ;
PROVIDE(__FLASH_CONFIG_START__ = .) ;
KEEP(*(.FlashConfig))
PROVIDE(__FLASH_CONFIG_END__ = .) ;
ASSERT(!(__FLASH_CONFIG_START__ == __FLASH_CONFIG_END__), "Linker Flash Config Support Enabled, but no .FlashConfig section provided within application");
/* End of Kinetis Flash Configuration data */
} >PROGRAM_FLASH
.text : ALIGN(4)
{
*(.text*)
*(.rodata .rodata.* .constdata .constdata.*)
. = ALIGN(4);
} > PROGRAM_FLASH
/*
* for exception handling/unwind - some Newlib functions (in common
* with C++ and STDC++) use this.
*/
.ARM.extab : ALIGN(4)
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > PROGRAM_FLASH
__exidx_start = .;
.ARM.exidx : ALIGN(4)
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > PROGRAM_FLASH
__exidx_end = .;
_etext = .;
/* USB_RAM */
.m_usb_data (NOLOAD) :
{
*(m_usb_bdt)
*(m_usb_global)
} > USB_RAM
/* possible MTB section for USB_RAM */
.mtb_buffer_RAM2 (NOLOAD) :
{
KEEP(*(.mtb.$RAM2*))
KEEP(*(.mtb.$USB_RAM*))
} > USB_RAM
/* DATA section for USB_RAM */
.data_RAM2 : ALIGN(4)
{
FILL(0xff)
PROVIDE(__start_data_RAM2 = .) ;
*(.ramfunc.$RAM2)
*(.ramfunc.$USB_RAM)
*(.data.$RAM2*)
*(.data.$USB_RAM*)
. = ALIGN(4) ;
PROVIDE(__end_data_RAM2 = .) ;
} > USB_RAM AT>PROGRAM_FLASH
/* MAIN DATA SECTION */
/* Default MTB section */
.mtb_buffer_default (NOLOAD) :
{
KEEP(*(.mtb*))
} > SRAM
.uninit_RESERVED : ALIGN(4)
{
KEEP(*(.bss.$RESERVED*))
. = ALIGN(4) ;
_end_uninit_RESERVED = .;
} > SRAM
/* Main DATA section (SRAM) */
.data : ALIGN(4)
{
FILL(0xff)
_data = . ;
*(vtable)
*(.ramfunc*)
*(.data*)
. = ALIGN(4) ;
_edata = . ;
} > SRAM AT>PROGRAM_FLASH
/* BSS section for USB_RAM */
.bss_RAM2 : ALIGN(4)
{
PROVIDE(__start_bss_RAM2 = .) ;
*(.bss.$RAM2*)
*(.bss.$USB_RAM*)
. = ALIGN (. != 0 ? 4 : 1) ; /* avoid empty segment */
PROVIDE(__end_bss_RAM2 = .) ;
} > USB_RAM
/* MAIN BSS SECTION */
.bss : ALIGN(4)
{
_bss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4) ;
_ebss = .;
PROVIDE(end = .);
} > SRAM
/* NOINIT section for USB_RAM */
.noinit_RAM2 (NOLOAD) : ALIGN(4)
{
*(.noinit.$RAM2*)
*(.noinit.$USB_RAM*)
. = ALIGN(4) ;
} > USB_RAM
/* DEFAULT NOINIT SECTION */
.noinit (NOLOAD): ALIGN(4)
{
_noinit = .;
*(.noinit*)
. = ALIGN(4) ;
_end_noinit = .;
} > SRAM
.heap : ALIGN(4)
{
_pvHeapStart = .;
. += 0x1000;
. = ALIGN(4);
_pvHeapLimit = .;
} > SRAM
.heap2stackfill :
{
. += 0x1000;
} > SRAM
.stack ORIGIN(SRAM) + LENGTH(SRAM) - 0x1000 - 0: ALIGN(4)
{
_vStackBase = .;
. = ALIGN(4);
_vStackTop = . + 0x1000;
} > SRAM
}
I've tried to find information in this guide about de GNU linker but my ideas haven't worked so far.
What I've tried:
Setting the location counter to a different value after the Config Words and copying the ISR_vector code snipped before the text section:
...
/* End of Kinetis Flash Configuration data */
} >PROGRAM_FLASH
.text : ALIGN(4)
{
/* MODIFIED CODE */
. = 0x2000; /* First position of App code */
FILL(0xff)
__vectors_start__ = ABSOLUTE(.) ;
KEEP(*(.isr_vector))
/* END OF MODIFIED CODE */
*(.text*)
*(.rodata .rodata.* .constdata .constdata.*)
. = ALIGN(4);
} > PROGRAM_FLASH
...
When I do this and I open the .hex file, the space between the Config Words (0x400) and the start of the App space (0x2000) is effectively empty (full of 0xFF) but the code after 0x2000 is nothing like the IVT table.
If I move location counter to 0x2000 before the IVT code lines it effectively moves the IVT adresses to the 0x2000 position. To do this, I move the Config Words part before the IVT part because de location counter can't move backwards.
I've tried creating a Bootloader section in the memory map, with the correct starting and length positions, and copying every line that by default gets placeD in the PROGRAM_FLASH section into a new one that goes to BOOTLOADER (the same code with ">BOOTLOADER" at the end). In this case de IVT only appears in the Boot space.
Is it possible that the linker script places de IVT only in the first place it is indicated and then ignores every other call? What am I doing wrong? Should I try another way to achieve this?
Thank you very much, I know it's quite a long!
I don't think it's possible to make a copy of the vector table using only linker shenanigans. The linker script will not let you match the same symbol multiple times so that you can output it twice.
From the binutils 2.29 manual:
If a file name matches more than one wildcard pattern, or if a file name appears explicitly and is also matched by a wildcard pattern, the linker will use the first match in the linker script.
I tested it without using any wildcard patterns at all with similar results, so I don't think the linker will ever let you output the same symbol twice.
I also tried using objcopy to create a renamed copy of the vector table that could referenced from the linker script but that table ended up as all zeroes and the whole approach was rather convoluted, so I don't think that's worth pursuing.
If you want to keep the application code as similar as possible between now and when the bootloader is completed, I would suggest a different approach:
Make use of the __vectors_start__ symbol provided by the existing linker script so that your code always knows where the vector table is placed, even if you make changes to the linker script.
void relocate_vector_table(void) {
extern unsigned __vectors_start__;
SCB->VTOR = (unsigned)&__vectors_start__;
}
This will allow the same code to work with your current configuration (no bootloader, ROM starting at 0x0) and your eventual bootloader configuration (ROM starting at 0x2000).
My experience with M4 application and bootloader shows that it is enough to set the Flash start at some offset address, and then in the application to initialize VTOR to this address.
From linker script:
#
/* Specify the memory areas */
MEMORY
{
CLASSBRAM (rw) : ORIGIN = 0x20000000, LENGTH = 0x80
/*RAM length = 192K - CLASSBRAM-length */
RAM (xrw) : ORIGIN = 0x20000080, LENGTH = 0x2FF80
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
/* FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K */
FLASH (rx) : ORIGIN = 0x08010000, LENGTH = 448K /*in case of 64K for Bootloader*/
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
PROVIDE( _Rom_Start = . );
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
.....
#
code:
extern const uint32_t _Rom_Start;
....
#define ROM_START ((uint32_t *)&_Rom_Start)
...
SCB->VTOR = (uint32_t)ROM_START;
My MCU projects typically have Makefile targets that will actually flash the chip (with building as a dependency of course), so what I did for this was make a special target that flashes the main firmware "solo".
My openocd-driven programmers can flash flat binaries and not just hex files, so I was able to do this by using dd to copy just the vector table off the start of the main firmware binary into its own file. I then write this to the start of flash, and the main firmware to its usual location in separate operations. The chip boots, gets the reset and stack addresses out of the copied vector table, starts the main firmware, and that then repoints the vector table address to its own copy at the higher address.
If your programmer doesn't support flat binaries you can use objdump or some other tool to turn a flat binary back into a hex file, or likely to change the base address of a hex file / fragment.

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;

Resources