Skip to content

Application Development*

1. Configuration Menu Introduction*

Run make menuconfig to open the configuration interface. The top-level menu structure is as follows:

.config - NationalChip Offline Voice Process Configuration
───────────────────────────────────────────────────────────────────────────────
┌───────────── NationalChip Offline Voice Process Configuration ──────────────┐
│  Arrow keys navigate the menu.  <Enter> selects submenus ---> (or empty     │  
│  submenus ----).  Highlighted letters are hotkeys.  Pressing <Y> includes,  │  
│  <N> excludes, <M> modularizes features.  Press <Esc><Esc> to exit, <?> for │  
│  Help, </> for Search.  Legend: [*] built-in  [ ] excluded  <M> module  < > │  
│ ┌─────────────────────────────────────────────────────────────────────────┐ │  
│ │        Chipset Family selects: (Fornax Family [GX8005/GX8006])  --->    │ │  
│ │        Chipset Type selects: (Fornax Type GX8005A)  --->                │ │  
│ │        Vendor selects: (NationalChip)  --->                             │ │  
│ │        Board selects: (Fornax GX8005(6) Module)  --->                   │ │  
│ │        Board version selects:  (V1.0)  --->                             │ │  
│ │        Board Pin Configuration  --->                                    │ │  
│ │                                                                         │ │  
│ │        MCU settings  --->                                               │ │  
│ │        Time Profiler settings  --->                                     │ │  
│ │                                                                         │ │  
│ │        OVP Workmode Settings  --->                                      │ │  
│ │        I/O Buffer Settings  --->                                        │ │  
│ │        Voice Process Algorithm select:  (Olab Panda)  --->              │ │  
│ │        Olab Panda Settings  --->                                        │ │  
│ │                                                                         │ │  
│ │        OVP Application Settings  --->                                   │ │  
│ │        Common Function Settings  --->                                   │ │  
│ │                                                                         │ │  
│ │        Object Info  --->                                                │ │  
│ └─────────────────────────────────────────────────────────────────────────┘ │  
├─────────────────────────────────────────────────────────────────────────────┤  
│          <Select>    < Exit >    < Help >    < Save >    < Load >           │  
└─────────────────────────────────────────────────────────────────────────────┘ 

1.1. Board-Level Options*

Board Notes

The current board-level code is located in the boards/nationalchip/fornax_module_1v/ directory. To create a new board, refer to this directory structure:

boards/<vendor>/<board_name>/
├── board.c            # Pin mux table, board-level initialization
├── board.h            # Board-level header file
├── board_config.h     # Board-level configuration macros
├── board.name         # Kconfig board type declaration
├── clock_board.c      # Clock configuration
├── Kconfig            # Board-level configuration options (UART pin selection, etc.)
├── Makefile           # Build configuration
└── sample_config.[ch] # Sample rate configuration
After creating a new board, create vendor.name under boards/<vendor>/, create board.name in the board directory, and include it via source in boards/Kconfig.

Option Description Default Value
Chipset Family Chipset family selection: Fornax (GX8005/GX8006) Fornax
Chipset Type Chipset model: GX8005A or GX8006A GX8005A
Vendor Vendor selection: NationalChip NationalChip
Board Board type selection: Fornax GX8005(6) Module Fornax GX8005(6) Module
Board version Board version: V1.0 V1.0
Board Pin Configuration Pin mux configuration (see Section 1.4)

1.2. MCU Settings*

Notes

  • The current floating-point printing feature has defects. If you need to output logs with floating-point calculation results, it is recommended to multiply the floating-point number by 10000 and cast it to an integer for output, for example:
    float val = 1.23456;
    printf("val = %d\n", (int)(val * 10000));  // Outputs val = 12345
    
  • The Timer ID defaults to 5. If the hardware design requires this Timer, be sure to configure it to an unused Timer ID.
  • Dual-bank upgrade is not recommended; it is recommended to use bare-chip upgrade
