Arduino C Code Generation
SKaiNET provides a specialized compiler backend for exporting trained neural networks to highly optimized, standalone C99 code suitable for microcontrollers like Arduino.
Technical Deep Dive
1. Tape-based Tracing
Instead of static analysis of the Kotlin code, SKaiNET uses a dynamic tracing mechanism. When you call exportToArduinoLibrary, the framework executes a single forward pass of your model using a specialized RecordingContext.
-
Every operation (Dense, ReLU, etc.) is recorded onto an Execution Tape.
-
This approach handles Kotlin’s language features (loops, conditionals) naturally, as it only records the actual operations that were executed.
2. Compute Graph Construction
The execution tape is converted into a directed acyclic graph (DAG) called ComputeGraph.
-
Nodes represent operations (Ops).
-
Edges represent data flow (Tensors).
-
During this phase, the compiler performs Shape Inference to ensure every tensor has a fixed, known size.
3. Static Memory Management
Microcontrollers typically have very limited RAM and lack robust heap management. SKaiNET uses a Ping-Pong Buffer Strategy to eliminate dynamic memory allocation (malloc/free) during inference.
Ping-Pong Buffer Strategy
The compiler calculates the maximum size required for any intermediate tensor in the graph and allocates exactly two static buffers of that size.
-
Buffer Reuse: Instead of allocating space for every layer’s output, buffers are reused.
-
Direct Output Optimization: The first layer reads from the input pointer, and the last layer writes directly to the output pointer, avoiding unnecessary copies.
4. Code Generation (Emission)
The CCodeGenerator emits C99-compatible code using templates.
-
Weights & Biases: Extracted from the trained Kotlin model and serialized as
static const floatarrays. This places them in Flash memory (PROGMEM) on many microcontrollers, saving precious RAM. -
Kernel Implementation: Operations like
Dense(Linear) are implemented as optimized nested loops. -
Header Generation: Produces a clean API for the user:
int model_inference(const float* input, float* output);