Using NMSIS in Embedded Applications
Introduction
To use the NMSIS-Core, the following files are added to the embedded application:
Startup File startup_<Device>.S, which provided asm startup code and vector table.
Interrupt and Exception Handling File: intexc_<Device>.S, which provided general exception handling code for non-vector interrupts and exceptions.
Device Linker Script: gcc_<device>.ld, which provided linker script for the device.
System Configuration Files system_<Device>.c and system_<Device>.h, which provided general device configuration (i.e. for clock and BUS setup).
Device Header File <Device.h> gives access to processor core and all peripherals.
Note
The best example usage of NMSIS device template is our Nuclei SDK project’s evalsoc support source code, it is modified based on NMSIS device template and may contains newer experimental features not in this device template, and it also provided very good user experience for both command line and IDE itegration like Nuclei Studio and IAR Workbench.
The files Startup File startup_<Device>.S, Interrupt and Exception Handling File: intexc_<Device>.S, Device Linker Script: gcc_<device>.ld and System Configuration Files system_<Device>.c and system_<Device>.h may require application specific adaptations and therefore should be copied into the application project folder prior configuration.
The Device Header File <Device.h> is included in all source files that need device access and can be stored on a central include folder that is generic for all projects.
The Startup File startup_<Device>.S is executed right after device reset, it will do
necessary stack pointer initialization, exception and interrupt entry configuration, then
call SystemInit()
, after system initialization, will return to assemble startup code
and do c/c++ runtime initialization which includes data, bss section initialization, c++ runtime
initialization, then it will call main()
function in the application code.
In the Interrupt and Exception Handling File: intexc_<Device>.S, it will contain all exception and interrupt vectors and implements a default function for every interrupt. It may also contain stack and heap configurations for the user application.
The System Configuration Files system_<Device>.c and system_<Device>.h performs the setup for the processor
clock. The variable SystemCoreClock
indicates the CPU clock speed.
Systick Timer(SysTimer) describes the minimum feature set. In addition
the file may contain functions for the memory BUS setup and clock re-configuration.
The Device Header File <Device.h> is the central include file that the application programmer is using in the C source code. It provides the following features:
Peripheral Access provides a standardized register layout for all peripherals. Optionally functions for device-specific peripherals may be available.
Interrupts and Exceptions can be accessed with standardized symbols and functions for the ECLIC are provided.
CPU Intrinsic Functions allow to access special instructions, for example for activating sleep mode or the NOP instruction.
Intrinsic Functions for SIMD Instructions provide access to the DSP-oriented instructions.
Systick Timer(SysTimer) function to configure and start a periodic timer interrupt.
Core CSR Register Access function to access the core csr registers.
Cache Functions to access the I-CACHE and D-CACHE unit
FPU Functions to access the Floating point unit.
PMP Functions to access the Physical Memory Protection unit
Version Control which defines NMSIS release specific macros.
Compiler Control is compiler agnostic #define symbols for generic C/C++ source code
The NMSIS-Core system files are device specific.
In addition, the Startup File startup_<Device>.S is also compiler vendor specific, currently only GCC version is provided. The versions provided by NMSIS are only generic templates. The adopted versions for a concrete device are typically provided by the device vendor through the according device familiy package.
For example, the following files are provided by the GD32VF103 device family pack:
File |
Description |
./Device/Source/GCC/startup_gd32vf103.S |
Startup File startup_<device>.S
for the GD32VF103 device variants.
|
./Device/Source/GCC/intexc_gd32vf103.S |
Exception and Interrupt Handling File
intexc_<device>.S for the GD32VF103 device variants.
|
./Device/Source/GCC/gcc_gd32vf103.ld |
Linker script File gcc_<device>.ld
for the GD32VF103 device variants.
|
./Device/Source/system_gd32vf103.c |
System Configuration File system_<device>.c
for the GD32VF103 device families
|
./Device/Include/system_gd32vf103.h |
System Configuration File system_<device>.h
for the GD32VF103 device families
|
./Device/Include/gd32vf103.h |
Device Header File <device.h>
for the GD32VF103 device families.
|
Note
The silicon vendors create these device-specific NMSIS-Core files based on NMSIS-Core Device Templates provided by Nuclei.
Thereafter, the functions described under NMSIS Core API can be used in the application.
Basic NMSIS Example
A typical example for using the NMSIS layer is provided below. The example is based on a GD32VF103 Device.
1#include <gd32vf103.h> // File name depends on device used
2
3uint32_t volatile msTicks; // Counter for millisecond Interval
4#define SysTick_Handler eclic_mtip_handler
5#define CONFIG_TICKS (SOC_TIMER_FREQ / 1000)
6
7void SysTick_Handler (void) { // SysTick Interrupt Handler
8 SysTick_Reload(CONFIG_TICKS);
9 msTicks++; // Increment Counter
10}
11
12void WaitForTick (void) {
13 uint32_t curTicks;
14
15 curTicks = msTicks; // Save Current SysTick Value
16 while (msTicks == curTicks) { // Wait for next SysTick Interrupt
17 __WFI (); // Power-Down until next Event/Interrupt
18 }
19}
20
21void TIMER0_UP_IRQHandler (void) { // Timer Interrupt Handler
22 ; // Add user code here
23}
24
25void timer0_init(int frequency) { // Set up Timer (device specific)
26 ECLIC_SetPriorityIRQ (TIMER0_UP_IRQn, 1); // Set Timer priority
27 ECLIC_EnableIRQ (TIMER0_UP_IRQn); // Enable Timer Interrupt
28}
29
30
31void Device_Initialization (void) { // Configure & Initialize MCU
32 if (SysTick_Config (CONFIG_TICKS)) {
33 ; // Handle Error
34 }
35 timer0_init (); // setup device-specific timer
36}
37
38// The processor clock is initialized by NMSIS startup + system file
39void main (void) { // user application starts here
40 Device_Initialization (); // Configure & Initialize MCU
41 while (1) { // Endless Loop (the Super-Loop)
42 __disable_irq (); // Disable all interrupts
43 Get_InputValues (); // Read Values
44 __enable_irq (); // Enable all interrupts
45 Calculation_Response (); // Calculate Results
46 Output_Response (); // Output Results
47 WaitForTick (); // Synchronize to SysTick Timer
48 }
49}
Using Interrupt and Exception/NMI
Nuclei processors provide NMI(Non-Maskable Interrupt), Exception, Vector Interrupt and Non-Vector Interrupt features.
Using NMSIS with generic Nuclei Processors
Nuclei provides NMSIS-Core Device template files for the supported Nuclei Processors and for various compiler vendors. These files can be used as templates, and you can modify based on it to match your processor design.
And you can also refer to Nuclei SDK project for an quick and easily startup project to work with Nuclei RISC-V processor, it is based on NMSIS project and support build/debug c/c++ project in both command line or many different IDEs such as Nuclei Studio.
The table below lists the template folder.
Folder |
Processor |
RISC-V |
Description |
./Device/_Template_Vendor |
|
RV32 RV64 |
Contains Include and Source template files configured for the
Nuclei RISC-V processor.
|
Create generic Libraries with NMSIS
The NMSIS Processor and Core Peripheral files allow also to create generic libraries. The NMSIS-DSP Libraries are an example for such a generic library.
To build a generic library set the define __NMSIS_GENERIC and include the nmsis_core.h NMSIS CPU & Core Access header file for the processor.
The define __NMSIS_GENERIC disables device-dependent features such as the SysTick timer and the Interrupt System.
Example
The following code section shows the usage of the nmsis_core.h header files to build a generic library for N200, N300, N600, NX600.
One of these defines needs to be provided on the compiler command line.
By using this header file, the source code can access the functions for Core CSR Register Access, CPU Intrinsic Functions and Intrinsic Functions for SIMD Instructions.
1#define __NMSIS_GENERIC // Disable Eclic and Systick functions
2#include <nmsis_core.h>