Chapter 04 - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki

04. Linker Script & Startup Code

Goal

STM32F439ZI๊ฐ€ Reset๋œ ๋’ค main()์— ๋„๋‹ฌํ•˜๊ธฐ๊นŒ์ง€์˜ ๊ณผ์ •์„ ์ดํ•ดํ•˜๊ณ , Linker Script์™€ Startup Code๊ฐ€ .data, .bss, Stack, Vector Table์„ ์–ด๋–ป๊ฒŒ ์ค€๋น„ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

Reset
  โ†“
Vector Table
  โ†“
Initial MSP / Reset_Handler
  โ†“
.data Copy
  โ†“
.bss Clear
  โ†“
SystemInit()
  โ†“
main()

Ch02~03์—์„œ๋Š” ์ž„์‹œ linker/startup ํŒŒ์ผ์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

Ch04๋ถ€ํ„ฐ๋Š” ์ดํ›„ ๋ชจ๋“  chapter์—์„œ ์‚ฌ์šฉํ• :

common/linker/stm32f439zi.ld
common/startup/startup_stm32f439zi.s

๋ฅผ ๊ธฐ์ค€์œผ๋กœ buildํ•œ๋‹ค.


1. Theory

STM32F439ZI Memory

์ด๋ฒˆ project์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ฃผ์š” memory:

FLASH
Start : 0x08000000
Size  : 2048 KB

SRAM
Start : 0x20000000
Size  : 128 KB

CCM RAM
Start : 0x10000000
Size  : 64 KB

CCM RAM์€ ์ด๋ฒˆ ์‹ค์Šต์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

STM32F439ZI์˜ 128KB SRAM ๋ ์ฃผ์†Œ:

0x20000000 + 0x00020000
=
0x20020000

๋”ฐ๋ผ์„œ initial stack pointer๋กœ ์‚ฌ์šฉํ•˜๋Š” _estack์€:

0x20020000

์ด๋‹ค.


2. Linker Script

Linker Script๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ section์„ ์–ด๋А memory address์— ๋ฐฐ์น˜ํ• ์ง€ ๊ฒฐ์ •ํ•œ๋‹ค.

๋Œ€ํ‘œ section:

.isr_vector โ†’ FLASH
.text       โ†’ FLASH
.rodata     โ†’ FLASH
.data       โ†’ RAM, initial value๋Š” FLASH
.bss        โ†’ RAM

๊ฐœ๋…์ ์ธ memory layout:

FLASH
0x08000000
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ .isr_vector      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ .text            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ .rodata          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ .data init value โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜


RAM
0x20000000
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ .data            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ .bss             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ heap / stack     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
0x20020000

.data

์ดˆ๊ธฐ๊ฐ’์ด ์žˆ๋Š” global/static variable.

Example:

unsigned int g_blink_count = 5;

์‹คํ–‰ ์ค‘์—๋Š” RAM์— ์žˆ์–ด์•ผ ํ•˜์ง€๋งŒ, ์ „์›์ด ๊บผ์ ธ๋„ ์ดˆ๊ธฐ๊ฐ’ 5๋Š” firmware์— ๋‚จ์•„ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ:

FLASH
initial value = 5
      โ†“
Reset_Handler copy
      โ†“
RAM
g_blink_count = 5

๊ฐ€ ๋œ๋‹ค.


.bss

์ดˆ๊ธฐ๊ฐ’์ด ์—†๋Š” global/static variable.

Example:

unsigned int g_toggle_flag;

C์—์„œ๋Š” program ์‹œ์ž‘ ์‹œ:

g_toggle_flag = 0

์ด์–ด์•ผ ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ Reset_Handler๊ฐ€ .bss ์˜์—ญ์„ RAM์—์„œ 0์œผ๋กœ ์ฑ„์šด๋‹ค.


3. LMA and VMA

.data๋ฅผ ์ดํ•ดํ•  ๋•Œ ์ค‘์š”ํ•œ ๊ฐœ๋…์ด๋‹ค.

LMA

Load Memory Address

Firmware image์—์„œ ์ดˆ๊ธฐ๊ฐ’์ด ์ €์žฅ๋œ ์œ„์น˜.

.data์˜ ๊ฒฝ์šฐ:

FLASH

VMA

Virtual / Run Memory Address

์‹คํ–‰ ์ค‘ CPU๊ฐ€ ์‹ค์ œ variable์„ ์ ‘๊ทผํ•˜๋Š” ์œ„์น˜.

