승코딩당당당

[메모리 구조] 변수 선언 방식에 따른 메모리 영역 확인 본문

개발/임베디드

[메모리 구조] 변수 선언 방식에 따른 메모리 영역 확인

승코딩당당당 2026. 2. 26. 11:56

 

이번 실습에서는 변수의 선언 방식에 따라 메모리의 어느 영역에 배치되는지를 직접 확인해보았다.
임베디드 시스템에서는 메모리 구조를 이해하는 것이 매우 중요하며, 특히 전역 변수, static 변수, 지역 변수, const 상수 등이 각각 어떤 메모리 영역에 저장되는지를 명확히 아는 것이 필요하다.

 

이를 위해 전역 변수(.data / .bss), 읽기 전용 상수(.rodata), 지역 변수(Stack), 그리고 전역 배열을 이용한 힙 역할 영역을 코드로 선언하고, ADS 디버거를 통해 실제 메모리 주소를 확인하였다. 단순히 이론적으로 배우는 것이 아니라, 브레이크포인트를 걸어 Expressions 창에서 변수의 주소를 직접 확인함으로써 각 영역의 차이를 비교하였다.

 

이 과정을 통해 Flash 영역, RAM 영역(.data, .bss), Stack 영역이 실제 주소상에서 어떻게 구분되는지를 명확하게 이해할 수 있다.

 


 

실습 준비

이번 실습은 Infineon AURIX TC275 보드에서 진행되었다.
TC275는 TriCore 아키텍처 기반의 마이크로컨트롤러로, 명확하게 분리된 Flash와 RAM 메모리 구조를 가지고 있다. 따라서 변수의 선언 방식에 따른 실제 메모리 배치를 디버거를 통해 직접 확인하기에 적합하다.

 

특히 TC275에서는:

  • Flash 영역 (읽기 전용 상수 저장)
  • RAM 영역 (.data, .bss)
  • Stack 영역

이 실제 주소 값으로 명확하게 구분되기 때문에, 임베디드 시스템의 메모리 구조를 학습하기에 매우 좋은 실습 환경이다.

따라서 이번 실습은 TC275 보드와 ADS 디버깅 환경이 필수적이다.

 


 

코드

/**********************************************************************************************************************
 * \file Cpu0_Main.c
 * \copyright Copyright (C) Infineon Technologies AG 2019
 *
 * Use of this file is subject to the terms of use agreed between (i) you or the company in which ordinary course of
 * business you are acting and (ii) Infineon Technologies AG or its licensees. If and as long as no such terms of use
 * are agreed, use of this file is subject to following:
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and
 * accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute,
 * and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the
 * Software is furnished to do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including the above license grant, this restriction
 * and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all
 * derivative works of the Software, unless such copies or derivative works are solely in the form of
 * machine-executable object code generated by a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *********************************************************************************************************************/
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"
#include "IfxPort.h"
#include "IfxPort_PinMap.h"

#include "IfxScuEru.h"
#include "IfxSrc.h"
#include "_PinMap/IfxScu_PinMap.h"

#include "Platform_Types.h"

#include "Driver_Stm.h"
#include "Driver_Adc.h"
#include "GTM_TOM_PWM.h"

#define SCLK IfxPort_P00_0
#define RCLK IfxPort_P00_1
#define DIO  IfxPort_P00_2

#define LED_R IfxPort_P02_7
#define LED_G IfxPort_P10_5
#define LED_B IfxPort_P10_3

#define LED_D13 IfxPort_P10_2
#define LED_D12 IfxPort_P10_1


/////////////////
volatile const uint32 g_constVal = 0x12345678;
volatile uint32 g_initGlobal     = 0xAAAAAAAA;
volatile uint32 g_uninitGlobal;
static volatile uint32 s_initStatic  = 0xBBBBBBBB;
static volatile uint32 s_uninitStatic;

uint32 g_heapArea1[4];
uint32 g_heapArea2[4];
uint32 *g_heapPtr1 = g_heapArea1;
uint32 *g_heapPtr2 = g_heapArea2;

IfxCpu_syncEvent cpuSyncEvent = 0;

void initGPIO(void);
void initERU(void);
void LedOn(IfxPort_Pin pin);
void LedOff(IfxPort_Pin pin);
void LedToggle(IfxPort_Pin pin);
void fnd_display_unit(uint8_t value, uint8_t position);
void send(uint8_t X);
void send_port(uint8_t X, uint8_t port);

void AppTask1ms(void);
void AppTask10ms(void);
void AppTask100ms(void);
void AppTask1000ms(void);
void AppScheduling(void);


typedef struct
{
    uint32 u32nuCnt1ms;
    uint32 u32nuCnt10ms;
    uint32 u32nuCnt100ms;
    uint32 u32nuCnt1000ms;
} TestCntType;

