IAR 제품 XLINK로 RAM에서 코드 실행하기

기술노트 80460

아키텍처:

All

컴포넌트:

linker

업데이트:

2021-06-23 오전 10:21

소개

본문은 ROM 컨텐츠가 실행되는 ROM에서 RAM으로 복사하는 방법을 설명합니다. 일부 IAR 컴파일러는 키워드를 통해 이를 지원합니다. 하지만 지원받을 수 없다고 가정하고, 보다 일반적으로 적용할 수 있는 접근방식을 사용합니다. 가장 일반적인 경우는 ROM에서 RAM(사용되는 위치)으로 콘텐츠를 복사하는 것이지만, 이 매커니즘은 한 곳에 콘텐츠를 연결한 다음 다른 곳에 바이트를 배치하는 것을 지원한다는 점에서 더 일반적입니다.

기본

다음은 코드를 RAM으로 복사하는 과정입니다:

  1. Place the code in its own segment.
  2. Use the -Q linker option to create an initializer segment
  3. Place the original segment in RAM.
  4. Place the initializer segment in ROM.
  5. Copy the bytes from the initializer segment to the segment.
  6. Debug the application

1. 세그먼트에 컨텐츠 배치

ROM에서 RAM으로 복사하려는 모든 컨텐츠는 자체 세그먼트에 배치합니다. 이는 어셈블러, 컴파일러 및 이미지 입력에서 다양한 방식으로 이루어집니다.

  • 어셈블러를 사용할 때, 예제:

    어셈블리 파일에서는 RSEG 지시어을 통해 구성에 사용되는 세그먼트를 명시적으로 제어할 수 있습니다.
       RSEG RAMCODE:CODE:NOROOT(2)
    do_stuff:
    .
    .
    .
    do_stuff 진입을 정의하는 RAMCODE 세그먼트 내의 타입이 CODE인 세그먼트 부분이 생성됩니다. 루트가 아니고, 4-byte 정렬되어 있습니다.

    참고: 세그먼트 유형 CODE는 세그먼트 이름 CODE와 동일하지 않지만, 일반적으로 이름이 CODE 인 세그먼트도 CODE 유형으로 지정됩니다.
  • 컴파일러 사용시:

    일부 IAR 컴파일러는 전체 모듈에 대해 유사한 기능을 제공하는 커멘드 라인 옵션을 제공합니다(특정 컴파일러의 자세한 내용은 메뉴얼 참고). 대부분 최신 IAR 컴파일러는 다음 구문에 대해 세그먼트를 제어하는 pragma(#pragma location)를 제공합니다.
    #pragma location="SPECIAL_CODE"
    int do_strange_things(int a, int b)
    {
    .
    .
    .
    }

    SPECIAL_CODE 세그먼트 안에 do_strange_things 함수가 배치됩니다. 파일 위 또는 아래에 선언하는 데에 영향을 미치지 않습니다.

    #pragma location을 많이 사용하는 것은 권하지 않습니다. 이 문자열을 많개는 백개 혹은 천개 가지는 것은 코드를 복잡하게 하고 위치(location)를 변경하려고 할때 문제가 발생할 수 있습니다. 통상적으로 #pragma와 매크로는 잘 어울리지 않지만, 이러한 문제를 위해 IAR 확장 키워드 _Pragma가 있습니다.

  • #define RAMCODE(x) _Pragma("location=\"RAMCODE\"") x
    이 매크로를 사용하여 인수 선언이 RAMCODE 세그먼트에 배치됩니다. 예시:
  • RAMCODE(int do_strange_things(int a, int b))
    {
    .
    .
    .
    }

    이것은 코드에만 국한되진 않습니다.

  • #define CONST(x) _Pragma("location=\"CONST_SEG\"") x
    이 매크로는 인수 선언을 CONST_SEG 세그먼트에도 배치합니다. 예시:
  • CONST(const int a = 4711;)
    CONST(const int arr[3] = { 45, 34, -2};)
  • --image_input 사용:

    링커 옵션 --image_input을 사용하면 바이트 단위로 모든 종류의 파일을 가져와 세그먼트에 배치할 수 있습니다.
  • --image_input=image1.raw,image1_start,IMAGE_SEG,0
    image1.raw 파일의 내용을 IMAGE_SEG 세그먼트로 가져옵니다. 세그먼트는 image1_start 심볼을 정의하고 정렬되지 않습니다. --image_input을 사용하여 가져온 코드는 이미지를 생성한 링크 작업에 배치된 주소와 동일한 주소에 배치해야 합니다. 여기에는 위치 독립 코드와 같은 일부 예외가 있지만, 별도로 주의를 기울이지 않는 한 가져온 코드는 원래 연결된 위치에서만 실행해야 합니다.

2. 이니셜라이저 세그먼트 생성

-Q 링커 옵션을 사용하여 이니셜라이즈 세그먼트를 생성합니다. -Q를 사용하면 한 곳에 컨텐츠를 연결하고 (RAM 인 경우), 바이트를 다른 곳에 배치할 수 있습니다 (이 경우 ROM).

-QRAMCODE=RAMCODE_ID

RAMCODE 세그먼트에 대한 이니셜라이저 세그먼트 RAMCODE_ID가 생성됩니다.

RAMCODE에는 레이블과 디버그 정보가 포함되어 있습니다. 실제 실행 시 코드가 상주하게 됩니다. RAMCODE가 어디서 만들어졌는지에 대한 모든 참조가 있고, RAMCODE가 만든 모든 참조를 생성하는 위치입니다.

RAMCODE_ID(RAMCODE와 크기가 동일)은 레이블이나 디버그 정보가 없고, 실제 RAMCODE 바이트만 포함합니다. 이 바이트는 RAMCODE에 복사하는 용도로만 사용됩니다. 현재 있는 코드를 복사하기 전에 실행하거나, 다른 주소로 복사하는 등의 다른 용도로 바이트를 사용하면 정의되지 않은 동작(undefined behavior)이 발생합니다.

3. 원본 세그먼트를 RAM에 배치

RAMCODE는 복사 초기화된 세그먼트이므로 세그먼트 부분의 정확한 순서가 유지되도록 -Z 세그먼트 배치 명령과 함께 배치해야 합니다.

-Z(DATA)RAMCODE=RAM_START-RAM_END

세그먼트 배치 명령은 .xcl 파일안에 있는 것과 동일한 순서로 처리하고 모든 배치에서는 이전 배치를 고려합니다. 특정 세그먼트가 특정 세그먼트 앞이나 뒤에 배치되도록 배치 위치를 이동해야 할 수 있습니다.

4. 이니셜라이저 세그먼트를 ROM에 배치

RAMCODE는 복사 초기화된 세그먼트이므로 세그먼트 부분의 정확한 순서가 유지되도록 -Z 세그먼트 배치 명령과 함께 배치해야 합니다.

-Z(CONST)RAMCODE_ID=ROM_START-ROM_END

5. 이니셜라이저 세그먼트에서 세그먼트로 바이트를 복사

이 툴은 실제로 RAMCODE_ID에서 RAMCODE로 바이트를 복사하려고 시도하지 않기에, 프로그래머는 런타임에 이 작업을 수행해야 합니다. 실행 전 main에 도달하기 전에 이를 자동으로 수행하도록 startup 코드를 수정할 수 있지만, RAMCODE의 컨텐츠를 사용하기 전에 바이트를 복사하는 한 복시 시점은 문제가 되지 않습니다.

실제 복사는 꽤 간단합니다. memcpy 호출로도 충분하며, 약간 까다로운 부분은 IAR 확장기능(또는 어셈블러에 작성된 함수를 호출)을 사용하여 세그먼트의 주소들을 구하는 것입니다. 이 코드는 주소 공간이 하나인 프로세서에서 테스트되었습니다. 프로세서가 ROM에서 RAM으로 바이트를 복사하기 위한 특별한 요구 사항(다른 주소 공간, 특별 지침, 특정 주소 요구사항 등)이 있는 경우, 복사 메커니즘을 통해 이러한 요구 사항을 모두 충족해야 합니다.

/* copy all bytes between s (inclusive) and e (exclusive) to d */
void activate(void * s, void * e, void * d)
{
size_t size = (size_t)e - (size_t)s;
memcpy(d,s,size);
}

/* copy the bytes from RAMCODE_ID to RAMCODE */
void activate_RAMCODE(void)
{
#pragma segment="RAMCODE"
#pragma segment="RAMCODE_ID"
activate(__sfb("RAMCODE_ID"), __sfe("RAMCODE_ID"), __sfb("RAMCODE"));
}

#pragma segment=""는 컴파일러에게 그 이름을 가진 세그먼트에 __sfb와 __sfe를 사용하는 것 외에 다른 효과는 없다고 알립니다.

세그먼트의 시작 주소를 반환하는 IAR 확장 기능은 __sfb (segment frame begin)입니다. 세그먼트는 앞선 #pragma에서 언급되어야 합니다. 세그먼트가 0x500-0x52F를 점유하면 __sfb는 0x500를 반환합니다.

__sfb를 보완하는 __sfe (segment frame end)는 세그먼트 뒤에 있는 첫 번째 free 바이트의 주소입니다. 세그먼트가 0x500-0x52F를 점유하면 __sfe는 0x530를 반환합니다.

activate_RAMCODE를 호출한 뒤 모든게 사용할 준비가 됩니다.

6. 디버깅

이 예에서, 복사 초기화된 코드는 바이트가 복사된 뒤 완벽한 디버그 능력을 갖추어야 합니다.

 

모든 제품 이름은 해당 소유자의 상표 또는 등록 상표입니다.

죄송하지만, 당사 사이트에서는 Internet Explorer를 지원하지 않습니다.보다 편안한 사이트를 위해 Chrome, Edge, Firefox 등과 같은 최신 브라우저를 사용해 주시길 부탁드립니다.