.data์˜ ๊ฒฝ์šฐ:

RAM

๋”ฐ๋ผ์„œ:

.data

LMA โ†’ FLASH
VMA โ†’ RAM

์ด๋‹ค.


4. Startup Code

Cortex-M4๋Š” Reset ์งํ›„ main()์œผ๋กœ ๋ฐ”๋กœ ๊ฐ€์ง€ ์•Š๋Š”๋‹ค.

Vector Table์˜ ์ฒซ ๋‘ word๋ฅผ hardware๊ฐ€ ์ฝ๋Š”๋‹ค.

Vector[0]
โ†’ Initial MSP

Vector[1]
โ†’ Reset_Handler

์ฆ‰ conceptually:

MSP โ† Vector[0]
PC  โ† Vector[1]

๊ฐ€ ๋œ๋‹ค.


Reset_Handler

Startup code์˜ ํ•ต์‹ฌ ์ˆœ์„œ:

Reset_Handler
     โ†“
Initial Stack
     โ†“
Copy .data
FLASH โ†’ RAM
     โ†“
Clear .bss
RAM โ†’ 0
     โ†“
SystemInit()
     โ†“
main()

์ด ์ž‘์—…์ด ๋๋‚˜์•ผ C program์ด ๊ธฐ๋Œ€ํ•˜๋Š” runtime environment๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค.


5. Vector Table & Weak Handler

STM32F439ZI๋Š” Cortex exception๋ฟ ์•„๋‹ˆ๋ผ ๋งŽ์€ peripheral IRQ๋ฅผ ๊ฐ€์ง„๋‹ค.

์ด project์˜ startup file์—๋Š” STM32F439ZI์— ๋งž๋Š” IRQ vector table์„ ๊ตฌ์„ฑํ–ˆ๋‹ค.

์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” handler๋Š” ๋ณดํ†ต:

weak symbol
     โ†“
Default_Handler

๋กœ ์—ฐ๊ฒฐํ•œ๋‹ค.

๋‚˜์ค‘์— application์—์„œ:

void EXTI15_10_IRQHandler(void)
{
    ...
}

์ฒ˜๋Ÿผ ๊ฐ™์€ ์ด๋ฆ„์˜ handler๋ฅผ ์ •์˜ํ•˜๋ฉด strong symbol์ด weak symbol์„ ๋Œ€์‹ ํ•œ๋‹ค.

์ฆ‰ startup file์„ ๋งค๋ฒˆ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.


6. Practice Files

์ด chapter์˜ ์‹ค์ œ ์‹ค์Šต ๊ตฌ์„ฑ:

Ch04/
โ”œโ”€โ”€ main.c
โ”œโ”€โ”€ stm32f439zi.ld
โ””โ”€โ”€ startup_stm32f439zi.s

์ดํ›„ chapter์—์„œ๋Š” ๊ณต์šฉํ™”ํ•ด์„œ:

common/
โ”œโ”€โ”€ linker/
โ”‚   โ””โ”€โ”€ stm32f439zi.ld
โ””โ”€โ”€ startup/
    โ””โ”€โ”€ startup_stm32f439zi.s

ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

stm32f439zi.ld์™€ startup_stm32f439zi.s์˜ ์ „์ฒด ์›๋ณธ์€ ํ˜„์žฌ Notion ๋ณธ๋ฌธ์—๋Š” ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค. ์žฌ์‹ค์Šต ์‹œ archived project์˜ ์‹ค์ œ ํŒŒ์ผ์„ ์ด Wiki์— ์ถ”๊ฐ€ํ•œ๋‹ค.


7. Practice Code

main.c

#define PERIPH_BASE        (0x40000000UL)
#define AHB1PERIPH_BASE    (PERIPH_BASE + 0x00020000UL)

#define RCC_BASE           (AHB1PERIPH_BASE + 0x3800UL)
#define GPIOB_BASE         (AHB1PERIPH_BASE + 0x0400UL)

#define RCC_AHB1ENR \
    (*(volatile unsigned int *)(RCC_BASE + 0x30UL))

#define GPIOB_MODER \
    (*(volatile unsigned int *)(GPIOB_BASE + 0x00UL))

#define GPIOB_ODR \
    (*(volatile unsigned int *)(GPIOB_BASE + 0x14UL))

#define GPIOBEN   (1UL << 1)
#define LD1_PIN   (1UL << 0)

/* .data section verification */
unsigned int g_blink_count = 5;

