:sectnums:
== Software Framework

To make actual use of the NEORV32 processor, the project comes with a complete software ecosystem. This
ecosystem is based on the RISC-V port of the GCC GNU Compiler Collection and consists of the following elementary parts:

* <<_compiler_toolchain>>
* <<_core_libraries>>
* <<_application_makefile>>
* <<_executable_image_format>>
** <<_linker_script>>
** <<_ram_layout>>
** <<_c_standard_library>>
** <<_start_up_code_crt0>>
* <<_bootloader>>
* <<_neorv32_runtime_environment>>

A summarizing list of the most important elements of the software framework and their according
files and folders is shown below:

[cols="<6,<4"]
[grid="none"]
|=======================
| Application start-up code               | `sw/common/crt0.S`
| Application linker script               | `sw/common/neorv32.ld`
| Core hardware driver libraries ("HAL")  | `sw/lib/include/` & `sw/lib/source/`
| Central application makefile            | `sw/common/common.mk`
| Tool for generating NEORV32 executables | `sw/image_gen/`
| Default bootloader                      | `sw/bootloader/bootloader.c`
| Example programs                        | `sw/example`
|=======================

.Software Documentation
[TIP]
All core libraries and example programs are documented "in-code" using **Doxygen**.
The documentation is automatically built and deployed to GitHub pages and is available online
at https://stnolting.github.io/neorv32/sw/files.html.

.Example Programs
[TIP]
A collection of annotated example programs, which show how to use certain CPU functions
and peripheral/IO modules, can be found in `sw/example`.


// ####################################################################################################################
:sectnums:
=== Compiler Toolchain

The toolchain for this project is based on the free RISC-V GCC-port. You can find the compiler sources and
build instructions on the official RISC-V GNU toolchain GitHub page: https://github.com/riscv/riscv-gnutoolchain.

The NEORV32 implements a 32-bit RISC-V architecture and uses a 32-bit integer and soft-float ABI by default.
Make sure the toolchain / toolchain build is configured accordingly.

* `MARCH=rv32i`
* `MABI=ilp32`
* `RISCV_PREFIX=riscv32-unknown-elf-`

These default configurations can be override at any times using <<_application_makefile>> variables.

[TIP]
More information regarding the toolchain (building from scratch or downloading the prebuilt ones)
can be found in the user guides' section https://stnolting.github.io/neorv32/ug/#_software_toolchain_setup[Software Toolchain Setup].


<<<
// ####################################################################################################################
:sectnums:
=== Core Libraries

The NEORV32 project provides a set of pre-defined C libraries that allow an easy integration of the processor/CPU features
(also called "HAL" - hardware abstraction layer). All driver and runtime-related files are located in
`sw/lib`. These are automatically included and linked by adding the following include statement:

[source,c]
----
#include <neorv32.h> // NEORV32 HAL, core and runtime libraries
----

[cols="<3,<4,<8"]
[options="header",grid="rows"]
|=======================
| C source file | C header file | Description
| -                   | `neorv32.h`            | main NEORV32 definitions and library file
| `neorv32_cfs.c`     | `neorv32_cfs.h`        | HW driver (stubs) functions for the custom functions subsystem
footnote:[This driver file only represents a stub, since the real CFS drivers are defined by the actual CFS implementation.]
| `neorv32_cpu.c`     | `neorv32_cpu.h`        | HW driver functions for the NEORV32 **CPU**
| `neorv32_cpu_cfu.c` | `neorv32_cpu_cfu.h`    | HW driver functions for the NEORV32 **CFU** (custom instructions)
| `neorv32_gpio.c`    | `neorv32_gpio.h`       | HW driver functions for the **GPIO**
| `neorv32_gptmr.c`   | `neorv32_gptmr.h`      | HW driver functions for the **GPTRM**
| -                   | `neorv32_intrinsics.h` | macros for intrinsics & custom instructions
| `neorv32_mtime.c`   | `neorv32_mtime.h`      | HW driver functions for the **MTIME**
| `neorv32_neoled.c`  | `neorv32_neoled.h`     | HW driver functions for the **NEOLED**
| `neorv32_onewire.c` | `neorv32_onewire.h`    | HW driver functions for the **ONEWIRE**
| `neorv32_pwm.c`     | `neorv32_pwm.h`        | HW driver functions for the **PWM**
| `neorv32_rte.c`     | `neorv32_rte.h`        | NEORV32 **runtime environment** and helper functions
| `neorv32_slink.c`   | `neorv32_slink.h`      | HW driver functions for the **SLINK**
| `neorv32_spi.c`     | `neorv32_spi.h`        | HW driver functions for the **SPI**
| `neorv32_trng.c`    | `neorv32_trng.h`       | HW driver functions for the **TRNG**
| `neorv32_twi.c`     | `neorv32_twi.h`        | HW driver functions for the **TWI**
| `neorv32_uart.c`    | `neorv32_uart.h`       | HW driver functions for the **UART0** and **UART1**
| `neorv32_wdt.c`     | `neorv32_wdt.h`        | HW driver functions for the **WDT**
| `neorv32_xip.c`     | `neorv32_xip.h`        | HW driver functions for the **XIP**
| `neorv32_xirq.c`    | `neorv32_xirq.h`       | HW driver functions for the **XIRQ**
| `syscalls.c`        | -                      | newlib "system calls"
| -                   | `legacy.h`             | backwards compatibility wrappers and functions (do not use for new designs)
|=======================

