Run applications on simulator and target
This page describes how you can run your application on the Hexagon simulator and on target by using the command line interface (CLI) and Hexagon integrated development environment (IDE). This page also describes the run_main_on_hexagon utility to run dynamic tests on the Hexagon simulator as well as offload a simple computational algorithm to the DSP.
Run applications on simulator
Framework for unit testing
The Hexagon SDK provides a framework that enables building dynamic tests and standalone tests and executing them on the Hexagon simulator as part of the build process. hexagon-lldb
also allows to debug the unit test on the simulator.
Each of the provided Hexagon SDK example contains its own unit test.
A unit test is specified as follows in hexagon.min
:
BUILD_QEXES += mylib_test # Specify the name of the library (mylib_test.so) or executable (mylib_test) unit test
mylib_test_C_SRCS += src_app/mylib_test # List source file(s) to build mylib_test.so. Sources must include a main(), which is the test entry point
mylib_test_DLLS+= rpcmem # Specify test dependencies
In this example:
- As explained in the description of the build targets, the use of
QEXES
instead ofEXES
indicates that the test binary should be run automatically on the Hexagon simulator after being built - The unit test is named
mylib_test
ormylib_test.so
depending on whether we are building a dynamic test or standalone test - The test source file only includes one file,
src_app/mylib_test.c
- The test links the rpcmem support library and the test module
QuRT-based dynamic tests
By default, all unit tests defined using the unit testing framework described above are built and run as dynamic tests, which rely on a utility called run_main_on_hexagon to be executed. This approach is the recommended approach to run simulator tests as it provides a simulation environment that closely matches the target environment in which the dynamic library is used.
Simulator tests relying on run_main_on_hexagon
are referred as dynamic tests because they involve shared objects instead of executables. For example, the calculator C++ example generates a dynamic test calculator_q.so
, which is to be passed as an argument to run_main_on_hexagon
for execution on the Hexagon simulator hexagon-sim
.
run_main_on_hexagon
calls dlopen()
of the simulator test, obtains the address of main()
using dlsym()
on a shared object handle, and calls the main()
function defined in the compiled shared library with argc
and argv
if provided.
All QuRT and test libraries are built into run_main_on_hexagon and QuRT header files are included by default by the build system for simulator tests using run_main_on_hexagon
. This means that you should not link rtld, test_util and atomic libraries to your dynamic tests.
When a QuRT-based test is run using run_main_on_hexagon
, the user shared library is loaded in a separate thread whose stack size can be configured by passing the argument stack_size=<byte size in decimal or hexadecimal format>
to run_main_on_hexagon_sim
binary. For QuRT-based tests, the heap used by QuRT kernel during bootstrapping time can be configured by adjusting the global variable heapSize
. The default heap value is set to 1024 MB
in test_util library. To change the heap size in user code, simply re-define the global variable and set its value to the desired heap value. For example, to set the heap size to 2048 MB, use:
unsigned int heapSize = 0x80000000;
Standalone tests
Standalone tests are simulator test executables that do not use QuRT. These tests are executed directly by the Hexagon simulator and do not run on target.
Simulator tests with a QuRT dependency should be using run_main_on_hexagon.
The calculator example is an example that generates a standalone test calculator_q
. This binary is executed by being passed to the Hexagon simulator.
A standalone test is defined the same way as a dynamic test with the exception that hexagon.min
must set the NO_QURT_INC
variable to 1
:
This results in skipping the inclusion of QuRT libraries and headers when building the test:
NO_QURT_INC = 1 # Do not include QuRT dependencies and generate a standalone test instead of a dynamic test
Since standalone tests do not run with run_main_on_hexagon
, any test library dependency such as rtld
, test_util
or atomic
needs to be listed in <unit_test_name>_LIBS
. For example, to modify the calculator example to use a dynamic test instead of a standalone test, simply remove the NO_QURT_INC = 1
line in hexagon.min
and remove rtld test_util atomic
from the list of dependencies specified in calculator_q_LIBS
.
Note: It is not possible to test a dynamic library with a standalone test as a dynamic library can not be linked to a static standalone executable.
Note: You can handle C/C++ library dependencies in your standalone code as follows:
#include <dlfcn.h>
...
DL_vtbl vtbl;
char *builtin[] = { (char*)"libc.so", (char*)"libgcc.so"};
// Note: you may specify additional dependencies such as (char*)"libgcc.so" if need be
memset(&vtbl, 0, sizeof(DL_vtbl));
vtbl.size = sizeof(DL_vtbl);
vtbl.msg = HAP_debug_v2;
(void)dlinitex(2, builtin, &vtbl);
The code sample above tells the loader that libc
and libgcc.so
symbols are built-in and therefore, that the shared objects libc
and libgcc.so
should not be loaded even if specified as dependent libraries in the Makefile
. This approach is not needed for dynamic tests and for libraries running on target as run_main_on_hexagon
and fastrpc_shell
, the FastRPC user PD ELF on target, execute similar instructions.
The default values of stack and heap memory available to standalone-tests are 1MB
and 64 MB
, respectively. However, the stack and heap memory values are configurable with the help of the parameters STACK_SIZE
and HEAP_SIZE
via command-line. Please refer to Hexagon Standalone Application User Guide (80 N2040-22) for more information about simulator command-line options.
Test library support
The Hexagon SDK provides unit test support libraries. For detailed information see the header and source files contained in the $HEXAGON_SDK_ROOT/utils
directory.
test_util
This library provides support for the unit test environment. It includes simulator versions of features otherwise present on the target DSP image, such as memory allocation, debug message support, VTCM manager, L2 cache locking manager, and performance measurement.
Test execution
The build framework builds and executes unit tests automatically. In the case of standalone tests, the unit test is built into a Hexagon simulator executable, which is then run by the Hexagon simulator. In the case of a dynamic tests, the simulator executes run_main_on_hexagon
, which calls the main()
defined in the dynamic library to execute the unit test.
In the event of a test failure, the build will fail and display the output of the failing test.
To enable more detailed information for the make process, including the output of a successful test run, set VERBOSE=1
on the make
command.
For example:
make hexagon DSP_ARCH=v65 BUILD=Debug VERBOSE=1
NOTE: To learn how to run on simulator using the Hexagon IDE, see IDE.
Run applications on target
Connect to the device
To install USB drivers on Windows/Linux and establish a connection between the device and host PC, see USB drivers setup and ADB driver setup.
Compile the binaries
Before you push the required executables and shared objects to the device, make sure you have compiled the HLOS and DSP binaries. For instructions on compiling, see build.
File locations of binaries
Once the binaries are compiled, push them to the appropriate location on the device.
-
On Linux Android (LA), the recommended location on target for the application executable is
/vendor/bin
, and/vendor/lib64
for their libraries. -
On Linux Environment (LE), the recommended location for the application executable is
/usr/bin
, and/usr/lib64
for their libraries. -
For recommended locations on where to push DSP binaries, see the discussion on the remote file systen.
Run the examples
To run an example on target, see the calculator example.
To run an example on target using the IDE, see the IDE section.
run_main_on_hexagon
run_main_on_hexagon is a utility to run dynamic tests on the simulator as well as offload an algorithm compiled as a shared object to the DSP. On both the Hexagon simulator and on the device, run_main_on_hexagon
calls dlopen()
on the dynamic shared object, obtains the address of main()
using dlsym()
on the shared object handle, and calls the main()
function defined in the compiled shared object with argc
and argv
if provided. The main()
is called in a new thread spawned with a stack whose size may be configured with the optional argument stack_size
.
run_main_on_hexagon
allows to invoke the main()
of a DSP shared library without the need for providing an IDL file and the source for a CPU executable. This approach is illustrated in the QHL example and the QHL HVX example.
As explained above, run_main_on_hexagon
is also used for dynamic simulator tests where it provides a simulation environment that closely matches the target environment in which the dynamic library will be used. This is achieved with the help of QuRT-provided root program called runelf.pbn
.
runelf.pbn is a boot loader that loads the dynamic executable on top of hexagon-sim
. It has a main thread that loads the user program and an exception thread that waits to process any exceptions that are then routed back to root. Exceptions in the user program are displayed in the console and can be debugged further using this approach.
Using run_main_on_hexagon
also allows to abstract the linking of the simulator test with QuRT libraries and other common libraries.
NOTE:* For more details on how run_main_on_hexagon
is implemented, see the $HEXAGON_SDK_ROOT/libs/run_main_on_hexagon/src
directory.
On-target usage
Usage:
adb shell /vendor/bin/run_main_on_hexagon <domain> <path> [stack_size=<size>] [args]
OPTIONS:
domain: Specify the DSP domain on which to offload the program, expressed as numeric domain id
supported domains: 0 (ADSP), 1 (MDSP), 2 (SDSP), 3 (CDSP)
path: File path of the shared object that includes the definition of `main()`
stack_size= : Optional argument to configure the stack size of new `ribbon` thread that runs main()
default stack size is 256kb and stack size less than 256kb is not supported
args: Optional string arguments to pass to main()
Note: The stack size of the thread where the main()
is called is configurable to allow users to offload complex computations to the DSP conveniently.
Here is a command-line example invoking run_main_on_hexagon
on target.
adb shell /vendor/bin/run_main_on_hexagon 0 test_main.so stack_size=0x400000 1 foo2 2 bar
With this command line, run_main_on_hexagon
calls main()
defined in test_main.so
in a thread of stack size 0x400000 with arguments 1 foo2 2 bar
, on domain 0, i.e. on the ADSP
.
Simulator usage
run_main_on_hexagon
can also be invoked from the Hexagon simulator. Here is a command-line example that calls the main
function in test_main.so
on the hexagon simulator (assuming the desired Hexagon architecture version is v65):
hexagon-sim -mv65 --simulated_returnval --usefs hexagon_ReleaseG_toolv87_v65 --pmu_statsfile hexagon_ReleaseG_toolv87_v65/pmu_stats.txt --cosim_file hexagon_ReleaseG_toolv87_v65/q6ss.cfg --l2tcm_base 0xd800 --rtos hexagon_ReleaseG_toolv87_v65/osam.cfg $HEXAGON_SDK_ROOT/rtos/qurt/computev65/sdksim_bin/runelf.pbn -- hexagon_ReleaseG_toolv87_v65/run_main_on_hexagon_sim stack_size=0x400000 -- test_main.so 1 foo 2 bar
With this command line, run_main_on_hexagon_sim
calls main()
defined in test_main.so
in a thread of stack size 0x400000
with arguments 1 foo2 2 bar
on the simulator.
Note: In simulator tests, the argument stack_size
is to be passed before --
. Any argument passed after --
is considered as an argument to the main
implemented in the shared object.