/* .bss section verification */
unsigned int g_toggle_flag;

static void delay(volatile unsigned int count)
{
    while (count--)
    {
        __asm__("nop");
    }
}

int main(void)
{
    RCC_AHB1ENR |= GPIOBEN;

    GPIOB_MODER &= ~(0x3UL << (0 * 2));
    GPIOB_MODER |=  (0x1UL << (0 * 2));

    /*
     * .bss initialization check
     *
     * g_toggle_flag should be 0 after Reset_Handler.
     * If not, use fast blinking as an error indication.
     */
    if (g_toggle_flag != 0)
    {
        while (1)
        {
            GPIOB_ODR ^= LD1_PIN;
            delay(50000);
        }
    }

    while (1)
    {
        for (unsigned int i = 0;
             i < g_blink_count;
             i++)
        {
            GPIOB_ODR ^= LD1_PIN;
            delay(800000);
        }

        delay(3200000);

        g_toggle_flag++;
    }
}

8. Why These Two Variables?

g_blink_count

unsigned int g_blink_count = 5;

์ดˆ๊ธฐ๊ฐ’์ด ์žˆ์œผ๋ฏ€๋กœ:

.data

section์— ์œ„์น˜ํ•œ๋‹ค.

Startup์ด ์ •์ƒ์ด๋ผ๋ฉด:

FLASH์˜ 5
   โ†“
Reset_Handler
   โ†“
RAM g_blink_count = 5

๊ฐ€ ๋˜์–ด์•ผ ํ•œ๋‹ค.


g_toggle_flag

unsigned int g_toggle_flag;

์ดˆ๊ธฐ๊ฐ’์ด ์—†์œผ๋ฏ€๋กœ:

.bss

section์— ์œ„์น˜ํ•œ๋‹ค.

Startup์ด ์ •์ƒ์ด๋ผ๋ฉด main() ์ง„์ž… ์‹œ:

g_toggle_flag = 0

์ด์–ด์•ผ ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ:

if (g_toggle_flag != 0)

์ด๋ฉด startup ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜๊ณ  LED๋ฅผ ๋น ๋ฅด๊ฒŒ ๊นœ๋นก์ด๊ฒŒ ํ•œ๋‹ค.

์ฆ‰ ์ด chapter์˜ LED ๋™์ž‘์€ ๋‹จ์ˆœ blink๊ฐ€ ์•„๋‹ˆ๋ผ:

.data / .bss Startup Verification

์šฉ์ด๋‹ค.


9. Linker Symbols

Startup code์™€ linker script๋Š” symbol์„ ํ†ตํ•ด ์ฃผ์†Œ๋ฅผ ๊ณต์œ ํ•œ๋‹ค.

๋Œ€ํ‘œ์ ์œผ๋กœ:

_estack
_sidata
_sdata
_edata
_sbss
_ebss

์˜ ์˜๋ฏธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

_estack
โ†’ initial stack address

_sidata
โ†’ FLASH์— ์ €์žฅ๋œ .data ์ดˆ๊ธฐ๊ฐ’ ์‹œ์ž‘

_sdata
โ†’ RAM์˜ .data ์‹œ์ž‘

_edata
โ†’ RAM์˜ .data ๋

_sbss
โ†’ .bss ์‹œ์ž‘

_ebss
โ†’ .bss ๋

Startup์€ ์ด๋ฅผ ์ด์šฉํ•ด:

_sidata โ†’ _sdata ~ _edata

๋ฒ”์œ„๋กœ data๋ฅผ ๋ณต์‚ฌํ•˜๊ณ :

_sbss ~ _ebss

๋ฒ”์œ„๋ฅผ 0์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.


10. Execution Flow

Power / Reset
      โ†“
Vector Table
      โ†“
MSP = _estack
      โ†“
PC = Reset_Handler
      โ†“
.data Copy
      โ†“
.bss Clear
      โ†“
SystemInit()
      โ†“
main()
      โ†“
g_blink_count = 5
g_toggle_flag = 0

11. Practice Commands

Build:

make

Symbol ํ™•์ธ:

arm-none-eabi-nm \
    ch04_linker_startup.elf \
    | grep -E "_estack|_sdata|_edata|_sbss|_ebss"

Section ํ™•์ธ:

arm-none-eabi-objdump \
    -h ch04_linker_startup.elf

Program size:

arm-none-eabi-size \
    ch04_linker_startup.elf

12. Recorded Result

์‹ค์Šต์—์„œ ํ™•์ธํ•œ symbol:

20000008 B _ebss
20000004 D _edata
20020000 R _estack
20000004 B _sbss
20000000 D _sdata

_estack

RAM Start = 0x20000000
RAM Size  = 128 KB = 0x20000

0x20000000
+
0x00020000
=
0x20020000

์‹ค์ œ ๊ฒฐ๊ณผ:

_estack = 0x20020000

์ •ํ™•ํžˆ ์ผ์น˜ํ•œ๋‹ค.


.data

_sdata = 0x20000000
_edata = 0x20000004

ํฌ๊ธฐ:

4 bytes

์ด๋Š”:

unsigned int g_blink_count = 5;

ํ•˜๋‚˜์™€ ์ผ์น˜ํ•œ๋‹ค.


.bss

_sbss = 0x20000004
_ebss = 0x20000008

์‹ค์ œ global variable ์˜์—ญ:

4 bytes

๋กœ:

unsigned int g_toggle_flag;

์™€ ์ผ์น˜ํ•œ๋‹ค.

Build ๊ฒฐ๊ณผ์—์„œ๋Š” heap/stack reservation๊นŒ์ง€ ํฌํ•จํ•ด .bss ๊ด€๋ จ RAM ์‚ฌ์šฉ๋Ÿ‰์ด ๋” ํฌ๊ฒŒ ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ๋‹ค.


13. Verification

์žฌ์‹ค์Šตํ•  ๋•Œ๋Š” build ์„ฑ๊ณต๋งŒ ๋ณด๋Š” ๊ฒƒ๋ณด๋‹ค runtime variable๊นŒ์ง€ ์ง์ ‘ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

1. Build

make clean
make

2. Symbol

arm-none-eabi-nm \
    ch04_linker_startup.elf \
    | grep -E "_estack|_sdata|_edata|_sbss|_ebss"

ํ™•์ธ:

_estack = 0x20020000

3. ELF Sections

arm-none-eabi-objdump \
    -h ch04_linker_startup.elf

ํ™•์ธ:

.isr_vector
.text
.data
.bss

4. Debugger

main() ์ง„์ž… ์งํ›„:

g_blink_count = 5
g_toggle_flag = 0

์ธ์ง€ ํ™•์ธํ•œ๋‹ค.

์ด ํ™”๋ฉด์ด:

.data copy PASS
.bss clear PASS

๋ฅผ ๊ฐ€์žฅ ์ง์ ‘์ ์œผ๋กœ ๋ณด์—ฌ์ค€๋‹ค.


5. Board

์ •์ƒ startup:

Normal blink pattern

.bss ์ดˆ๊ธฐํ™” ์ด์ƒ:

Fast continuous blink

14. Screenshots to Add

images/ch04_build.png
images/ch04_symbols.png
images/ch04_sections.png
images/ch04_debug_variables.png
images/ch04_led.gif

์ด chapter์—์„œ๋Š” ํŠนํžˆ:

nm symbol result
+
Debugger variable result

๋‘ ์žฅ์ด ๊ฐ€์žฅ ์ค‘์š”ํ•˜๋‹ค.


15. Key Points

Linker Script
โ†’ Where?

Startup Code
โ†’ What happens after reset?
  • Linker Script๋Š” Flash/RAM๊ณผ section ๋ฐฐ์น˜๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.
  • Vector Table์˜ ์ฒซ word๋Š” initial MSP, ๋‘ ๋ฒˆ์งธ word๋Š” Reset_Handler๋‹ค.
  • .data๋Š” ์ดˆ๊ธฐ๊ฐ’์ด Flash์— ์žˆ๊ณ  runtime์—๋Š” RAM์— ์กด์žฌํ•œ๋‹ค.
  • .bss๋Š” RAM ๊ณต๊ฐ„๋งŒ ํ•„์š”ํ•˜๋ฉฐ startup์—์„œ 0์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
  • .data๋Š” LMA์™€ VMA๊ฐ€ ๋‹ค๋ฅด๋‹ค.
  • Reset_Handler๊ฐ€ C runtime์„ ์ค€๋น„ํ•œ ๋’ค main()์„ ํ˜ธ์ถœํ•œ๋‹ค.
  • weak interrupt handler๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด application์—์„œ ํ•„์š”ํ•œ IRQ๋งŒ overrideํ•  ์ˆ˜ ์žˆ๋‹ค.
  • nm, objdump, debugger๋ฅผ ํ†ตํ•ด linker/startup ๋™์ž‘์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค.