How to link denser code in IAR in split sections? - c

I make the program for STM8L151G6 on IAR Embedded Workbench for STM8 (version 3.11.1)
I need to place the instruction JPF 0xf000 at 0x008426 address.
I do this. In C code:
__root static const uint8_t jpfat0x8426 [] # "ENTRY_POINT" = {0xac, 0x00, 0xf0, 0x00}; // jpf 0xf000
In .icf file:
define region EntryPoint = [from 0x8426 to 0x842A];
define region VectorsRegion = [from 0x8000 size 0x80];
define region NearFuncCode = [from 0x8080 to 0xEF7F] - EntryPoint;
define region FarFuncCode = [from 0x8080 to 0xEF7F] - EntryPoint;
define region HugeFuncCode = [from 0x8080 to 0xEF7F] - EntryPoint;
place at start of EntryPoint { ro section ENTRY_POINT };
place in EntryPoint { };
Linker builds the code in next way:
...
"A2": 0x80
INTVEC 0x008000 0x80 <Block>
.intvec const 0x008000 0x80 interrupt.o [4]
- 0x008080 0x80
"A3": 0x4
ENTRY_POINT const 0x008426 0x4 project51.o [1]
- 0x00842a 0x4
"P3-P5": 0x20cb
.near_func.text ro code 0x00842b 0x3a6 float.o [4]
.near_func.text ro code 0x0087d1 0x2a1 data_exchange.o [1]
.near_func.text ro code 0x008a72 0x1fa fuel_gauge.o [1]
...
It is right. But the range [from 0x008080 to 0x00842b] is empty, so code is not compact.
I loose close 1K bytes, it is too many for stm8 MCU. For example object float.o (size 0x3a6)
can be placed to this range, but linker doesn't do this. Is there some way to tell the linker to do denser code and fill empty chunks of sections with objects.
Thank you.

Related

IAR Linker Configuration File. Knowing the end address of memory section at runtime