volatile TestCntType stTestCnt;

#define LIGHT_DARK_ON   (2500u)
#define LIGHT_DARK_OFF  (2700u)

typedef enum { STATE_NORMAL, STATE_CRUISE, STATE_EMERGENCY } SystemState_t;

uint8_t _LED_0F[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90};

volatile SystemState_t g_CurrentState = STATE_NORMAL;

volatile uint8 g_Sw1Event = 0;
volatile uint8 g_Sw2Event = 0;

uint16_t g_CurrentSpeed = 0;
uint16_t g_TargetSpeed  = 70;
uint32_t g_LightLevel   = 0;


IFX_INTERRUPT(ISR0_cruise, 0, 0x10);
void ISR0_cruise(void){
    g_Sw1Event = 1;
}

IFX_INTERRUPT(ISR1_emergency, 0, 0x11);
void ISR1_emergency(void){
    g_Sw2Event = 1;
}

int core0_main(void)
{
    IfxCpu_enableInterrupts();
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());

    volatile uint32 localMain1 = 0x11111111;
    volatile uint32 localMain2 = 0x22222222;

    g_initGlobal++;             
    g_uninitGlobal = 0x13572468; 
    s_initStatic++;              
    s_uninitStatic = 0x24681357; 

    if (g_constVal == 0x12345678)
    {
         
    }

    g_heapPtr1[0] = 0xDEADBEEF;
    g_heapPtr2[0] = 0xCAFEBABE;

    while (1)
    {
        // breakPoint
        localMain1++;   
    }

    return 0;
}

void LedOn(IfxPort_Pin pin)
{
    IfxPort_setPinHigh(pin.port, pin.pinIndex);
}

void LedOff(IfxPort_Pin pin)
{
    IfxPort_setPinLow(pin.port, pin.pinIndex);
}

void LedToggle(IfxPort_Pin pin)
{
    IfxPort_togglePin(pin.port, pin.pinIndex);
}

void fnd_display_unit(uint8_t value, uint8_t position)
{
    uint8_t content = _LED_0F[value];
    send_port(content, position);
}

void AppTask1ms(void)
{
    static uint8 position = 0;
    uint32 v = g_CurrentSpeed;

    switch(position)
    {
        case 0: fnd_display_unit((uint8)(v % 10), 0x1); break;
        case 1: fnd_display_unit((uint8)((v / 10) % 10), 0x2); break;
        case 2: fnd_display_unit((uint8)((v / 100) % 10), 0x4); break;
        case 3: fnd_display_unit((uint8)((v / 1000) % 10), 0x8); break;
        default: break;
    }

    position = (position + 1) % 4;
    stTestCnt.u32nuCnt1ms++;
}

void AppTask10ms(void)
{
    if (g_Sw2Event && g_CurrentState != STATE_EMERGENCY) {
        g_CurrentState = STATE_EMERGENCY;
        g_Sw2Event = 0;
    }

    switch (g_CurrentState) {
        case STATE_NORMAL:
            if (g_Sw1Event) {
                g_CurrentState = STATE_CRUISE;
                g_Sw1Event = 0;
            }
            LedOff(LED_R); LedOn(LED_G); LedOff(LED_B);
            break;

        case STATE_CRUISE:
            if (g_Sw1Event) {
                g_CurrentState = STATE_NORMAL;
                g_Sw1Event = 0;
            }
            g_CurrentSpeed = g_TargetSpeed;
            LedOff(LED_R); LedOff(LED_G); LedOn(LED_B);
            break;

        case STATE_EMERGENCY:
            if (g_Sw2Event) {
                g_CurrentState = STATE_NORMAL;
                g_Sw2Event = 0;
            }
            g_CurrentSpeed = 0;
            LedOn(LED_R); LedOff(LED_G); LedOff(LED_B);
            break;
    }
}

void AppTask100ms(void)
{
    uint32_t potVal = Driver_Adc0_DataObtain(7);
    uint32_t ldrVal = Driver_Adc0_DataObtain(6);

    if (g_CurrentState == STATE_NORMAL) {
        g_CurrentSpeed = (uint16)((potVal * 200) / 4095);
    }

    if (IfxPort_getPinState(LED_D13.port, LED_D13.pinIndex) == FALSE)
    {
        if (ldrVal < LIGHT_DARK_ON)
        {
            LedOn(LED_D13);
        }
    }
    else
    {
        if (ldrVal > LIGHT_DARK_OFF)
        {
            LedOff(LED_D13);
        }
    }

    if (g_CurrentState == STATE_EMERGENCY)
    {
        LedToggle(LED_D12);
        makeSound(12);
    }
    else if (g_CurrentSpeed > 120)
    {
        LedToggle(LED_D12);
        makeSound(7);
    }
    else
    {
        LedOff(LED_D12);
        makeSound(14);
    }
}

