주요 내용으로 건너뛰기

메모리 사용량

서론

이 섹션에서는 TouchGFX 애플리케이션의 메모리 사용량에 대해 알아보겠습니다. TouchGFX 애플리케이션은 일반적으로 사용되는 하드웨어에 따라 다음과 같이 4가지 유형의 메모리를 사용합니다.

메모리 유형용도
내부 RAM내부 RAM은 모든 위젯의 좌표나 색상과 같은 구성 데이터에 사용됩니다. 현재 스크린에서 일부 객체가 여기에 할당됩니다.
또한 UI 작업의 런타임 스택을 포함해 운영 체제 메모리 역시 내부 RAM에 저장됩니다. 그 밖에 파일 시스템이나 디스플레이 드라이버와 같은 소프트웨어 구성요소 데이터도 내부 RAM에 저장됩니다.
내부 플래시내부 플래시는 애플리케이션, TouchGFX 라이브러리 및 기타 라이브러리의 프로그램 코드에 사용됩니다.
외부 RAM외부 RAM은 일반적으로 프레임버퍼를 비롯한 비트맵 캐시에 사용됩니다.
외장 플래시외장 플래시는 이미지, 글꼴 및 텍스트를 저장하는 데 사용됩니다.

정적 메모리 할당

TouchGFX는 정적 메모리 할당만 사용합니다. 이는 모든 메모리가 사전에 할당된다는 것을 의미합니다. 런타임 시 TouchGFX에서 할당하는 메모리는 없습니다. 따라서 애플리케이션이 처음부터 메모리 크기에 적합하다면 메모리가 부족해질 일이 없습니다.

스크린 및 위젯

TouchGFX에서 사용자 인터페이스는 다수의 C++ 클래스를 개발하여 생성됩니다. 클래스는 스크린을 설계할 때 TouchGFX Designer에서 생성됩니다. TouchGFX Designer에서 설계한 각 화면에 대해 자동으로 다수의 클래스(MVP 아키텍처)를 얻게 됩니다. 디스플레이에 스크린을 표시하면 내부 RAM의 TouchGFX에서 클래스의 객체가 자동으로 할당됩니다.

이후 스크린을 전환하면 이전 스크린에 할당되었던 객체는 더는 사용되지 않고 새로운 스크린에 할당되는 객체만 사용됩니다. 따라서 새로운 객체가 내부 RAM에서 이전 객체가 할당되었던 공간에 할당됩니다(이전 객체를 덮어씁니다). 내부 RAM에는 한 시점에 한 스크린의 객체만 저장되기 때문입니다.

C++ 컴파일러는 정의된 클래스를 기준으로 가장 큰 스크린 클래스의 크기를 계산하여 해당 클래스에 필요한 메모리를 할당할 수 있습니다.

따라서 내부 RAM의 메모리 사용량은 애플리케이션의 스크린 수가 아니라 가장 큰 스크린의 크기에 따라 결정됩니다.

이러한 객체를 위해 따로 설정된 메모리를 FrontendHeap라고 합니다.

TouchGFX

애플리케이션 코드

애플리케이션 코드는 일반적으로 내부 플래시에 저장됩니다. 애플리케이션 코드는 개발자가 직접 작성한 프로그램 코드, TouchGFX Designer에서 생성된 코드, TouchGFX 라이브러리의 코드, 그리고 개발자가 사용하는 다른 라이브러리로 구성됩니다.

물론 코드를 많이 작성하고, 스크린을 애플리케이션에 추가할수록 애플리케이션 코드의 크기도 증가하게 됩니다. 또한 기능을 처음 사용하면 라이브러리에서 가져오는 코드가 많아집니다. 예를 들어 버튼을 스크린에 처음으로 추가하면 TouchGFX 라이브러리의 버튼 코드가 애플리케이션에 추가되어 크기가 커집니다. 이후 두 번째부터는 버튼을 해당 스크린이나 다른 스크린에 추가해도 TouchGFX 라이브러리에서 추가로 가져오는 코드는 없고 애플리케이션은 개발자가 작성하거나 TouchGFX Designer가 생성한 코드량에 의해서만 증가합니다.