Option Description Default Value
Enable UART printf Enable UART print output y
Enable Float printf Enable floating-point printing y
UART Port For Print UART port used for printing (0/1/2), must correspond to an enabled UART 0
UART Baud Rate For Print Print UART baud rate 921600
Timer ID For gx_get_time_ms Hardware timer ID used by gx_get_time_ms(), range 0~5, ensure no conflict with the application 5
Enable dual bank upgrade Dual-bank upgrade, allows rollback on upgrade failure (requires GX8006A) n
Build Schema Compilation optimization:
Debug(-O0 -g)
Release(-O2)
Speed(-O3)
Size(-Os)
Release
MAIN Stack Minimum Size (KBytes) Main stack minimum size, range 3~40 3
Enable Stack Monitoring Enable stack overflow monitoring n

1.3. Time Profiler Settings*

Used for performance analysis of algorithms and NPU models, to verify whether computational requirements are met during development. General users do not need to pay attention to this: (if multiple NPU model tasks are running simultaneously, each model needs to be run separately to check computational load)

Option Description
Enable VPA Process Profile Enable VPA processing flow timing statistics
Enable NPU Profile Enable NPU inference timing statistics
Enable KWS Decoder Profile Enable keyword decoder timing statistics

1.4. Board Pin Configuration*

The Fornax family chips have a total of 17 pins (PIN0 ~ PIN16). Each pin selects its multiplexed function by configuring the Function value.

Kconfig Configuration Options*

Option Description Default Value
Enable UART0 Enable UART0: TX=PIN3(Function 2), RX=PIN2(Function 2) y
Enable UART0 TX Only Enable UART0 TX only (for debug printing only) n
Enable UART0 TX Open Drain UART0 TX open-drain output n
Enable UART1 Enable UART1: TX=PIN0(Function 2), RX=PIN1(Function 2) n
Enable UART2 Enable UART2, TX/RX pins can be selected independently n
UART2 TX Pin Select UART2 TX pin: PIN6 / PIN8 / PIN16 PIN8
UART2 RX Pin Select UART2 RX pin: PIN5 / PIN7 / PIN15 PIN15

These options take effect in _board_padmux_init() in board.c, switching pins from the default IDLE(Function 0) to the corresponding function.

Pin Function Table*

The following is the pin function mapping defined in boards/nationalchip/fornax_module_1v/board.c ( bold indicates the default configuration)

For more details, see:

gpio-id Function 0 Function 1 Function 2 Function 3 Function 4 Function 5 Function 6 Function 7 Pin
0 IDLE(Hi-Z) PA_N UART1_TX PWM0 I2S_DOUT/LR I2C_SDA DAC_P GPIO0 20
1 IDLE(Hi-Z) PA_P UART1_RX PWM1 I2S_BCLK I2C_SCL DAC_N GPIO1 18
2 IDLE(pull-up) I2S_DIN UART0_RX PWM2 JTAG_TMS I2C_SDA DAC_P GPIO2 11
3 IDLE(pull-up) UART0_TX PWM3 JTAG_TCK I2C_SCL DAC_N GPIO3 10
4 RSTN(pull-up) I2S_DIN JTAG_TMS IRC_EN GPIO4 9
5 JTAG_TMS(Hi-Z) I2S_DIN UART2_RX PWM5 I2S_LR I2C_SDA IRC_TX GPIO5 23
6 JTAG_TCK(pull-up) UART2_TX PWM0 I2S_MCLK I2C_SCL IRC_EN GPIO6 22
7 IDLE(Hi-Z) SPI2_CLK UART2_RX PWM1 I2S_MCLK I2C_SDA IRC_RX GPIO7 21
8 IDLE(Hi-Z) SPI2_CS UART2_TX PWM2 I2S_BCLK I2C_SCL IRC_TX GPIO8 24
9 IDLE(Hi-Z) SPI2_MISO UART2_CTS PWM3 I2S_DOUT IRC_RX GPIO9 1
10 IDLE(pull-up) SPI2_MOSI UART2_RTS PWM4 I2S_LR JTAG_TCK IRC_EN GPIO10 7
11 IDLE(Hi-Z) SPI2_CS PWM1 MSPI1_CS GPIO11 8
12 IDLE(Hi-Z) SPI2_CLK PWM2 MSPI1_CLK GPIO12 6
13 IDLE(Hi-Z) SPI2_MOSI PWM3 MSPI1_MOSI GPIO13 5
14 IDLE(Hi-Z) SPI2_MISO PWM4 MSPI1_MISO JTAG_TCK GPIO14 2
15 IDLE(Hi-Z) I2S_LR UART2_RX PWM5 MSPI1_WP I2C_SDA IRC_TX GPIO15 4
16 BOOT(pull-up) I2S_MCLK UART2_TX PWM0 MSPI1_HOLD I2C_SCL IRC_RX GPIO16 3