.Core Library Documentation
[TIP]
The _doxygen_-based documentation of the software framework including all core libraries is available online at
https://stnolting.github.io/neorv32/sw/files.html.

.CMSIS System View Description File (SVD)
[TIP]
A CMSIS-SVD-compatible **System View Description (SVD)** file including all peripherals is available in `sw/svd`.


<<<
// ####################################################################################################################
:sectnums:
=== Application Makefile

Application compilation is based on a single, centralized **GNU makefile** (`sw/common/common.mk`). Each project in the
`sw/example` folder provides a makefile that just includes this central makefile.

[TIP]
When creating a new project, copy an existing project folder or at least the makefile to the new project folder.
It is recommended to create new projects also in `sw/example` to keep the file dependencies. However, these
dependencies can be manually configured via makefile variables if the new project is located somewhere else.

[NOTE]
Before the makefile can be used to compile applications, the RISC-V GCC toolchain needs to be installed and
the compiler's `bin` folder has to be added to the system's `PATH` variable. More information can be found in
https://stnolting.github.io/neorv32/ug/#_software_toolchain_setup[User Guide: Software Toolchain Setup].

The makefile is invoked by simply executing `make` in the console. For example:

[source,bash]
----
neorv32/sw/example/demo_blink_led$ make
----

:sectnums:
==== Targets

Just executing `make` (or executing `make help`) will show the help menu listing all available targets.

[source,makefile]
----
$ make
<<< NEORV32 SW Application Makefile >>>
Make sure to add the bin folder of RISC-V GCC to your PATH variable.

=== Targets ===
 help       - show this text
 check      - check toolchain
 info       - show makefile/toolchain configuration
 asm        - compile and generate <main.asm> assembly listing file for manual debugging
 elf        - compile and generate <main.elf> ELF file
 bin        - compile and generate <neorv32_raw_exe.bin> RAW executable file (binary file, no header)
 hex        - compile and generate <neorv32_raw_exe.hex> RAW executable file (hex char file, no header)
 image      - compile and generate VHDL IMEM boot image (for application, no header) in local folder
 install    - compile, generate and install VHDL IMEM boot image (for application, no header)
 sim        - in-console simulation using default/simple testbench and GHDL
 all        - exe + install + hex + bin + asm
 elf_info   - show ELF layout info
 clean      - clean up project home folder
 clean_all  - clean up whole project, core libraries and image generator
 bl_image   - compile and generate VHDL BOOTROM boot image (for bootloader only, no header) in local folder
 bootloader - compile, generate and install VHDL BOOTROM boot image (for bootloader only, no header)

=== Variables ===
 USER_FLAGS   - Custom toolchain flags [append only], default ""
 EFFORT       - Optimization level, default "-Os"
 MARCH        - Machine architecture, default "rv32i"
 MABI         - Machine binary interface, default "ilp32"
 APP_INC      - C include folder(s) [append only], default "-I ."
 ASM_INC      - ASM include folder(s) [append only], default "-I ."
 RISCV_PREFIX - Toolchain prefix, default "riscv32-unknown-elf-"
 NEORV32_HOME - NEORV32 home folder, default "../../.."
----


:sectnums:
==== Configuration

