Platform Integration Aspects: Memory Footprint

In almost every embedded system, the available data memory (e.g. SRAM, SDRAM, PSRAM, HyperRAM, ...) and the available flash memory (e.g. ROM, QSPI flash, OctoSPI flash, ...) are limited system resources and they have to be managed carefully. This article helps to estimate the overall memory footprint and gives you some guidelines to optimize and to reduce the memory footprint.

Embedded Wizard GUI applications can be used in a wide range of systems or environments, e.g. as Javascript application in a browser, as executable on a microprocessor (MPU) with Embedded Linux or as part of a binary on a microcontroller (MCU). Even though Embedded Wizard is a complex and powerful GUI solution, it is designed for the needs of all embedded systems and can be configured to fulfill the requirements of low-end MCUs with hard memory constraints. Due to the fact that the overall memory consumption depends mostly on your GUI application, on your target system and on the system configuration, it is always difficult to provide absolute numbers in terms of kilobytes. Nevertheless, the complete memory footprint can be analyzed and optimized.

Flash Memory (ROM) usage

Usually, the flash memory is used for the entire code and all constant data. Beside the GUI application itself, there are a lot of other software components in a real system requiring flash memory: operating system, standard libraries, device drivers, BSP and finally, your entire software stack (business logic, middleware, ...).

In order to run an Embedded Wizard generated GUI application, the Runtime Environment and the Graphics Engine are needed. These are provided as libraries (for customers using the 'Small Business' edition or for users working with the 'Free' edition) or as source code (for customers using the 'Professional' edition). In case you have the source code available, the Graphics Engine can be configured by a couple of macros to optimize the flash (ROM) usage.

The following list contains all items of a GUI application that have to be located within the flash memory:

Classes - the implementation (the logic) of your GUI application. Only those classes of your project are generated (and require flash memory) that are used within your GUI application.

Constants - all constant members of your project.

Strings - all the converted contents of all string constant members and string literals found in your project. The flash consumption for strings can be controlled with the attribute ModeOfStringConstants within the Profile of your GUI application.

Autoobjects - the initialization for all Autoobject members (global objects) of your GUI project.

Bitmap Resources - all bitmap resource members of your project together with the converted pixel data. The flash consumption for bitmap resources can be controlled with the attribute ModeOfBitmapResources within the Profile of your GUI application.

Font Resources - all font resource members of your GUI project together with the converted glyph pixel data and metrics. Fonts are usually stored compressed within the flash memory and loaded dynamically during runtime into the RAM.

Estimating the Flash Memory (ROM) usage

During the code generation Embedded Wizard Studio creates a Code Generation Report, containing an estimation of all above items. This report gives you a good overview of the expected flash memory usage of the GUI application. However, to get exact numbers of the entire binary file, the map file generated by your compiler / linker must be analyzed.

Code Generation Report of a GUI example to estimate the flash memory (ROM) usage.

Data Memory (RAM) usage

The situation with data memory (RAM) consumption is similar to the flash consumption: Beside the GUI application itself, a lot of other software components are using data memory for static / global variables, or they are allocating data memory dynamically by a memory manager during runtime. This depends completely on the software components that you are using.

Additionally, the stack has to be located within the data memory. The recommended stack size for Embedded Wizard GUI applications is 4...8 kilobyte.

Within an Embedded Wizard GUI application, the following items are allocating data memory (RAM):

Chora objects - memory for all dynamically created Chora objects.

Signals and observers - memory needed to store signals and observers.

Strings - memory for dynamically created strings or for constant strings that are stored compressed within the flash memory. In case that string constants are stored uncompressed within the flash memory, there is no need to load them into the data memory during runtime.

Glyph cache - all glyphs that are required to draw text are loaded from the font resource into the glyph cache. The size of the glpyh cache can be configured.

Bitmap resources - memory for the bitmap resource that are stored compressed within the flash memory and that are loaded into data memory for drawing operations. In case that bitmap resources are stored uncompressed within the flash memory, there is no need to load them into the data memory during runtime.

Off-screen buffers - memory for buffered GUI components.

Interal - memory for path-data, text rendering and data structures to process all drawing instructions.

Optional: Off-screen framebuffer - memory used as a separate off-screen buffer (e.g. composition buffer used on Linux systems).

Beside of static variables, all the memory that is required for the Embedded Wizard GUI application is allocated dynamically by a memory manager. The Embedded Wizard Runtime Environment provides a dedicated heap manager that is optimized for the needs of GUI applications and that works together with the Garbage Collector. This heap manager is recommended to be used, but it is also possible to use any other dynamic memory manager (e.g. provided by the operating system). The heap manager operates with one or more fixed memory areas - this means, all dynamic memory allocations required by the GUI application are taken out of this predefined memory pool. The location and the size of the memory pool can be defined usually within the file ewconfig.h by using the macros EW_MEMORY_POOL_ADDR and EW_MEMORY_POOL_SIZE.

