Skip to content

Core & Utility Modules

SyntropicOS provides header-only foundation macros, ISR-safe data structures, binary structure packing, and fixed-point math utilities.


Technical Specifications

Feature Specification
RAM Cost Zero heap allocation. All structures (SYN_RingBuf, SYN_SPSC_Queue, SYN_Stream, SYN_PingPong) are caller-owned.
ISR Safety SPSC (Single-Producer / Single-Consumer) ring buffers and queues are lock-free and ISR-safe.
Asserts Runtime checks strippable in production via #define SYN_DISABLE_ASSERT.

1. Cooperative Byte Stream (util/syn_stream.h)

SYN_Stream wraps an SPSC ring buffer with protothread-aware readability conditions. It supports 3 operational modes: - Default Mode: De-blocks when any byte is available. - Threshold Mode: De-blocks when ≥ N bytes arrive (packet-oriented protocols). - Delimiter Mode: De-blocks when a specific delimiter byte (e.g. \n or 0x00) arrives. If an oversized frame fills the buffer without a delimiter, SYN_Stream automatically flushes the corrupted frame, enters a resync state, and discards trailing bytes until the delimiter arrives. This guarantees protothreads on PT_STREAM_WAIT wake ONLY on 100% complete, un-truncated frames.

Stream Data Flow

flowchart LR
    ISR["UART ISR / DMA"] -->|syn_stream_put| RingBuffer["Lock-Free Ring Buffer"]
    RingBuffer -->|PT_STREAM_WAIT| Protothread["Protothread Task"]
    Protothread -->|Read Line / Frame| Processing["Process Message"]

Complete Code Example (Line-Oriented Serial Stream)

#include <syntropic/util/syn_stream.h>
#include <syntropic/pt/syn_pt.h>

static uint8_t raw_buffer[128];
static SYN_Stream rx_stream;

void stream_setup(void) {
    syn_stream_init(&rx_stream, raw_buffer, sizeof(raw_buffer));

    // De-block task only when a newline '\n' character is received
    syn_stream_set_delimiter(&rx_stream, '\n');
}

// ISR Producer: Lock-free put
void USART1_IRQHandler(void) {
    uint8_t byte = (uint8_t)USART1->DR;
    syn_stream_put(&rx_stream, byte);
}

// Consumer Protothread: Reads complete lines
SYN_PT_Status line_consumer_task(SYN_PT *pt, SYN_Task *task) {
    static uint8_t line_buf[64];
    static size_t bytes_read;

    PT_BEGIN(pt);
    for (;;) {
        // Yields CPU until '\n' delimiter arrives in rx_stream
        PT_STREAM_WAIT(pt, &rx_stream);

        bytes_read = syn_stream_read_line(&rx_stream, line_buf, sizeof(line_buf));
        process_cmd((char*)line_buf, bytes_read);
    }
    PT_END(pt);
}

2. Binary Serialization & Structure Packing (util/syn_pack.h)

Provides endianness-independent serialization (pack_u16_be, pack_u32_le, etc.) to prevent unaligned memory access traps on Cortex-M0/M3 microcontrollers.

#include <syntropic/util/syn_pack.h>

void serialize_packet(uint8_t *dest, uint16_t id, uint32_t val) {
    size_t offset = 0;
    offset += syn_pack_u16_be(&dest[offset], id);
    offset += syn_pack_u32_be(&dest[offset], val);
}

3. Lock-Free SPSC Ring Buffer & Queue (util/syn_spsc_queue.h & util/syn_ringbuf.h)

SyntropicOS provides single-producer single-consumer lock-free ring buffers for ISR-to-thread communication.

Usage Example

#include <syntropic/util/syn_spsc_queue.h>

static uint8_t queue_buf[256];
static SYN_SPSC_Queue rx_queue;

void setup_queue(void) {
    syn_spsc_queue_init(&rx_queue, queue_buf, sizeof(queue_buf));
}

// In USART RX ISR:
void USART1_IRQHandler(void) {
    uint8_t byte = (uint8_t)USART1->DR;
    syn_spsc_queue_push(&rx_queue, byte);
}

// In main task loop:
void process_rx(void) {
    uint8_t byte;
    while (syn_spsc_queue_pop(&rx_queue, &byte) == SYN_OK) {
        // Feed protocol state machine (Modbus, COBS, BACnet, DALI)
        syn_modbus_feed(&mb_slave, byte);
    }
}