void send(uint8_t X)
{
    for (int i = 8; i >= 1; i--)
    {
        if (X & 0x80)
        {
            IfxPort_setPinHigh(DIO.port, DIO.pinIndex);
        }
        else
        {
            IfxPort_setPinLow(DIO.port, DIO.pinIndex);
        }

        X <<= 1;
        IfxPort_setPinLow(SCLK.port, SCLK.pinIndex);
        IfxPort_setPinHigh(SCLK.port, SCLK.pinIndex);
    }
}

void send_port(uint8_t X, uint8_t port)
{
    send(X);
    send(port);
    IfxPort_setPinLow(RCLK.port, RCLK.pinIndex);
    IfxPort_setPinHigh(RCLK.port, RCLK.pinIndex);
}


void AppTask1000ms(void)
{
    stTestCnt.u32nuCnt1000ms++;
}

void AppScheduling(void)
{
    if (stSchedulingInfo.u8nuScheduling1msFlag == 1u)
    {
        stSchedulingInfo.u8nuScheduling1msFlag = 0u;
        AppTask1ms();

        if (stSchedulingInfo.u8nuScheduling10msFlag == 1u)
        {
            stSchedulingInfo.u8nuScheduling10msFlag = 0u;
            AppTask10ms();
        }

        if (stSchedulingInfo.u8nuScheduling100msFlag == 1u)
        {
            stSchedulingInfo.u8nuScheduling100msFlag = 0u;
            AppTask100ms();
        }

        if (stSchedulingInfo.u8nuScheduling1000msFlag == 1u)
        {
            stSchedulingInfo.u8nuScheduling1000msFlag = 0u;
            AppTask1000ms();
        }
    }
}

void initGPIO(void)
{
    IfxPort_setPinModeOutput(IfxPort_P10_2.port,IfxPort_P10_2.pinIndex,
                             IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);
    IfxPort_setPinModeOutput(IfxPort_P10_1.port, IfxPort_P10_1.pinIndex,
                             IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);

    LedOff(LED_D13);
    LedOff(LED_D12);

    IfxPort_setPinModeOutput(LED_R.port, LED_R.pinIndex,
                             IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);
    IfxPort_setPinModeOutput(LED_G.port, LED_G.pinIndex,
                             IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);
    IfxPort_setPinModeOutput(LED_B.port, LED_B.pinIndex,
                             IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);

    LedOff(LED_R);
    LedOff(LED_G);
    LedOff(LED_B);

    IfxPort_setPinModeOutput(SCLK.port, SCLK.pinIndex, IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);
    IfxPort_setPinModeOutput(RCLK.port, RCLK.pinIndex, IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);
    IfxPort_setPinModeOutput(DIO.port, DIO.pinIndex, IfxPort_OutputMode_pushPull, IfxPort_OutputIdx_general);
}

void initERU(void)
{
    IfxPort_setPinModeInput(IfxPort_P02_0.port, IfxPort_P02_0.pinIndex, IfxPort_InputMode_pullUp);

    IfxScuEru_selectExternalInput(IfxScuEru_InputChannel_3, IfxScuEru_ExternalInputSelection_2);

    IfxScuEru_enableFallingEdgeDetection(IfxScuEru_InputChannel_3);
    IfxScuEru_disableRisingEdgeDetection(IfxScuEru_InputChannel_3);

    IfxScuEru_enableTriggerPulse(IfxScuEru_InputChannel_3);

    IfxScuEru_connectTrigger(IfxScuEru_InputChannel_3, IfxScuEru_InputNodePointer_0);

    IfxScuEru_setInterruptGatingPattern(IfxScuEru_OutputChannel_0,
                                        IfxScuEru_InterruptGatingPattern_alwaysActive);
    IfxScuEru_setFlagPatternDetection(IfxScuEru_OutputChannel_0, IfxScuEru_InputChannel_3, TRUE);

    IfxScuEru_clearEventFlag(IfxScuEru_InputChannel_3);

    IfxSrc_init(&SRC_SCU_SCU_ERU0, IfxSrc_Tos_cpu0, 0x10u);
    IfxSrc_enable(&SRC_SCU_SCU_ERU0);

    IfxPort_setPinModeInput(IfxPort_P02_1.port, IfxPort_P02_1.pinIndex, IfxPort_InputMode_pullUp);

    IfxScuEru_selectExternalInput(IfxScuEru_InputChannel_2, IfxScuEru_ExternalInputSelection_1);

    IfxScuEru_enableFallingEdgeDetection(IfxScuEru_InputChannel_2);
    IfxScuEru_disableRisingEdgeDetection(IfxScuEru_InputChannel_2);

    IfxScuEru_enableTriggerPulse(IfxScuEru_InputChannel_2);
    IfxScuEru_connectTrigger(IfxScuEru_InputChannel_2, IfxScuEru_InputNodePointer_1);

    IfxScuEru_setInterruptGatingPattern(IfxScuEru_OutputChannel_1,
                                        IfxScuEru_InterruptGatingPattern_alwaysActive);
    IfxScuEru_setFlagPatternDetection(IfxScuEru_OutputChannel_1, IfxScuEru_InputChannel_2, TRUE);

    IfxScuEru_clearEventFlag(IfxScuEru_InputChannel_2);

    IfxSrc_init(&SRC_SCU_SCU_ERU1, IfxSrc_Tos_cpu0, 0x11u);
    IfxSrc_enable(&SRC_SCU_SCU_ERU1);
}

 

