Share memory region mapping between two binaries - c

The way you write a program for a dual-core MCU is that each core executes its own separate binary.
I want to create a shared static variables in common source file for which static allocations will be managed by the linker, so that the addresses of individual variables are the same from the perspective of the two cores. Is there any way to achieve this without explicity assigning addresses?
Already I created overlapping memory region in linker scripts for both cores,
/* Specify the memory areas */
MEMORY
{
RAM4_D3_NO_CACHE (xrw) : ORIGIN = 0x38000000, LENGTH = 8K
}
.ram4_d3_non_cachable :
{
. = ALIGN(8);
_sram4_d3_non_cachable = .;
*(.ram4_d3_non_cachable)
*(.ram4_d3_non_cachable*)
. = ALIGN(8);
_eram4_d3_non_cachable = .;
} >RAM4_D3_NO_CACHE AT> FLASH
but then location of each symbol is different (as expected from two independent linking processes per binary):
.ram4_d3_non_cachable
0x0000000038001fc0 0x9c build/ic_queue.o
0x0000000038001fc0 isInit
0x0000000038001fc4 m4ToM7
0x0000000038001fd0 descriptors
0x0000000038002050 m7ToM4
*(.ram4_d3_non_cachable*)
.ram4_d3_non_cachable
0x0000000038001fc0 0x9c build/ic_queue.o
0x0000000038001fc0 m4ToM7
0x0000000038001fcc m7ToM4
0x0000000038001fd8 descriptors
0x0000000038002058 isInit
*(.ram4_d3_non_cachable*)

Related

How to place an array of struct at specific location in RAM such as backup RAM in STM32F4 MCUs?

I am trying to store an array in the backup RAM on STM32F4 MCUs, so the content can survive system power cycle such as reboot caused by watchdog.
typedef struct {
//
} Foo;
Foo foos[40];
Is there a way to make sure foos points to the start of backup RAM(BKPSRAM_BASE) ?
Thanks in advance.
Edit your linker script and add section which will be in your backup RAM.
to memory segments:
MEMORY
{
/* other segments */
BKPRAM (rw) : ORIGIN = 0x40024000, LENGTH = 4k
}
Add section
.bkpram :
{
_BKPRAM_START = .;
. = ALIGN(4);
KEEP(*(.bkpram))
_BKPRAM_END = .;
} >BKPRAM
Then:
__attribute__((section(".bkpram"))) Foo foos[40];
But remember that access to this SRAM has to be enabled first.

GCC Linker . CC3200 - why data is copied from Application Image in SRAM to another location in SRAM?