Finally, data memory (RAM) is used for the framebuffer. Depending on the framebuffer concept that is used on the target system, memory is necessary for one or two framebuffers.

The necessary memory size in bytes for one framebuffer can be calculated by: FRAME_BUFFER_WIDTH * FRAME_BUFFER_HEIGHT * FRAME_BUFFER_DEPTH. In most cases, the physical size of the framebuffer has to correspond to the ScreenSize of the GUI project.

In case that an external display controller with its own graphics memory (GRAM) is used, there is no need for a complete framebuffer. In this case, Embedded Wizard GUI applications can be created that are using a small scratch-pad buffer, which requires only data memory for a few display lines.

The size and the location of the framebuffer(s) can be defined usually within the file ewconfig.h.

Estimating the Data Memory (RAM) usage

The window Memory (RAM) usage can be used to estimate the memory usage during prototyping: It tracks the memory usage of the entire GUI application. In the upper area the window lists all Chora object classes and their memory (RAM) usage. In the lower area, it displays the estimated total memory (RAM) usage of the tested application as a bar chart. The chart reflects the amount of memory needed by the application within the real target device. The window also gives the lowest possible configuration parameters for the glyph cache and issue buffers estimated during the runtime of the application.

Data Memory (RAM) usage window of a GUI example running within the Prototyper.

Monitoring the Data Memory (RAM) usage

Embedded Wizard provides an 'in-target' debugging/analyzing feature for monitoring the memory (RAM) usage during runtime. This option is controlled by the macro EW_PRINT_MEMORY_USAGE that can be activated within the file ewconfig.h. The function EwPrintProfilerStatistic() which is placed within the main loop will then print a short report about the current memory allocation of Chora objects, strings and resources. Additionally, the current total memory allocation and the maximum peak value is reported.

Data Memory (RAM) usage of a GUI example reported from target via serial connection to console window.

Optimizing the Flash Memory (ROM) usage

The following list contains recommendations to reduce the flash memory (ROM) consumption of your Embedded Wizard UI application. Please note, that some measures to reduce flash memory can result in increased RAM consumption.

Font Resources - Try to avoid too many different fonts and font sizes. Each font resource in your project causes Embedded Wizard to generate an extra set of glyphs. If you need many different font sizes and/or very large character ranges, it might be better to use a Platform Package with an external True Type Font Engine.

Font Resources - Reduce the font range to the necessary limit. By using the Excel sheet template for managing your strings, you can extract the used character codes of your strings in order to define the minimum range.

Font Resources - If possible, you can try to reduce the font quality. Even if the glyphs are compressed in the code, reducing the quality may help to save flash memory. Unfortunately, this measure can also degrade the display quality of texts.

Strings - Setting the attribute ModeOfStringConstants to Compressed ensures that all string constants are stored compressed within the flash memory. Please consider that compressed strings are loaded on request into RAM during runtime.

Bitmap Resources - Setting the attribute ModeOfBitmapResources to Compressed ensures that the bitmap pixel information of the bitmap resources are stored compressed within the flash memory. With the attribute ModeOfBitmapResources you determine the global mode affecting per default all bitmap resources. This setting can be overridden for each bitmap resource individually by using its attribute Mode. Please consider that compressed bitmap resources are loaded on request into RAM during runtime.

Bitmap Resources - Choose the attribute Format of your bitmap resources according the necessary pixel format. Reducing the color format of bitmap resources can help to save flash memory - especially if they are stored uncompressed by setting the attribute ModeOfBitmapResources or Mode to DirectAccess)

Bitmap Resources - Merge similar icons or images to a single file. By this way Embedded Wizard can reach a higher compression with the resource converter and you can easily access such an image frame by setting the attribute FrameSize and select the frame number in your image views.

Software architecture - Try to make an object-oriented software design by using inheritance in your class hierarchy. Try to avoid copies of similar classes in order to reduce the code size.

Software configuration - A set of macros (you will find them within the file ewconfig.h) can be used to remove function blocks from the Graphics Engine in order to reduce the code size of the resulting binary. Removing functionality makes sense only if you are sure that it is not needed by your application. If the application nevertheless uses the functionality, a runtime error is reported. In order to remove a function block just uncomment the respective macro within the file ewconfig.h. Please note, that activating the different macros requires the complete source code of the Graphics Engine to be recompiled with the new setting. As such it works only for customers who have licensed the 'Professional' edition. For customers using the 'Small Business' edition or users working with the 'Free' edition, the macros will not have any effect!

Macro

Description

EW_DONT_USE_WARP_FUNCTIONS