The compilation flow is configured via variables right at the beginning of the central
makefile (`sw/common/common.mk`):

[TIP]
The makefile configuration variables can be overridden or extended directly when invoking the makefile. For
example `$ make MARCH=rv32ic clean_all exe` overrides the default `MARCH` variable definitions.
Permanent modifications/definitions can be made in the project-local makefile
(e.g., `sw/example/demo_blink_led/makefile`).

.Default Makefile Configuration
[source,makefile]
----
# *****************************************************************************
# USER CONFIGURATION
# *****************************************************************************
# User's application sources (*.c, *.cpp, *.s, *.S); add additional files here
APP_SRC ?= $(wildcard ./*.c) $(wildcard ./*.s) $(wildcard ./*.cpp) $(wildcard ./*.S)
# User's application include folders (don't forget the '-I' before each entry)
APP_INC ?= -I .
# User's application include folders - for assembly files only (don't forget the '-I' before each
entry)
ASM_INC ?= -I .
# Optimization
EFFORT ?= -Os
# Compiler toolchain
RISCV_PREFIX ?= riscv32-unknown-elf-
# CPU architecture and ABI
MARCH ?= rv32i
MABI  ?= ilp32
# User flags for additional configuration (will be added to compiler flags)
USER_FLAGS ?=
# Relative or absolute path to the NEORV32 home folder
NEORV32_HOME ?= ../../..
# *****************************************************************************
----

.Variables Description
[cols="<3,<10"]
[grid="none"]
|=======================
| `APP_SRC`      | The source files of the application (`*.c`, `*.cpp`, `*.S` and `*.s` files are allowed;
files of these types in the project folder are automatically added via wild cards). Additional files can be added separated by white spaces
| `APP_INC`      | Include file folders; separated by white spaces; must be defined with `-I` prefix
| `ASM_INC`      | Include file folders that are used only for the assembly source files (`*.S`/`*.s`).
| `EFFORT`       | Optimization level, optimize for size (`-Os`) is default; legal values: `-O0`, `-O1`, `-O2`, `-O3`, `-Os`, `-Ofast`, ...
| `RISCV_PREFIX` | The toolchain prefix to be used; follows the triplet naming convention `[architecture]-[host_system]-[output]-...`
| `MARCH`        | The targeted RISC-V architecture/ISA; enable compiler support of optional CPU extension by adding the according extension
name (e.g. `rv32im` for `M` CPU extension; see https://stnolting.github.io/neorv32/ug/#_enabling_risc_v_cpu_extensions[User Guide: Enabling RISC-V CPU Extensions]
for more information
| `MABI`         | Application binary interface (default: 32-bit integer ABI `ilp32`)
| `USER_FLAGS`   | Additional flags that will be forwarded to the compiler tools
| `NEORV32_HOME` | Relative or absolute path to the NEORV32 project home folder; adapt this if the makefile/project is not in the project's
default `sw/example` folder
|=======================

:sectnums:
==== Default Compiler Flags

The following default compiler flags are used for compiling an application. These flags are defined via the
`CC_OPTS` variable.

[cols="<3,<9"]
[grid="none"]
|=======================
| `-Wall`               | Enable all compiler warnings.
| `-ffunction-sections` | Put functions and data segment in independent sections. This allows a code optimization as dead code and unused data can be easily removed.
| `-nostartfiles`       | Do not use the default start code. Instead, the NEORV32-specific start-up code (`sw/common/crt0.S`) is used (pulled-in by the linker script).
| `-Wl,--gc-sections`   | Make the linker perform dead code elimination.
| `-lm`                 | Include/link with `math.h`.
| `-lc`                 | Search for the standard C library when linking.
| `-lgcc`               | Make sure we have no unresolved references to internal GCC library subroutines.
| `-mno-fdiv`           | Use built-in software functions for floating-point divisions and square roots (since the according instructions are not supported yet).
| `-g`                  | Include debugging information/symbols in ELF.
|=======================

:sectnums:
==== Custom (Compiler) Flags

Custom flags can be _appended_ to the `USER_FLAGS` variable. This allows to customize the entire software framework while
calling `make` without the need to change the makefile(s) or the linker script.

The following example will add debug symbols to the executable (`-g`) and will also define the linker script's
`__neorv32_heap_size` setting the maximal heap size to 4096 bytes:

.Example: using the `USER_FLAGS` variable for customization
[source,bash]
----
$ make USER_FLAGS+="-g -Wl,--__neorv32_heap_size,__heap_size=4096" clean_all exe
----


<<<
// ####################################################################################################################
:sectnums:
=== Executable Image Format

In order to generate an executable for th processors all source files have to be compiled, linked
and packed into a _final executable_.

:sectnums:
==== Linker Script

After all the application sources have been compiled, they need to be _linked_.
For this purpose the makefile uses the NEORV32-specific linker script `sw/common/neorv32.ld` for
linking all object files that were generated during compilation. In general, the linker script defines
three memory sections: `rom`, `ram` and `iodev`.

.Linker script - memory sections
[cols="<2,<8"]
[options="header",grid="rows"]
|=======================
| Memory section  | Description
| `ram`           | Data memory address space (processor-internal/external DMEM)
| `rom`           | Instruction memory address space (processor-internal/external IMEM) _or_ internal bootloader ROM
| `iodev`         | Processor-internal memory-mapped IO/peripheral devices address space
|=======================

[NOTE]
The `iodev` section is entirely defined by the processor hardware layout and should not be modified at all.

[NOTE]
The `rom` section is automatically re-mapped to the processor-internal <<_bootloader_rom_bootrom>> when (re-)compiling the
bootloader

Each section has two main attributes: `ORIGIN` and `LENGTH`. `ORIGIN` defines the base address of the according section
while `LENGTH` defines its size in bytes. The attributes are configured indirectly via variables that provide default values.

.Linker script - section configuration
[source]
----
/* Default rom/ram (IMEM/DMEM) sizes */
__neorv32_rom_size = DEFINED(__neorv32_rom_size) ? __neorv32_rom_size : 2048M;
__neorv32_ram_size = DEFINED(__neorv32_ram_size) ? __neorv32_ram_size : 8K;

/* Default section base addresses - do not change this unless the hardware-defined address space layout is changed! */
__neorv32_rom_base = DEFINED(__neorv32_rom_base) ? __neorv32_rom_base : 0x00000000; /* = VHDL package's "ispace_base_c" */
__neorv32_ram_base = DEFINED(__neorv32_ram_base) ? __neorv32_ram_base : 0x80000000; /* = VHDL package's "dspace_base_c" */
----

Only the region **sizes** should be modified by the user. The base addresses are defined by the processor's hardware (see section
<<_address_space>>) and should not be altered at all. The size (and base) configuration can be edited by the user - either by explicitly
changing the default values in the linker script or by overriding them when invoking `make`:
 
