Memory region calculation is often one of the most confusing topics especially for newcomers in firmware development (MCUs).
At first, it looks simple: set a start address and a size, and the job is done. But the real challenge comes when you ask: Does the end address belong to the region (inclusive) or not (exclusive)?
This small detail can make a big difference. If you get it wrong, your memory layout might overlap, waste space, or even cause hard-to-find bugs in your code.
In this article, we will explain the concept step by step with a real STM32 flash memory example. We will also show how to apply it in IAR Embedded Workbench linker scripts.
So let’s get started.
A memory region is always described by two primary attributes:
Start Address:
The start address defines where your memory region begins. In linker scripts, this is usually aligned with the beginning of a memory block (e.g., flash or RAM). Getting this right is critical, since all subsequent addresses are calculated relative to this point.
Size:
The size parameter specifies how much memory is available in that region. This value is usually written in hexadecimal and must match the actual hardware specification. If the size is incorrect, you risk linker errors or, worse, memory corruption at runtime.
Using these attributes, we can determine the end address. However, there are two possible conventions for calculating it: Inclusive End Address and Exclusive End Address
Inclusive End Address:
- Inclusive means the end address is part of the region.
- Defines memory as
[start_address, end_address]. - Both start and end are valid addresses inside the region.
Formula:
end = start + size - 1
Example:
Start = 0x08022000, Size = 0x003DA000
end = 0x08022000 + 0x003DA000 - 1 end = 0x083FFFFF
So the valid range is 0x08022000 – 0x083FFFFF.
Exclusive End Address
- Exclusive means the end address is NOT part of the region.
- The region stops right before the end.
- Defines memory as
[start_address, end_address). - The start address is valid, but the end is just beyond the region.
Formula:
end = start + size
Example:
Start = 0x08022000, Size = 0x003DA000
end = 0x08022000 + 0x003DA000 end = 0x083FC000
So, the half-open interval is [0x08022000, 0x083FC000). And here the valid range is 0x08022000 – 0x083FBFFF ( 0x083FC000 -1).
IAR Linker Script Example:
In IAR .icf files, memory regions are defined using the syntax:[from A to B]
This range is inclusive on both ends:
- from A → the first valid byte (inclusive)
- to B → the last valid byte (inclusive)
So [from A to B] covers all addresses from A through B, including both boundaries. It is not a half-open (exclusive) interval.
for example,
define symbol __ICFEDIT_region_ROM_start__ = 0x08022000;
define symbol __ICFEDIT_region_ROM_end__ = 0x083FBFFF;
define symbol __ICFEDIT_region_ROM_size__ = 0x003DA000;
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__
to __ICFEDIT_region_ROM_end__];
- 0x08022000 → first valid byte
- 0x083FBFFF → last valid byte
This region spans 64 KB (0x10000 bytes), since both start and end addresses are included.
Practical Tips for Engineers:
When working with memory regions in IAR .icf files (or any linker script), small mistakes in address calculation can lead to hard-to-trace bugs. Here are some practical tips:
1. Check the convention:
- IAR .icf → to is inclusive → always calculate end = start + size – 1.
- Datasheets → almost always list memory regions as inclusive (first and last valid addresses).
- Some GCC/Keil linker scripts → effectively use exclusive notation (end = start + size).
2. Avoid overlaps:
- A miscalculated end address can cause subtle bugs or clear errors like: “region overflowed by 1 byte.”
3. Be explicit:
- Define symbols like __region_end_inclusive__ or __region_end_exclusive__.
- Add a comment in your linker script explaining the convention.
- This prevents confusion for your future self or teammates.