If this macro is defined, all functions needed to calculate and display transformed image contents are removed from the binary. In particular, it disables the possibility to scale and rotate images as well as their perspective projection resulting in 3D visual effects.

EW_DONT_USE_PATH_FUNCTIONS

If this macro is defined, all functions needed to calculate and raster vector graphics (to display polygons and paths) are removed from the binary.

EW_DONT_USE_BIDI_FUNCTIONS

If this macro is defined, all functions needed to process bidirectional text, as it is usually required to display right-to-left Arabic or Hebrew scripts, are removed.

EW_DONT_USE_INDEX8_SURFACES

If this macro is defined, the support for Index8 bitmap format is removed. Thereupon creation, loading and displaying of images stored in Index format is not possible anymore.

EW_DONT_USE_RGB565_SURFACES

If this macro is defined, the support for RGB565 bitmap format is removed. Thereupon creation, loading and displaying of images stored in RGB565 format is not possible anymore.

Compiler optimization - Use compiler optimization to get a compact binary.

Optimizing the Data Memory (RAM) usage

The following list contains recommendations to reduce the data memory (RAM) consumption of your Embedded Wizard UI application. Please note, that some measures to save data memory can result in an increased flash footprint.

Color Format Selection - In order to save memory for the framebuffer, a Graphics Engine with a reduced color format can be selected. The attribute PlatformPackage determines the color format of the target system - this has to correspond to the color format of the framebuffer.

Framebuffer Configuration - The selection of the framebuffer concept has a big impact to the resulting memory footprint. For many GUI applications without excessive animations, the use of double buffering is not necessary. The macro EW_USE_DOUBLE_BUFFER can be used to switch on/off the usage of a double-buffered framebuffer.

Strings - Setting the attribute ModeOfStringConstants to DirectAccess ensures that all string constants are stored uncompressed within the flash memory so that there is no need to load them into RAM during runtime. Please consider that uncompressed string constants require more flash memory than compressed string constants.

Bitmap Resources - Setting the attribute ModeOfBitmapResources to DirectAccess ensures that the bitmap pixel information of the bitmap resources are stored uncompressed within the flash memory. These type of bitmaps are drawn directly from the flash memory without the need to load them into the RAM during runtime. With the attribute ModeOfBitmapResources you determine the global mode affecting per default all bitmap resources. This setting can be overridden for each bitmap resource individually by using its attribute Mode. Please consider that uncompressed bitmap resources require more flash memory than compressed bitmap resources. Generally, the usage of DirectAccess for bitmap resources should be considered carefully! Typically the data rate of flash memory (ROM) is much slower compared to data memory (RAM) - as a result the graphics performance may decrease significantly.

Buffered Objects - Try to avoid buffered GUI components as these are allocating an internal bitmap to render their content. The size of the bitmaps correspond to the actual size of the components.

Glyph Cache - Define a reasonable/minimal size for the glyph cache: Within the file ewconfig.h the width and height of the gylph cache surface can be configured by using the macros EW_MAX_GLYPH_SURFACE_WIDTH and EW_MAX_GLYPH_SURFACE_HEIGHT. This surface stores all glyphs used during text drawing operations. The larger the surface the more glyphs can be reused without needing to decompress or rasterize them again. The absolute minimum values for a certain GUI application are shown in the Memory (RAM) usage window. The resulting RAM usage is EW_MAX_GLYPH_SURFACE_WIDTH * EW_MAX_GLYPH_SURFACE_HEIGHT * 1 byte.

Surface Cache - Define a reasonable/minimal size for the surface cache: The macro EW_MAX_SURFACE_CACHE_SIZE in the file ewconfig.h specifies the threshold value for the capacity of the surface cache. This cache stores often used bitmap resources and off-screen image buffers preventing them from being reloaded or recreated repeatedly. Embedded Wizard tracks the amount of memory occupied by all cached surfaces and as soon as this size exceeds the value specified in the macro, discards oldest unused surfaces from the cache automatically. The value for this macro is expressed in bytes as an integer number lying between 0 and 0x20000000.

String Cache - Define a reasonable/minimal size for the string cache: The macro EW_MAX_STRING_CACHE_SIZE in the file ewconfig.h specifies the threshold value for the capacity of the string cache. The string cache stores frequently used string constants preventing them from being reloaded repeatedly. Embedded Wizard tracks the amount of memory occupied by all string constants and as soon as this size exceeds the value specified in the macro, discards oldest unused strings from the cache automatically. The value for this macro is expressed in bytes as an integer number lying between 0 and 0x800000.

