Comparing the startup and linker files across these architectures reveals a lot about their core design philosophies, from the simple, linear memory models of older architectures to the complex, vendor-specific ecosystems of modern ones.
Here is a detailed comparison, breaking down the purpose, key components, and architectural implications for each family.
Executive Summary
Microcontroller | Startup File Purpose | Linker File Purpose | Key Characteristics & Philosophy |
---|---|---|---|
8051 | Minimal. Copies initialized data from CODE to DATA memory. | Simple. Manages segments in overlapping physical memory. | Classic Harvard. Complex memory banking. Manual segment placement is common. |
PIC (8-bit) | Minimal. Clears RAM, copies initialized data. Often in assembly. | Defines memory regions and sections. Manages paging/banking. | Harvard Architecture. Banked memory. Simple and explicit, but can be cumbersome. |
AVR (8-bit) | Initializes stack, copies .data, clears .bss, jumps to main(). | Defines FLASH, RAM, EEPROM regions and places sections. | Clean, Modern Harvard. Linear memory spaces. Very straightforward and predictable. |
MSP430 | Initializes stack, copies .data, clears .bss, calls constructors, jumps to main(). | Defines FLASH and RAM regions. Manages low-power info and interrupt vectors. | Von Neumann. Unified memory space. Simple and efficient 16-bit RISC design. |
ARM (Cortex-M) | Highly Complex. Sets up stacks, vectors, clocks, RAM, calls C++ constructors. | Highly Complex. Manages multiple memory regions, defines entry point, heap/stack. | Core Agnostic. The core provides standard (CMSIS), vendors provide complex implementations for their specific chips. |
Detailed Breakdown
1. 8051
Architecture: Harvard, with separate address spaces for code and data. Memory is highly banked.
- Startup File:
- Purpose: Primarily to copy initialized variables from program memory (CODE/ROM) into the default data memory bank (DATA/RAM). On power-up, RAM is uninitialized.
- Key Actions:
- Initialize Stack: The stack is usually located in the IDATA (internal RAM) region.
- Data Initialization: A loop that copies the initial values of variables from the
?C_INITSEG
(or similar) code segment to the?DT?*
data segments. - Jump to main(): Transfers control to the C program’s entry point.
- Complexity: Low to Medium. The logic is simple, but understanding the banked memory model is crucial.
- Linker File (.l51, .lx51):
- Purpose: To manage the complex, overlapping memory spaces (CODE, XDATA, PDATA, DATA, IDATA, BIT) and assign segments to the correct physical addresses and banks.
- Key Components:
MEMORY
: Defines the physical memory ranges (e.g.,CODE(0x0000, 0x7FFF)
,XDATA(0x0000, 0xFFFF)
).SECTIONS
: Tells the linker which programmer-defined segments (like?PR?main?MAIN
for the code ofmain()
in fileMAIN.C
) to place in which memory region.
- Philosophy: Explicit and Manual. The developer often needs to be deeply aware of memory banking and manually place critical functions (
using n
) or variables in specific banks for efficiency.
2. PIC (8-bit)
Architecture: Harvard. Heavily banked memory (both RAM and ROM).
- Startup File (crt0.s / startup.as):
- Purpose: Performs very basic initialization before
main()
. - Key Actions:
- Initialize Stack: Sets up the hardware stack (which is often separate and limited in depth).
- Clear RAM: Zeros out the general-purpose RAM.
- Copy Data: Copies initialized variables from program memory to data memory.
- Jump to main().
- Complexity: Low. Often written in assembly and provided by the compiler toolchain (MPLAB XC8).
- Purpose: Performs very basic initialization before
- Linker File (.lkr):
- Purpose: To describe the memory layout of the specific target PIC microcontroller and assign code and data sections to the appropriate program and data memory banks.
- Key Components:
CODEPAGE
: Defines blocks of program memory (ROM).DATABANK
: Defines blocks of data memory (RAM banks).SECTION
: Directs standard sections (e.g.,.text
,.data
) to the defined memory areas.
- Philosophy: Bank-Centric. The linker script is crucial for managing bank switching. If a section spans multiple banks, the linker must generate the appropriate “bank selection” instructions.
3. AVR (e.g., ATmega328P)
Architecture: Modified Harvard. Has separate, linear address spaces for Flash (program) and RAM (data).
- Startup File (crt0.o / gcrt0.o):
- Purpose: A classic, clean example of a startup routine for an embedded system.
- Key Actions:
- Initialize Stack Pointer: Sets the SP to the end of RAM.
- Copy .data: Copies the initial values from Flash (.data section) to SRAM.
- Clear .bss: Zeros out the uninitialized data section in SRAM.
- Call main(): Jumps to the user’s
main()
function.
- Complexity: Low. Very straightforward and easy to understand.
- Linker File (.ld):
- Purpose: To define the memory layout (Flash, RAM, and EEPROM) and section placement for the target AVR chip.
- Key Components:
MEMORY
: Declares the size and origin of Flash (rx), RAM (rwx), and EEPROM (r).SECTIONS
: Defines where to place.text
,.data
,.bss
,.noinit
, and.eeprom
sections.
- Philosophy: Transparent and Linear. The memory map is simple and linear, making linker scripts easy to write and modify. The avr-libc provides excellent default scripts.
4. MSP430
Architecture: Von Neumann. Has a unified address space for RAM and Flash.
- Startup File (startup_<device>.c / .s):
- Purpose: Similar to AVR but often includes more functionality, especially for low-power modes and C++.
- Key Actions:
- Disable Watchdog: A critical first step on MSP430.
- Set up Stack Pointer.
- Copy .data: From Flash to RAM.
- Clear .bss.
- Call C++ Constructors: For global objects (if using C++).
- Call main().
- Complexity: Low to Medium. TI’s Code Composer Studio provides well-commented startup files for each device family.
- Linker File (.cmd):
- Purpose: To define the unified memory map and assign sections to either Flash or RAM.
- Key Components:
MEMORY
: Defines ranges (e.g.,FLASH : origin = 0x8000, length = 0x7FFF
,RAM : origin = 0x2000, length = 0x1000
).SECTIONS
: Assigns.text
,.data
,.bss
,.stack
, and, crucially, the INTERRUPT VECTOR TABLE to specific addresses.
- Philosophy: Unified and Efficient. The unified memory space simplifies the linker script compared to banked Harvard architectures. The focus is on efficient placement of code and data between volatile (RAM) and non-volatile (Flash) memory.
5. ARM (Cortex-M)
Architecture: Von Neumann or Harvard (depending on specific core), but presented with a unified memory map.
- Startup File (startup_<device>.s):
- Purpose: Extremely complex and critical. It does far more than just initialize variables.
- Key Actions:
- Initialize the Stack Pointer from the first entry in the vector table.
- Initialize the Process Stack Pointer (on cores that support it).
- Set the Vector Table Offset Register (VTOR).
- Copy .data from Flash to RAM.
- Zero out the .bss section.
- Configure System Clock and PLL. (Often done in
SystemInit()
called beforemain
). - Call C++ Constructors (for global objects,
__libc_init_array
). - Call main().
- Complexity: High. This file is highly vendor-specific (ST, NXP, TI, etc.) and is often provided as part of a Device Family Pack (DFP) or Hardware Abstraction Layer (HAL). It’s tightly coupled with the system initialization code.
- Linker File (.ld / .icf / .sct):
- Purpose: To define the complex memory map of a modern ARM SoC, which often includes multiple Flash banks (for dual-banking/OTA updates), multiple RAM banks (core, DMA, TCM), and peripheral memory.
- Key Components:
MEMORY
: Extensive definition of all memory regions (e.g.,FLASH
,RAM
,CCMRAM
,BACKUP_SRAM
).SECTIONS
: Detailed placement of:.isr_vector
: The interrupt vector table (must be placed at correct base address)..text
: Code and constants..data / .bss
: Initialized/Uninitialized data.._user_heap_stack
: Defines the heap and stack areas.- Special sections for other cores (e.g.,
.ARM.exidx
for exception unwinding).
- Philosophy: Flexible and Powerful. ARM linker scripts are the most complex, offering fine-grained control over memory layout. They are essential for optimizing performance (placing critical code in fast TCM RAM) and enabling advanced features like bootloaders.
Conclusion
The evolution from 8051/PIC to AVR/MSP430 to ARM shows a clear trend:
- From Manual to Automatic: Moving away from manual memory banking to automated, linear(ish) memory models.
- From Simple to Complex: As microcontrollers became more powerful, the startup and linker processes grew to handle complex silicon features like multiple memory types, system clocks, and C++ support.
- From Vendor-Agnostic to Vendor-Specific: While the ARM core is standard, the implementation (startup and linker files) is entirely vendor-specific, abstracted away by powerful IDEs and configuration tools (STM32CubeMX, MCUXpresso). For 8051/PIC/AVR, the files are simpler but often require more manual intervention from the developer.
Leave a Reply