.Overriding default rom size configuration (setting 4096 bytes)
[source, bash]
----
$ make USER_FLAGS+="-Wl,--defsym,__neorv32_rom_size=4096" clean_all exe
----

[IMPORTANT]
`neorv32_rom_base` (= `ORIGIN` of the `ram` section) has to be always identical to the processor's `dspace_base_c` hardware configuration.
Also, `neorv32_ram_base` (= `ORIGIN` of the `rom` section) has to be always identical to the processor's `ispace_base_c` hardware configuration.

[NOTE]
The default configuration for the `rom` section assumes a maximum of 2GB _logical_ memory address space. This size does not have
to reflect the _actual_ physical size of the instruction memory (internal IMEM and/or processor-external memory). It just provides a maximum
limit. When uploading a new executable via the bootloader, the bootloader itself checks if sufficient _physical_ instruction memory is available.
If a new executable is embedded right into the internal-IMEM the synthesis tool will check, if the configured instruction memory size
is sufficient (e.g., via the <<_mem_int_imem_size>> generic).

The linker maps all the regions from the compiled object files into five final sections: `.text`, `.rodata`, `.data`, `.bss` and `.heap`.
These regions contain everything required for the application to run:

.Linker script - memory regions
[cols="<1,<9"]
[options="header",grid="rows"]
|=======================
| Region    | Description
| `.text`   | Executable instructions generated from the start-up code and all application sources.
| `.rodata` | Constants (like strings) from the application; also the initial data for initialized variables.
| `.data`   | This section is required for the address generation of fixed (= global) variables only.
| `.bss`    | This section is required for the address generation of dynamic memory constructs only.
| `.heap`   | This section is required for the address generation of dynamic memory constructs only.
|=======================

The `.text` and `.rodata` sections are mapped to processor's instruction memory space and the `.data`,
`.bss` and `heap` sections are mapped to the processor's data memory space. Finally, the `.text`, `.rodata` and `.data`
sections are extracted and concatenated into a single file `main.bin`.

.Section Alignment
[NOTE]
The default NEORV32 linker script aligns _all_ regions so they start and end on a 32-bit (word) boundary. The default
NEORV32 start-up code (crt0) makes use of this alignment by using word-level memory instructions to initialize the `.data`
section and to clear the `.bss` section (faster!).


:sectnums:
==== RAM Layout

The default NEORV32 linker script uses all of the defined RAM (linker script memory section `ram`) to create four areas.
Note that depending on the application some areas might not be existent at all.

.Default RAM Layout
image::ram_layout.png[400]

[start=1]
. **Constant data (`.data`)**: The constant data section is placed right at the beginning of the RAM. For example, this section
contains _explicitly initialized_ global variables. This section is initialized by the executable.
. **Dynamic data (`.bss`)**: The constant data section is followed by the dynamic data section, which contains _uninitialized_ data
like global variables without explicit initialization. This section is cleared by the start-up code `crt0.S`.
. **Heap (`.heap`)**: The heap is used for dynamic memory that is managed by functions like `malloc()` and `free()`. The heap
grows upwards. This section is not initialized at all.
. **Stack**: The stack starts at the very end of the RAM at address `ORIGIN(ram) + LENGTH(ram) - 4`. The stack grows downwards.

There is _no explicit limit_ for the maximum stack size as this is hard to check. However, a physical memory protection rule could
be used to configure a maximum size by adding a "protection area" between stack and heap (a PMP region without any access rights).

.Heap Size
[IMPORTANT]
The maximum size of the heap is defined by the linker script's `__neorv32_heap_size` variable. This variable has to be
**explicitly defined** in order to define a heap size (and to use dynamic memory allocation at all) other than zero. The user
can define the heap size while invoking the application makefile: `$ USER_FLAGS+="-Wl,--defsym,__neorv32_heap_size=4k" make clean_all exe`
(defines a heap size of 4*1024 bytes).

.Heap-Stack Collisions
[WARNING]
Take care when using dynamic memory to avoid collision of the heap and stack memory areas. There is no compile-time protection
mechanism available as the actual heap and stack size are defined by _runtime_ data. Also beware of fragmentation when
using dynamic memory allocation.


:sectnums:
==== C Standard Library

The NEORV32 is a processor for _embedded_ applications, which is not capable of running desktop OSs like Linux
(at least not without emulation). Hence, the default software framework relies on **newlib** as default C standard library.

.RTOS Support
[NOTE]
The NEORV32 CPU and processor **do support** embedded RTOS like FreeRTOS and Zephyr. See the User guide section
https://stnolting.github.io/neorv32/ug/#_zephyr_rtos_support[Zephyr RTOS Support] and
https://stnolting.github.io/neorv32/ug/#_freertos_support[FreeRTOS Support]
for more information.

Newlib provides stubs for common "system calls" (like file handling and standard input/output) that are used by other
C libraries like `stdio`. These stubs are available in `sw/source/syscalls.c` and were adapted for the NEORV32 processor.

.Standard Console(s)
[NOTE]
<<_primary_universal_asynchronous_receiver_and_transmitter_uart0, UART0>>
is used to implement all the standard input, output and error consoles (`STDIN`, `STDOUT` and `STDERR`).

.Constructors and Destructors
[NOTE]
Constructors and destructors for plain C code or for C++ applications are supported by the software framework.
See `sw/example/hellp_cpp` for a minimal example.

.Newlib Test/Demo Program
[TIP]
A simple test and demo program, which uses some of newlib's core functions (like `malloc`/`free` and `read`/`write`)
is available in `sw/example/demo_newlib`


:sectnums:
==== Executable Image Generator

The `main.bin` file is packed by the NEORV32 image generator (`sw/image_gen`) to generate the final executable file.
The image generator can generate several types of executables selected by a flag when calling the generator:

[cols="<1,<9"]
[grid="none"]
|=======================
| `-app_bin` | Generates an executable binary file `neorv32_exe.bin` (including header) for UART uploading via the bootloader.
| `-app_img` | Generates an executable VHDL memory initialization image (no header) for the processor-internal IMEM. This option generates the `rtl/core/neorv32_application_image.vhd` file.
| `-raw_hex` | Generates a plain ASCII hex-char file `neorv32_raw_exe.hex` (no header) for custom purpose.
| `-raw_bin` | Generates a plain binary file `neorv32_raw_exe.bin` (no header) for custom purpose.
| `-bld_img` | Generates an executable VHDL memory initialization image (no header) for the processor-internal BOOT ROM. This option generates the `rtl/core/neorv32_bootloader_image.vhd` file.
|=======================

All these options are managed by the makefile. The "normal application2 compilation flow will generate the `neorv32_exe.bin`
executable for uploading via UART to the default NEORV32 bootloader.

.Image Generator Compilation
[NOTE]
The sources of the image generator are automatically compiled when invoking the makefile (requiring a native GCC installation).

.Executable Header
[NOTE]
The image generator add a small header to the `neorv32_exe.bin` executable, which consists of three 32-bit words located right at the
beginning of the file. The first word of the executable is the signature word and is always `0x4788cafe`. Based on this word the bootloader
can identify a valid image file. The next word represents the size in bytes of the actual program
image in bytes. A simple "complement" checksum of the actual program image is given by the third word. This
provides a simple protection against data transmission or storage errors. **Note that this executable format cannot be used for _direct_
execution (e.g. via XIP or direct memory access).**


:sectnums:
==== Start-Up Code (crt0)

The CPU and also the processor require a minimal start-up and initialization code to bring the CPU (and the SoC)
into a stable and initialized state and to initialize the C runtime environment before the actual application can be executed.
This start-up code is located in `sw/common/crt0.S` and is automatically linked _every_ application program
and placed right before the actual application code so it gets executed right after reset.

The `crt0.S` start-up performs the following operations:

[start=1]
. Clear <<_mstatus>>.
. Clear <<_mie>> disabling all interrupt sources.
. Install an early-boot dummy trap handler to <<_mtvec>>.
. Initialize the global pointer `gp` and the stack pointer `sp` according to the <<_ram_layout>> provided by the linker script.
. Initialize all integer register `x1 - x31` (only `x1 - x15` if the `E` CPU extension is enabled).
. Setup `.data` section to configure initialized variables.
. Clear the `.bss` section.
. Call all _constructors_ (if there are any).
. Call the application's `main` function (with no arguments: `argc` = `argv` = 0).
. If `main` returns:
** Tnterrupts are disabled by clearing <<_mie>>.
** `mains`'s return value is copied to the <<_mscratch>> CSR to allow inspection by the debugger.
** Call all _destructors_ (if there are any).
** An optional <<_after_main_handler>> is called (if defined at all).
** The CPU enters sleep mode (using the `wfi` instruction) or remains in an endless loop (if `wfi` "returns").

.Bootloader Start-Up Code
[NOTE]
The bootloader uses the same start-up code as any "usual" application. However, certain parts are omitted when compiling
`crt0` for the bootloader (like calling constructors and destructors). See the `crt0` source code for more information.


:sectnums:
===== After-Main Handler

If the application's `main()` function actually returns, an _after main handler_ can be executed. This handler is a "normal" function
as the C runtime is still available when executed. If this handler uses any kind of peripheral/IO modules make sure these are
already initialized within the application. Otherwise you have to initialize them _inside_ the handler.

.After-main handler - function prototype
[source,c]
----
void __neorv32_crt0_after_main(int32_t return_code);
----

The function has exactly one argument (`return_code`) that provides the _return value_ of the application's main function.
For instance, this variable contains `-1` if the main function returned with `return -1;`. The after-main handler itself does
not provide a return value.

A simple UART output can be used to inform the user when the application's main function returns
(this example assumes that UART0 has been already properly configured in the actual application):

.After-main handler - simple example
[source,c]
----
void __neorv32_crt0_after_main(int32_t return_code) {

  neorv32_uart0_printf("\n<RTE> main function returned with exit code %i. </RTE>\n", return_code); <1>
}
----
<1> Use `<RTE>` here to make clear this is a message comes from the runtime environment.

[NOTE]
The after-main handler is executed _after_ executing all destructor functions (if there are any at all).


<<<
// ####################################################################################################################

include::software_bootloader.adoc[]


<<<
// ####################################################################################################################

include::software_rte.adoc[]