I have noticed that in the TI for the CC3200 (ARMv8 / ARM Cortex M4) examples of the startup_gcc.c the actual data section within the application image is copied to a different location. The application image itself is copied from flash to SRAM by the cc3200s internal bootloader.
The application image itself is loaded into SRAM and run this way.
So in my opinion this is a total waste of memory, as the copies the data section to another place in SRAM. Am I missing something? Would the removing the code section out of the ResetISR and altering the Linker file would work fine and just use the memory within the application image in SRAM itself?
ResetISR:
uint32_t *pui32Src, *pui32Dest;
pui32Src = &__init_data;
for(pui32Dest = &_data; pui32Dest < &_edata; )
{
*pui32Dest++ = *pui32Src++;
}
Linker:
.text :
{
_text = .;
KEEP(*(.intvecs))
*(.bss.gpCtlTbl)
*(.text*)
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
_etext = .;
} > SRAM
.rodata :
{
*(.rodata*)
} > SRAM
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > SRAM
__init_data = .;
.data : AT(__init_data)
{
_data = .;
*(.data*)
. = ALIGN (8);
_edata = .;
} > SRAM
Edited Linker without copy (and changing the linker):
.data
{
_data = .;
*(.data*)
. = ALIGN (8);
_edata = .;
} > SRAM
This kind of thing is normal when you are loading to ROM. I would expect __init_data to point to an address in ROM, in which case the copy loads it from there to RAM.
In your case it appears that everything is already in SRAM, so there is no need to do a copy of the initialized data.
The only question is, how does the internal bootloader know how big the image is and how much to copy? As long as it includes the data section in its image size then you should be fine to remove the copy loop, and the : AT(__init_data).
It should be easy to test, just define a static int x = 42; and then if (x == 42) { led(on); } or similar.
I am not aware of the capabilities of the particular processor you are using but on X86 for example doing this allows the image to be loaded read-only. The data is then copied to pages that can be written to (actually for X86 in particular, copy-on-write is generally used for these pages so that multiple processes can initialize .data from the same memory and not copy pages that aren't actually changed).
In order to not need this step the image would need to be written with the various sections padded to page-alignment but people generally prefer that the image be as small as possible while containing all needed information.

Get address of ram sections during runtime

I want to implement a stack usage monitor for my NRF52840-Mikrocontroller with Segger Embedded Studio.
To monitor the maximum stack usage, I need some information during runtime like the end address of the .bss segment, which is the start of my free memory.
My approach is, to fill the ram from the .tbss section until to the stackpointer with a magic word.
During runtime, the stack will grow and will overwrite my magic words with data.
In a cyclic check, I am able to dedicate the end of the stack. From that information, I can derive the approximate stack usage.
Is it possible to get the addresses from the picture below during runtime in my c (or ASM) Program?
Here is a part of my .map file, where for example the symbol __bss_start is defined. Is it possible to access this symbol from c code?
*(COMMON)
0x0000000020020ec4 __bss_end__ = (__bss_start__ + SIZEOF (.bss))
0x000000000001b8c8 __bss_size__ = SIZEOF (.bss)
0x0000000020020ec4 __bss_load_end__ = __bss_end__
0x0000000000000001 . = ASSERT (((__bss_start__ == __bss_end__) || ((__bss_end__ - __RAM_segment_start__) <= __RAM_segment_size__)), error: .bss is too large to fit in RAM memory segment)
0x0000000020020ec4 __tbss_load_start__ = ALIGN (__bss_end__, 0x4)
Thanks for your help, I have solved the Problem now.
To access the symbols of the .map file during runtime, I used the following code:
extern char __bss_end__;
int main()
{
char * bss = &__bss_end__;
}
After this line of code, the bss variable contains the start address of the bss-section.
With this code, I am able to get the addresses of the RAM segments during runtime to monitor my stack usage.

Define ARM MCU memory boundaries

I'm working on a project that involves getting the CMSIS-RTOS packaging of FreeRTOS working on an STM32F051C6. I'm writing and debugging the code with VisualGDB inside of Visual Studio, and generating the project code using the STM32CubeMX tool provided by ST. The RTOS is running incredibly well and I'm all smiles, however, I added a queue and a memory pool to handle sending and receiving messages between tasks and the compiler complained that the .bss memory section that was compiled/generated would not fit in the memory section set out in the linker. This was resolved by decreasing the heap size in the FreeRTOS configuration header.
I'm a little unhappy about where this may take me when I want to make the project more complex (more tasks, queues etc) since I may start having to decrease the stack even further to allow the .bss section to fit.
So my question is - would a solution to this be to extend the .bss section into the .data section (the section above) to allow for more heap and uninitialized data in the .bss section? After some looking around and experimenting, I found that only about 1% (if not, less) of the .data section is actually being used, according to VisualGDB's Memory Explorer window at build time, and it seems crazy to have all that unused RAM.
In an attempt to do this myself, I had a thorough look through both the linker scripts and the startup code and I could not find where to define the start and end of the .bss. Is it possible to define these boundaries, how would I be able to do so if possible? If not possible, how does the linker know where these boundaries are on the target chip?
Below are what I think are the relevant sections in the linker script:
.data :
{
. = ALIGN(4);
_sidata = .;
_sdata = _sidata;
PROVIDE(__data_start__ = _sdata);
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .;
PROVIDE(__data_end__ = _edata);
} > SRAM
.bss :
{
. = ALIGN(4);
_sbss = .;
PROVIDE(__bss_start__ = _sbss);
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
PROVIDE(__bss_end__ = _ebss);
} > SRAM
PROVIDE(end = .);
...and the startup file:
extern void *_sidata, *_sdata, *_edata;
extern void *_sbss, *_ebss;
void __attribute__((naked, noreturn)) Reset_Handler()
{
//Normally the CPU should will setup the based on the value from the first entry in the vector table.
//If you encounter problems with accessing stack variables during initialization, ensure
//asm ("ldr sp, =_estack");
void **pSource, **pDest;
for (pSource = &_sidata, pDest = &_sdata; pDest != &_edata; pSource++, pDest++)
*pDest = *pSource;
for (pDest = &_sbss; pDest != &_ebss; pDest++)
*pDest = 0;
SystemInit();
__libc_init_array();
main();
for (;;) ;
}
I think you can not set size of .bss or .data section explicitly. It is only defined by your program. You can view how much memory each variable consumes from map-file.
Example:
.bss.xHeap 0x0000000020000690 0xa000 obj/heap_2.o
In my setup FreeRTOS configured with
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 40 * 1024 ) )
and I see all this 40 KB in my map-file.
Linker knows how much memory your MCU has and tries to place all memory objects to it. If it doesn't fit you see an error.
You can not "extend" .bss into .data section because the former is occupied by uninitialized statically-allocated variables and the latter by initialized statically-allocated variables. If you can get rid of some of statically-allocated variables (e.g. globals) your .bss or/and .data section sizes will reduce. 8 KB of RAM is not very much.
Maybe you should look for ._user_heap_stack section (or similar name) in you linker script and optimize heap (don't confuse with FreeRTOS heap) and stack size values. E.g. if you use only FreeRTOS memory allocation then you can set heap size as 0 and this will allow more space for .bss section.
From my project:
_Min_Heap_Size = 0; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
._user_heap_stack :
{
. = ALIGN(4);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(4);
} >RAM

GNU linker marking section with no initialized data for LOAD - Raw binary huge

I'm writing a bare metal ARM boot loader and am trying to use some internal SRAM as a scratch pad to communicate to the application code. For my needs I don't need to initialise or zero the memory. Using this script I can place my desired variables in the memory just fine.
/**
* Linker script for secondary bootloader.
*
* Allocatest the first 1Mb of DRAM for its use.
* Scratchpad in internal SRAM.
*/
MEMORY
{
SRAM : o = 0x402F0400, l = 0x0000FC00 /* 63kB available internal SRAM */
DDR0 : o = 0x80000000, l = 1M /* 1Mb external DDR Bank 0 */
}
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SECTIONS
{
.startcode :
{
__AppBase = .;
. = ALIGN(4);
*init.o (.text)
} >DDR0
.text :
{
. = ALIGN(4);
*(.text*)
*(.rodata*)
} >DDR0
.data :
{
. = ALIGN(4);
*(.data*)
} >DDR0
.bss :
{
. = ALIGN(4);
_bss_start = .;
*(.bss*)
*(COMMON)
_bss_end = .;
} >DDR0
.stack :
{
. = ALIGN(4);
__StackLimit = . ;
*(.stack*)
. = __AppBase + 1M;
__StackTop = .;
} >DDR0
_stack = __StackTop;
.internal_ram :
{
. = ALIGN(4);
*(.internal_ram*)
} >SRAM
}
When using objcopy to create the raw binary, I'm getting huge files. I'm assuming this is because the first bytes of the raw binary are actually the internal memory with megabytes of padding up to the start of the .text section. Objdump -h shows that the internal_ram section being marked with the CONTENTS, LOAD, and DATA flags even though the variables placed there are not initialised.
I can clean this up in objcopy using --remove-section=.internal_ram but it seems there should be a way to get the linker to recognise that the data is not initialised.
Is there a way to mark the section appropriately?
The correct section declaration is:
.internal_ram (NOLOAD) :
{
. = ALIGN(4);
*(.internal_ram*)
} >SRAM
The NOLOAD section attribute is documented but speaks in terms of program loaders handling the section at load time. At first this doesn't seem to apply to bare metal images but, for that purpose, objcopy acts like a program loader and honors the flag settings in the object file, omitting the section from the raw image.
The other answer mentions this as well - the key is to make the section NOLOAD so that the data remains uninitialized.
The `(NOLOAD)’ directive will mark a section to not be loaded at run time. The linker will process the section normally, but will mark it so that a program loader will not load it into memory.
A quote from Ashley Duncan that you might find useful:
NOLOAD is useful in embedded projects for making sure a block of RAM is not initialised or zeroed. For example if you want the contents of that RAM to not lose its values during a software reset (e.g. if you want to set a variable with the reason you are resetting). Another useful application is to pass information from a boot loader to application without the application startup code overwriting the values of that memory area. Of course in this case both the boot loader and application linker files need to declare the exact same memory area location and size.
Some more explanation/story can be found here

Resources