The Function value ranges from 0 to 7, corresponding to the Function column in the pin_table.
Function 0 is IDLE (not all have IDLE), Function 7 is GPIO.
PIN4(RSTN), PIN5(JTAG_TMS), PIN6(JTAG_TCK), PIN16(BOOT) are configured as GPIO by default rather than IDLE.

Default Pin Mux*

At system startup, board_init() calls padmux_init(pin_table, PIN_TABLE_SIZE) to initialize all pins according to the table above. Pins configured as GPIO(Function 7) are automatically set to input direction.

Setting Mux Functions Not Available in Kconfig*

Method 1: Add a New Kconfig Configuration

Pin functions are determined by the hardware design, and the software selects from 8 predefined functions. To use a function not currently enabled for a pin:

  1. Refer to the chip manual to confirm hardware support
  2. Call padmux_set(pin_id, function) to switch to the target function
  3. To control via menuconfig, add an option in boards/.../Kconfig and add logic in _board_padmux_init()

Dynamic Switching in Application Code

Switch at runtime via padmux_set(pin_id, function):

#include <gx_padmux.h>

padmux_set(0, 7);  // PIN0 → GPIO (Function 7)
padmux_set(5, 3);  // PIN5 → PWM5 (Function 3)
padmux_set(7, 4);  // PIN7 → I2S_MCLK (Function 4)

Application examples:

Function Code Example
PWM output padmux_set(5, 3); padmux_set(6, 3);
I2S audio padmux_set(7, 4); padmux_set(8, 4); padmux_set(10, 4); padmux_set(5, 1); padmux_set(9, 4);
DAC output padmux_set(0, 6); padmux_set(1, 6);
LED indication padmux_set(CONFIG_APP_LED_VAD_PIN, 7);

Advanced Pin Control API*

drivers/drv-fornax/include/gx_padmux.h provides:

API Description
padmux_set(pad_id, function) Set pin mux function (0~7)
padmux_get(pad_id) Get current pin function
padmux_check(pad_id, function) Verify whether the pin is set to the specified function
padmux_init(pin_table, size) Initialize all pins from a table
padmux_set_io_drive_strength(pad_id, strength) Set drive strength (6.6/10.9/8.8/13.1 mA)
padmux_get_io_drive_strength(pad_id, *strength) Get drive strength
padmux_set_io_pull_up_down(pad_id, pull) Set internal pull-up/pull-down
padmux_get_io_pull_up_down(pad_id, *pull) Get internal pull-up/pull-down
padmux_set_io_open_drain(pad_id, enable) Set open-drain (only supported on PIN0~3)

1.5. OVP Workmode Settings*

Work mode selection:

Option Description
Has Offline Asr workmode Offline wake-up + ASR mode (enabled by default)
Has NN Denoise workmode Neural network denoising mode (disabled by default)
Has Bypass workmode Runs no algorithms, used for recording raw audio
Initial Workmode Selects Default work mode initialized at startup

1.6. I/O Buffer Settings*

Note

  • REF Channel Source requires an external ADC for loopback sampling (requires hardware support)

Audio data path configuration:

Option Description Default Value
MIC Channel Number Microphone channel count (0~2) 1
REF Channel Number Reference signal channel count (0~1) 0
REF Channel Source When REF>0, select the loopback source ADC (external ADC sampling)
FFT Channel Number FFT channel count (0~1) 0
Logfbank Channel Number Logfbank channel count 1
Output Channel Number Output channel count (0~10) 0
MIC Gain Microphone gain (0~54) 26
REF Gain Reference signal gain (0~54) 0
Sample Rate Sample rate, currently only 16KHz 16KHz
PCM Frame Length PCM frame length: 10ms or 16ms 10ms
Context Number Context count (1~100) 4
PCM Frame Number in a Context Number of PCM frames per Context 3
SNPU Buffer Number SNP buffer count 3
PCM Frame Number in a Channel Number of PCM frames per channel Typically Context Number * PCM Frame Number in a Context
Logfbank Frame Number in a Channel Number of Logfbank frames per channel Typically equal to PCM Frame Number in a Channel
Logfbank Source Logfbank source: Hardware or User PCM Hardware

