ARM Cortex-M4 마이크로 컨트롤러의 힙 및 스택 크기를 정의 하시겠습니까?


11

나는 작은 임베디드 시스템 프로젝트를 켜고 끄는 작업을 해왔다. 이러한 프로젝트 중 일부는 ARM Cortex-M4 기본 프로세서를 사용했습니다. 프로젝트 폴더에는 startup.s 파일이 있습니다. 그 파일 안에 다음 두 가지 명령 줄이 있습니다.

;******************************************************************************
;
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
;
;******************************************************************************
Stack   EQU     0x00000400

;******************************************************************************
;
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
;
;******************************************************************************
Heap    EQU     0x00000000

마이크로 컨트롤러 의 스택 크기를 어떻게 정의 합니까? 데이터 시트에 정확한 정보를 제공하기위한 특정 정보가 있습니까? 그렇다면 데이터 시트에서 무엇을 찾아야합니까?


참고 문헌 :

답변:


12

스택 및 힙은 하드웨어 개념이 아니라 소프트웨어 개념입니다. 하드웨어가 제공하는 것은 메모리입니다. 메모리 영역을 정의하는 것 중 하나는 "스택"이라고하고 다른 하나는 "힙"이라고하며 프로그램을 선택합니다.

하드웨어는 스택에 도움이됩니다. 대부분의 아키텍처에는 스택 포인터라는 전용 레지스터가 있습니다. 프로그램이 함수 호출을 수행 할 때 함수 매개 변수 및 리턴 주소가 스택으로 푸시되고 함수가 종료되고 호출자에게 리턴 될 때 팝업됩니다. 스택에 푸시한다는 것은 스택 포인터에 의해 주어진 주소에 기록하고 이에 따라 스택 포인터를 감소시키는 것 (또는 스택이 성장하는 방향에 따라 증가)을 의미합니다. 포핑은 스택 포인터를 증가 (또는 감소)하는 것을 의미하고; 반환 주소는 스택 포인터가 제공 한 주소에서 읽습니다.

ARM이 아닌 일부 아키텍처에는 스택 포인터가 제공 한 주소에 대한 쓰기와 점프를 결합하는 서브 루틴 호출 명령과 스택 포인터가 제공 한 주소의 읽기와이 주소로의 점프를 결합하는 서브 루틴 리턴 명령이 있습니다. ARM에서는 주소 저장 및 복원이 LR 레지스터에서 수행되며 호출 및 반환 명령어는 스택 포인터를 사용하지 않습니다. 그러나 함수 포인터를 푸시하고 팝하기 위해 스택 포인터에 의해 주어진 주소에 여러 레지스터를 쓰거나 읽는 것을 용이하게하는 명령이 있습니다.

힙 및 스택 크기를 선택하려면 하드웨어에서 유일하게 관련된 정보는 총 메모리 용량입니다. 그런 다음 메모리에 저장할 항목에 따라 선택합니다 (코드, 정적 데이터 및 기타 프로그램 허용).

프로그램은 일반적으로 이러한 상수를 사용하여 스택의 맨 위 주소, 스택 오버플로를 확인하기위한 값, 힙 할당 자에 대한 경계 등 코드의 나머지 부분에서 사용할 메모리의 일부 데이터를 초기화합니다. 등

에서 당신이보고있는 코드Stack_Size상수 (A를 통해 코드 영역의 메모리 블록을 예약하는 데 사용되는 SPACE조립 ARM 지시문). 이 블록의 상위 주소에는 레이블이 지정되며 __initial_sp벡터 테이블에 저장되며 (프로세서가이 항목을 사용하여 소프트웨어 재설정 후 SP를 설정 함) 다른 소스 파일에 사용하기 위해 내 보냅니다. Heap_Size상수는 유사 메모리 블록을 예약하는 데 사용하고 경계 (에 레이블입니다 __heap_base__heap_limit다른 소스 파일에 사용하기 위해 수출하고 있습니다).

; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

…
__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler

…

                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit

당신은 그 값 0x00200 및 0x000400이 어떻게 결정되는지 아십니까
마헨드라 Gunawardena에게

@MahendraGunawardena 프로그램이 필요로하는 것을 기반으로 결정해야합니다. Niall의 답변은 몇 가지 팁을 제공합니다.
Gilles 'SO- 악마 중지

