URL
https://opencores.org/ocsvn/neorv32/neorv32/trunk
Subversion Repositories neorv32
[/] [neorv32/] [trunk/] [docs/] [datasheet/] [software.adoc] - Rev 74
Compare with Previous | Blame | View Log
:sectnums:== Software FrameworkTo make actual use of the NEORV32 processor, the project comes with a complete software ecosystem. Thisecosystem 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 accordingfiles 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 highly documented using **Doxygen**.See section <<Building the Software Framework Documentation>>.The documentation is automatically built and deployed to GitHub pages and is available onlineat https://stnolting.github.io/neorv32/sw/files.html .// ####################################################################################################################:sectnums:=== Compiler ToolchainThe toolchain for this project is based on the free RISC-V GCC-port. You can find the compiler sources andbuild 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`Alternatively, you can download my prebuilt `rv32i/e` toolchains for 64-bit x86 Linux from: https://github.com/stnolting/riscv-gcc-prebuiltThe default toolchain prefix used by the project's makefiles is **`riscv32-unknown-elf`**, which can be changesusing makefile flags at any time.[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 LibrariesThe NEORV32 project provides a set of C libraries that allows an easy usage 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> // add NEORV32 HAL 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 subsystemfootnote:[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 custom intrinsics & 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_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|=======================[TIP]A CMSIS-SVD-compatible **System View Description (SVD)** file including all peripherals is available in `sw/svd`.`sw/lib/include`. Currently, the following library files are available:<<<// ####################################################################################################################:sectnums:=== Application MakefileApplication compilation is based on a single, centralized **GNU makefiles** `sw/common/common.mk`. Each project in the`sw/example` folder features a makefile that just includes this central makefile. When creating a new project copy anexisting project folder or at least the makefile to the new project folder. It is suggested to create new projects alsoin `sw/example` to keep the file dependencies. However, these dependencies can be manually configured via makefilesvariables when 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. Furthermore,the `bin` folder of the compiler needs to be added to the system's `PATH` variable. More information can be found inhttps://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/blink_led$ make----:sectnums:==== TargetsJust 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 textcheck - check toolchaininfo - show makefile/toolchain configurationexe - compile and generate <neorv32_exe.bin> executable for upload via bootloaderhex - compile and generate <neorv32_exe.hex> executable raw fileimage - compile and generate VHDL IMEM boot image (for application) in local folderinstall - compile, generate and install VHDL IMEM boot image (for application)sim - in-console simulation using default/simple testbench and GHDLall - exe + hex + installelf_info - show ELF layout infoclean - clean up projectclean_all - clean up project, core libraries and image generatorbl_image - compile and generate VHDL BOOTROM boot image (for bootloader only!) in local folderbootloader - compile, generate and install VHDL BOOTROM boot image (for bootloader only!)== 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:==== ConfigurationThe compilation flow is configured via variables right at the beginning of the centralmakefile (`sw/common/common.mk`):[TIP]The makefile configuration variables can be overridden or extended directly when invoking the makefile. Forexample `$ 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/blink_led/makefile`)..Default Makefile Configuration[source,makefile]----# *****************************************************************************# USER CONFIGURATION# *****************************************************************************# User's application sources (*.c, *.cpp, *.s, *.S); add additional files hereAPP_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 eachentry)ASM_INC ?= -I .# OptimizationEFFORT ?= -Os# Compiler toolchainRISCV_PREFIX ?= riscv32-unknown-elf-# CPU architecture and ABIMARCH ?= rv32iMABI ?= ilp32# User flags for additional configuration (will be added to compiler flags)USER_FLAGS ?=# Relative or absolute path to the NEORV32 home folderNEORV32_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 extensionname (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'sdefault `sw/example` folder|=======================:sectnums:==== Default Compiler FlagsThe following default compiler flags are used for compiling an application. These flags are defined via the`CC_OPTS` variable. Custom flags can be _appended_ to it using the `USER_FLAGS` 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, use the NEORV32-specific start-up code (`sw/common/crt0.S`).| `-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).| `-falign-functions=4` .4+| Force a 32-bit alignment of functions and labels (branch/jump/call targets). This increases performance as it simplifies instruction fetch when using the C extension. As a drawback this will also slightly increase the program code.| `-falign-labels=4`| `-falign-loops=4`| `-falign-jumps=4`|=======================<<<// ####################################################################################################################:sectnums:=== Executable Image FormatIn order to generate a file, which can be executed by the processor, all source files have to be compiler, linkedand packed into a final _executable_.:sectnums:==== Linker ScriptWhen all the application sources have been compiled, they need to be _linked_ in order to generate a unifiedprogram file. For this purpose the makefile uses the NEORV32-specific linker script `sw/common/neorv32.ld` forlinking all object files that were generated during compilation.The linker script defines three memory _sections_: `rom`, `ram` and `iodev`. Each section provides specificaccess _attributes_: read access (`r`), write access (`w`) and executable (`x`)..Linker memory sections - general[cols="<2,^1,<7"][options="header",grid="rows"]|=======================| Memory section | Attributes | Description| `ram` | `rwx` | Data memory address space (processor-internal/external DMEM)| `rom` | `rx` | Instruction memory address space (processor-internal/external IMEM) _or_ internal bootloader ROM| `iodev` | `rw` | Processor-internal memory-mapped IO/peripheral devices address space|=======================These sections are defined right at the beginning of the linker script:.Linker memory sections - cut-out from linker script `neorv32.ld`[source,c]----MEMORY{ram (rwx) : ORIGIN = 0x80000000, LENGTH = DEFINED(make_bootloader) ? 512 : 8*1024rom (rx) : ORIGIN = DEFINED(make_bootloader) ? 0xFFFF0000 : 0x00000000, LENGTH = DEFINED(make_bootloader) ? 32K : 2048Miodev (rw) : ORIGIN = 0xFFFFFE00, LENGTH = 512}----Each memory section provides a _base address_ `ORIGIN` and a _size_ `LENGTH`. The base address and size of the `iodev` section isfixed and should not be altered. The base addresses and sizes of the `ram` and `rom` regions correspond to the total available instructionand data memory address space (see section <<_address_space_layout>>) as defined in `rtl/core/neorv32_package.vhd`.[IMPORTANT]`ORIGIN` of the `ram` section has to be always identical to the processor's `dspace_base_c` hardware configuration. ++`ORIGIN` of the `rom` section has to be always identical to the processor's `ispace_base_c` hardware configuration.The sizes of `rom` section is a little bit more complicated. The default linker script configuration assumes a _maximum_ of 2GB _logical_memory space, which is also the default configuration of the processor's hardware instruction memory address space. This size does not haveto reflect the _actual_ physical size of the instruction memory (internal IMEM and/or processor-external memory). It just provides a maximumlimit. When uploading 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 sizeis sufficient (e.g., via the <<_mem_int_imem_size>> generic).[IMPORTANT]The `rom` region uses a conditional assignment (via the `make_bootloader` symbol) for `ORIGIN` and `LENGTH` that is used to place"normal executable" (i.e. for the IMEM) or "the bootloader image" to their according memories. ++The `ram` region also uses a conditional assignment (via the `make_bootloader` symbol) for `LENGTH`. When compiling the bootloader(`make_bootloader` symbol is set) the generated bootloader will only use the _first_ 512 bytes of the data address space. This isa fall-back to ensure the bootloader can operate independently of the actual _physical_ data memory size.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 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`.:sectnums:==== RAM LayoutThe 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 Layoutimage::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 sectioncontains _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_ datalike 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 heapgrows 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 couldbe used to configure a maximum size by adding a "protection area" between stack and heap (a PMP region without any access rights).The maximum size of the heap is defined by the linker script's `__heap_size` symbol. This symbol can be overridden at any time.By default, the maximum heap size is 1/4 of the total RAM size..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 protectionmechanism available as the actual heap and stack size are defined by _runtime_ data. Also beware of fragmentation whenusing dynamic memory allocation.:sectnums:==== C Standard Library.Constructors and Deconstructors[IMPORTANT]The NEORV32 processor is an embedded system intended for running bare-metal or RTOS applications. To simplify this setupexplicit constructors and deconstructors are not supported by default. However, a minimal "deconstructor-alike" support isprovided by the <<_after_main_handler>>.The NEORV32 is a processor for _embedded_ applications. Hence, it is not capable of running desktop OSs like Linux(at least not without emulation). Hence, the software framework relies on a "bare-metal" setup that uses **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 sectionhttps://stnolting.github.io/neorv32/ug/#_zephyr_rtos_support[Zephyr RTOS Support] andhttps://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 otherC 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`)..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 GeneratorThe `main.bin` file is packed by the NEORV32 image generator (`sw/image_gen`) to generate the final executable file.[NOTE]The sources of the image generator are automatically compiled when invoking the makefile.The image generator can generate three 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` (for UART uploading via the bootloader).| `-app_hex` | Generates a plain ASCII hex-char file `neorv32_exe.hex` that can be used to initialize custom (instruction-) memories (in synthesis/simulation).| `-app_img` | Generates an executable VHDL memory initialization image for the processor-internal IMEM. This option generates the `rtl/core/neorv32_application_image.vhd` file.| `-bld_img` | Generates an executable VHDL memory initialization image 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 application_ compilation flow will generate the `neorv32_exe.bin`executable to be upload via UART to the NEORV32 bootloader.The image generator add a small header to the `neorv32_exe.bin` executable, which consists of three 32-bit words located right at thebeginning of the file. The first word of the executable is the signature word and is always `0x4788cafe`. Based on this word the bootloadercan identify a valid image file. The next word represents the size in bytes of the actual programimage in bytes. A simple "complement" checksum of the actual program image is given by the third word. Thisprovides a simple protection against data transmission or storage errors.: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 programand 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]. Disable interrupts globally by clearing <<_mstatus>>`.mie`.. Initialize all integer registers `x1 - x31` (or just `x1 - x15` when using the `E` CPU extension) to a defined value.. Initialize all CPU core CSRs and also install a default "dummy" trap handler for _all_ traps. This handler catches all traps** All interrupt sources are disabled and all pending interrupts are cleared.. Initialize the global pointer `gp` and the stack pointer `sp` according to the <<_ram_layout>> provided by the linker script.during the early boot phase.. Clear all counter CSRs and stop auto-increment.. Clear IO area: Write zero to all memory-mapped registers within the IO region (`iodev` section). If certain devices have notbeen implemented, a bus access fault exception will occur. This exception is captured by the dummy trap handler.. Clear the `.bss` section defined by the linker script.. Copy read-only data from the `.text` section to the `.data` section to set initialized variables.. Call the application's `main` function (with _no_ arguments: `argc` = `argv` = 0).. If the main function returns...** the return value is copied to the <<_mscratch>> CSR to allow inspection by the on-chip debugger.** an optional <<_after_main_handler>> is called (if defined at all).** the last step the CPU does is entering endless sleep mode (using the `wfi` instruction).:sectnums:===== After-Main HandlerIf the application's `main()` function actually returns, an _after main handler_ can be executed. This handler is a "normal" functionas the C runtime is still available when executed. If this handler uses any kind of peripheral/IO modules make sure these arealready 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 doesnot 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.<<<// ####################################################################################################################include::software_bootloader.adoc[]<<<// ####################################################################################################################include::software_rte.adoc[]