1.7. Olab Panda Settings > VMA Settings*

Note

  • Enable Opus Codec currently requires a frame length of 40ms per context
  • When Opus Frame Length is set to 20ms, the internal 40ms audio is split into two 20ms encodings; 40ms divided into two 20 ms encodings must be enabled

Audio algorithm processing configuration:

Option Description Default Value
Denoise Level Denoise level (0~3), higher values provide stronger denoising but also higher distortion 0
Enable AEC Enable echo cancellation n
AEC Filter Length AEC filter length 1024
Enable Opus Codec Enable Opus encoding (mutually exclusive with Speex) y
Opus Bit Rate Opus encoding bit rate: 16kbps or 24kbps 16kbps
Opus Frame Length Opus encoding frame length: 40ms or 20ms 40ms
Enable DRC Enable dynamic range compression n
DRC Amplify dB DRC gain (dB) 12

1.8. Olab Panda Settings > VUI Settings*

Voice interaction function configuration:

Option Description Default Value
Enable Keyword Recognition Enable keyword recognition (wake word detection) y
Enable VAD Enable voice activity detection (for ASR) n
Enable Voiceprint Enable voiceprint recognition n
Enable SED Enable sound event detection n
Enable NN Noise Enable neural network denoising n

1.9. OVP Application Settings*

Application Selection*

Application Configuration Option Description
None OVP_APP_NONE No application selected
Hello World APP_HELLO_WORLD Minimal application template
Offline ASR App APP_OFFLINE_ASR Offline ASR application
Smartbot App APP_SMARTBOT WiFi large model application
NN Denoise App APP_NN_DENOISE Neural network denoising demo
Acoustic Test App APP_ACOUSTIC_TEST Acoustic testing
... ... ...

Smartbot App Configuration (appears after selecting Smartbot App)*

Option Description Default Value
Select flow control type UART flow control type: ACK flow control or GPIO flow control ACK flow control
KWS Time Out Wake-up timeout (s) 10
Delay Awake Delay(ms) First wake-up delay (ms) 1000
Enable UART transmission Enable UART audio transmission y
Enable I2S transfer Enable I2S audio transmission n
Enable LED Enable LED indication y
VAD LED Pin VAD indication LED pin 6
Status LED Pin Status indication LED pin 13
Disable Wake Timeout Debug option: disable wake-up timeout n

1.10. Common Function Settings*

General function settings, including UART protocol, recording, player, watchdog, etc.

UART Protocol (UART Message 2.0)*

Option Description Default Value
Has UART protocol Message2.0 Enable UART protocol Message2.0 y
Select UART port for protocol UART port used by the protocol: UART0 / UART1 / Both Both
Send Queue Length Send queue length 8
Recv Queue Length Receive queue length 8

UART Recording*

Option Description Default Value
Enable UART record Enable UART recording n
UART port Recording UART port number 0
UART baudrate Recording UART baud rate 500000

Player Settings (Voice Player)*

Option Description Default Value
Has voice player Enable voice player n
Set volume Playback volume 100
Enable External PA Enable external power amplifier n
Mute Pin External PA mute pin 7
Mute Level Mute level: High or Low Low
Select DAC channel Channel mode: Stereo/Left channel/Right channel/Mono Stereo
Enable pcm Enable PCM playback y
Enable opus decode Enable Opus decode playback y

Other Functions*

Option Description Default Value
Has Viva Enable Viva interaction framework n
Has watchdog Enable watchdog n
Watchdog Reset Time Watchdog reset time: 1s / 2s / 5s / 10s 5s

2. Creating Your First App*

2.1. App Framework Description*

Each application needs to implement the OVP_APP structure (defined in ovp/app_core/ovp_app.h):

