neorv32/docs/datasheet/software.adoc
2025-04-13 08:06:43 +02:00

479 lines
23 KiB
Text

<<<
:sectnums:
== Software Framework
The NEORV32 project comes with a complete software ecosystem called the "software framework" which
is based on the C-language RISC-V GCC port and consists of the following parts:
* <<_compiler_toolchain>>
* <<_core_libraries>>
* <<_system_view_description_file_svd>>
* <<_application_makefile>>
* <<_default_compiler_flags>>
* <<_linker_script>>
* <<_c_standard_library>>
* <<_start_up_code_crt0>>
* <<_executable_image_formats>>
* <<_neorv32_runtime_environment>>
* <<_bootloader>>
.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 illustrating 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 and open RISC-V GCC-port. You can find the compiler sources and
build instructions in the official RISC-V GNU toolchain GitHub repository: https://github.com/riscv/riscv-gnutoolchain.
.Toolchain Installation
[TIP]
More information regarding the toolchain (building from scratch or downloading prebuilt ones) can be found in the
user guide 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 library files are automatically included and linked by adding the following include statement:
[source,c]
----
#include <neorv32.h> // NEORV32 HAL, core and runtime libraries
----
The NEORV32 HAL consists of the following files.
.NEORV32 Hardware Abstraction Layer File List
[cols="<3,<3,<6"]
[options="header",grid="rows"]
|=======================
| C source file | C header file | Description
| - | `neorv32.h` | Main NEORV32 library file
| `neorv32_aux.c` | `neorv32_aux.h` | General auxiliary/helper function
| `neorv32_cfs.c` | `neorv32_cfs.h` | <<_custom_functions_subsystem_cfs>> HAL
| `neorv32_clint.c` | `neorv32_clint.h` | <<_core_local_interruptor_clint>> HAL
| `neorv32_cpu.c` | `neorv32_cpu.h` | <<_neorv32_central_processing_unit_cpu>> HAL
| | `neorv32_cpu_csr.h` | <<_control_and_status_registers_csrs>> definitions
| `neorv32_cpu_cfu.c` | `neorv32_cpu_cfu.h` | <<_custom_functions_unit_cfu>> HAL
| `neorv32_crc.c` | `neorv32_crc.h` | <<_cyclic_redundancy_check_crc>> HAL
| `neorv32_dma.c` | `neorv32_dma.h` | <<_direct_memory_access_controller_dma>> HAL
| `neorv32_gpio.c` | `neorv32_gpio.h` | <<_general_purpose_input_and_output_port_gpio>> HAL
| `neorv32_gptmr.c` | `neorv32_gptmr.h` | <<_general_purpose_timer_gptmr>> HAL
| - | `neorv32_intrinsics.h` | Macros for intrinsics and custom instructions
| `neorv32_neoled.c` | `neorv32_neoled.h` | <<_smart_led_interface_neoled>> HAL
| `neorv32_onewire.c` | `neorv32_onewire.h` | <<_one_wire_serial_interface_controller_onewire>> HAL
| `neorv32_pwm.c` | `neorv32_pwm.h` | <<_pulse_width_modulation_controller_pwm>> HAL
| `neorv32_rte.c` | `neorv32_rte.h` | <<_neorv32_runtime_environment>>
| `neorv32_sdi.c` | `neorv32_sdi.h` | <<_serial_data_interface_controller_sdi>> HAL
| `neorv32_slink.c` | `neorv32_slink.h` | <<_stream_link_interface_slink>> HAL
| `neorv32_smp.c` | `neorv32_smp.h` | HAL for the SMP <<_dual_core_configuration>>
| `neorv32_spi.c` | `neorv32_spi.h` | <<_serial_peripheral_interface_controller_spi>> HAL
| | `neorv32_sysinfo.h` | <<_system_configuration_information_memory_sysinfo>> HAL
| `neorv32_trng.c` | `neorv32_trng.h` | <<_true_random_number_generator_trng>> HAL
| `neorv32_twd.c` | `neorv32_twd.h` | <<_two_wire_serial_device_controller_twd>> HAL
| `neorv32_twi.c` | `neorv32_twi.h` | <<_two_wire_serial_interface_controller_twi>> HAL
| `neorv32_uart.c` | `neorv32_uart.h` | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0>> and UART1 HAL
| `neorv32_wdt.c` | `neorv32_wdt.h` | <<_watchdog_timer_wdt>> HAL
| `neorv32_newlib.c` | - | Platform-specific system calls for _newlib_
|=======================
.Defines and Macros
[TIP]
Macros and defines provides by the NEORV32 software framework are written in capital letters.
.Core Libraries 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.
<<<
// ####################################################################################################################
:sectnums:
=== System View Description File (SVD)
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 centralized GNU makefile (`sw/common/common.mk`). Each software project
(for example the ones in `sw/example` folder) should provide a local makefile that just includes the central makefile:
```makefile
# Set path to NEORV32 root directory
NEORV32_HOME ?= ../../..
# Include the main NEORV32 makefile
include $(NEORV32_HOME)/sw/common/common.mk
```
Thus, the functionality of the central makefile (including all targets) becomes available for the project.
A project-local makefile should be used to define all setup-relevant configuration options instead of changing the
central makefile to keep the code base clean. Setting variables in the project-local makefile will override the default
configuration. Most example projects already provide a makefile that list all relevant configuration options.
The following example shows the configuration of a local Makefile:
```makefile
# Override the default CPU ISA
MARCH = rv32imc_zicsr_zifencei
# Override the default RISC-V GCC prefix
RISCV_PREFIX ?= riscv-none-elf-
# Override default optimization goal
EFFORT = -Os
# Add extended debug symbols for Eclipse
USER_FLAGS += -ggdb -gdwarf-3
# Additional sources
APP_SRC += $(wildcard ./*.c)
APP_INC += -I .
# Adjust processor IMEM size and base address
USER_FLAGS += -Wl,--defsym,__neorv32_rom_size=16k
USER_FLAGS += -Wl,--defsym,__neorv32_rom_base=0x00000000
# Adjust processor DMEM size and base address
USER_FLAGS += -Wl,--defsym,__neorv32_ram_size=8k
USER_FLAGS += -Wl,--defsym,__neorv32_ram_base=0x80000000
# Adjust maximum heap size
USER_FLAGS += -Wl,--defsym,__neorv32_heap_size=2k
# Set path to NEORV32 root directory
NEORV32_HOME ?= ../../..
# Include the main NEORV32 makefile
include $(NEORV32_HOME)/sw/common/common.mk
```
.Setup of a New Project
[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.
For more complex projects, it may be useful to use explicit `source` and `include` folders. See `sw/example/coremark` for an example.
:sectnums:
==== Makefile Targets
Invoking a project-local makefile (executing `make` or `make help`) will show the help menu that lists all
available targets as well as all variable including their _current_ setting.
[source,makefile]
----
neorv32/sw/example/hello_world$ make
NEORV32 Software Makefile
Find more information at https://github.com/stnolting/neorv32
Use make V=1 or set BUILD_VERBOSE to increase build verbosity
Targets:
help show this text
check check toolchain
info show makefile/toolchain configuration
gdb start GNU debugging session
asm compile and generate <main.asm> assembly listing file for manual debugging
elf compile and generate <main.elf> ELF file
exe compile and generate <neorv32_exe.bin> executable image file for bootloader upload (includes a HEADER!)
bin compile and generate <neorv32_raw_exe.bin> executable memory image
hex compile and generate <neorv32_raw_exe.hex> executable memory image
coe compile and generate <neorv32_raw_exe.coe> executable memory image
mem compile and generate <neorv32_raw_exe.mem> executable memory image
mif compile and generate <neorv32_raw_exe.mif> executable memory image
image compile and generate VHDL IMEM application boot image <neorv32_application_image.vhd> in local folder
install compile, generate and install VHDL IMEM application boot image <neorv32_application_image.vhd>
sim in-console simulation using default/simple testbench and GHDL
hdl_lists regenerate HDL file-lists (*.f) in NEORV32_HOME/rtl
all exe + install + hex + bin + asm
elf_info show ELF layout info
elf_sections show ELF sections
clean clean up project home folder
clean_all clean up project home folder and image generator
bl_image compile and generate VHDL BOOTROM bootloader boot image <neorv32_bootloader_image.vhd> in local folder
bootloader compile, generate and install VHDL BOOTROM bootloader boot image <neorv32_bootloader_image.vhd>
Variables:
BUILD_VERBOSE Set to increase build verbosity: 0
USER_FLAGS Custom toolchain flags [append only]: "-ggdb -gdwarf-3 -Wl,--defsym,__neorv32_rom_size=16k -Wl,--defsym,__neorv32_ram_size=8k"
USER_LIBS Custom libraries [append only]: ""
EFFORT Optimization level: "-Os"
MARCH Machine architecture: "rv32i_zicsr_zifencei"
MABI Machine binary interface: "ilp32"
APP_INC C include folder(s) [append only]: "-I ."
APP_SRC C source folder(s) [append only]: "./main.c "
APP_OBJ Object file(s) [append only]
ASM_INC ASM include folder(s) [append only]: "-I ."
RISCV_PREFIX Toolchain prefix: "riscv-none-elf-"
NEORV32_HOME NEORV32 home folder: "../../.."
GDB_ARGS GDB (connection) arguments: "-ex target extended-remote localhost:3333"
GHDL_RUN_FLAGS GHDL simulation run arguments: ""
----
.Build Artifacts
[NOTE]
All _intermediate_ build artifacts (like object files and binaries) will be places into a (new) project-local
folder named `build`. The _resulting_ build artifacts (like executable, the main ELF and all memory
initialization/image files) will be placed in the root project folder.
.Increse Verbosity
[TIP]
Use `make V=1` or set `BUILD_VERBOSE` in your environment to increase build verbosity.
:sectnums:
==== Default Compiler Flags
The central makefile uses specific compiler flags to tune the code to the NEORV32 hardware. Hence, these flags should not
be altered. However, experienced users can modify them to further tune compilation.
.Compiler Options (`CC_OPTS`)
[cols="<3,<9"]
[grid="none"]
|=======================
| `-Wall` | Enable all compiler warnings.
| `-ffunction-sections` | Put functions in independent sections. This allows a code optimization as dead code can be easily removed.
| `-fdata-sections` | Put data segment in independent sections. This allows a code optimization as 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).
| `-mno-fdiv` | Use built-in software functions for floating-point divisions and square roots (since the according instructions are not supported yet).
| `-mstrict-align` | Unaligned memory accesses cannot be resolved by the hardware and require emulation.
| `-mbranch-cost=10` | Branching costs a lot of cycles.
| `-Wl,--gc-sections` | Make the linker perform dead code elimination.
| `-ffp-contract=off` | Disable floating-point expression contraction.
| `-g` | Add (simple) debug information.
|=======================
.Linker Libraries (`LD_LIBS`)
[cols="<2,<9"]
[grid="none"]
|=======================
| `-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.
|=======================
.Advanced Debug Symbols
[IMPORTANT]
By default, only "simple" symbols are added to the ELF (`-g`). Extended debug flags (e.g. for Eclipse) can be added
using the `USER_FLAGS` variable (e.g. `USER_FLAGS += -ggdb -gdwarf-3`). Note that other debug flags may be required
depending of the GCC/GDB version
<<<
// ####################################################################################################################
:sectnums:
=== Linker Script
The NEORV32-specific linker script (`sw/common/neorv32.ld`) is used to link the compiled sources according to the
processor's <<_address_space>>). For the final executable, only two memory segments are required:
.Linker script - Memory Segments
[cols="<2,<8"]
[options="header",grid="rows"]
|=======================
| Memory section | Description
| `rom` | Instruction memory address space (processor-internal <<_instruction_memory_imem>> and/or external memory)
| `ram` | Data memory address space (processor-internal <<_data_memory_dmem>> and/or external memory)
|=======================
These two sections are configured by several variables defined in the linker script and exposed to the build
framework (aka the makefile). Those variable allow to customized the RAM/ROM sizes and base addresses. Additionally,
a certain amount of the RAM can be reserved for the software-managed heap (see <<_ram_layout>>).
.Linker script - Configuration
[cols="<2,<7,^1"]
[options="header",grid="rows"]
|=======================
| Memory section | Description | Default
| `__neorv32_rom_size` | "ROM" size (instruction memory / IMEM) | 16kB
| `__neorv32_ram_size` | "RAM" size (data memory / DMEM) | 8kB
| `__neorv32_rom_base` | "ROM" base address (instruction memory / IMEM) | `0x00000000`
| `__neorv32_ram_base` | "RAM" base address (data memory / DMEM) | `0x80000000`
| `__neorv32_heap_size` | Maximum heap size; part of the "RAM" | 0kB
|=======================
Each variable provides a default value (e.g. "16K" for the instruction memory /ROM /IMEM size). These defaults can
be overridden by setup-specific values to take the user-defined processor configuration into account (e.g. a different IMEM
size). The `USER_FLAGS` variable provided by the <<_application_makefile>> can also be used to customize the memory
configuration. For example, the following line can be added to a project-specific local makefile to adjust the memory
sizes:
.Overriding Default Memory Sizes (configuring 64kB IMEM and 32kB DMEM)
[source, makefile]
----
USER_FLAGS += "-Wl,--defsym,__neorv32_rom_size=64k -Wl,--defsym,__neorv32_ram_size=32k"
----
.Memory Configuration Constraints
[IMPORTANT]
Memory sizes have to be a multiple of 4 bytes. Memory base addresses have to be 32-bit-aligned.
:sectnums:
==== RAM Layout
The default NEORV32 linker script uses the defined RAM size to map several sections.
Note that depending on the application some sections might have zero size.
.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 <<_start_up_code_crt0>>.
. **Dynamic data (`.bss`)**: The constant data section is followed by the dynamic data section that contains _uninitialized_
data like global variables without explicit initialization. This section is cleared by the <<_start_up_code_crt0>>.
. **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 end of the RAM at the last 16-byte aligned address. According to the RISC-V ABI / calling
convention the stack is 128-bit-aligned before procedure entry. The stack grows downwards.
.Heap Size
[IMPORTANT]
The maximum size of the heap is defined by the `__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.
.Heap-Stack Collision
[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.
<<<
// ####################################################################################################################
:sectnums:
=== C Standard Library
The default software framework relies on **newlib** as default C standard library. Newlib provides hooks for common
"system calls" (like file handling and standard input/output) that are used by other C libraries like `stdio`.
These hooks are available in `sw/lib/source/newlib.c` and were adapted for the NEORV32 processor.
.Standard Consoles
[NOTE]
The <<_primary_universal_asynchronous_receiver_and_transmitter_uart0, UART0>>
is used to implement all the standard input, output and error consoles (`STDIN`, `STDOUT` and `STDERR`).
Note that `\n` (newline) is automatically converted to `\r\n` (carriage-return and newline).
.Constructors and Destructors
[NOTE]
Constructors and destructors for plain C code or for C++ applications are supported by the software framework.
See `sw/example/hello_cpp` for a minimal example. Note that constructor and destructors are only executed
by core 0 (primary core) in the SMP <<_dual_core_configuration>>.
.Newlib Test/Demo Program
[TIP]
A simple test and demo program that uses some of newlib's system functions (like `malloc`/`free` and `read`/`write`)
is available in `sw/example/demo_newlib`.
<<<
// ####################################################################################################################
:sectnums:
=== Start-Up Code (crt0)
The CPU and also the processor require a minimal start-up and initialization code to bring the hardware into an
operational state. Furthermore, the C runtime requires an initialization before compiled code can be executed.
This setup is done by the start-up code (`sw/common/crt0.S`) which is automatically linked with _every_ application
program and gets mapped before the actual application code so it gets executed right after boot.
The `crt0.S` start-up performs the following operations:
[start=1]
. Setup the stack pointer and the global pointer according to the <<_ram_layout>> provided by the <<_linker_script>> symbols.
. Initialize <<_mstatus>> CSR disabling machine-level interrupts.
. Install an <<_early_trap_handler>> to <<_mtvec>> CSR.
. Clear <<_mie>> CSR disabling all interrupt sources.
. Initialize all integer register `x1` - `x31` (only `x1` - `x15` if the `E` CPU extension is enabled).
. If the executing CPU core is not core 0 an SMP-specific code is executed and the CPU is halted in sleep mode. See section <<_dual_core_boot>> for more information.
. 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:
** All interrupt sources are disabled by clearing <<_mie>> CSR.
** The return value of `main()` is copied to the <<_mscratch>> CSR to allow inspection by the debugger.
** The <<_early_trap_handler>> is re-installed to <<_mtvec>> CSR.
** Call all _destructors_ (if there are any).
** Execute an `ebreak` instruction to enter debug mode if an external debugger is connected.
** The CPU enters sleep mode executing the `wfi` instruction in an endless loop.
:sectnums:
==== Early Trap Handler
The start-up code provides a very basic trap handler for the early boot phase. This handler does nothing but
trying to move on to the next linear instruction whenever an interrupt or synchronous exception is encountered.
This simple trap handler does not interact with the stack at all as it just uses a single register that is backup-ed
using the <<_mscratch>> CSR.
<<<
// ####################################################################################################################
:sectnums:
=== Executable Image Formats
The compiled and linked executable (ELF file) is further processed by the NEORV32 image generator (`sw/image_gen`) to
generate the final executable file. The image generator can generate several types of executable file formats selected
by a flag when calling the generator.
**Note that all these options are managed by the makefile (see <<_makefile_targets>>).**
[cols="<2,<8"]
[grid="none"]
|=======================
| `-app_bin` | Generates an executable binary file (including a bootloader header) for upload via the bootloader.
| `-app_vhd` | Generates an executable VHDL memory initialization image for the processor-internal IMEM.
| `-bld_vhd` | Generates an executable VHDL memory initialization image for the processor-internal BOOT ROM.
| `-raw_hex` | Generates a raw 8x ASCII hex-char file for custom purpose.
| `-raw_bin` | Generates a raw binary file `for custom purpose.
| `-raw_coe` | Generates a raw COE file for FPGA memory initialization.
| `-raw_mem` | Generates a raw MEM file for FPGA memory initialization.
| `-raw_mif` | Generates a raw MIF file for FPGA memory initialization.
|=======================
.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]
for the `app_bin` option the image generator adds a small header to the executable. This header is required by the
<<_bootloader>> to identify and manage the executable. The header 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.**
<<<
// ####################################################################################################################
include::software_bootloader.adoc[]
<<<
// ####################################################################################################################
include::software_rte.adoc[]