This section presents an overview of emApps, its structure,
and its capabilities.
Getting started
This chapter explains how to get started with emApps by using
simple examples that can be run on the desktop.
Terminology
This document uses the following conventions to avoid confusion:
“on the desktop” means “in a command-line environment
on your computer.” For Windows machines, this is the
Windows command prompt.
“application” refers to the source code or compiled
code of an emApps application that runs in the emApps
virtual machine inside of a sandboxed environment.
“program” refers to the desktop or embedded firmware
that is responsible for launching the execution of
an (emApps) application and provides the sandboxed
environment.
“tool” refers to a precompiled program used to
prepare, trace, execute, profile, or otherwise manipulate
applications on the desktop.
Where to find things
The emApps distribution is divided into a number of folders:
Folder
Content
Doc
emApps documentation including the reference guide
and release notes.
Bin
All tools, including the emApps C Compiler and
the emApps Desktop Executor.
Apps
Source code of demonstration applications that can be
run on the desktop and on a SEGGER Flasher.
Files in this folder act as examples
and can be customized as required.
Src
Source code of the emApps loader and executor. This is
compiled into a program that intends to run emApps
applications. Files in this folder must not be
modified.
Inc
Public header files that define the API provided
by the emApps product to the program. The functions
defined in these headers are implemented by the source
files in the Src and Etc folders. Files in
this folder must not be modified.
Config
Configuration files that customize how the emApps product
is built, selecting both features and capabilities.
Files in this folder are intended to be edited to
configure emApps facilities.
Etc
Additional source code that demonstrates how to provide
features to an application by adding code to the program.
Files in this folder act as examples and can be
customized as required.
Distributed tools
Two tools are provided in the distribution:
The emApps C compiler.
The emApps desktop executor.
The C compiler translates source code, written in C, into
executable code. The emApps desktop executor takes the
executable code, loads it, and then executes it in a
sandbox.
The compiler and executor are shipped in the Bin folder
of the distributions.
Hello, world!
This section will describe how to compile and run the ubiquitous
“Hello, World!” application.
It skips irrelevant details and concentrates on a simple application
running on the desktop as soon as possible.
Get ready
Open up a command-line environment, such as a Windows Command Prompt:
Microsoft Windows [Version 10.0.26200.7171]
(c) Microsoft Corporation. All rights reserved.
C:> _
Add the emApps Bin folder to the search path. For example,
if emApps is installed to C:\Work\SEGGER\emApps, use:
C:> set PATH=C:\Work\SEGGER\emApps\Bin;%PATH%
C:> _
Check that both the emApps C compiler s32cc and emApps
Desktop Executor apprun are found and execute:
C:> s32cc
SEGGER S32 C Compiler V4.16.0 compiled Nov 27 2026 03:39:06
Copyright (c) 2023-2026 SEGGER Microcontroller GmbH www.segger.com
Usage:
s32cc [option...] file
Control:
-v, --verbose Run in verbose mode
...and so on...
Notes:
Use '--extra-help' for more detailed help.
C:> apprun
SEGGER emApps Desktop Executor V1.10.0 compiled Nov 28 2026 14:30:57
Copyright (c) 2008-2026 SEGGER Microcontroller GmbH www.segger.com
Usage:
AppRun [option...] file
Options:
-nfunc Execute 'func' after loading [default: main]
-t Trace instructions executed
-s Trace lines executed
-r Include register dump (before execution) in instruction trace
...and so on...
Streams:
stdout - Application output.
stderr - Trace and message output.
C:> _
Select the application
With that done, navigate to the folder containing the source code of the
demonstration applications directory, Apps\Src:
C:> cd \Work\SEGGER\emApps\Apps\Src
C:> _
In there will be a few demonstration applications:
This is the application’s source code, is known worldwide
to programmers, and can’t be any simpler.
Compile the application
The emApps executor does not run this source code directly.
The human-readable source code must be translated to machine
code for execution by compiling it.
The verbose output from compilation can safely be ignored for
now, it’s good enough to know that the application compiled
without error: 0 errors.
The compiler takes the source code, checks that it is a valid
program, and writes a file containing the machine-executable
code and other structural information to a file with a
.pex file extension, in this case DemoHelloWorld.pex.
The application is now ready to be run as an emApp.
Run the application
Running, or executing, the application requires an emApps
sandbox environment. On the desktop, this is provided by the
emApps Desktop Executor, AppRun.
To execute the application and see what it does, invoke
AppRun and provide the executable-code file DemoHelloWorld.pex:
That’s it. The output is printed along with some additional information.
To hide the program’s output and show only the application’s output,
redirect the standard error output to the NUL device:
Compiling and running other demonstration applications is just
as easy. Try compiling the Dhrystone_100k application and
run it to compare your machine’s performance to a MacBook Pro
(M2 Max) running emApps:
Note: As shipped, the desktop executor supports application telemery,
tracing, and profiling which would not be present in an embedded
production produce. If the program is configured without these
features, execution performance doubles.
The statistic that benchmarks the performance of the S32 in MIPS
is only presented when the application takes more than 100 ms
to complete.
Apps on real hardware
Running an application on the desktop is convenient, but emApps is designed
primarily to execute within an embedded system. Fortunately, SEGGER
has been using the technology behind emApps for years and it is deployed
in Flashers.
Flashers can run these demonstration applications without modification.
To do so, make sure that the
Flasher Software and Documentation Pack,
version 8.90 or later is installed and that the Flasher’s firmware
is up to date—use Flasher Configurator to update the Flasher’s firmware
if required.
The default installation directory for the Flasher software V8.90
on Windows is /Program Files/SEGGER/Flasher_V890. Add this
to the path:
C:> set PATH=C:\Program Files\SEGGER\Flasher_V890;%PATH%
C:> _
Check that the Flasher App Runner utility is found and executes:
Plug in the Flasher and run the “Hello, World!” and Dhrystone applications
on the Flasher using FlasherRun, in just the same way as they are run
on the desktop using AppRun:
C:> flasherrun DemoHelloWorld.pex
SEGGER Flasher App Runner Utility V8.90 (Compiled Nov 26 2026 17:52:51)
Copyright (c) 2026-2026 SEGGER Microcontroller GmbH www.segger.com
DLL version V8.90, compiled Nov 26 2026 17:29:40
Connecting to Flasher via USB...O.K.
Firmware: J-Link / Flasher Compact V7 compiled Nov 26 2026 15:58:58
Hardware version: V7.00
Flasher uptime (since boot): 0d 00h 02m 39s
License(s): JFlash, GDB
USB speed mode: High speed (480 MBit/s)
VTref=0.000V
Successfully loaded '\Work\SEGGER\emApps\Apps\Src\DemoHelloWorld.pex'.
Executing app... (press <Enter> to cancel)
Hello, world!
App execution finished with return value 0 after 0.031ms.
C:> flasherrun DemoDhrystone_100k.pex
SEGGER Flasher App Runner Utility V8.90 (Compiled Nov 26 2026 17:52:51)
Copyright (c) 2026-2026 SEGGER Microcontroller GmbH www.segger.com
DLL version V8.90, compiled Nov 26 2026 17:29:40
Connecting to Flasher via USB...O.K.
Firmware: J-Link / Flasher Compact V7 compiled Nov 26 2026 15:58:58
Hardware version: V7.00
Flasher uptime (since boot): 0d 00h 00m 49s
License(s): JFlash, GDB
USB speed mode: High speed (480 MBit/s)
VTref=0.000V
Successfully loaded '\Work\SEGGER\emApps\Apps\Src\DemoDhrystone_100k.pex'.
Executing app... (press <Enter> to cancel)
30969 Dhrystones/s, 17 DMIPS
App execution finished with return value 0 after 3229.743ms.
C:> _
A brief aside for the technically curious
From using AppRun on the desktop, we know Dhrystone executes 93,300,642
instructions on the S32E virtual CPU. The Flasher accomplishes this in
3,229,743 microseconds. Therefore, the Flasher executes applications
at approximately 30 million instructions per second.
Summary
This has been a brief, simplified tour of emApps that demonstrates
some of its major features. emApps offers more capabilities than
those demonstrated here and the following sections will describe
how to extend the software to expose emApps for use by internal
and external customers.
Running applications
This chapter describes how load and run applications.
Built-in applications
An application would typically be loaded from disk or from a
network connection in order that it can be updated or extended
when new features or bug fixes become available.
Being able to replace the application clearly means it can’t
be compiled into the program as that would mean it’s almost
impossible to replace.
It can be desirable, though, to build an application into the
program so that it is always available. In this way, a hardware
test application compiled into the program is always ready, as
is some system configuration or recovery application, if desired.
Delivering prebuilt applications in this manner is just another
way of using applications and emApps.
Preparing a built-in application
This section explains how to load and run “Hello, World!”
as a built-in application. The program is straightforward,
simple to describe, and easy to understand.
The application’s executable image is already provided as
a built-in application in the file Apps/Src/DemoHelloWorld_pex.h.
The program to load and execute the “Hello, World!” application
is contained in the file Apps/Prj/RunHelloWorld.c.
To generate a built-in application with the emApps C compiler,
use the --embed option. The built-in “Hello, World!”
application is created as follows:
An additional output file that ends _pex.h contains a declaration
of an array initialized to the binary content of the equivalent emApps
executable file:
To prepare loading an application, the application geometry structure
must be initialized. Application geometry is used to locate each section
in the PEX file and is updated as loading progresses. In addition, an
array of service bindings are presented that enumerate the
services available to an application. This will be discussed in detail
later, but for now the table contains a single entry that exposes printf
to be used by the application, and only printf. The geometry
is not needed when it comes to execution so can be disposed of after
loading.
Load the header
After initializing the geometry, the header is presented for verification.
Only the first 32 bytes of the header need be presented: if more are
presented, the excess will be ignored, and if fewer are presented, the
file is invalid and an error is returned.
The value returned from S32_LoadHeader() is negative when an error occurs;
a return value that is zero of greater indicates the number of additional
bytes that must be allocated beyond the application file’s image to run the
application. The exact details of this will be discussed later, and for
the “Hello World!” application this number is zero.
Allocate space to run the application
The memory used to execute the image is entirely allocated by the
program: the emApps library does not use malloc() or free()
internally. This step allocates the space for the application file,
plus any additional requirement reported by S32_LoadHeader().
This example uses malloc(), but equally some other memory-allocation
function could be used, or even a fixed-size static array.
Prepare the execution image
Once memory is allocated for the image, the entire PEX file must be
copied to the start. memcpy() is used here to copy the file
stored in a static array into the memory allocated for execution.
Validate the image
Now that the image is in read-write memory, it can be fully validated
and all references to service functions that it uses can be resolved.
The loader is very careful to examine the structure of the application
file in memory, ensuring that it is not damaged in an unintentional
or malicious manner. To do this, each offset and reference within the
application image is validated to ensure it is entirely contained in
the image.
The loader processes the list of imports in the application and
matches them to the the service bindings given to S32_InitGeo().
If an import is required by the application but is not present
in the API service table, an error is returned by S32_LoadFile()
and the application must not be executed.
Prepare to execute
The image is loaded and validated and passes all integrity checks
and all imports are available. The function to execute, in this
case main() is prepared to run using S32_PrepareByName(). The
arguments to this function are the geometry, an execution context,
and the name of the function to run. The execution context,
which contains the state of the virtual machine, is entirely
initialized by calling S32_PrepareByName() and is not initialized
separately.
If the function cannot be found in the application’s export
list, an error is returned.
Execute the function
If all the previous steps execute correctly, execution can
commence. Execution of the function is started by calling
S32_Exec() with the execution context. The application
runs until the top-level function called returns or an
exception is raised.
Wrap up
Once execution returns to the program, the memory used for the
application can be freed and the value returned from executing
the application’s main() is used as an exit code.
Example run
When compiled and run, this output is:
C:> RunHelloWorld
Hello, world!
*** Application exited with no error (0)
C:> _
Using multiple entry points
All applications presented so far have run to completion and
stopped. This section will present another way of calling
applications, essentially as a library of functions or as a
dynamic-link library, to perform some actions, maintaining
state between each call.
Laying out the application
The following is an application that exports three entry points:
Init(), Main(), and Fini():
The compiler must be told which functions to export as entry
points. The entry points are published using #pragma S32C export
which takes the name of the function to export. The pragma will
accept function names with or without quotation marks according to
preference, and will accept a comma-separated list of such names.
Declare static data
Any data declared with static storage class will retain
values over the lifetime of the application. In this application,
Data will be updated by successive entry points and its
value displayed prior to update.
Display current data on each entry
Each entry point displays the value of Data immediately
on entry.
Update the state
Each entry point then alters the value of Data as part of
its execution.
A preview of expected output
The code to call the three entry points is presented in the following
section. Concentrating on the application, if the three functions
Init(), Main(), and Fini() are called in that order
by C code, the expected output is:
In Init(), Data=10
In Main(), Data=11
In Fini(), Data=13
Similarly, if those three emApps entry points are executed from a
program, the output will be identical: static data retains its
state between executions.
Executing entry points
This section demonstrates how to call the three entry points
in sequence. The application’s executable image is already provided
as a built-in application in the file Apps/Src/DemoMultiEntry_pex.h.
The program to load and execute the application is contained in the
file Apps/Prj/RunMultiEntry.c.
Loading the application follows the same steps as the “Hello, World!”
application. Once loaded, the multi-entry application can be queried
to expose one, some, or all of its entry points. The presence
of a single named entry point can be queried using S32_FindExport()
and prepared for execution using S32_PrepareByAddr():
Use S32_FindExport() to find the entry point, passing the
application geometry and the entry point name. The entry
point is the name of the exported function without parentheses.
In this case, three entry points are looked up. The
application will call Init() if it exists, followed by
Main() if it exists, followed by Fini() if it exists.
Check if Init() exists
If the entry point for Init() does not exist or the executable
image has been corrupted in a detectable manner, an negative error
status is returned. This will be either S32_ERROR_NOT_FOUND
or S32_ERROR_BAD_PEX_FILE.
Prepare for execution
Once the entry point is determined, it can be prepared for
execution using S32_PrepareByAddr(). In normal operation,
the address returned from S32_FindExport() will be valid and
S32_PrepareByAddr() will also succeed with this address, but
S32_PrepareByAddr() checks the address for validity anyway
to ensure that the application is prepared correctly.
Execute Init()
After preparation is complete, the application is ready to
launch, so it can be started using S32_Exec().
Abbreviated execution of Main()
This fragment of code checks that Main() exists as
an entry point, and then proceeds to prepare for execution
and executes it. It does not check that preparation succeeds
as, if preparation fails, an exceptional condition is
registered in the execution context and any attempt to
execute will immediately fail.
Minimal execution of Fini()
This fragment of code takes brevity even further: any negative
value is never a valid address, so S32_PrepareByAddr() will
immediately fail, and so will S32_Exec(). Of course, it is
better programming style to check for errors when they occur,
this is simply an example demonstrating that detected errors
do not cause erratic execution of random application code.
Passing parameters to functions
All applications presented so far have executed functions
that take no parameters and return no result. This section
describes how to pass parameters to functions and how to
retrieve the value returned by a function. The application’s
executable image is already provided as a built-in application
in the file Apps/Src/DemoParaPassing_pex.h.
The program to load and execute the application is contained in the
file Apps/Prj/RunParaPassing.c.
Laying out the application
The following application exports a function that computes
the y coordinate of a straight line given the x
coordinate, the slope of the line m, and its y
intercept c:
The function CalcY requires three integer parameters, x,
m, and c and returns an integer result. Preparing to
call this function is straightforward, it uses the same framework
to determine the application geometry and prepare a function for
execution presented before. Once prepared, the parameters are
pushed to the stack before execution commences. The following
code calls the function passing three parameters and retrieves
the function result.
After preparing a function for execution, the parameters are pushed
to the stack in reverse order, which is the standard convention for
the C programming language. The function is invoked with x=7,
m=3, and c=10.
Execute the function
After pushing the parameters, the function is executed. The result
of execution is returned—this is not the result of the function,
but a standard status code indication successful or exceptional
execution.
Proceed only if not exceptional
If execution of the function completed without raising an exception,
the value returned by S32_Exec() is nonnegative. If the function
completed normally, execution continues to retrieve the function
result.
Retrieve function result
The function result is returned by S32_GetResult(). This is the value
that is returned by the function after normal execution: if the execution
is terminated by an exception, the value returned by S32_GetResult() is
not defined.
In this case, the value returned is 31 which is 3×7 + 10.
Finding and executing services at runtime
Direct references made to functions that the application uses,
offered by the program through the populated API service table,
are resolved when an application is loaded. If the named service
is not present, loading fails with a “not found” error.
It may be convenient to dynamically determine, when the application
is running, whether a service is offered by the program or not.
This can be used to introduce updated product models that offer
better performance by moving compute-heavy code into the program,
but otherwise continue to work on older harware, whilst the
application’s executable is the same across all models. Or it
might be used to run the same application on base models and
more featureful models, adapting behavior as necessary.
Preparing for service lookup
The service lookup feature is exposed to the application
developer through Default.h as follows:
The following program uses the introspection feature to see
whether printf() is an installed service and, if it is,
calls it to print a message.
This program is contained in the file Apps/Src/DemoFindService.c.
Service functions that are looked up at runtime are not invoked
by calling through a function pointer. Instead, a handle
is used that refers to the particular service. This declares
such a handle.
Look up the service function
The function S32_FindSevice() takes a service function name,
without parentheses, and returns a handle that identifies the
service. If the service is not found, the handle is a negative
error code.
Execute the service
As explained above, the service is not invoked through a function
pointer, but by S32_ExecService() which calls the particular
service, passing through the provided arguments. Note that it is the
user’s responsibility to provide the correct number of arguments,
each of of the correct type, to match the prototype of the invoked
service function.
Bechmarking S32 against native code
The following application benchmarks strcpy() written in
S32 instructions against a version provided by the program,
if such a service function exists.
This program is contained in the file Apps/Src/DemoBenchStrcpy.c.
charaDst[8192];charaSrc[8192];intmain(void){unsignedT;intStrcpyHandle;inti;//StrcpyHandle =SYS_FindService("strcpy");//if(StrcpyHandle >0){//printf("strcpy() is present as a service function\n\n");printf("Benchmarking:\n\n");//memset(aSrc,'x',8191);//T =SYS_GetTime_ms();for(i =0;i <2000;++i){strcpy(aDst,aSrc);}T =SYS_GetTime_ms()-T;printf(" strcpy() using S32 code: %5d ms\n",T);//T =SYS_GetTime_ms();for(i =0;i <2000;++i){SYS_ExecService(StrcpyHandle,aDst,aSrc);}T =SYS_GetTime_ms()-T;printf(" strcpy() using service: %5d ms\n\n",T);printf("Done\n\n");//}else{printf("strcpy() is not present as a service.");}return0;}
Running it shows the relative performance:
C:> apprun DemoBenchStrcpy.pex
SEGGER emApps Desktop Executor V1.10.0 compiled Dec 17 2026 22:33:12
Copyright (c) 2008-2026 SEGGER Microcontroller GmbH www.segger.com
strcpy() is present as a service function
Benchmarking:
strcpy() using S32 code: 494 ms
strcpy() using service: 10 ms
Done
Execution complete:
98354052 instructions executed in 504.835 ms.
S32 benchmarks at 194.824 MIPS.
main() returned 0.
C:> _
Running applications on the desktop
The desktop application executor that is used throughout this
manual is provided in source code in Tools/AppRun/Src/AppRun.cpp.
This can be customized, for instance to emulate the system that
the application will eventually be installed on, and to start
developing applications before even prototype hardware is available.
AppRun entire listing
/********************************************************************** (c) SEGGER Microcontroller GmbH ** The Embedded Experts ** www.segger.com ***********************************************************************-------------------------- END-OF-HEADER -----------------------------Purpose : S32 sample application executor.*//*********************************************************************** #include section************************************************************************/#include<string>#include<cstring>#include<vector>#include<list>#include<set>#include<map>#include<chrono>#include<filesystem>#include<cstdarg>#include"S32.h"/*********************************************************************** Defines, configurable************************************************************************///// Coarse-grained inclusion of service functions.//#if!defined(CONFIG_API_C)#defineCONFIG_API_C 1#endif#if!defined(CONFIG_API_UTIL)#defineCONFIG_API_UTIL 1#endif/*********************************************************************** Conditional includes************************************************************************/#ifCONFIG_API_C#include"S32_API_C.h"#endif#ifCONFIG_API_UTIL#include"S32_API_Util.h"#endif/*********************************************************************** Prototypes************************************************************************/[[noreturn]]staticvoid_Die(constchar*sFormat,...);/*********************************************************************** Data types************************************************************************/structTELEMETRY_DATA {S32_U32PrecisePC;S32_U64InsnCnt;std::chrono::high_resolution_clock::time_point ServiceTimestamp;std::chrono::high_resolution_clock::duration ServiceTime;std::chrono::high_resolution_clock::duration ServiceMinDuration;std::chrono::high_resolution_clock::duration ServiceMaxDuration;S32_U32ServiceCnt;std::chrono::high_resolution_clock::time_point ExecTimestamp;std::chrono::high_resolution_clock::duration ExecTime;S32_U32ExecCnt;TELEMETRY_DATA(){ServiceMinDuration =ServiceMinDuration.max();ServiceMaxDuration =ServiceMaxDuration.min();ExecTimestamp =ExecTimestamp.min();ServiceTimestamp =ServiceTimestamp.min();ExecTime =ExecTime.zero();ServiceTime =ServiceTime.zero();PrecisePC =0;InsnCnt =0;ServiceCnt =0;ExecCnt =0;}};/*********************************************************************** Prototypes************************************************************************/staticvoid_Telemetry_ExcpDetect (S32_EXEC_CONTEXT*pCtx);staticvoid_Telemetry_InsnBegin_Full (S32_EXEC_CONTEXT*pCtx);staticvoid_Telemetry_InsnBegin_Time (S32_EXEC_CONTEXT*pCtx);staticvoid_Telemetry_ExecBegin (S32_EXEC_CONTEXT*pCtx);staticvoid_Telemetry_ExecEnd (S32_EXEC_CONTEXT*pCtx);staticvoid_Telemetry_ServiceBegin (S32_EXEC_CONTEXT*pCtx);staticvoid_Telemetry_ServiceEnd (S32_EXEC_CONTEXT*pCtx);/*********************************************************************** Static const data************************************************************************/staticconstS32_TELEMETRY_API_FullTelemetry ={_Telemetry_ExcpDetect,_Telemetry_InsnBegin_Full,nullptr,_Telemetry_ServiceBegin,_Telemetry_ServiceEnd,_Telemetry_ExecBegin,_Telemetry_ExecEnd,};staticconstS32_TELEMETRY_API_TimeTelemetry ={nullptr,_Telemetry_InsnBegin_Time,nullptr,_Telemetry_ServiceBegin,_Telemetry_ServiceEnd,_Telemetry_ExecBegin,_Telemetry_ExecEnd,};staticconstS32_TELEMETRY_API_StandardTelemetry ={nullptr,nullptr,nullptr,nullptr,nullptr,_Telemetry_ExecBegin,_Telemetry_ExecEnd,};staticstd::vector<S32_SERVICE_BINDING>_aServices{{"printf",S32_API_C_printf},{"__S32_udiv",S32_API_S32_udiv},{"__S32_umod",S32_API_S32_umod},{"__S32_idiv",S32_API_S32_idiv},{"__S32_imod",S32_API_S32_imod},{"SYS_FindService",S32_API_S32_FindService},{"SYS_ExecService",S32_API_S32_ExecService},#ifCONFIG_API_C{"memset",S32_API_C_memset},{"memcpy",S32_API_C_memcpy},{"memmove",S32_API_C_memmove},{"memchr",S32_API_C_memchr},{"strcpy",S32_API_C_strcpy},{"strchr",S32_API_C_strchr},{"strcmp",S32_API_C_strcmp},{"strlen",S32_API_C_strlen},{"puts",S32_API_C_puts},{"printf",S32_API_C_printf},{"sprintf",S32_API_C_sprintf},{"snprintf",S32_API_C_snprintf},#endif#ifCONFIG_API_UTIL{"UTIL_MulDiv",S32_API_Util_MulDiv},{"SYS_GetTime_ms",S32_API_Util_GetTime_ms},{"SYS_GetTime_us",S32_API_Util_GetTime_us},{"SYS_Sleep_us",S32_API_Util_Sleep_us},{"SYS_Sleep_ns",S32_API_Util_Sleep_ns},{"FOpen",S32_API_Util_FOpen},{"FClose",S32_API_Util_FClose},{"FRead",S32_API_Util_FRead},{"FWrite",S32_API_Util_FWrite},#endif};/*********************************************************************** Static data************************************************************************/staticstd::map<unsigned,std::string>InsnMap;staticstd::map<unsigned,std::string>CodeMap;staticstd::list<std::string>ListFile;staticstd::string LstName;staticstd::string PrfName;staticstd::string PexName;staticstd::map<unsigned,unsigned>InsnCntMap;staticstd::set<std::string>Imports;staticstd::set<std::string>MissingImports;staticstd::string MainName;staticS32_U8*pPexImage;staticlongPexImageLen;staticboolTraceInsns;// Print instruction tracestaticboolTraceRegs;// Print registers with tracestaticboolTraceSource;// Print source line associated with instructionstaticboolTime;// Time the applicationstaticboolFast;// Run application without telemetrystaticboolProfile;// Write instruction profile filestaticboolPrintAPI;// Print application API entriesstaticboolPrintExports;// Print exported functions, do not runstaticboolPrintImports;// Print exported functions, do not runstaticboolPrintMissing;// Print imported functions missing from exposed APIstaticboolVerbose;// Print configuration informationstaticTELEMETRY_DATA Telemetry;/*********************************************************************** Static code************************************************************************//*********************************************************************** _Die()** Function description* Die fatally.** Parameters* sFormat - Format string.*/staticvoid_Die(constchar*sFormat,...){va_list ap;//va_start(ap,sFormat);fprintf(stderr,"fatal: ");vfprintf(stderr,sFormat,ap);va_end(ap);fprintf(stderr,"\n");exit(EXIT_FAILURE);}/*********************************************************************** _IsHex()** Function description* String contains all hexadecimal digit?** Parameters* Str - String to test.** Return value* True if only valid hexadecimal digits.*/staticbool_IsHex(conststd::string &Str){for(autoc :Str){if('0'<=c &&c <='9'){/* Pass */}elseif('a'<=c &&c <='f'){/* Pass */}elseif('A'<=c &&c <='F'){/* Pass */}else{returnfalse;}}returntrue;}/*********************************************************************** _DeHex()** Function description* Convert ASCII hexadecimal digit to binary.** Parameters* c - Character to convert.** Return value* Decoded value.*/staticint_DeHex(charc){if('0'<=c &&c <='9'){returnc -'0';}elseif('a'<=c &&c <='f'){returnc -'a'+10;}elseif('A'<=c &&c <='F'){returnc -'A'+10;}else{return-1;}}/*********************************************************************** _IsExecLine()** Function description* Does line correspond to executable content?** Parameters* Text - Line from listing.** Return value* True if the line corresponds to executable content.*/staticbool_IsExecLine(conststd::string &Text){returnText.length()>13&&_IsHex(Text.substr(0,6))&&Text[6]==' '&&Text[7]==' '&&_IsHex(Text.substr(8,4))&&Text[12]==' ';}/*********************************************************************** _LineLC()** Function description* Decode line's location counter.** Parameters* Text - Line from listing.** Return value* Location counter.*/staticunsigned_LineLC(conststd::string &Text){return_DeHex(Text[0])*0x100000+_DeHex(Text[1])*0x10000+_DeHex(Text[2])*0x1000+_DeHex(Text[3])*0x100+_DeHex(Text[4])*0x10+_DeHex(Text[5])*0x1;}/*********************************************************************** _DeriveFileNames()** Function description* Construct listing and profile file names.** Parameters* FileName - File name of PEX file.*/staticvoid_DeriveFileNames(conststd::string &FileName){LstName =std::filesystem::path(FileName).replace_extension(".lst").string();PrfName =std::filesystem::path(FileName).replace_extension().string()+"_pro.txt";}/*********************************************************************** _RdPexFile()** Function description* Read PEX file into memory in binary mode.** Parameters* FileName - File name of PEX file.*/staticvoid_RdPexFile(conststd::string &FileName){FILE *pPexFile;//pPexFile =fopen(FileName.c_str(),"rb");if(pPexFile ==NULL){_Die("cannot open '%s' for reading",FileName.c_str());}//fseek(pPexFile,0,SEEK_END);PexImageLen =ftell(pPexFile);fseek(pPexFile,0,SEEK_SET);//pPexImage =newS32_U8[PexImageLen];fread(pPexImage,1,PexImageLen,pPexFile);//fclose(pPexFile);}/*********************************************************************** _RdLstFile()** Function description* Read and parse S32 compiler listing file.** Parameters* FileName - File name of listing file.*/staticvoid_RdLstFile(conststd::string &FileName){std::string CodeLine;//FILE *pLstFile =fopen(FileName.c_str(),"r");if(pLstFile ==NULL){_Die("cannot open '%s' for reading",FileName.c_str());}//for(;;){std::string Line;intc;//if(feof(pLstFile)){break;}for(;;){c =fgetc(pLstFile);if(c =='\n'||c ==EOF){break;}Line +=c;}ListFile.push_back(Line);//if(Line.length()>=6&&Line[5]==':'){//// C source line.//CodeLine =Line;}elseif(_IsExecLine(Line)){//// Assembly language line.//InsnMap[_LineLC(Line)]=Line;CodeMap[_LineLC(Line)]=CodeLine;}}}/*********************************************************************** _VisitImport()** Function description* Visitor callback to collect imported function information.** Parameters* pGeo - Pointer to application geometry.* sName - Exported function name.* Index - Imported function index.* pUserCtx - Pointer to user-supplied context.** Return value* S32_ERROR_NONE, always succeeds.*/staticS32_I32_VisitImport(S32_EXEC_GEO*pGeo,constchar*sName,unsignedIndex,void*pUserCtx){boolPresent =false;//Imports.insert(sName);//for(unsignedi =0;!Present &&i <_aServices.size();++i){Present =strcmp(_aServices[i].sName,sName)==0;}//if(!Present){MissingImports.insert(sName);}//returnS32_ERROR_NONE;}/*********************************************************************** _PrMissing()** Function description* Print imported functions missing from API table.** Parameters* pGeo - Pointer to application geometry.*/staticvoid_PrMissing(S32_EXEC_GEO*pGeo){S32_IterateImports(pGeo,_VisitImport,NULL);fprintf(stderr,"Functions imported by application but missing from API:\n");if(MissingImports.empty()){fprintf(stderr," None\n");}else{for(auto&Name :MissingImports){fprintf(stderr," %s()\n",Name.c_str());}}}/*********************************************************************** _VisitExport()** Function description* Visitor callback to print exported function information.** Parameters* pGeo - Pointer to application geometry.* pInfo - Pointer to export descriptor.* pUserCtx - Pointer to user-provided context.** Return value* S32_ERROR_NONE, always succeeds.*/staticS32_I32_VisitExport(S32_EXEC_GEO*pGeo,S32_EXPORT_INFO*pInfo,void*pUserCtx){fprintf(stderr," %s(), %u parameters, entry point 0x%04X\n",pInfo->sName,pInfo->ParaCnt,pInfo->Addr);returnS32_ERROR_NONE;}/*********************************************************************** _PrExports()** Function description* Print functions exported by the application.** Parameters* pGeo - Pointer to application geometry.*/staticvoid_PrExports(S32_EXEC_GEO*pGeo){fprintf(stderr,"Functions exported by application:\n");S32_IterateExports(pGeo,_VisitExport,NULL);}/*********************************************************************** _PrImports()** Function description* Print functions imported by the application.** Parameters* pGeo - Pointer to application geometry.*/staticvoid_PrImports(S32_EXEC_GEO*pGeo){S32_IterateImports(pGeo,_VisitImport,NULL);fprintf(stderr,"Functions imported by application:\n");for(auto&Name :Imports){fprintf(stderr," %s()\n",Name.c_str());}}/*********************************************************************** _PrAPI()** Function description* Print functions exported by emApps API.*/staticvoid_PrAPI(){fprintf(stderr,"Functions exported by the API:\n");for(autoAPI :_aServices){fprintf(stderr," %s()\n",API.sName);}}/*********************************************************************** _WrPrfFile()** Function description* Write profile to disk.*/staticvoid_WrPrfFile(){FILE *pPrfFile =fopen(PrfName.c_str(),"w");for(autoLine :ListFile){if(_IsExecLine(Line)){fprintf(pPrfFile,"%7d %s",InsnCntMap[_LineLC(Line)],Line.c_str());}else{fprintf(pPrfFile," %s",Line.c_str());}}fclose(pPrfFile);}/*********************************************************************** _ShowUsage()** Function description* Print help usage.*/staticvoid_ShowUsage(){fprintf(stderr,"Usage:\n");fprintf(stderr," AppRun [option...] file\n");fprintf(stderr,"\n");fprintf(stderr,"Options:\n");fprintf(stderr," -nfunc Execute 'func' after loading [default: main]\n");fprintf(stderr," -t Trace instructions executed\n");fprintf(stderr," -s Trace lines executed\n");fprintf(stderr," -r Include register dump (before execution) in instruction trace\n");fprintf(stderr," -f Run fast with no telemetry\n");fprintf(stderr," -p Write instruction profile file\n");fprintf(stderr," -e List functions provided by emApps API\n");fprintf(stderr," -i List application's imported functions\n");fprintf(stderr," -x List application's exported functions\n");fprintf(stderr," -m List application's imported functions missing from exposed API\n");fprintf(stderr," -a List application's imported, exported, and missing functions\n");fprintf(stderr," -?, --help Print help information\n");fprintf(stderr,"\n");fprintf(stderr,"Streams:\n");fprintf(stderr," stdout - Application output.\n");fprintf(stderr," stderr - Trace and message output.\n");exit(EXIT_SUCCESS);}/*********************************************************************** _Telemetry_ExcpDetect()** Function description* Entry point for exception trace trace.** Parameters* pCtx - Pointer to execution context.*/staticvoid_Telemetry_ExcpDetect(S32_EXEC_CONTEXT*pCtx){//intException;//// Break raised for function termination?//Exception =S32_RdXR(pCtx);if(Exception ==S32_ERROR_BRK &&S32_RdPC(pCtx)==4){return;}//fprintf(stderr,"Exception %d raised (%s)\n",Exception,S32_GetErrorText(Exception));fprintf(stderr,"\n");//fprintf(stderr,"PC: %08X (imprecise)\n",S32_RdPC(pCtx));fprintf(stderr,"PC: %08X (precise)\n",Telemetry.PrecisePC);fprintf(stderr,"ML: %08X\n",S32_RdML(pCtx));//for(unsignedRn =0;Rn <16;++Rn){if(Rn %8==0){fprintf(stderr,"R%u:",Rn);}fprintf(stderr," %08X",S32_RdReg(pCtx,Rn));if(Rn %8==7){fprintf(stderr,"\n");}}fprintf(stderr,"\n");}/*********************************************************************** _Telemetry_InsnBegin_Full()** Function description* Entry point for per-instruction trace.** Parameters* pCtx - Pointer to execution context.*/staticvoid_Telemetry_InsnBegin_Full(S32_EXEC_CONTEXT*pCtx){staticstd::string LastCodeLine;//Telemetry.PrecisePC =S32_RdPC(pCtx);// Register precise PC if an exception is raisedTelemetry.InsnCnt +=1;//if(TraceSource ||TraceInsns ||Profile){S32_U32PC =S32_RdPC(pCtx);if(PC ==4){// BRK on RET from top-level function}elseif(InsnMap.find(PC)==InsnMap.end()){S32_Raise(pCtx,S32_ERROR_ACC_VIOLATION);}else{InsnCntMap[PC]+=1;if(TraceSource){if(LastCodeLine !=CodeMap[PC]){LastCodeLine =CodeMap[PC];if(!LastCodeLine.empty()){if(TraceInsns){fprintf(stderr,"\n");}fprintf(stderr,"%s\n",LastCodeLine.c_str());if(TraceInsns){fprintf(stderr,"\n");}}}}if(TraceInsns){std::string Txt =InsnMap[PC];if(Txt.find(';')!=std::string::npos){Txt =Txt.substr(0,Txt.find(';'));}if(TraceRegs){while(Txt.size()<60){Txt +=' ';}fprintf(stderr,"%s ",Txt.c_str());for(inti =0;i <16;++i){fprintf(stderr,"%08X",S32_RdReg(pCtx,i));if(i %8==3){fprintf(stderr," ");}elseif(i %8==7){fprintf(stderr," ");}else{fprintf(stderr," ");}}}else{while(!Txt.empty()&&Txt.substr(Txt.length()-1,1)==" "){Txt =Txt.substr(0,Txt.length()-1);}fprintf(stderr,"%s",Txt.c_str());}fprintf(stderr,"\n");}}}}/*********************************************************************** _Telemetry_InsnBegin_Time()** Function description* Entry point to collect executed instruction count.** Parameters* pCtx - Pointer to execution context.*/staticvoid_Telemetry_InsnBegin_Time(S32_EXEC_CONTEXT*pCtx){Telemetry.InsnCnt +=1;}/*********************************************************************** _Telemetry_ServiceBegin()** Function description* "Begin Service" telemetry point.** Parameters* pCtx - Pointer to execution context.*/staticvoid_Telemetry_ServiceBegin(S32_EXEC_CONTEXT*pCtx){Telemetry.ServiceCnt +=1;Telemetry.ServiceTimestamp =std::chrono::high_resolution_clock::now();}/*********************************************************************** _Telemetry_ServiceEnd()** Function description* "End Service" telemetry point.** Parameters* pCtx - Pointer to execution context.*/staticvoid_Telemetry_ServiceEnd(S32_EXEC_CONTEXT*pCtx){autoDuration =std::chrono::high_resolution_clock::now()-Telemetry.ServiceTimestamp;Telemetry.ServiceTime +=Duration;//// Don't register ultra-small service calls.//if(Duration.count()!=0){Telemetry.ServiceMinDuration =std::min(Telemetry.ServiceMinDuration,Duration);}Telemetry.ServiceMaxDuration =std::max(Telemetry.ServiceMaxDuration,Duration);}/*********************************************************************** _Telemetry_ExecBegin()** Function description* "Begin Exec" telemetry point.** Parameters* pCtx - Pointer to execution context.*/staticvoid_Telemetry_ExecBegin(S32_EXEC_CONTEXT*pCtx){Telemetry.ExecCnt +=1;Telemetry.ExecTimestamp =std::chrono::high_resolution_clock::now();}/*********************************************************************** _Telemetry_ExecEnd()** Function description* "End Exec" telemetry point.** Parameters* pCtx - Pointer to execution context.*/staticvoid_Telemetry_ExecEnd(S32_EXEC_CONTEXT*pCtx){Telemetry.ExecTime +=std::chrono::high_resolution_clock::now()-Telemetry.ExecTimestamp;}staticstd::string _FormatAsSeconds(S32_U64Ns){characBuf[32];//sprintf(acBuf,"%5lld.%03lld %03lld %03lld",Ns /1000000000uLL,Ns /1000000uL%1000u,Ns /1000u%1000u,Ns %1000u);returnacBuf;}staticstd::string _FormatWithPeriod(S32_U64Value){std::string Text;characBuf[32];while(Value >=1000){sprintf(acBuf,"%03lld",Value %1000);if(!Text.empty()){Text =" "+Text;}Text =acBuf +Text;Value /=1000;}//sprintf(acBuf,"%lld",Value);if(!Text.empty()){Text =" "+Text;}returnacBuf +Text;}/*********************************************************************** Public code************************************************************************//*********************************************************************** main()** Function description* Main function.** Parameters* argc - Argument count.* argv - Argument vector.** Return value* Exit code.*/intmain(intargc,constchar**argv){S32_EXEC_GEOGeo;S32_EXEC_CONTEXTCtx;S32_EXPORT_INFOInfo;boolNewline;S32_U8*pExecImage;intLoadStatus;intExecStatus;//MainName ="main";//fprintf(stderr,"\n");fprintf(stderr,"SEGGER emApps Desktop Executor V%s compiled "__DATE__" "__TIME__"\n",S32_GetVersionText());fprintf(stderr,"%s www.segger.com\n",S32_GetCopyrightText());fprintf(stderr,"\n");//for(inti =1;i <argc;++i){std::string Arg =argv[i];//if(Arg =="-t"){TraceInsns =true;}elseif(Arg =="-r"){TraceInsns =true;TraceRegs =true;}elseif(Arg =="-s"){TraceSource =true;}elseif(Arg =="-p"){Profile =true;}elseif(Arg =="-m"){Time =true;}elseif(Arg =="-f"){Fast =true;}elseif(Arg =="-e"){PrintAPI =true;}elseif(Arg =="-x"){PrintExports =true;}elseif(Arg =="-i"){PrintImports =true;}elseif(Arg =="-m"){PrintMissing =true;}elseif(Arg =="-a"){PrintImports =true;PrintExports =true;PrintMissing =true;}elseif(Arg =="-v"){Verbose =true;}elseif(Arg =="-n"){MainName ="main";}elseif(Arg =="-?"){_ShowUsage();}elseif(Arg =="--help"){_ShowUsage();}elseif(Arg.substr(0,2)=="-n"){MainName =Arg.substr(2);}elseif(Arg.substr(0,1)=="-"){_Die("unrecognized option '%s'",Arg.c_str());}elseif(!PexName.empty()){_Die("cannot execute multiple applications");}else{PexName =Arg;}}//if(PrintAPI &&PexName.empty()){_PrAPI();exit(EXIT_SUCCESS);}//if(PexName.empty()){_ShowUsage();}//if(Verbose){fprintf(stderr,"Sandbox: %s\n",S32_CONFIG_SANDBOX?"Enabled":"Disabled");fprintf(stderr,"\n");}//_DeriveFileNames(PexName);_RdPexFile(PexName);if(TraceSource ||TraceInsns ||Profile){_RdLstFile(LstName);}//LoadStatus =S32_InitGeo(&Geo,_aServices.data(),(unsigned)_aServices.size());if(LoadStatus <0){fprintf(stderr,"Initialization failed: %s\n",S32_GetErrorText(LoadStatus));returnLoadStatus;}//LoadStatus =S32_LoadHeader(&Geo,pPexImage,PexImageLen);if(LoadStatus >=0){//// Create an execution image for the application.//pExecImage =newS32_U8[PexImageLen +LoadStatus];memset(pExecImage,0,PexImageLen +LoadStatus);// Fill stack and xdata, not strictly necessary...memcpy(pExecImage,pPexImage,PexImageLen);//LoadStatus =S32_LoadFile(&Geo,pExecImage);if(LoadStatus >=0){Newline =false;if(PrintAPI){_PrAPI();Newline =true;}if(PrintImports){if(Newline){fprintf(stderr,"\n");}_PrImports(&Geo);Newline =true;}if(PrintExports){if(Newline){fprintf(stderr,"\n");}_PrExports(&Geo);Newline =true;}if(PrintMissing){if(Newline){fprintf(stderr,"\n");}_PrMissing(&Geo);Newline =true;}if(Newline){exit(EXIT_SUCCESS);}//LoadStatus =S32_FindExportEx(&Geo,MainName.c_str(),&Info);if(LoadStatus <0){fprintf(stderr,"fatal: function '%s()' is not exported\n",MainName.c_str());fprintf(stderr,"\n");_PrExports(&Geo);exit(EXIT_FAILURE);}else{LoadStatus =S32_PrepareByName(&Geo,&Ctx,MainName.c_str());if(LoadStatus ==0){if(TraceInsns ||TraceSource){LoadStatus =S32_SetTelemetryAPI(&Ctx,&_FullTelemetry);}elseif(Time){LoadStatus =S32_SetTelemetryAPI(&Ctx,&_TimeTelemetry);}elseif(!Fast){LoadStatus =S32_SetTelemetryAPI(&Ctx,&_StandardTelemetry);}if(LoadStatus !=0){_Die("problem activating telemetry: '%s'",S32_GetErrorText(LoadStatus));}ExecStatus =S32_Exec(&Ctx);}elseif(LoadStatus >0){_Die("unsupported: '%s' declared with %d arguments",MainName.c_str(),LoadStatus);}}}}//if(LoadStatus <0){fprintf(stderr,"Loading failed\n");fprintf(stderr," %d returned - %s.\n",LoadStatus,S32_GetErrorText(LoadStatus));//if(LoadStatus ==S32_ERROR_NOT_FOUND){fprintf(stderr,"\n");_PrMissing(&Geo);}returnLoadStatus;}else{//fprintf(stderr,"\n");fprintf(stderr,"Execution complete:\n");if(Profile){_WrPrfFile();fprintf(stderr," Instruction profile written to '%s'.\n",PrfName.c_str());}//if(Telemetry.InsnCnt >0){autoExecNs =std::chrono::duration_cast<std::chrono::nanoseconds>(Telemetry.ExecTime).count();autoSvcNs =std::chrono::duration_cast<std::chrono::nanoseconds>(Telemetry.ServiceTime).count();autoMinSvcNs =std::chrono::duration_cast<std::chrono::nanoseconds>(Telemetry.ServiceMinDuration).count();autoMaxSvcNs =std::chrono::duration_cast<std::chrono::nanoseconds>(Telemetry.ServiceMaxDuration).count();autoClockRes =(double)std::chrono::high_resolution_clock::period::num/std::chrono::high_resolution_clock::period::den*1.e9;//fprintf(stderr,"\n");fprintf(stderr," Summary:\n");fprintf(stderr," Total elapsed time: %17s s\n",_FormatAsSeconds(ExecNs).c_str());fprintf(stderr," Clock resolution: %17s s (reported)\n",_FormatAsSeconds(ClockRes).c_str());fprintf(stderr," Execution:\n");fprintf(stderr," Calls made: %17s completed\n",_FormatWithPeriod(Telemetry.ExecCnt).c_str());fprintf(stderr," Total instructions: %17s executed\n",_FormatWithPeriod(Telemetry.InsnCnt).c_str());fprintf(stderr," Total time: %17s s\n",_FormatAsSeconds(ExecNs -SvcNs).c_str());fprintf(stderr," Services:\n");fprintf(stderr," Calls made: %17s completed\n",_FormatWithPeriod(Telemetry.ServiceCnt).c_str());fprintf(stderr," Total time: %17s s\n",_FormatAsSeconds(SvcNs).c_str());fprintf(stderr," Min duration: %17s s\n",_FormatAsSeconds(MinSvcNs).c_str());fprintf(stderr," Max duration: %17s s\n",_FormatAsSeconds(MaxSvcNs).c_str());if(ExecNs >100000){fprintf(stderr," Performance:\n");fprintf(stderr," Benchmark: %17.3f MIPS.\n",1000.*Telemetry.InsnCnt /ExecNs);}fprintf(stderr,"\n");}//if(ExecStatus ==S32_ERROR_NONE){fprintf(stderr," 'main()' returned %d.\n",(S32_I32)S32_RdReg(&Ctx,S32_REG_R0));return0;}else{fprintf(stderr," exception %d raised - %s.\n",ExecStatus,S32_GetErrorText(ExecStatus));returnExecStatus;}}}/*************************** End of file ****************************/
Configuring emApps
This section describes how to configure the emApps module when
integrating it into a program.
The configuration file
emApps is completely configured entirely by editing the file
Config/S32_Conf.h. It defines some preprocessor macros that
configures the executor to use, how memory is accessed, what
features the executor provides.
Configuration options have defaults, set using by the
fileInc/S32_ConfDefaults.h. This file must not
be edited configure emApps, all configuration must be
accomplished by editing Config/S32_Conf.h to avoid
problems when contacting SEGGER support.
These must be configured to exactly match the number
of bits required. The defaults are generally good for
32-bit architectures. These are not configured to the
“intx_t” and “uintx_t” types from <stdint.h> as the ISO
standard makes no guarantee that such types exist.
Sandbox memory access
Description
These macros read and write data to the sandbox.
They can be configured for architectures that are big-endian,
that fault on misaligned accesses, or both.
Mark the parameter X as used in order to avoid ’unused parameter’
warning messages from a compiler. It usually suffices to use
the standard idiom of casting the parameter to void.
S32_INLINE
Description
Control explicit declaration of an inline function.
This symbol expands to a token sequence that declares
a function inline. If the compiler used to compile emApps
does not support inline, defined S32_INLINE to expand to
the empty symbol sequence.
These macro is used to set memory to a value. By default
it is configured to use a locally provided memory-set
function rather than the C library function memset().
These macro is used to copy memory. By default it
is configured to use a locally provided memory-copy
function rather than the C library function memcpy().
These macro is used to compare strings. By default it
is configured to use a locally provided string-compare
function rather than the C library function memcpy().
Incorrect configuration of data types, context offsets, or service table.
S32_ERROR_UNIMP_FUNC
Unimplemented function.
S32_ERROR_UNSUP_INSN
Valid but unsupported instruction.
S32_ERROR_VENEER_ERROR
Native code generator cannot construct veneers.
S32_ERROR_OUT_OF_MEMORY
General “out of memory” error.
S32_ERROR_PHASE_ERROR
Phase error between native code generation passes.
S32_ERROR_NOT_SUPPORTED
Request feature is not supported.
Additional information
These error codes, other than S32_ERROR_NONE, are
user-configurable in order that they do not conflict
with existing codes used by other software (and then
combined with emApps). All errors must be assigned
negative values.
S32_CONFIG_EXECUTOR
Description
Configuration of S32 execution engine implementation.
If set to zero, the generic C executor will not perform
software-based access violation trapping. In this configuration,
it is expected that the executor is run in its own protected
environment provided by a memory protection unit (MPU) or
a memory management unit (MMU) under control of an operating
system.
If set to nonzero, the generic C executor performs software-based
access violation trapping on all accesses: instruction fetch and
data read/writes.
This setting controls the instruction set for the Arm assembly
language executor.
This symbol can be set to:
S32_CONFIG_ARM_ISA_T16: Use the T16 instruction set.
S32_CONFIG_ARM_ISA_T32: Use the T32 instruction set.
S32_CONFIG_ARM_ISA_A32: Use the A32 instruction set.
For some architectures, selection is clear. For other
architectures and cores, such as Arm v7-A implementation
in the Cortex-A9, it is a trade-off between improved
execution speed of the A32 instruction set compared
to the T32 instruction set it also implements, and the
fact that the executor in A32 is slightly larger than
the executor coded in T32.
This flag controls whether the opcode decode dispatch
table is held in RAM or (usually) flash. Setting this
to zero places the table in a section marked as
read-only memory. Setting this to nonzero instructs
the executor to copy the instruction dispatch table
from read-only memory to the stack when S32_Exec()
is called.
As the dispatch table is accessed once per instruction
executed, placing the small table in RAM can accelerate
opcode dispatch and improve execution performance.
If RAM dispatch and TBB/TBH instructions are both
activated, a RAM-based dispatch table is created
and used but other instruction-field dispatching uses
TBB/TBH if possible.
This flag controls the instructions selected by the Arm
assembly language executor. Setting this symbol to zero
indicates to the executor that 32-bit accesses to 16-bit-aligned
addresses complete successfully without an unaligned access
exception. If this symbol is set to a nonzero value, a
32-bit load from the code stream is synthesized using two
16-bit accesses.
This flag controls whether opcode decode dispatch is
appended to each opcode executor laying down one opcode
dispatcher per opcode group (32 groups), or whether
there is a single, common opcode dispatcher.
Using a single dispatcher saves some code memory at the
possible expense of execution speed, whereas appending
a next-opcode dispatcher to each opcode group saves a
branch instruction at the expense of extra code for
each group.
The exact performance gains and code sizes are highly
dependent on the target architecture and instruction
cache presence.
This flag controls whether opcode dispatch uses the TBB
and TBH instructions of the ARMv6 and ARMv7 architectures.
Using TBB and TBH reduces code size but, after benchmarking
on some readily available ARMv7 cores, it also seems to
reduce performance compared to a table-based opcode
dispatcher.
This flag controls whether the targets of Arm branch
instructions are aligned to a 32-bit boundary. For some
cores, such as a cortex-A9, aligning branch targets is a
small performance gain at the expense of slightly larger
code.
Actual performance gains will be dependent upon the core
that run the executor and its cache, branch predictor,
and instruction pipeline.
S32_CONFIG_USE_PREDICTION
Description
Prime branch predictor for faster opcode dispatch.
This flag controls whether the instruction following the
current instruction being executed is decoded early in
order to prime the pipeline and branch prediction unit.
For cores that support a deeper pipeline and branch
predictor, performance is generally improved when emApps
is configured to prime the branch predictor.
Native code generator configuration
S32_NCG_CONFIG_T32_REG_CNT
Description
Number of registers used to mirror register file context.
The symbol defines the number of registers used to mirror
the low registers of the S32 context. The best code density
is achieved, by experimentation, to be four registers (R0-R3)
in addition to the stack pointer.
At present, the only value supported is 4.
S32_NCG_CONFIG_T32_FLAGS
Description
Capability flags supported by the T32 code generator.
The symbol defines the flags used to select the optimization
capabilities of the T32 native code generator that are compiled
into firmware.
C compiler reference
This chapter contains the documentation for the emApps C compiler.
Introduction
The compiler is located in the Bin folder of the emApps product.
It is used to compile application source code files into portable
executable files that can be loaded and executed in an emApps sandbox
environment. A linker is not required.
The compiler implements a subset of the C90 standard with some C99
extensions (see Extensions). Restrictions and deviations
tha are imposed for deeply embedded applications are listed in
Restrictions and Deviations.
Usage
The compiler is called from the command line as follows:
S32CC [options] source-file
A list with all available options can be shown by invoking the compiler
without any arguments.
To compile a source code file, it needs at least the path to the
Inc directory, which is defined as a system include,
and the file to compile. The Inc directory contains the file
Default.h, which is automatically included by the compiler
and provides the sandbox’s C interface to the user.
When the compiler finishes compilation successfully, there are two
new files in the same folder as the source code file:
The portable executable file, with extension .pex,
containing the code and data for the application.
A listing file, with extension .lst, containing a
listing of the executable form of the application.
Language
Extensions
C++ single-line comments are supported.
Data within functions need not immediately following the open
brace of a block, but can be defined anywhere within the function.
Anonymous structures and unions are supported.
Enumerated types can specify the underlying storage unit.
For instance, enum char { x=1 }; declares an enumeration type
with underlying 8-bit storage. By default, enumerations are declared
with underlying 32-bit int storage.
Restrictions
The supported C programming language is subject to the following restrictions:
volatile is not supported and will be ignored if used. All
pointers are automatically considered volatile by the code generator.
Bitfields are not supported.
float and double are not supported.
long long is not supported.
Passing structures by value to functions and returning structures
from functions is not supported.
Structure assignment is supported (x = y; where x and y are
structures), but cascaded structure assignment x = y = z; is not.
The comma operator in expressions is not supported.
Deviations
The supported C programming language is subject to following deviations:
As there is no linker, the extern specifier serves is
not required.
Preprocessor
The compiler has a standard C90 preprocessor with following extensions:
The preprocessor controls #warning and #remark function
exactly as #error but produce diagnostics with warning and
remark severity.
Macros can be defined with variadic arguments lists with the
variable aguments collected into the preprocessor symbol __VA_ARGS__.
Predefined macros
Following macros are predefined by the compiler:
Macro
Description
Example Output
__DATE__
Date the source code file was compiled.
"Feb 20 2026"
__TIME__
Time the source code file was compiled.
"12:02:05"
__FILE__
Name of the source code file.
"App.c"
__LINE__
The line number the macro was used in.
80
__S32C__
The full version number containing the major, minor and patch number (Mmmpp).
21202
__S32C_MAJOR__
The major version number.
2
__S32C_MINOR__
The minor version number.
12
__S32C_PATCHLEVEL__
The patch level.
3
__S32C_VERSION__
The version string (“M.mm.pp”).
"2.12.3"
Pragmas
The following pragmas are supported by the S32 C compiler.
Specifies a function to be exported from the application.
xdata_size
Syntax
#pragma S32C xdata_size (Size)
Description
This pragma specifies the size of the memory allocated for the xdata area
pointed to by __S32_XDataBase. The xdata area can be used by the program
to provide the data to an application, or for the application to return data to
the program.
Parameter
Description
Size
Size of the xdata area to be allocated.
Example
#pragmaS32C xdata_size (0x800)
stack_size
Syntax
#pragma S32C stack_size (size)
Description
This pragma is not usually required because the S32 compiler analyzes
each entry point to determine maximum stack depth and sets this automatically.
However, additional space can be added “for safety reasons” by using this
pragma and override the compiler’s calculation.
The compiler will disregard stack sizes smaller than that calculated
by static analysis, and therefore it is not possible to produce an application
that will fail because of lack of stack space.
It is also possible to increase the allocated stack above that calculated
by the compiler by a given amount that is specified using a ’+’.
#pragmaS32C stack_size(4096)// Set stack size to 4096#pragmaS32C stack_size(0x1000)// Same as above#pragmaS32C stack_size(+256)// Calculated stack size increased by 256 bytes.
In the case where there is direct or indirect recursion or when using function
pointers, an exact stack bound cannot be calculated. The compiler calculates the
maximum stack requirement of all functions, including those with direct or
indirect recursion up to the point of recursion, and uses this as the calculated
bound. A warning is generated when the compiler detects such recursion, and
the cycle is described in the compiler’s map file.
In this case, it is the user’s responsibility to determine the minimum stack
space required by whatever means are at hand, and instruct the compiler by
using this pragma. If the declared stack space is smaller than that actually
required, overwriting of other data or the program will occur, but sandboxing
ensures that no write occurs to data outside of the sandbox.
The function with the given name is placed into the export list contained
in the application’s executable such that it can be located using
S32_FindExport().
Program API reference
This chapter explains the API functions of emApps that are needed for
loading and execution. The emApps API is kept as simple as possible to
provide a straightforward way to integrate emApps into a product.
Fields in this structure are considered private. Access
functions are provided, e.g. S32_RdReg(), to extract
useful information from the context. SEGGER does not
guarantee that the fields in this structure preserve their
name, order, type, or presence between emApps versions.
S32_SERVICE_BINDING
Description
Association of service name and its implementation.
The Exception-Detected telemetry point is called on
detection of an exception; this does not fire immediately
an exception is raised, i.e. when calling S32_Raise(),
it fires when the executor detects the exception in
the instruction fetch-decode-execute loop.
It is guaranteed that the exception is raised outside
of instruction execution, i.e. never between
Instruction-Begin and Instruction-End telemetry points.
If this function fails, check the configuration of the S32
to ensure that data types are correct and, if using the
assembly language executor, that calculated offsets into the
execution context are correct.
Loading functions
The table below lists the functions that are used to prepare
an application for execution.
Success, number of bytes of additional memory required to hold the stack and xdata areas.
< 0
Failure.
Additional information
The PEX file header consists of eight 32-bit words, and this is
all that is examined by S32_LoadHeader(). If FileLen indicates
the PEX file does not contain a complete header, an error is returned.
If the file contains a complete header, it is examined and validated
as far as it can be. It is not necessary to provide the entire file
at this time, only the header part (i.e. 32 bytes). Full validation
is deferred to S32_LoadFile(). Note that there is no security risk
associated with partial validation of the header, everything that is
required during a call to S32_LoadHeader() undergoes validation.
On successful completion, the number of bytes required to hold the
stack and xdata areas is returned, which can be used to allocate
memory for the execution image in memory before calling S32_LoadFile().
Pointer to object containing application geometry.
pFile
Pointer to PEX file content plus additional memory allocated for stack and xdata areas.
Return value
≥ 0
Success, PEX file is valid.
< 0
Failure.
Additional information
Once the PEX file header is validated successfully, the entire
execution image is presented for validation in preparation for
application execution. S32_LoadFile() validates that the
file is structurally sound, that import-by-name functions
exist before execution, and that all in-file structural
“section offsets” point within and are entirely contained by
the section content pointed to.
Pointer that is a native address. Note that a sandbox
address of zero, corresponding to a null pointer, is
converted to a pointer to the start of memory and not
to a native null pointer.
It is expected that sandbox addresses are checked for
acceptability outside of this function. S32_AcceptStr()
and S32_AcceptMem() ensure that accesses through null
pointers are detected and, therefore, any string or
memory block that passes through S32_AcceptStr() or
S32_AcceptMem() is guaranteed to point to a memory
block entirely contained in the sandbox address
space.
S32_SandboxAddrToPtrOrNull()
Description
Convert sandbox address to native pointer or NULL.
Pointer that is a native address. Sandbox address zero
converted to a corresponding to a null pointer, otherwise
it is converted as S32_SandboxAddrToPtr() would convert
it.
It is expected that sandbox addresses are checked for
acceptability outside of this function.
This function starts executing instructions until an exception
is raised. When a top-level function returns, a “break”
exception is raised causing termination.
Parameter index, zero being the first function parameter.
Return value
If the parameter lies outside of the sandbox address space,
the return value has zero substituted and an access violation
exception is raised in the execution context.
Timestamp function installed and telemetry activated.
= S32_ERROR_NOT_SUPPORTED
Basic telemetry is not supported.
Additional information
This function installs basic telemetry support using timestamps
delivered by the function pointed to by pfGetTimestamp.
Note that telemetry data is not zeroed when the execution
context is prepared using S32_PreapreByName() or S32_PrepareByAddr(),
and it is not zeroed when selecting basic telemetry using
S32_BT_ConfigTelemetry(). Basic telemetry data must be zeroed
by calling S32_ResetTelemetry() before acquisition commences.
Basic telemetry is configured by building with S32_CONFIG_TELEMETRY=1.
Pointer to object that receives the basic telemetry data.
Mul
Numerator of fractional multiplier.
Div
Denominator of fractional multiplier.
Additional information
This function queries the basic telemetry data for
total time spent in service functions. If no basic
telemetry is configured, or user-defined telemetry
is configured, all telemetry data returned are zero.
Telemetry data is collected using the timestamps
returned by the get-timestamp function passed to
S32_BT_ConfigTelemetry(). Typically the processor’s
cycle counter or an RTOS’s tick counter are used
as a reference.
The telemetry data returned by S32_BT_QueryTelemetry()
can be scaled by the fraction Mul/Div to convert
timestamp units into a more accessible form for
presentation.
For instance, a 600 MHz device using the processor’s
cycle counter as reference provides accurate telemetry
in cycles, but is not that useful when analyzing it.
Therefore, passing Mul=1 and Div=600 would return
telemetry data in microsecond units, or Mul=1 and
Div=60000 would return it in millisecond units.
If no exception has been raised, S32_ERROR_NONE (defined
as zero) is returned. Any other value raised by
S32_Raise() is taken to be an exceptional state and is
the exception value returned.
U8 at the sandbox address if the address is valid,
and zero if the sandbox address is not valid.
Additional information
If the datum lies outside of the sandbox address space,
the return value has zero substituted and an access violation
exception is raised in the execution context.
U16 at the sandbox address if the address is valid,
and zero if the address is not valid.
Additional information
If the datum lies outside of the sandbox address space,
the return value has zero substituted and an access violation
exception is raised in the execution context.
U32 at the sandbox address if the address is valid,
and zero if the address is not valid.
Additional information
If the datum lies outside of the sandbox address space,
the return value has zero substituted and an access violation
exception is raised in the execution context.
If any of the the data lies outside of the sandbox address
space, the read is canceled and an access violation exception
is raised in the execution context.
If the datum lies outside of the sandbox address space,
the write is canceled and and an access violation exception
is raised in the execution context.
If any of the the data lies outside of the sandbox address
space, the write is canceled and an access violation exception
is raised in the execution context.
If the address written when pushing lies outside the
sandbox address space, the write is canceled and an
access violation exception is raised in the execution
context.
This chapter details some optional functions that can be added to the
service API table to offer additional functions available to the
application.
Runtime functions
The table below lists the functions that are used for C-language
runtime support. If your application uses division or modulo
operators, it will likely require these functions to be present
in the service API table.
The following is the interface provided to the application
and may be required by the C compiler to implement division
and modulus with unknown inputs:
The functions in this section can be installed into the service
API table to provide fully sandboxed implementations of the
equivalent C library functions.
The C source files for these functions are provided in the Etc
folder.
This function implements the standard C function
sprintf():
int snprintf(char *pDst, unsigned MaxLen, const char *sFmt, …);
Utility functions
The functions in this section can be installed into the service
API table as generic functions. The C and C++ source files for
these functions are provided in the Etc folder.
int FWrite(int Handle, const void *pData, unsigned DataLen);
Native code generator (Add-on)
This chapter describes the native code generation add-on for emApps.
What is the native code generator?
The emApps native code generator add-on provides a means to accelerate
execution of applications by translating them into native machine instructions.
The native code generator is small and fast, does not require excessive
resources for translation, and produces excellent code, and is uniquely
positioned for on-device just-in-time code tranlsation on low-end
microcontrollers.
Not all applications can be native coded
The native code generator is not able to translate all applications
into natve code. Whilst it is certainly possible to always translate
applications to native code, doing so would complicate the code generator,
make its code footprint much bigger, it would take longer to analyze and
generate native code, and the workspace required would be much larger.
As such, the native code generator will translate typical applications into
native code without issue. Applications that make use of function pointers
will not be translated to native code by the native code generator.
Using the code generator
Accelerating execution by the native code generator requires that
code is generated after the application is loaded into memory by
S32_LoadFile(). Once loaded, code generation can be requested using
S32_NCG_GenerateCode() or S32_NCG_GenerateCodeEx() to provide detailed
information about the generated code.
The code generator context
The code generator context is required to hold overall progress
of the code generation process.
The code generation workspace
The native code generator requires a workspace to use for bookkeeping
when generating native code and for storing the native code generated.
The workspace required depends upon the complexity and size of the
application to native code and the options selected for code generation.
For the T32 instruction set, for instance, using an estimate of “code
size multiplied by 1.7” is generally adequate, however workspace allocation
is described in more detail in Strategies for workspace allocation.
For simplicity, a single block of memory can be reserved using the C
malloc() function, or a static array can be used, e.g.
staticunsignedaWorkspace[1024];// 4K workspace on 32-bit machine
The workspace provided to the code generator must be correctly aligned
for the target architecture, e.g. be on a double-word boundary for
Arm architectures—the C malloc() and realloc() functions
guarantee that allocated memory blocks are aligned to the maximum
alignment for the target architecture.
Relying on alignment using “unsigned” as the underlying datatype
does not, in general, guarantee alignment to the maximal alignment
of the machine. For instance, on Arm, the alignment of unsigned
is 4, but the maximal alignment is 8 in order that 64-bit data can be
loaded and stored using LDRD and STRD instructions and that
items on the stack are 64-bit aligned as required by the Arm calling
convention.
Alignment can be forced by using either compiler attributes, which is
generally nonportable, or by forcing alignment using a union.
For instance, alignment can be specified using GCC or clang using
an attribute:
Alignment can be specified in a compiler-indepdent way using a
union:
staticunion{unsignedcharaBytes[4096];// 4KbytesunsignedlonglongForceAlign;// Force 64-bit alignment}_Workspace;
Strategies for workspace allocation
It is possible to attempt code generation multiple times, increasing
the size of the workspace until code generation succeeds. For instance,
a simple “double the workspace size and try again” strategy would be:
void*pWs;// Pointer to current workspacevoid*pNewWs;// Pointer to new workspaceunsignedWsSize;// Current workspace sizeintStatus;//WsSize =1024;// 1 kilobyte to start withpWs =NULL;// Workspace not yet allocatedStatus =S32_ERROR_OUT_OF_MEMORY;// Code generation has not yet succeeded//for(;;){//// Try to extend the workspace.//pNewWs =realloc(pWs,WsSize);if(pNewWs ==NULL){break;// Failed to allocate workspace}//// Workspace is extended, try code generation.//pWs =pNewWs;Status =S32_NCG_GenerateCodeEx(&Geo,// Application geometry&S32_NCG_T32_API,// Code for T32 instruction set~0u,// Enable all optimizationspWs,// Workspace, correctly alignedWsSize);// Size of workspace, in bytes//// Exit the loop if code generation succeeded or something// other than an out-of-memory error occurred.//if(Status >=0||Status !=S32_ERROR_OUT_OF_MEMORY){break;}//// Double the workspace size and try again.//WsSize *=2;}//// If code generation failed, recover workspace memory.//if(Status <0){free(pWs);}
The reallocation strategy is entirely under user control. Other strategies
could be:
Linear increase, e.g. WsSize += 1024.
Double-and-add, e.g. WsSize = 2 * WsSize + 512.
Example NCG application
The following application generates code for a Cortex device that
implements the T32 instruction set. The example is for 2000
iterations of Dhrystone:
/********************************************************************** (c) SEGGER Microcontroller GmbH ** The Embedded Experts ** www.segger.com ***********************************************************************-------------------------- END-OF-HEADER -----------------------------Purpose : Simple load-and-go for DemoDhrystone.c using native code generation.*//*********************************************************************** #include section************************************************************************/#include<stdio.h>#include<stdlib.h>#include<string.h>#include"S32.h"#include"S32_API_C.h"#include"S32_API_Util.h"/*********************************************************************** Check configuration************************************************************************//*********************************************************************** Prototypes************************************************************************/staticS32_U32_SYS_GetTime_ms(S32_EXEC_CONTEXT*pCtx);/*********************************************************************** Static const data************************************************************************///// Application as an array of bytes.//#include"DemoDhrystone_2k_pex.h"//// Service API exported to the application.//staticconstS32_SERVICE_BINDING_aServices[]={{"printf",S32_API_C_printf},{"__S32_idiv",S32_API_S32_idiv},{"__S32_udiv",S32_API_S32_udiv},{"UTIL_MulDiv",S32_API_Util_MulDiv},{"SYS_GetTime_ms",_SYS_GetTime_ms },};/*********************************************************************** Static data************************************************************************/staticunion{S32_U8aBytes[4096];// 4KbytesunsignedlonglongForceAlign;// Force 64-bit alignment}_Workspace;/*********************************************************************** Static code************************************************************************//*********************************************************************** _SYS_GetTime_ms()** Function description* Get current millisecond timer.** Parameters* pCtx - Pointer to execution context.*/staticS32_U32_SYS_GetTime_ms(S32_EXEC_CONTEXT*pCtx){unsignedCycleCnt;//(void)pCtx;//// Get cycle count from core. Assume core runs at 100 MHz// then milliseconds elapsed is CycleCnt / 100000.//CycleCnt =*(unsigned*)0xE0001004;returnCycleCnt /100000;}/*********************************************************************** Public code************************************************************************//*********************************************************************** main()** Function description* Program entry point.** Return value* Application exit code.*/intmain(void){S32_EXEC_GEOGeo;S32_EXEC_CONTEXTCtx;S32_U8*pImage;S32_NCG_INFOInfo;unsignedFlags;S32_I32Status;//if(S32_CheckConfig()!=S32_ERROR_NONE){printf("S32 configuration check failed\n");exit(EXIT_FAILURE);}//pImage =NULL;Status =S32_InitGeo(&Geo,_aServices,sizeof(_aServices)/sizeof(_aServices[0]));if(Status >=0){Status =S32_LoadHeader(&Geo,_aDemoDhrystone_2k,sizeof(_aDemoDhrystone_2k));if(Status >=0){pImage =malloc(sizeof(_aDemoDhrystone_2k)+Status);if(pImage ==NULL){Status =S32_ERROR_OUT_OF_MEMORY;}else{memcpy(pImage,_aDemoDhrystone_2k,sizeof(_aDemoDhrystone_2k));Status =S32_LoadFile(&Geo,pImage);if(Status >=0){//// Select all configured optimization features.//Flags =S32_NCG_CONFIG_T32_FLAGS;//// Turn off default generation of all sandbox checks.//Flags &=~S32_NCG_T32_FLAG_SANDBOX_INLINE;Flags &=~S32_NCG_T32_FLAG_SANDBOX_OUTLINE;//// Force generation of sandbox checks with inline code,// not millicode subroutines.//Flags |=S32_NCG_T32_FLAG_SANDBOX_INLINE;//Status =S32_NCG_GenerateCodeEx(&Geo,&S32_NCG_T32_API,Flags,&_Workspace,sizeof(_Workspace),&Info);if(Status >=0){//printf("Native code generation successful:\n");printf(" %d bytes of native code generated...\n",Info.NativeCodeSize);printf(" ...clear instruction cache from %08X to %08X\n",Info.NativeCodeAddr,Info.NativeCodeAddr +Info.NativeCodeSize -1);printf(" ...%d bytes of workspace must be retained\n",Status);printf("\n");}else{printf("Native code generation failed:\n");printf(" %s (%d)\n",S32_GetErrorText(Status),Status);printf("Reverting to standard executor\n");printf("\n");}Status =S32_PrepareByName(&Geo,&Ctx,"main");if(Status >=0){Status =S32_Exec(&Ctx);}}}}}//free(pImage);printf("\n*** Application exited with %s (%d)\n",S32_GetErrorText(Status),Status);//returnStatus;}/*************************** End of file ****************************/
Code generator functions
The table below lists the functions that are used to
inquire and support native code generation.
Active size of workspace after generation, in bytes.
Additional information
The returned value indicates the number of bytes of the
provided workspace are to be retained after code generation.
This portion of the workspace contains entry point information
and the generated native code. If the workspace has been
allocated dynamically by a client, the client can shrink the
size of the workspace but such reallocation must not move
the workspace as embedded pointers and native code are not
guaranteed to be position independent. As such, the C function
realloc() generally cannot be used to shrink the workspace as
it does not guarantee that an allocation remains at the same
address even if the reallocation is of a smaller size.
Note that the peak workspace allocation requirement during
code generation will be larger than that returned by this
function when S32_NCG_T32_FLAG_MINIMIZE_IMAGE is set in Flags
and emApps configured to support workspace minimization.
Pointer to object that receives generated code statistics.
Return value
< 0
Failure status.
≥ 0
Active size of workspace after generation, in bytes.
Additional information
The returned value indicates the number of bytes of the
provided workspace are to be retained after code generation.
This portion of the workspace contains entry point information
and the generated native code. If the workspace has been
allocated dynamically by a client, the client can shrink the
size of the workspace but such reallocation must not move
the workspace as embedded pointers and native code are not
guaranteed to be position independent. As such, the C function
realloc() generally cannot be used to shrink the workspace as
it does not guarantee that an allocation remains at the same
address even if the reallocation is of a smaller size.
Note that the peak workspace allocation requirement during
code generation will be larger than that returned by this
function when S32_NCG_T32_FLAG_MINIMIZE_IMAGE is set in Flags
and emApps configured to support workspace minimization.
Pointer to zero-terminated flag description if Flag is valid,
else NULL.
Resource use and performance
This chapter provides some guidance on the memory use of
emApps and the performance that is achieved in practice.
Note that these numbers are correct at the time of writing,
and are indicative only. The actual memory footprint and
performance will vary on configuration of emApps, the compiler
used, and its enabled features and optimizations.
The following shows the size in bytes for the executor and the performance
delivered in Dhrystones per second for the Arm assembly language executor
in various configurations for each instruction set. The code was compiled
for a Cortex-A9 using the SEGGER Compiler version 20.1.2, optimized for speed.
Flags
T16 ISA
T32 ISA
A32 ISA
Perf T16
Perf T32
Perf A32
None
696
720
960
17 370
26 021
26 539
USE_TBB_TBH
624
640
—
15 905
20 588
—
TAIL_THREADING
900
916
1 120
19 054
28 926
29 735
RAM_DISPATCH
712
724
980
17 743
25 926
26 281
USE_PREDICTION
—
876
1 152
N/A
32 669
34 199
Native code generation
Read-only memory required
The native code generator for T32 can be statically configured
to support each code optimization feature individually, allowing
the code footprint of the code generator to be tuned as required
whilst achieving great conversion speeds with excellent execution
performance.
The following are the sizes of the T32 code generator compiled
for a Cortex-A9 using the SEGGER Compiler version 20.1.2,
optimized for size and for speed.
Flags
Small code
Fast code
With S32_NCG_CONFIG_T32_REG_CNT = 4
None
4 740
10 004
S32_NCG_T32_FLAG_FUSE_MOV_LDR
4 896
10 250
S32_NCG_T32_FLAG_FUSE_MOV_ADD
4 792
10 162
S32_NCG_T32_FLAG_FUSE_MOV_SUB
4 800
10 402
S32_NCG_T32_FLAG_FUSE_CLX_BXX
4 836
10 630
S32_NCG_T32_FLAG_REPLACE_OR_0
4 772
10 044
S32_NCG_T32_FLAG_SHRINK_JUMPS
4 902
10 216
Cumulative (all above)
5 468
11 502
Execution performance
The assembly-language executor running on a 600 MHz Cortex-A9
achieves 32669 Dhrystones per second with the T32 instruction set
and the executor tuned for prediction. The following is the relative
performance of the same application after code generation
for the T32 instruction set.
Flags
Code size
Dhrystones/s
Factor
With S32_NCG_CONFIG_T32_REG_CNT = 4
None
2 088
518 134
x15.9
S32_NCG_T32_FLAG_FUSE_MOV_LDR
2 028
523 560
x16.0
S32_NCG_T32_FLAG_FUSE_MOV_ADD
2 088
518 134
x15.9
S32_NCG_T32_FLAG_FUSE_MOV_SUB
2 068
518 820
x15.9
S32_NCG_T32_FLAG_FUSE_CLX_BXX
2 012
537 634
x16.5
S32_NCG_T32_FLAG_REPLACE_OR_0
2 082
495 095
x15.2
S32_NCG_T32_FLAG_SHRINK_JUMPS
2 002
523 560
x16.0
Cumulative (all above)
1 844
540 540
x16.6
Conversion performance
It is essential that just-in-time code generation is fast.
The T32 code generator generates compact, fast code and does
so very quickly. The following table shows the duration for
and conversion rate of S32 instructions to Arm T32 instructions,
running on on a 600 MHz Cortex-A9, for the Dhrystone application
comprising 1454 bytes of S32 executable code.
This chapter describes the S32 machine and its architecture.
Register file
The S32 comprises a general-purpose register file of 16 registers
plus a program counter PC and a transitory zero flag Z.
General-purpose registers
Registers R0 through R14 are general-purpose registers
used for calculation and parameter passing. Register R14,
or WP, is a general-purpose register in the S32 architecture
but has a dedicated purpose in C programs, described below.
Program counter
The program counter PC is not directly addressable.
It increments to fetch and execute S32 instructions. Execution
is controlled by subroutine call and return instructions (CALL, RET)
and by branch instructions (BRA, BEQ, BNE, DBNZ)
which update the program counter as required.
Stack pointer
The stack pointer SP is directly addressable and is a
synonym for R15. The stack pointer is directly affected by
CALL, RET, PUSH, and POP instructions.
It is only possible to modify the stack pointer, for instance
to construct or destroy an activation record, using the ADD
and SUB instructions: any other operation addressing the
stack pointer is defined to be illegal.
Work pointer
The work pointer WP is directly addressable and a synonym
for R14. It is conventionally used by the S32 C compiler
to extend the range of conditional and unconditional branch
instuctions and also for the CALL instruction. Otherwise,
it can be used as a general-purpose register in any instruction.
Exception register
The exception register XR is not directly addressable. Its value
is zero until an exception is raised, registering the nonzero exception
cause into XR, and execution halts.
The exception register can be read from an execution context
using the S32_RdXR() function; an exception can be raised
from a service function using the S32_Raise() function.
Memory limit register
The memory limit register ML is not directly addressable. Its value
is set to point to the first address beyond application-addressable
memory. It is used internally within the S32 architecture to ensure
that all reads and writes are to valid memory addresses within the
S32 address space.
The memory limit register can be read from an execution context
using the S32_RdML() function. No facility is provided for a user
to write to the memory limit register.
Zero flag
The zero flag Z is used to conditionally branch using the
BEQ and BNE instructions. Its value, 0 or 1,
can also be placed into a general-purpose register using the
SZ instruction.
The zero flag has a defined value only after execution
of some instructions. The value is defined following
nearly all arithmetic instructions and also after the compare
instructions.
Memory layout
The S32 memory space comprises four distinct areas:
Data area
This contains the static data of the application.
Code area
This contains the executable code of the application.
Stack area
This contains the space reserved to contain activation
frames as the program executes.
XData area
This contains an unmanaged communication area that is shared
between application and program to communicate bulk data.
Instruction descriptions
This section lists every instruction.
In the Operation section of a description, an ILLEGAL reference
indicates that the instruction encoding is illegal and must not
occur in a valid S32 program. Illegal instructions are detected at
runtime by the executor and at compile-time by the native code
generator.
Any instruction coding not covered by the following descriptions
is ILLEGAL.
Reg[R0] := CALL(service function identified by well-known index #Index)
Reg[SP] := Reg[SP] - 4 * Cnt
Reg[R1] := UNDEFINED
Reg[R2] := UNDEFINED
Reg[R3] := UNDEFINED
Z := UNDEFINED
Exceptions
Defined by API function invoked
ADD
Register form #1
Description
Add register.
Encoding
Syntax
ADD Rn, Rx
Operation
Reg[Rn] := Reg[Rn] + Reg[Rx]
Z := Reg[Rn] == 0
Exceptions
None
Register form #2
Description
Add register and constant.
Encoding
Syntax
ADD Rn, Rx, #Imm4u
Operation
Reg[Rn] := Reg[Rn] + Reg[Rx] + ZeroExtend(Imm4u)
Z := UNDEFINED
Exceptions
None
Immediate form #1
Description
Add constant.
Encoding
Syntax
ADD Rn, #Imm4u
Operation
Reg[Rn] := Reg[Rn] + ZeroExtend(Imm4u)
Z := Reg[Rn] == 0
Exceptions
None
Immediate form #2
Description
Add constant.
Encoding
Syntax
ADD Rn, #Imm7s
Operation
Reg[Rn] := Reg[Rn] + SignExtend(Imm7)
Z := UNDEFINED
Exceptions
None
AND
Immediate form
Description
Bitwise-and constant.
Encoding
The encoding with Rn=SP or Rx=SP is ILLEGAL.
Syntax
AND Rn, Rx
Operation
if Rn == SP or Rx == SP then ILLEGAL
Reg[Rn] := Reg[Rn] & Reg[Rx]
Z := Reg[Rn] == 0
Exceptions
None
Register form
Description
Bitwise-and register.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
AND Rn, #Imm4u
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := Reg[Rn] & ZeroExtend(Imm4u)
Z := Reg[Rn] == 0
Exceptions
None
BEQ
Description
Branch on “equal” condition.
Encoding
Syntax
BEQ Label
Operation
if Z == 1 then PC := PC + 2 * SignExtend(Disp8)
Z := UNDEFINED
Exceptions
None
BNE
Description
Branch on “not equal” condition.
Encoding
Syntax
BNE Label
Operation
if Z == 0 then PC := PC + 2 * SignExtend(Disp8)
Z := UNDEFINED
Exceptions
None
BRA
Immediate form
Description
Branch to address.
Encoding
Syntax
BRA Label
Operation
PC := PC + 2 * SignExtend(Disp10)
Z := UNDEFINED
Exceptions
None
Register form
Description
Branch to address.
Encoding
Syntax
BRA Rn
Operation
PC := (Reg[Rn] & 1) == 0 then Reg[Rn] else UNDEFINED
Z := UNDEFINED
Exceptions
None
BRK
Description
Raise a BREAK exception.
Encoding
Syntax
BRK
Operation
XR := BREAK
STOP
Exceptions
BREAK
CALL
Immediate form
Description
Call address.
Encoding
Syntax
CALL Label
Operation
Reg[SP] := Reg[SP] - 4
Mem[Reg[SP]]::WORD := PC
PC := PC + 2 * SignExtend(Disp10)
Z := UNDEFINED
Exceptions
None
Register form
Call address.
Encoding
Syntax
CALL Rn
Operation
Reg[SP] := Reg[SP] - 4
Mem[Reg[SP]]::WORD := PC
PC := (Reg[Rn] & 1) == 0 then Reg[Rn] else UNDEFINED
Z := UNDEFINED
Exceptions
None
CLO
Description
Compare register, unsigned.
Encoding
Syntax
CLO Rn, Rx
Operation
Z := if Reg[Rn] < Reg[Rx] then 0 else 1
Exceptions
None
CLT
Description
Compare register, signed.
Encoding
Syntax
CLT Rn, Rx
Operation
Z := if Reg[Rn] <(S) Reg[Rx] then 0 else 1
Exceptions
None
DBNZ
Description
Decrement and branch not equal.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
DBNZ Rn, Label
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := Reg[Rn] - 1
if Reg[Rn] != 0 then PC := PC - 2 * ZeroExtend(Disp7u)
Z := UNDEFINED
Exceptions
None
ICALL
Description
Call service function by name.
Encoding
Syntax
ICALL Index, #Cnt
Operation
Reg[R0] := CALL(service function identified by import index #Index)
Reg[SP] := Reg[SP] - 4 * Cnt
Reg[R1] := UNDEFINED
Reg[R2] := UNDEFINED
Reg[R3] := UNDEFINED
Z := UNDEFINED
Exceptions
Defined by API function invoked
LDR
Register form
Description
Load word from memory.
Encoding
Syntax
LDR Rn, [Rx]
Operation
Reg[Rn] := Mem[Reg[Rx]]::WORD
Z := UNDEFINED
Exceptions
ACCVIO on read outside of sandbox
Stack form
Description
Store word to stack.
Encoding
Syntax
LDR Rn, [SP, #Off5s]
Operation
Reg[Rn] := Mem[Reg[SP] + 4*SignExtend(Off5s)]::WORD
Z := UNDEFINED
Exceptions
ACCVIO on read outside of sandbox
LDRB
Description
Load byte from memory.
Encoding
Syntax
LDRB Rn, [Rx]
Operation
Reg[Rn] := ZeroExtend(Mem[Reg[Rx]]::BYTE)
Z := UNDEFINED
Exceptions
ACCVIO on read outside of sandbox
LDRH
Description
Read halfword from memory.
Encoding
Syntax
LDRH Rn, [Rx]
Operation
Reg[Rn] := ZeroExtend(Mem[Reg[Rx]]::HALF)
Z := UNDEFINED
Exceptions
ACCVIO on read outside of sandbox
LSL
Immediate form
Description
Shift left immediate.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
LSL Rn, #Imm4u
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := Reg[Rn] << Imm4u
Z := Reg[Rn] == 0
Exceptions
None
Register form
Description
Shift leftAssign register.
Encoding
The encoding with Rn=SP or Rx=SP is ILLEGAL.
Syntax
LSL Rn, Rx
Operation
if Rn == SP or Rx == SP then ILLEGAL
Reg[Rn] := if Reg[Rx] <= 31 then Reg[Rn] << Reg[Rx] else UNDEFINED
Z := if Reg[Rx] <= 31 then Reg[Rn] == 0 else UNDEFINED
Exceptions
None
LSR
Immediate form
Description
Shift right constant.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
LSR Rn, #Imm4u
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := Reg[Rn] >> Imm4u
Z := Reg[Rn] == 0
Exceptions
None
Register form
Description
Shift right register.
Encoding
The encoding with Rn=SP or Rx=SP is ILLEGAL.
Syntax
LSR Rn, Rx
Operation
if Rn == SP or Rx == SP then ILLEGAL
Reg[Rn] := if Reg[Rx] <= 31 then Reg[Rn] >> Reg[Rx] else UNDEFINED
Z := if Reg[Rx] <= 31 then Reg[Rn] == 0 else UNDEFINED
Exceptions
None
MOV
Description
Move 7-bit constant.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
MOV Rn, #Imm7u
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := ZeroExtend(Imm7u)
Exceptions
None
MOVL
Description
Move 32-bit constant.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
MOVL Rn, #Imm32
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := Imm32
Exceptions
None
MOVW
Description
Move 16-bit constant.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
MOVW Rn, #Imm16u
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := ZeroExtend(Imm16u)
Exceptions
None
MUL
Immediate form
Description
Multiply by constant.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
MUL Rn, #Imm4u
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := Reg[Rn] * ZeroExtend(Imm4u)
Z := Reg[Rn] == 0
Exceptions
None
Register form
Description
Multiple by register.
Encoding
The encoding with Rn=SP or Rx=SP is ILLEGAL.
Syntax
MUL Rn, Rx
Operation
if Rn == SP or Rx == SP then ILLEGAL
Reg[Rn] := Reg[Rn] * Reg[Rx]
Z := Reg[Rx] == 0
Exceptions
None
OR
Immediate form
Description
Bitwise-or constant.
Encoding
The encoding with or Rn=SP is ILLEGAL.
Syntax
OR Rn, #Imm4u
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := Reg[Rn] | ZeroExtend(Imm4u)
Z := Reg[Rn] == 0
Exceptions
None
Register form
Description
Bitwise-or register.
Encoding
The encoding with Rn=SP or Rx=SP is ILLEGAL.
Syntax
OR Rn, Rx
Operation
if Rn == SP or Rx == SP then ILLEGAL
Reg[Rn] := Reg[Rn] | Reg[Rx]
Z := Reg[Rn] == 0
Exceptions
None
POP
Description
Pop registers from stack.
Encoding
Syntax
POP Rn, #Cnt
Operation
if Cnt == 0 then ILLEGAL
if Rn + Cnt > SP then ILLEGAL
for N := 0 to Cnt-1
Reg[Rn + N] := Mem[Reg[SP] + 4*N]::WORD
Reg[SP] := Reg[SP] + 4*Cnt
Z := UNDEFINED
Exceptions
ACCVIO on read outside of sandbox
PUSH
Description
Push registers to stack.
Encoding
Syntax
PUSH Rn, #Cnt
Operation
if Cnt == 0 then ILLEGAL
if Rn + Cnt > SP then ILLEGAL
Reg[SP] := Reg[SP] - 4*Cnt
for N := 0 to Cnt-1
Mem[Reg[SP] + 4*N]::WORD := Reg[Rn + N]
Z := UNDEFINED
Exceptions
ACCVIO on write outside of sandbox
RET
Description
Return from subroutine.
Encoding
Syntax
RET #Cnt
Operation
PC := Mem[Reg[SP]]::WORD
Reg[SP] := Reg[SP] + 4 * (Cnt+1)
Z := UNDEFINED
Exceptions
None
STR
Register form
Description
Store word to memory.
Encoding
Syntax
STR Rn, [Rx]
Operation
Mem[Reg[Rx]]::WORD := Reg[Rn]
Z := UNDEFINED
Exceptions
ACCVIO on write outside of sandbox
Stack form
Description
Store word to stack.
Encoding
Syntax
STR Rn, [SP, #Off5s]
Operation
Mem[Reg[SP] + 4*SignExtend(Off5s)]::WORD := Reg[Rn]
Z := UNDEFINED
Exceptions
ACCVIO on write outside of sandbox
STRB
Description
Store byte to memory.
Encoding
Syntax
STRB Rn, [Rx]
Operation
Mem[Reg[Rx]]::BYTE := Reg[Rn] & 0xFF
Z := UNDEFINED
Exceptions
ACCVIO on write outside of sandbox
STRH
Description
Store halfword to memory.
Encoding
Syntax
STRH Rn, [Rx]
Operation
Mem[Reg[Rx]]::HALF := Reg[Rn] & 0xFFFF
Z := UNDEFINED
Exceptions
ACCVIO on write outside of sandbox
SUB
Immediate form
Description
Subtract constant.
Encoding
Syntax
SUB Rn, #Imm4u
Operation
Reg[Rn] := Reg[Rn] - ZeroExtend(Imm4u)
Z := Reg[Rn] == 0
Exceptions
None
Register form
Description
Subtract register.
Encoding
The encoding with Rx=SP is ILLEGAL.
Syntax
SUB Rn, Rx
Operation
if Rx == SP then ILLEGAL
Reg[Rn] := Reg[Rn] - Reg[Rx]
Z := Reg[Rn] == 0
Exceptions
None
SZ
Description
Store zero flag.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
SZ Rn
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := Z
Z := UNDEFINED
Exceptions
None
XOR
Immediate form
Description
Exclusive-or constant.
Encoding
The encoding with Rn=SP is ILLEGAL.
Syntax
XOR Rn, #Imm4u
Operation
if Rn == SP then ILLEGAL
Reg[Rn] := Reg[Rn] ^ ZeroExtend(Imm4u)
Z := Reg[Rn] == 0
Exceptions
None
Register form
Description
Eclusive-or register.
Encoding
The encoding with Rn=SP or Rx=SP is ILLEGAL.
Syntax
XOR Rn, Rx
Operation
if Rn == SP or Rx == SP then ILLEGAL
Reg[Rn] := Reg[Rn] ^ Reg[Rx]
Z := Reg[Rn] == 0
Exceptions
None
Encoding quick reference
The following table is a quick refererence to the encoding
of all instructions, grouped by function.
SEGGER formatter reference
Introduction
The SEGGER formatter can be used to implement all C library “formatted
output” functions. It is also able to provide useful functionality
that goes beyond that of the C standard which is particularly useful
in embedded systems.
The way in which the formatter works is entirely parameterized
by a context held in a SEGGER_FORMAT_CONTEXT object. The task is to
initialize the context to match the expected semantics of the
function being emulated and to provide the necessary mechanisms for
extracting arguments as formatting progresses.
The following sections will describe how to achieve this.
Format control strings
The formatter implements a subset of the C library format
specification. In particular:
Format
Description
Format conversions
%c
Character.
%s
String.
%d, %i
Signed decimal.
%o
Unsigned octal.
%u
Unsigned decimal.
%x, %X
Unsigned hexadecimal.
Length modifiers
l
long size modifier.
ll
long long size modifier.
h
short size modifier.
hh
char size modifier.
Flags
0
Zero-pad value.
space
Space-separate values.
-
Left-adjust value.
+
Force sign.
#
Use alternate form.
Field width and precision are supported both as literals
contained in the format string and as runtime inputs when
specified by * in the format string.
The formatter does not implement the following format conversions:
Format
Description
%a, %A
Format floating-point value in hexadecimal form.
%e, %E
Format floating-point value in exponential form.
%f, %F
Format floating-point value in fixed form.
%g, %G
Format floating-point value in general form.
Using the formatter
Formatting contexts
There are two contexts used by the formatter:
SEGGER_FORMAT_CONTEXT containing the control parameters that
the formatter will use.
SEGGER_FORMAT_USER_CONTEXT containing parameters related
to argument extraction and parameters for the output function,
and so on.
The structure SEGGER_FORMAT_CONTEXT is defined by the formatter
and its is described below. The structure SEGGER_FORMAT_USER_CONTEXT
is defined by the user and several examples are shown in order
to implement the functions in the C library.
Implementing printf()
This section describes how to use the SEGGER formatter to implement a
function that is equivalent to printf() from the C standard library,
The top-level code
The example here implements HOST_printf() using the SEGGER
formatter. Here is the top-level code to set up and call the
SEGGER formatter:
Some of the mechanisms behind the formatter will be ignored
for the moment; they will, however, be described later.
Initialize the formatting context
The formatting context is initialized by calling SEGGER_FORMAT_Init().
All members within the context are set to default values which
are zero for all integer-style members and NULL for pointer-style
members.
Set the maximum number of characters output
The printf() function prints all its output and is not
bounded; the MaxLen member is set to the largest
unsigned value so that output is not truncated during formatting.
MaxLen will be important when implementing functions
such as snprintf() where output must be truncated.
Establish the user context
The user context, together with some functions described below,
is used to extract the incoming arguments that have been passed
to HOST_printf(). This assignment links the formatting
context to a specific user context provided for this purpose.
Provide a method to access incoming integer-style parameters
The formatting context uses the function pointed to by
pfGetVal to get the next argument to format.
The function pointed to by pfGetVal is passed the user
context along with some additional information in order to
accomplish this. Not to complicate matters, the specific
implementation of _HOST_GetVal() used here will be
considered a black box for now.
Provide a method to access incoming string-style parameters
Much like the integer-based arguments above, it will be necesary
to access string parameters. When a string is necessary,
the function pointed to by pfGetStr to get this. Again,
the implementation of _HOST_GetStr() will be considered
a black box.
Tell the formatter how to output
The formatter does not output formatted strings directly.
Instead, it uses the function pointed to by pfFlush
to output characters that must be printed. The implementation
for this printf() is to use fwrite() to write the
presented data to the standard output stream:
The user context is initialized by calling va_start()
to commence iteration over the incoming parameters. The
definition of the user context for this is:
The formatter is called and formatting takes place. It
is provided the formatting context and the format control
string. The vlaue returned is the number of characters
that were output, or a negative value if there was any
output error.
Wrap up
For compliance with the ISO standard, a call to va_start()
must have a matching call to to va_end(). Once done,
the number of characters or error indication is returned
to the caller.
Retrieving string arguments
The code for _HOST_GetStr() used above is very simple
in this case:
This simply extracts the string from the user context using va_arg().
The parameter MaxLen is ignored here, it simply encodes the precision
argument provided in the format string. This parameter is important in
some cases, especially when implementing a sandboxed environment.
Retrieving integer-style arguments
The code for _HOST_GetVal() used above is more complex than
_HOST_GetStr() and requires explanation.
The prototype for the function to retrieve an integer is:
The user context is provided in pCtx; the value retrieved must
be stored into the object pointed to by pValue which is
never NULL; the value is extracted according to the formatting
flags in Flags.
The combination of these parameters is as follows.
Flags is a set of flags that encode the expected form
of the incoming argument. Each flag is bitwise-or’d into the set.
The combination of flags are described in the following table:
Flags
Argument is…
Signedness
SEGGER_FORMAT_FLAG_SIGNED
Signed integer else an unsigned integer. This
is combined with the length modifier.
Length
SEGGER_FORMAT_FLAG_CHAR
char size (specified by hh modifier).
SEGGER_FORMAT_FLAG_SHORT
short size, (specified by h modifier).
SEGGER_FORMAT_FLAG_LONG
long size, (specified by l modifier).
SEGGER_FORMAT_FLAG_LONG_LONG
short size, (specified by ll modifier).
SEGGER_FORMAT_FLAG_PTR
void * size (specified by %p conversion).
None of the above set
int size (no length modifier).
Only one of the length flags will be set and enable correct
interpretation of the incoming argument. The following
implementation is highly generic and works on both 32-bit and 64-bit
operating systems and with compilers that implement both int
and long as 32 bits, compilers that implement int as 32 bits
and long as 64 bits, and even compilers that implements both
int and long as 64 bits.
This opaque type contains the context required to
extract arguments passed to the formatting function
as the format string is processed. Its structure
is entirely defined by the user.