7

스택 및 힙의 크기는 마이크로 컨트롤러 데이터 시트의 어느 곳이 아니라 애플리케이션에 의해 정의됩니다.

스택

스택은 함수 내에 로컬 변수의 값, 로컬 변수에 사용 된 이전의 CPU 레지스터 값 (함수에서 나올 때 복원 될 수 있음), 해당 함수를 떠날 때 리턴 할 프로그램 주소를 저장하는 데 사용됩니다. 스택 자체를 관리하기위한 약간의 오버 헤드.

임베디드 시스템을 개발할 때 예상되는 최대 호출 깊이를 추정하고 해당 계층 구조의 함수에있는 모든 로컬 변수의 크기를 합한 다음 패딩을 추가하여 위에서 언급 한 오버 헤드를 허용 한 다음 프로그램 실행 중 발생할 수있는 모든 인터럽트

RAM이 제한되지 않는 대체 추정 방법은 필요한 것보다 훨씬 많은 스택 공간을 할당하고 스택을 센티넬 값으로 채운 다음 실행 중에 실제로 얼마나 많이 사용하는지 모니터링하는 것입니다. C 언어 런타임의 디버그 버전을 자동으로 수행하는 것을 보았습니다. 그런 다음 개발이 끝나면 원하는 경우 스택 크기를 줄일 수 있습니다.

필요한 힙 크기를 계산하는 것이 까다로울 수 있습니다. 힙은 사용 그렇다면, 동적으로 할당 된 변수에 사용되는 malloc()free()C 언어 프로그램, 또는 new하고 delete이 변수가 살고있는 곳을 C에서 + +, 그건.

그러나 C ++에서는 특히 숨겨진 동적 메모리 할당이 진행될 수 있습니다. 예를 들어, 정적으로 할당 된 객체가있는 경우, 언어는 프로그램이 종료 될 때 소멸자를 호출해야합니다. 소멸자의 주소가 동적으로 할당 된 링크 된 목록에 저장되는 적어도 하나의 런타임을 알고 있습니다.

따라서 필요한 힙 크기를 추정하려면 호출 트리를 통해 각 경로의 모든 동적 메모리 할당을보고 최대 값을 계산하고 패딩을 추가하십시오. 언어 런타임은 총 힙 사용량, 조각화 등을 모니터링하는 데 사용할 수있는 진단 기능을 제공 할 수 있습니다.


응답 주셔서 감사합니다, 나는 0x00400 등과 같은 특정 숫자를 결정하는 방법을 좋아합니다
Mahendra Gunawardena 18

5

다른 답변 외에도 스택과 힙 공간 사이에 RAM을 조각 할 때 정적이 아닌 정적 데이터 (예 : 파일 전역, 함수 정적 및 프로그램 전체)를위한 공간을 고려해야한다고 덧붙이고 싶습니다. C 관점에서 글로벌, 아마도 C ++에서 다른 것).

스택 / 힙 할당 작동 방식

시작 어셈블리 파일이 영역을 정의하는 한 가지 방법이라는 점은 주목할 가치가 있습니다. 툴체인 (빌드 환경 및 런타임 환경 모두)은 스택 공간의 시작 (벡터 테이블에 초기 스택 포인터를 저장하는 데 사용됨) 및 힙 공간의 시작 및 끝 (동적에 의해 사용됨)을 정의하는 기호를 주로 관리합니다. 일반적으로 libc에서 제공하는 메모리 할당 자)

OP의 예에서는 1kiB의 스택 크기와 0B의 힙 크기라는 두 개의 심볼 만 정의됩니다. 이 값은 실제로 스택 및 힙 공간을 생성하기 위해 다른 곳에서 사용됩니다.

@Gilles 예제에서, 크기는 어셈블리 파일에서 정의 되어 사용 되어 어디에서나 크기를 유지하는 스택 공간을 설정합니다. Stack_Mem 기호로 식별되고 끝에 __initial_sp 레이블을 설정합니다. 공간이 Heap_Mem (0.5kiB 크기)이지만 시작과 끝에 레이블 (__heap_base 및 __heap_limit)이있는 힙의 경우에도 마찬가지입니다.

