Skip to content

neuralSPOT Utilities Library

The ns-utils library is a assorted collection of utilities to help with various aspects of development, debug, and optimization. It includes:

Utility Description
ns_energy_monitor Tools to help mark 'regions of interest' for external energy monitors via GPIO
ns_perf_profile Tools to capture, analyze, and display cache and instruction performance counters
ns_power_profile Prints out Ambiq configuration registers impacting power - useful for interacting with Ambiq FAEs
ns_timer Implements various clocks and timers
ns_malloc RTOS-friendly malloc() and free()

Energy Monitor

This simple library allows you to control two GPIO pins which can be monitored by some external energy monitor tools to mark 'regions of interest' (e.g. feature extraction vs. inference). For example, Joulescopes have two inputs which can be displayed along with the energy waveforms - in the diagram below, in0 and in1 distinguish between audio capture (in0 = 1 and in1=0), feature extraction (in0 = 1 and in1 = 0), inference (both 1) and idle (both 0).

image-20221209110633653

Using this requires connecting GPIO pins 22 and 23 to the energy monitor, and the following code:

    ns_init_power_monitor_state(); // Initialize the monitor
    ns_set_power_monitor_state(NS_IDLE); // Set a state (in this case, idle)

The pre-defined states are below (of course, these can be used however needed - they don't have to correspond to the state names):

#define NS_IDLE 0
#define NS_DATA_COLLECTION 1
#define NS_FEATURE_EXTRACTION 2
#define NS_INFERING 3

Performance Profile

The ns_perf_profile library includes helper functions for collecting, analyzing, and printing cache and instruction performance counters.

Cache Monitor

Using the cache monitor requires that you enable it, capture values before and after the region of value, and print the results.

Note The Cache monitor system slightly increases power consumption - we recommend it is only used during development, not in production.

// Variables
ns_cache_config_t cc;
ns_cache_dump_t start;
ns_cache_dump_t end;

// Init and enable the DMON
cc.enable = true;
ns_cache_profiler_init(&cc);

// Capture the start values
ns_capture_cache_stats(&start);

// Code you're interested in watching
interesting_foo();

// Capture the end values
ns_capture_cache_stats(&end);

// Print out the difference between start and end
ns_print_cache_stats_delta(&start, &end);

Instruction Performance Monitor

Likewise, the instruction performance counters (based on Arm DWT) require that enable it, reset values before, read values after the region of value, and print the results.

ns_init_perf_profiler(); // resets the counters, but doesn't enable them

ns_reset_perf_counters(); // if not sure of intervening code, reset counters again
ns_start_perf_profiler(); // enables all counters

// Code you're interested in profiling
interesting_foo();

ns_stop_perf_profiler(); // Stop the counters to make sure your not measuring yourself
ns_capture_perf_profiler(&pp); // Capture values
ns_print_perf_profile(&pp); // Print values and analysis

Timer

The ns_timer helper functions allow the instantiation of 4 timers with specific functions:

Timer Function
NS_TIMER_COUNTER Enable timerticks, useful for timing code
NS_TIMER_INTERRUPT Enables an periodic interrupt callback
NS_TIMER_USB Dedicated USB timer, should only be used by ns_usb
NS_TIMER_TEMPCO Dedicated temperature compensation timer, should only be used by ns_power

Counter

The NS_TIMER_COUNTER is used to keep track of time thusly:

ns_timer_config_t example_tickTimer = {
    .api = &ns_timer_V1_0_0,
    .timer = NS_TIMER_COUNTER,
    .enableInterrupt = false,
};

main() {
    ns_timer_init(&example_tickTimer);

  ns_lp_printf("Before: %d\n",ns_us_ticker_read(&example_tickTimer));
  // area of interest to be timed
    interesting_foo();
  ns_lp_printf("After: %d\n",ns_us_ticker_read(&example_tickTimer));
}

Periodic Interrupt

The NS_TIMER_INTERRUPT is useful when a periodic interrupt is needed. When configured, it will invoke the defined callback every defined period.

static void
example_periodic_callback(ns_timer_config_t *c) {
    // Invoked in ISR context every timertick (per below, 1ms)
    do_periodic_task(); // whatever you need
}

ns_timer_config_t exampleTimer  = {
    .api = &ns_timer_V1_0_0,
    .timer = NS_TIMER_INTERRUPT,
    .enableInterrupt = true,
    .periodInMicroseconds = 1000, // one 1ms
    .callback = example_periodic_callback
};

main() {
    ns_timer_init(&exampleTimer);
    ...
}

Malloc/Free

Dynamic memory is usually avoided in RTOS environments, and neuralSPOT manages to do so except for RPC (which isn't intended to be used in production). However, there are many cases in which malloc/free are needed (e.g. edgeimpulse integration). The ns_malloc helper function is an instantiation of FreeRTOS's heap_4 implementation, which provides a reasonable compromise between heap management and real-time behavior for infrequent malloc invocations.

ns_malloc() allocates from a statically defined heap. The heap must be allocated by each application at compile time. NeuralSPOT components that use malloc such as ns-ble and ns-rpc have default heap sizes that can serve as starting points for what the size should be, but the needs of each application will vary.

Malloc and Free work as usual:

// Some compilers need this to be word aligned
uint8_t ucHeap[NS_RPC_MALLOC_SIZE_IN_K*1024]  __attribute__ ((aligned (4)));

void *memPtr = ns_malloc(requested_size);

// do stuff with memPtr

ns_free(memPtr); // put allocated block back into free heap