애셋

이미지, 텍스트, 글꼴 같은 애셋(assets)은 C++ 파일로 변환되어 애플리케이션에 연결됩니다. 애셋 데이터는 일반적으로 외장 플래시에 저장되지만 내부 플래시에 저장하는 것도 가능합니다. 이러한 저장 위치는 링커 스크립트에서 제어합니다.

이미지를 추가하면 애플리케이션의 크기가 이미지 크기에 비례하여 커집니다.

텍스트를 추가하면 애플리케이션의 크기가 텍스트에 포함된 문자마다 2바이트씩 커집니다. 하지만 동일한 문자열을 두 번 사용하면 한 번만 포함됩니다.

글꼴 파일에서 가져오는 문자는 애플리케이션에서 사용되는 문자로 제한됩니다. 따라서 애플리케이션에서 대문자(A~Z)만 사용할 경우 글꼴 소문자(a~z)는 애플리케이션에 포함되지 않습니다. 나중에 이러한 문자가 포함된 텍스트를 추가하면 애플리케이션의 글꼴 데이터도 크기가 증가합니다.

플래시에 저장되는 문자의 크기는 선택한 글꼴 크기에 따라 달라집니다. 따라서 글꼴이 커지면 애플리케이션도 커집니다.

메모리 사용량 확인

특정 애플리케이션의 메모리 사용량은 링커에서 생성되는 맵 파일을 검사하면 알 수 있습니다.

여기에서는 IAR Embedded Workbench에서 생성된 맵 파일을 검사합니다. 그 밖에 다른 컴파일러에서도 비슷한 맵 파일이 생성됩니다.

먼저 TouchGFX Designer에서 STM32F746Discovery 평가 키트에 사용할 빈 프로젝트를 생성합니다.

박스와 버튼이 포함된 STM32F746 프로젝트

IAR에서 해당 프로젝트를 열고 IAR이 .MAP 파일을 생성하는 속성을 체크합니다.

링커 맵 파일 생성

IAR에서 컴파일을 마치면 EWARM/STM32F746G_DISCO/List 폴더에서 링커 맵 파일인 STM32F746G_DISCO.map을 확인할 수 있습니다.

IAR 링커 맵 파일에는 쉽게 알아볼 수 있도록 요약된 테이블이 있습니다. MODULE SUMMARY를 검색합니다.

*******************************************************************************
*** MODULE SUMMARY
***

Module ro code ro data rw data
------ ------- ------- -------
command line/config:
------------------------------------------------------------------
Total:

C:\TouchGFXProjectsDocumentation\STM32F746MemoryUsage\EWARM\STM32F746G_DISCO\Obj: [1]
ApplicationFontProvider.o 20
BitmapDatabase.o 12 40
Blue_Buttons_Round_Edge_small.o 40'800
Blue_Buttons_Round_Edge_small_pressed.o 40'800
Font_verdana_10_4bpp_0.o 24
Font_verdana_20_4bpp_0.o 72
Font_verdana_40_4bpp_0.o 280
FrontendApplication.o 46 60
FrontendApplicationBase.o 706 816
GeneratedFont.o 84 84
Kerning_verdana_10_4bpp.o 4
Kerning_verdana_20_4bpp.o 4
Kerning_verdana_40_4bpp.o 4
Model.o 10
OSWrappers.o 156 1 9
STM32DMA.o 898 176
STM32TouchController.o 162 24 4
...
heap_4.o 444 32'792
...
touchgfx_core.a: [7]
AbstractButton.o 136
AbstractPartition.o 8
Application.o 2'218 290 28
Bitmap.o 1'064 604 36
Box.o 108 104
Button.o 276 308
ConstFont.o 62
Container.o 510 396
DMA.o 558 252
DisplayTransformation.o 192
Drawable.o 418
FontManager.o 12 4
Gestures.o 364 60
HAL.o 1'758 544 18
LCD24bpp.o 2'732 1'604 80
Screen.o 1'924 124
TouchCalibration.o 252 76
TypedText.o 14
------------------------------------------------------------------
Total: 12'728 4'286 256