typedef struct {
    const char *app_name;                              // Application name
    int (*AppInit)(void);                              // Initialization callback
    int (*AppEventResponse)(APP_EVENT *plc_event);     // Event handling callback
    int (*AppTaskLoop)(void);                          // Main loop callback
    int (*AppSuspend)(void *priv);                     // Suspend callback
    void *suspend_priv;
    int (*AppResume)(void *priv);                      // Resume callback
    void *resume_priv;
} OVP_APP;

#define OVP_REGISTER_APP(app)    \
    OVP_APP *app_core_ops = &app

The application registers itself to the system via the OVP_REGISTER_APP(ovp_app) macro. After system startup, the main loop in main() calls in sequence: 1. AppInit() — Application initialization 2. AppEventResponse() — Handles audio/wake-up events 3. AppTaskLoop() — Called repeatedly in while(1)

Common events (defined in ovp/app_core/ovp_app_core.h):

Event ID Description
EVENT_AUDIO_IN_RECORD_DONE Recording complete, audio data arrived
EVENT_VAD_STATUS VAD status change
EVENT_WAKEUP_ENTER_RX Entering wake-up state
EVENT_WAKEUP_EXIT_RX Exiting wake-up state
EVENT_WAKEUP_EXIT Internal trigger for wake-up exit

2.2. Hello World Example*

The following is the complete code of app/hello_world/app.c, which is the minimal application template:

#include <autoconf.h>
#include <ovp_app.h>
#include <stdint.h>

#define LOG_TAG "[APP_HELLO_WORLD]"
#define LOG_LVL 3
#include <log.h>

static int app_suspend(void *priv)
{
    LOG_D(" ---- %s, %d ----", __func__, __LINE__);
    return 0;
}

static int app_resume(void *priv)
{
    LOG_D(" ---- %s, %d ----", __func__, __LINE__);
    return 0;
}

static int app_init(void)
{
    LOG_I(" ---- %s, %d ----", __func__, __LINE__);
    return 0;
}

static int app_event_response(APP_EVENT *app_event)
{
    if (app_event->event_id == EVENT_AUDIO_IN_RECORD_DONE) {
        LOG_I("hello world!");
    }
    return 0;
}

static int app_task_loop(void)
{
    return 0;
}

OVP_APP ovp_app = {
    .app_name         = "app_hello_world",
    .AppInit          = app_init,
    .AppEventResponse = app_event_response,
    .AppTaskLoop      = app_task_loop,
    .AppSuspend       = app_suspend,
    .suspend_priv     = "app_suspend",
    .AppResume        = app_resume,
    .resume_priv      = "app_resume",
};

OVP_REGISTER_APP(ovp_app);

2.3. Steps to Create a New App*

Taking creating a my_app as an example:

Step 1: Create Directory and Files*

app/my_app/
├── app.c       # Application main code
├── app.name    # Kconfig application declaration
├── app.mk      # Build configuration
└── Kconfig     # Application configuration options

Step 2: Write app.name*

config APP_MY_APP
    bool "My App"
    help
        "My first application"

app.name is used to register the application in the menuconfig application selection list and is included by source "app/*/app.name" in app/Kconfig.

Step 3: Write Kconfig*

if APP_MY_APP

config MY_APP_SETTING
    int "My app setting"
    default 1

endif

Kconfig is used to define configuration options for the application and is included by source "app/*/Kconfig" in app/Kconfig.

Step 4: Write app.mk*

ifeq ($(CONFIG_APP_MY_APP), y)

APP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
APP_SRCS := $(wildcard $(APP_DIR)*.c)
app_objs += $(addprefix $(OBJ_DIR),$(APP_SRCS:.c=.o))

endif

app.mk is used to add the application source files to the build.

Step 5: Write app.c*

Refer to the Hello World template in Section 2.2 to write the application code.

Step 6: Build*

cp configs/example/app/8006_hello_world_app.config .config
make menuconfig
# Navigate to OVP Application Settings → Applications Selection → Select "My App"
# Configure application-related options as needed
# Save and exit

make

After the build completes, the firmware will include the my_app application. You can check the logs via UART0 at 921600 baud to verify success.

Once you are familiar with

You should have a basic grasp of SDK usage.