Instruction Buffer - Define a reasonable/minimal size for the instruction buffer: The macro EW_MAX_ISSUE_TASKS in the file ewconfig.h specifies the capacity of the instruction buffers used to store drawing operations. Larger buffers improve the optimization and elimination of superfluous drawing operations. In turn, small buffers cause the drawing process to be often interrupted to flush the prepared operations. The value for this macro is expressed in 'tasks' as an integer number lying between 1 and 8192. You should ensure that the issue buffer is at least as large as the most complex drawing operation performed in your application. The absolute minimum value for a certain GUI application is shown in the Memory (RAM) usage window. The following equation can be used to calculate the approximated RAM usage resulting from the configuration of the macro: (( EW_MAX_ISSUE_TASKS * 64 ) + 512 ) * 1 byte.

Memory Footprint Examples

Unfortunately, there is no simple answer concerning the minimum footprint of a complete GUI application, because it depends on many many different parameter: on your GUI application, your graphical resources, your software design, your business logic, ...

Nevertheless, we can assume a certain UI application and different display resolutions in order to present some realistic memory footprint values.

Let's analyze the memory footprint of a simple UI application containing some widgets, some text objects, some icons and a vector graphics object: The demo application 'WaveformGenerator' is delivered as example in almost every Build Environment.

Memory Footprint Example 1: 320x240 @ RGB565 with Scratch-Pad Buffer

WaveformGenerator example with a size of 320x240 pixel using the RGB565 color format.

Display size: 320x240 pixel

Color format: RGB565 (16 bit)

Framebuffer concept: Double-buffered Scratch-pad buffer

Scratch-pad buffer size: 320x24 (only 1/10 of the display framebuffer)

ModeOfStringConstants: DirectAccess

String cache size: 0

Glyph cache size: 64x64 pixel

ModeOfBitmapResources: DirectAccess

Surface cache size: 0

Instruction buffer size: 40

Based on the above configuration, the resulting data memory (RAM) usage for the GUI application is as following:

Chora Objects, Signals, Observers 15 KB Strings 2 KB Glyph Cache (64 * 64 * 1 Byte) 4 KB Bitmap Resources 0 KB Internal 10 KB ------------------------------------------------------------ Total 30 KB plus Scratch-Pad Buffer 320 * 24 Pixel * 2 Byte 15 KB plus optional Doublebuffer 320 * 24 Pixel * 2 Byte 15 KB ------------------------------------------------------------ Overall 60 KB

The flash memory (ROM) usage of this application is 350 KB (including Graphics Engine, Runtime Environment, FreeRTOS and BSP for the target system).

Memory Footprint Example 2: 480x272 @ RGB565 with one Framebuffer

WaveformGenerator example with a size of 480x272 pixel using the RGB565 color format.

Display size: 480x272 pixel

Color format: RGB565 (16 bit)

Framebuffer concept: One framebuffer (synchronous single-buffer)

Framebuffer size: 480x272 pixel

ModeOfStringConstants: Compressed

String cache size: 0

Glyph cache size: 128x128 pixel

ModeOfBitmapResources: Compressed

Surface cache size: 0

Instruction buffer size: 64

Based on the above configuration, the resulting data memory (RAM) usage for the GUI application is as following:

Chora Objects, Signals, Observers 18 KB Strings 2 KB Glyph Cache (128 * 128 * 1 Byte) 16 KB Bitmap Resources 66 KB Internal 12 KB ------------------------------------------------------------ Total 114 KB plus visible Framebuffer 480 * 272 Pixel * 2 Byte 255 KB ------------------------------------------------------------ Overall 369 KB

The flash memory (ROM) usage of this application is 310 KB (including Graphics Engine, Runtime Environment, FreeRTOS and BSP for the target system).

Memory Footprint Example 3: 800x480 @ RGBA8888 with Double-Buffering

WaveformGenerator example with a size of 800x480 pixel using the RGBA8888 color format.

Display size: 800x480 pixel

Color format: RGBA8888 (32 bit)

Framebuffer concept: Double-buffered Framebuffer

Framebuffer size: 800x480 pixel

ModeOfStringConstants: Compressed

String cache size: 0

Glyph cache size: 256x256 pixel

ModeOfBitmapResources: Compressed

Surface cache size: 0

Instruction buffer size: 100

Based on the above configuration, the resulting data memory (RAM) usage for the GUI application is as following:

Chora Objects, Signals, Observers 18 KB Strings 2 KB Glyph Cache (256 * 256 * 1 Byte) 64 KB Bitmap Resources 100 KB Internal 16 KB ------------------------------------------------------------ Total 200 KB plus visible Framebuffer 800 * 480 Pixel * 4 Byte 1500 KB plus optional Doublebuffer 800 * 480 Pixel * 4 Byte 1500 KB ------------------------------------------------------------ Overall 3200 KB

The flash memory (ROM) usage of this application is 330 KB (including Graphics Engine, Runtime Environment, FreeRTOS and BSP for the target system).