I am working with an ARM chip on IAR. Its linker file describes a RAM section like this:
The stack and RW global variables are stored at the end of the RAM region of physical memory. Having it at the end is important to my SW design albeit not necessarily relevant to this particular question.
My application consumes a rather large file that I want to store outside of the RAM section, like so:
The reason I want to put this "BLOB_FS" outside is because I only use it once at the beginning. That way, I can recycle that memory for other purposes later, which I prefer over permanently reserving a giant heap. For example, let's say the stack and RW data go from 0x0175000-0x017FFFF. I would want to populate the BLOB_FS data by doing something like:
(uint32_t*) blob_fs_base_ptr = (0x0175000 - sizeof(blob_fs));
memcpy(blob_fs_base_ptr, blob_fs, sizeof(blob_fs));
I do not know the size of the blob_fs until runtime.
The problem is the lower address of the RAM section varies depending on whatever global variables you may have so I don't know what exactly the "xxxxx" in 0x017xxxxx-0x017FFFFF is ahead of time. The relevant section of my IAR ICF file looks like this:
define symbol __ICFEDIT_region_RAM_start__ = 0x01000000;
define symbol __ICFEDIT_region_RAM_end__ = 0x017FFFFF; // 8192K
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__];
"RAM": place at end of RAM_region { readwrite,
block CSTACK, block SVC_STACK, block IRQ_STACK, block FIQ_STACK,
block UND_STACK, block ABT_STACK, block HEAP };
Which generates a map file like this:
"RAM": 0x11'21d8
rw-1 0x16e'de28 0xbc <Init block>
.data inited 0x16e'de28 0x9c main.cpp.obj [1]
.data inited 0x16e'dec4 0x1c rom_cmd_handler.cpp.obj [12]
.data inited 0x16e'dee0 0x4 system_cmsis_falcon2.c.obj [8]
.bss zero 0x16e'dee4 0x40 main.cpp.obj [1]
.bss zero 0x16e'df24 0x400 rom_cmd_handler.cpp.obj [12]
.bss zero 0x16e'e324 0x28 source.cpp.obj [11]
.bss zero 0x16e'e34c 0x4 blob_fs.cpp.obj [5]
.bss zero 0x16e'e350 0x4 blob_fs.cpp.obj [5]
.bss zero 0x16e'e354 0x8 rom_spi2c_handler.cpp.obj [12]
.bss zero 0x16e'e35c 0x2a0 spi2c_falcon2.cpp.obj [13]
.bss zero 0x16e'e5fc 0x7c vmem_falcon2.cpp.obj [14]
.bss zero 0x16e'e678 0x80 buffer_manager_common.cpp.obj [6]
.bss zero 0x16e'e6f8 0x24 drv_irq_cortex_m3.c.obj [10]
.bss zero 0x16e'e71c 0xe0 drv_irq_common.c.obj [10]
.bss zero 0x16e'e7fc 0x1 buffer_manager_falcon2.cpp.obj [6]
.bss zero 0x16e'e7fd 0x1 rom_spi2c_handler.cpp.obj [12]
.noinit uninit 0x16e'e800 0x11'0000 main.cpp.obj [1]
.noinit uninit 0x17f'e800 0x800 main.cpp.obj [1]
CSTACK 0x17f'f000 0x1000 <Block>
CSTACK uninit 0x17f'f000 0x1000 <Block tail>
- 0x180'0000 0x11'21d8
This configuration corresponds to the RAM section from 0x017xxxxx-0x017FFFFF.
Is there a way to get the exact value of 0x017xxxxx at runtime?
If you know a upper size limit for your blob_fs and have enough RAM available, you could let the linker reserve space for it by defining a separate block as recommended in the IAR C/C++ Development Guide section RESERVING SPACE IN RAM:
Often, an application must have an empty uninitialized memory area to be used
for temporary storage, for example, a heap or a stack.
It is easiest to achieve this at link time.
You must create a block with a specified size and then place it in a memory.
In the linker configuration file, it can look like this:
define block TempStorage with size = 0x1000, alignment = 4 { };
place in RAM { block TempStorage };
With the special operators __section_begin(), __section_end() and __section_size() you can then get access to the start address and the first address after the block as well as the size of the block.
The IAR manual has the following example:
To retrieve the start of the allocated memory from the application,
the source code could look like this:
/* Define a section for temporary storage. */
#pragma section = "TempStorage"
char *GetTempStorageStartAddress()
{
/* Return start address of section TempStorage. */
return __section_begin("TempStorage");
}
If you cannot define an explicit block for you blob_fs, it should also be possible to define a block for all other sections that should be placed at the end of the RAM. I think it should look like this (untested):
define block RAMDATA { readwrite, block CSTACK, block SVC_STACK, block IRQ_STACK,
block FIQ_STACK, block UND_STACK,
block ABT_STACK, block HEAP };
"RAM": place at end of RAM_region { readwrite, block RAMDATA };
Again you should then be able to use __section_being("RAMDATA") to determine the starting address of the RAM area reserved for variables etc. at link time and use that for address calculations at runtime.
I got it working by doing pretty much what Blue suggested. I turned my ICF file into (new in bold):
define symbol __ICFEDIT_region_RAM_start__ = 0x01000000;
define symbol __ICFEDIT_region_RAM_end__ = 0x017FFFFF; // 8192K
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__];
"RAM": place at end of RAM_region { **readwrite section .blobfssizeword,** readwrite,
block CSTACK, block SVC_STACK, block IRQ_STACK, block FIQ_STACK,
block UND_STACK, block ABT_STACK, block HEAP };
The new section .blobfssizeword ensures that I know the name of the section that I'm looking for. And by placing it first in the "RAM" memory block (is that the right word?), I make sure that .blobfssizeword occupies the lowest-value address in that group.
Next, I get the size of the blobfs at runtime from the user via a 32-bit integer. So in my main.cpp I make sure to declare that in the .blobfssizeword section:
#define BLOBFS_SIZE_WORD_LENGTH 4 //The blobfs size is stored in a 32-bit value
static uint8_t __attribute__((section(".blobfssizeword"))) blobfs_size_word[BLOBFS_SIZE_WORD_LENGTH] = {0};
#pragma section = ".blobfssizeword"
void calculate_blobfs_starting_address() {
uint32_t blobfs_size = *(reinterpret_cast<uint32_t*>(blobfs_size_word));
memcpy(blobfs_size_word, (uint8_t*)&blobfs_size, sizeof(blobfs_size_word));
//Write to array to make sure compiler doesn't optimize it out. There's probably a better way to do this, but I'm just trying to make it work
size_t blobfs_size_word_start = (size_t)__section_begin(".blobfssizeword");
size_t blobfs_start_addr = blobfs_size_word_start - (size_t)blobfs_size;
return blobfs_start_addr;
}
After doing the above, I ensure the compiler compiles in the .blobfssizeword section, so the map file looks like this (new in bold):
"RAM": 0x1'21d4
**.blobfssizeword 0x15e'de2c 0x4 <Block>
.blobfssizeword-1 0x15e'de2c 0x4 <Init block>
.blobfssizeword inited 0x15e'de2c 0x4 main.cpp.obj [1]**
rw-1 0x15e'de30 0x28 <Init block>
.data inited 0x15e'de30 0x4 main.cpp.obj [1]
.data inited 0x15e'de34 0x20 rom_cmd_handler.cpp.obj [13]
.data inited 0x15e'de54 0x4 system_cmsis_falcon2.c.obj [8]
.bss zero 0x15e'de58 0x40 main.cpp.obj [1]
.bss zero 0x15e'de98 0x4 main.cpp.obj [1]
.bss zero 0x15e'de9c 0xc main.cpp.obj [1]
.bss zero 0x15e'dea8 0x6c main.cpp.obj [1]
.bss zero 0x15e'df14 0x24 main.cpp.obj [1]
.bss zero 0x15e'df38 0x400 rom_cmd_handler.cpp.obj [13]
.bss zero 0x15e'e338 0x10 source.cpp.obj [12]
.bss zero 0x15e'e348 0x4 blob_fs.cpp.obj [5]
.bss zero 0x15e'e34c 0x8 rom_spi2c_handler.cpp.obj [13]
.bss zero 0x15e'e354 0x2a0 spi2c_falcon2.cpp.obj [14]
.bss zero 0x15e'e5f4 0x7c vmem_falcon2.cpp.obj [15]
.bss zero 0x15e'e670 0x8 real_otp.cpp.obj [11]
.bss zero 0x15e'e678 0x80 buffer_manager_common.cpp.obj [6]
.bss zero 0x15e'e6f8 0x24 drv_irq_cortex_m3.c.obj [10]
.bss zero 0x15e'e71c 0xe0 drv_irq_common.c.obj [10]
.bss zero 0x15e'e7fc 0x1 buffer_manager_falcon2.cpp.obj [6]
.bss zero 0x15e'e7fd 0x1 rom_spi2c_handler.cpp.obj [13]
.noinit uninit 0x15e'e800 0x1'0000 main.cpp.obj [1]
.noinit uninit 0x15f'e800 0x800 main.cpp.obj [1]
CSTACK 0x15f'f000 0x1000 <Block>
CSTACK uninit 0x15f'f000 0x1000 <Block tail>
- 0x160'0000 0x1'21d4