이들은 링커에 의해 처리되며, 스택 공간과 힙 공간 내에서 메모리가 점유되어 있기 때문에 스택 공간과 힙 공간 내에 아무것도 할당하지 않지만 필요한 경우 메모리와 모든 전역을 배치 할 수 있습니다. 레이블은 주어진 주소에서 길이가없는 기호입니다. __initial_sp는 링크 타임에 벡터 테이블에 직접 사용되며 런타임 코드에 따라 __heap_base 및 __heap_limit가 사용됩니다. 심볼의 실제 주소는 위치에 따라 링커에서 할당합니다.

위에서 언급했듯이 이러한 기호는 실제로 startup.s 파일에서 가져 오지 않아도됩니다. 링커 구성 (Keil의 스 캐터로드 파일, GNU의 링커 스크립트) 및 배치에 대한 세밀한 제어가 가능합니다. 예를 들어 스택을 RAM의 시작 또는 끝에 강제로 두거나 전역을 힙이나 원하는 곳에 두지 않도록 할 수 있습니다. 전역을 배치 한 후 남은 RAM을 HEAP 또는 STACK 만 차지하도록 지정할 수도 있습니다. 그러나 다른 메모리가 줄어드는 정적 변수를 더 추가하면주의해야합니다.

그러나 각 툴체인은 다르며 구성 파일을 작성하는 방법과 동적 메모리 할당자가 사용할 기호는 특정 환경의 문서에서 가져와야합니다.

스택 크기

스택 크기를 결정하는 방법에 대해서는 재귀 나 함수 포인터를 사용하지 않는 경우 많은 툴체인이 프로그램의 함수 호출 트리를 분석하여 최대 스택 깊이를 제공 할 수 있습니다. 이를 사용하는 경우 스택 크기를 추정하고 기본 값 (아마도 main 이전의 입력 기능을 통해)으로 미리 채운 다음 최대 깊이가있는 곳 (기본 값이있는 위치) 동안 프로그램을 실행 한 후 확인 종료). 프로그램을 한계까지 완전히 연습 한 경우 스택을 축소 할 수 있는지 또는 프로그램이 충돌하거나 기본 값이 남아 있지 않은 경우 스택을 늘리고 다시 시도해야하는지 상당히 정확하게 알 수 있습니다.

힙 사이징

힙 크기를 결정하는 것은 응용 프로그램에 따라 조금 더 다릅니다. 시작하는 동안 동적 할당 만 수행하는 경우 시작 코드에 필요한 공간을 추가하고 메모리 관리를위한 오버 헤드를 추가 할 수 있습니다. 메모리 관리자의 소스에 액세스 할 수 있으면 오버 헤드가 무엇인지 정확히 알 수 있으며 메모리를 사용하여 사용 정보를 제공하는 코드를 작성할 수도 있습니다. 동적 런타임 메모리 (예 : 인바운드 이더넷 프레임에 버퍼 할당)가 필요한 응용 프로그램의 경우 스택 크기를 신중하게 연마하고 스택 및 정적 후에 남은 모든 것을 힙에 제공하는 것이 좋습니다.

최종 메모 (RTOS)

OP의 질문은 베어 메탈 태그로 지정되었지만 RTOS에 메모를 추가하고 싶습니다. 종종 (항상?) 각 작업 / 프로세스 / 스레드 (간단히하기 위해 여기에 작업을 작성하겠습니다)에는 작업이 생성 될 때 스택 크기가 할당됩니다. 작업 스택 외에도 작은 OS가있을 것입니다 스택 (인터럽트 등에 사용)

작업 회계 구조와 스택은 어딘가에서 할당되어야하며, 이는 종종 애플리케이션의 전체 힙 공간에서 발생합니다. 이러한 경우 초기 스택 크기는 중요하지 않습니다. OS는 초기화 중에 만 스택을 사용하기 때문입니다. 예를 들어, 링크하는 동안 나머지 공간을 모두 HEAP에 할당하고 힙의 끝 부분에 초기 스택 포인터를 배치하여 힙으로 커지도록 OS가 힙의 시작 부분부터 시작하여 할당한다는 것을 알고 initial_sp 스택을 포기하기 직전에 OS 스택을 할당합니다. 그런 다음 모든 공간이 작업 스택 및 기타 동적으로 할당 된 메모리를 할당하는 데 사용됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.