위 코드는 기존에 실습하던 내용에서 추가하였기 때문에 이번 실습에서는 불필요하고 길다.

아래 코드를 통해 실습해도 무관하다. (다만, 주소값은 다르게 나올 수 있다.)

/**********************************************************************************************************************
 * \file Cpu0_Main.c
 * \copyright Copyright (C) Infineon Technologies AG 2019
 *********************************************************************************************************************/

#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"

volatile const uint32 g_constVal      = 0x12345678;
volatile uint32       g_initGlobal    = 0xAAAAAAAA;
volatile uint32       g_uninitGlobal;
static volatile uint32 s_initStatic   = 0xBBBBBBBB;
static volatile uint32 s_uninitStatic;

uint32 g_heapArea1[4];
uint32 g_heapArea2[4];
uint32 *g_heapPtr1 = g_heapArea1;
uint32 *g_heapPtr2 = g_heapArea2;

IfxCpu_syncEvent cpuSyncEvent = 0;

int core0_main(void)
{
    IfxCpu_enableInterrupts();
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());

    volatile uint32 localMain1 = 0x11111111;
    volatile uint32 localMain2 = 0x22222222;

    g_initGlobal++;
    g_uninitGlobal = 0x13572468;
    s_initStatic++;
    s_uninitStatic = 0x24681357;

    if (g_constVal == 0x12345678)
    {
    }

    g_heapPtr1[0] = 0xDEADBEEF;
    g_heapPtr2[0] = 0xCAFEBABE;

    while (1)
    {
        localMain1++;
    }

    return 0;
}

 


 

결과

while문에 breakpoint를 건 다음 디버깅 해보면 Expressions 창에서 아래와 같은 값들을 확인할 수 있다.

이때 주의할 점은 확인할 변수들을 등록할 때 &을 붙여줘야 주소값으로 표기된다. (포인터 변수 제외)

 

위 결과에서 확인할 수 있는 영역은 크게 3가지로 구분지을 수 있다.

  • 0x8*** **** → 플래시(코드/상수 영역)
  • 0x6*** **** → RAM의 전역 영역 (.data, .bss, 전역 배열, “힙”)
  • 0x7*** **** → 스택 (지역 변수)

 

읽기 전용 상수

  • volatile const uint32 g_constVal = 0x12345678;
    • 주소: 0x800000f8
    • 프로그램 플래시 / .rodata 영역

 

초기화된 전역 변수 (.data)

  • volatile uint32 g_initGlobal = 0xAAAAAAAA;
    • 주소: 0x60000014
  • static volatile uint32 s_initStatic = 0xBBBBBBBB;
    • 주소: 0x60000018
  • .data 섹션(초기값이 있는 전역/정적 변수들이 모여 있는 RAM 영역)

 

초기화되지 않은 전역 변수 (.bss)

  • volatile uint32 g_uninitGlobal;
    • 주소: 0x60000050
  • static volatile uint32 s_uninitStatic;
    • 주소: 0x60000054
  • 초기값 없이 시작했다가 스타트업 코드가 0으로 클리어하는 .bss 섹션에 들어감

 

힙처럼 쓴 전역 배열/포인터

  • 전역 배열: 주소는 아직 안 찍었지만 0x6000 00xx 대)
  • uint32 *g_heapPtr1 = g_heapArea1;
    • 주소: 0x60000030
  • uint32 *g_heapPtr2 = g_heapArea2;
    • 주소: 0x60000040
  • 이 값이 각각 g_heapArea1[0], g_heapArea2[0]의 시작 주소.
  • 실질적으로는 전역 RAM(.bss나 .data 부분 근처)인데, “동적 메모리/힙처럼 사용하는 영역”이라고 생각하면 된다.

 

지역 변수 (스택)

  • volatile uint32 localMain1 = 0x11111111;
    • 주소: 0x700195f8
  • volatile uint32 localMain2 = 0x22222222;
    • 주소: 0x700195fc
  • 둘 다 0x7001 95f? 처럼 높은 주소대에 연속으로 있고, 전역들(0x6000…)과 완전히 떨어져 있으니까 스택 영역이라고 볼 수 있다.