C Recursive Macros for table offsets

We need to create a static table of addresses for an external memory.
The size of the stored structures is known and fixed. However, the addresses of these structures must start at the beginning of a memory section.
We intended to do this by using macros, because lets the table change as the elements of the table change at compile time.
The implementation looks something like this:
#define BLOC (0xFF)
#define FINAL_ADR(N) (ADR_##N + SIZE_##N)
#define GET_ADR(N) (FINAL_ADR(N) + (BLOC - (FINAL_ADR(N) & BLOC)))
#define ADR_0 (0x0)
#define SIZE_0 (5)
#define ADR_1 GET_ADR(0)
#define SIZE_1 (300)
#define ADR_2 GET_ADR(1)
#define SIZE_2 (130)
...
const struct {
uint32_t addr, size;
} mem[] = {
{.addr = ADR_0, .size = SIZE_0},
{.addr = ADR_1, .size = SIZE_1},
{.addr = ADR_2, .size = SIZE_2},
};
The code above tries to assign an address from the previous address and size of element.
Element 0 starts at position 0x00 and ocupes 5 bytes (1 section).
Element 1 should start at position 0xFF and ocupes 300 bytes (2 sections).
Element 2 should start at position 0x2FD and ocupes 130 bytes (1 section).
If we try to implement the macros above, the compiler fails. Because the macros are referencing themselves at the Element 2:
ADR_2 = ((GET_ADR(1)+ SIZE_1) + (0xFF- ((GET_ADR(1)+ SIZE_1) & 0xFF)))
Is it possible to circumvent this indirect self-referencing macro? Is there a better way to accomplish this?
You can abuse an enumeration to get this.
#include <stdint.h>
#define BLOC (0xFF)
#define FINAL_ADR(N) (ADR_##N + SIZE_##N)
#define GET_ADR(N) (FINAL_ADR(N) + (BLOC + 1 - (FINAL_ADR(N) & BLOC)))
enum {
ADR_0 = 0x0, SIZE_0 = 5,
ADR_1 = GET_ADR(0), SIZE_1 = 300,
ADR_2 = GET_ADR(1), SIZE_2 = 130,
};
const struct {
uint32_t addr, size;
} mem[] = {
{.addr = ADR_0, .size = SIZE_0},
{.addr = ADR_1, .size = SIZE_1},
{.addr = ADR_2, .size = SIZE_2},
};
Compiled to assembler source (for x86_64):
.text
.section .rdata,"dr"
.globl mem # #mem
.p2align 4
mem:
.long 0 # 0x0
.long 5 # 0x5
.long 256 # 0x100
.long 300 # 0x12c
.long 768 # 0x300
.long 130 # 0x82
Oh, and your blocking formula needs that additional + 1 to reach the next multiple of 0x100.
This is not how you memory map variables in embedded systems.
All variables with a custom memory map gets configured in the linker script, not in the source code. You create segments (sometimes also called sections) in the linker script, where you specify the starting address and the size of each segment.
Then in the source, you use some compiler-specific non-standard extension to allocate variables inside that segment you created. Example for gcc:
struct element0 stuff __attribute__ (section ("ELEMENT_0")) = { ... };
Now if some other part of the code needs to know the address of this variable, then it can be obtained with &stuff.
In case your external memory is a serial one accessed by SPI etc and not memory mapped, then simply make a hard-coded lookup table with (named) numeric constants. No need for all these complex, unreadable macros. Good programmers write code as simple as possible, not as complicated as possible.

IAR Linker Configuration File - Missing ".intvec" Placement

I'm working with an IAR project where there are ILINK Configuration Files (.icf) for both a bootloader and the main application. Each file defines the __ICFEDIT_intvec_start__ symbol and later places it referencing their respective .intvec sections (there are 2 cstartup.s files, each with their own .intvec section):
Bootloader .icf:
define symbol __ICFEDIT_intvec_start__ = 0x18000000;
...
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
Application .icf:
define symbol __ICFEDIT_intvec_start__ = 0x18080000;
...
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
From what I understand, each of these .icf files are used to generate their own .map files. Seeing as how each of these reference two different sections (that share the same name), I'm confused as to why only the .map file for the bootloader references the .intvec section:
Bootloader .map:
*******************************************************************************
*** PLACEMENT SUMMARY
***
"A1": place at 0x18000000 { ro section .intvec };
"P1": place in [from 0x18000040 to 0x1807ffff] { ro };
"P2": place in [from 0x20020000 to 0x209fffff] {
rw, block CSTACK, block SVC_STACK, block IRQ_STACK, block FIQ_STACK,
block UND_STACK, block ABT_STACK, block HEAP };
Section Kind Address Size Object
------- ---- ------- ---- ------
"A1": 0x3c
.intvec ro code 0x18000000 0x3c cstartup.o [1]
- 0x1800003c 0x3c
...
Application .map:
*******************************************************************************
*** PLACEMENT SUMMARY
***
"INT_VEC_RAM":
place at 0x20020000 { section .intvec_RAM };
"ROM": place in [from 0x18080040 to 0x1bffffff] {
ro section .cstartup, block ROM_CONTENT };
"RAM": place in [from 0x20020040 to 0x209fffff] { block RAM_CONTENT };
Section Kind Address Size Object
------- ---- ------- ---- ------
"ROM": 0x2405e0
ROM_CONTENT 0x18080040 0x2405e0 <Block>
.text ro code 0x18080040 0x104 access.o [8]
.text ro code 0x18080144 0x18c cstartup.o [1]
What's happening here? I'm just starting to understand the role of the linker, so I'm fairly new to all of this.
Also, to further clarify, the two .intvec sections comprise the same interrupt vector table:
SECTION .intvec:CODE:NOROOT(2)
...
__vector: ; Make this a DATA label, so that stack usage
; analysis doesn't consider it an uncalled fun
ARM
; All default exception handlers (except reset) are
; defined as weak symbol definitions.
; If a handler is defined by the application it will take precedence.
LDR PC,Reset_Addr ; Reset
LDR PC,Undefined_Addr ; Undefined instructions
LDR PC,SWI_Addr ; Software interrupt (SWI/SVC)
LDR PC,Prefetch_Addr ; Prefetch abort
LDR PC,Abort_Addr ; Data abort
DCD 0 ; RESERVED
LDR PC,IRQ_Addr ; IRQ
LDR PC,FIQ_Addr ; FIQ
It seems that the answer is a lot more obvious than I thought. According to the section "Linking—an overview" in "IAR C/C++ Development Guide", IAR's linker software ILINK ignores duplicate sections. Thus, if a section is already referenced in one binary object or ILINK configuration file (ICF), all other references to it are ignored.
In this project, since the bootloader takes precedence (is loaded and flashed before the application [defined in the project's .board files; more info here]), the application code's .intvec is seen as a duplicate and is thus ignored/discarded.

How to make the arm linker decompress the second correctly with "RW data compression"?

title: How to make the arm linker decompress the second load region while both 1st and 2nd region are applied "RW data compression"?
I got a below scatter file (a simple version of my complex scatter file)
LOAD_REGION_01 0x0
{
EXECUTION_REGION_01 0x0
{
ABC.o (+RO,+RW)
}
}
LOAD_REGION_02 +0x0
{
EXECUTION_REGION_02 0x100
{
DEF.o (+RO,+RW)
}
}
The RW data compression is applied automatically and I got a binary with size 0x40 (0x20 for LOAD_REGION_01 and 0x20 for LOAD_REGION_02 ), the got a link.info as following MAP file.
Load Region LOAD_REGION_01 (Base: 0x0, Size: 0x30, Max: 0x000000a0, ABSOLUTE)
Execution Region EXECUTION_REGION_01 (Base:0x0,Size:0x30,Max:0xffffffff,ABSOLUTE, COMPRESSED[0x20])
Load Region LOAD_REGION_02 (Base: 0x30, Size: 0x30, Max: 0x000000a0, ABSOLUTE)
Execution Region EXECUTION_REGION_02 (Base:0x0,Size:0x30,Max:0xffffffff,ABSOLUTE, COMPRESSED[0x20])
I found that the size of load region after compression is 0x20, but the start address of second load region is 0x30 ! And, the decompression of first region work correctly, but arm try to decompress the second region from address "0x30" not "0x20".
Therefore, a wrong decompression result is produced. How to make the arm linker decompress the second correctly? Or how can I assign the load address of second load region LOAD_REGION_02 with attribute +0 (since the size load region 1 may changed...) ?
I read all the user manual but no answer...
It seems your issue is that both load regions are declared to start at address 0.
From a quick read of the ARM doc I see two options to resolve it:
Use one load region and two exec regions:
LOAD_REGION_01 0x0
{
EXECUTION_REGION_01 0x0
{
ABC.o (+RO,+RW)
}
EXECUTION_REGION_02 0x100
{
DEF.o (+RO,+RW)
}
}
Use two load regions with non-overlapping addresses. +0 should work if I'm reading it correctly.
LOAD_REGION_01 0x0
{
EXECUTION_REGION_01 0x0
{
ABC.o (+RO,+RW)
}
}
LOAD_REGION_02 +0
{
EXECUTION_REGION_02 0x100
{
DEF.o (+RO,+RW)
}
}

Where is function pointer stored?

In an embedded environment, for example, I declared a look-up table in following way:
const Operation Vehicle_OpNotification[] =
{
{ OP_SET , &Vehicle_Notification_Set},
{ OP_GET , &Vehicle_Notification_Get}
};
May I know this function pointer is pointing to RAM address or ROM address?
That depends on your compiler and target environment, but most likely it points to ROM—executable code is almost always placed in read-only memory when available. You have to take special steps (like using a custom linker script or allocating dynamic memory and asking the operating system to mark it as writable and executable) to get executable code in non-read-only memory.
The linker should be able to generate a map-file where the location of each variable/constant defined at file-scope is shown. Example:
main.c:
static const int constant = 42;
static int volatile variable = 43;
int main (int argc, const char * argv[]) {
variable = constant;
return 0;
}
Generated map-file: (notice how the constant constant is placed in the same memory segment as the read-only code (TEXT), while the variable variable is placed in the DATA section)
# Sections:
# Address Size Segment Section
0x00001F6C 0x0000008F __TEXT __text
0x00001FFC 0x00000004 __TEXT __literal4
0x00002000 0x00000018 __DATA __data
0x00002018 0x0000001C __DATA __dyld
0x00003000 0x00000008 __IMPORT __pointers
0x00003040 0x00000005 __IMPORT __jump_table
# Symbols:
# Address Size File Name
0x00001F6C 0x00000040 [ 1] start
0x00001FAC 0x00000014 [ 1] dyld_stub_binding_helper
0x00001FC0 0x0000000E [ 1] __dyld_func_lookup
0x00001FCE 0x0000002D [ 2] _main
0x00001FFC 0x00000004 [ 2] _constant
0x00002000 0x00000004 [ 1] ___progname
0x00002004 0x00000004 [ 1] _environ
0x00002008 0x00000004 [ 1] _NXArgv
0x0000200C 0x00000008 [ 1] _NXArgc
0x00002014 0x00000004 [ 2] _variable
0x00002018 0x0000001C [ 1] __dyld#0
0x00003000 0x00000004 [ 2] _variable$non_lazy_ptr
0x00003004 0x00000004 [ 2] _constant$non_lazy_ptr
0x00003040 0x00000005 [ 0] _exit$stub
Its address will be entirely dependent on the location of the function pointed to; the linker will tell you that. Most often application code ie either all in RAM or all in ROM, though for some targets, it may be split for performance reasons if RAM execution is faster as it typically is on processors faster than around 100MHz.
If you need to determine this at runtime (if for example it may be either, and is dynamic), you can simply compare the address with the start and end of the relevant memory sections. These addresses may be emitted as symbols by the linker given an appropriate linker script, or they may be defined constants in a target header file or BSP for example.
All that said, I can think of few reasons you should need to know at run-time (unless you are considering self modifying code!).
This is dependent on architecture.
However, if you have some idea of the address ranges for ROM and RAM, you can check the function pointer to the address.
Or, as Mehrdad says, just try writing to the address, it should be easy to figure out from there.
It all depends on which compiler/processor you use. If I put your definition in Keil Uvision for 8051, surely would point to ROM because of the "CONST" definer although I could modify with XRAM or CODE. But for ARM depends on address and not definer.
Keil Uvision Example:
// For 8051 Keil
const char code romdata[2] = {0,1}; //<< this point to ROM/FLASH
const char xram ramdata[2] = {0,1}; // this point to external RAM
const char romdata[2] = {0,1}; // this point to ROM/FLASH

Resources