Gaps 4 3
Linker created 36 2'560
----------------------------------------------------------------------
Grand Total: 38'676 88'973 42'731

위 테이블에는 3개의 숫자 열이 있습니다. ro codero data는 읽기 전용으로, 플래시에 저장됩니다. 그리고 rw data는 상수가 아닌 읽기-쓰기 변수이며 RAM에 저장됩니다.

테이블의 행은 7개의 블록으로 나뉩니다. 첫 번째 블록은 프로젝트에 사용되는 .cpp 파일들로 구성됩니다. 다름 블록 6개는 프로젝트에 사용되는 라이브러리(.a 파일)들로 구성됩니다. 마지막에는 TouchGFX 라이브러리가 있습니다.

TouchGFX 라이브러리("touchgfx_core.a: [7]” 섹션)를 보면 12,728바이트의 코드가 애플리케이션에 추가된 것을 알 수 있습니다(그 외 4,286바이트의 상수 데이터 포함).

내부 RAM

전체 내부 RAM 사용량을 알고 싶다면 Module Summary 테이블 하단에 있는 Grand Total 행을 확인하십시오. 세 번재 열이 내부 RAM입니다. 위에서는 프로젝트의 내부 RAM 사용량이 42,731바이트인 것을 알 수 있습니다. 또한 TouchGFX 라이브러리의 총 사용량을 보면 TouchGFX 라이브러리 [7]에서 256바이트가 사용된 것을 알 수 있습니다. heap_4.o에서는 32,792바이트가 사용되었습니다. 이 용량은 FREERTOS를 위해 예약된 동적 메모리 힙입니다. 32Kb는 기본값일 뿐이며, 힙 크기는 STM32CubeMX에서 구성할 수 있습니다. 일반적으로 TouchGFX 프로그램은 이 힙에서 일부 Kb만 사용하고, 주로 사용자 인터페이스 작업에 스택을 할당합니다.

FrontendHeap을 검색하면 스크린 객체의 크기를 찾을 수 있습니다.

FrontendHeap::getInstance()::instance
0x2000'95d0 0x240 Data Gb TouchGFXConfiguration.o [1]

위에서 사용자 인터페이스에 필요한 객체가 차지하는 용량은 0x240바이트, 즉 576바이트입니다.

내부 플래시

Grand Total 행을 보면 이 애플리케이션에서 38.676바이트 코드 + 88,973바이트 데이터를 사용한다는 것을 알 수 있습니다. 여기서는 일부만 내부 플래시에 해당합니다. 2개 이상의 버튼 이미지가 외장 플래시에 저장됩니다.

내부 플래시에 저장되는 코드와 데이터의 크기를 알고 싶다면 먼저 PLACEMENT SUMMARY(일부 정보 삭제됨)를 확인하십시오.

*******************************************************************************
*** PLACEMENT SUMMARY
***

"A0": place at address 0x800'0000 { ro section .intvec };
"P1": place in [from 0x800'0000 to 0x80f'ffff] { ro };
"P2": place in [from 0x2000'0000 to 0x2004'ffff] { rw };
"P3": place in [from 0x9000'0000 to 0x90ff'ffff] {
section ExtFlashSection, section FontFlashSection,
section TextFlashSection };

내부 플래시는 주소가 0x08000000으로 시작됩니다. 위에서는 두 영역인 "A0"과 "P1"에 저장됩니다.

맵 파일을 좀 더 자세히 보면 두 영역에 저장된 내용을 확인할 수 있습니다.

  Section                Kind         Address      Size  Object
------- ---- ------- ---- ------
"A0": 0x1c8
.intvec ro code 0x800'0000 0x1c8 startup_stm32f746xx.o [1]
- 0x800'01c8 0x1c8

"P1": 0xb05d
.text ro code 0x800'01c8 0x9b8 main.o [1]
.text ro code 0x800'0b80 0x14 memset.o [5]
...
.text ro code 0x800'b17a 0x2 AbstractButton.o [7]
.rodata const 0x800'b17c 0x1 unwind_debug.o [6]
.rodata const 0x800'b17d 0x0 zero_init3.o [5]
.rodata const 0x800'b17d 0x0 lz77_init_single.o [5]
Initializer bytes const 0x800'b17d 0xa8 <for P2-1>
- 0x800'b225 0xb05d

"A0”의 사용량은 0x1c8바이트, 즉 456바이트이고, “P1”의 사용량은 0xb05d바이트, 즉 45,149바이트입니다. 이에 따라 내부 플래시의 총 사용량은 45,605바이트가 됩니다.

외장 플래시

외장 플래시는 “P3” 영역입니다(주소가 0x90000000으로 시작). 이 영역의 내용은 다음과 같습니다.

"P3":                                          0x1'4076
ExtFlashSection const 0x9000'0000 0x9f60 Blue_Buttons_Round_Edge_small.o [1]
ExtFlashSection const 0x9000'9f60 0x9f60 Blue_Buttons_Round_Edge_small_pressed.o [1]
FontFlashSection const 0x9001'3ec0 0x118 Font_verdana_40_4bpp_0.o [1]
FontFlashSection const 0x9001'3fd8 0x48 Font_verdana_20_4bpp_0.o [1]
FontFlashSection const 0x9001'4020 0x18 Font_verdana_10_4bpp_0.o [1]
FontFlashSection const 0x9001'4038 0x10 Table_verdana_10_4bpp.o [1]
FontFlashSection const 0x9001'4048 0x10 Table_verdana_20_4bpp.o [1]
FontFlashSection const 0x9001'4058 0x10 Table_verdana_40_4bpp.o [1]
FontFlashSection const 0x9001'4068 0x4 Kerning_verdana_10_4bpp.o [1]
FontFlashSection const 0x9001'406c 0x4 Kerning_verdana_20_4bpp.o [1]
FontFlashSection const 0x9001'4070 0x4 Kerning_verdana_40_4bpp.o [1]
TextFlashSection const 0x9001'4074 0x2 Texts.o [1]
- 0x9001'4076 0x1'4076

위에서 외장 플래시의 총 사용량은 0x14076바이트, 즉 82,038바이트입니다. 이 용량의 대부분이 2개의 버튼 이미지에 사용됩니다(0x9f60바이트의 2배 = 40,800바이트). 나머지 데이터는 글꼴 3개에 사용됩니다. 이 예시에서는 텍스트 없이 ‘?’ 문자만 포함되어 있기 때문에 글꼴이 사용하는 공간이 많지 않습니다.

요약

외부 RAM에 저장되는 데이터는 프레임버퍼 밖에 없습니다. 프레임버퍼는 애플리케이션에서 변수로 정의되지 않기 때문에 링커 스크립트에서 찾을 수 없습니다. 해상도는 24비트에서 480x272 픽셀입니다. 프레임버퍼는 2개이고, 총 사용량은 480 * 272 * 3 * 2, 즉 786,360바이트가 됩니다.

메모리 유형용도
내부 RAM42,731바이트
TouchGFX 스크린 객체576바이트
내부 플래시45,605바이트
TouchGFX 프레임워크12.728바이트 코드
외부 RAM786,360바이트
외장 플래시82,028바이트

Demo 1

또 다른 예로 TouchGFX Demo1의 수치가 있는데, 이는 TouchGFX Designer에서 찾아볼 수 있습니다. 여기에는 5개의 스크린을 비롯해 100개 이상의 이미지가 포함되어 있습니다.

STM32F746 Demo 1

요약

메모리 유형용도
내부 RAM51,387바이트
TouchGFX 스크린 객체10,772바이트
내부 플래시187,768바이트
TouchGFX 프레임워크 코드85,174바이트 코드
외부 RAM786,360바이트
외장 플래시5,281,812바이트