rigless 0.0.2

Virtual hardware-in-the-loop runtime: run unmodified firmware on an emulated MCU with every peripheral, chip model, and unit of virtual time in Rust; needs the rigless-sidecar package from rigless-labs/qemu
Documentation
/*
 * Copyright (c) 2026 Rigless Labs.
 * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
 *
 * The shared page between the Rigless core and its QEMU sidecar, as the
 * sidecar compiles it. The sidecar module of the Rust crate rigless is the
 * source of truth; its test header_defines_match_rust fails when any define
 * disagrees with the Rust layout, its test header_compiles_as_c11 compiles
 * this file, and the static assertions at the bottom fail when the structs
 * disagree with the defines.
 *
 * Control block. magic is always MAGIC and version is the version the core
 * speaks, packed as (major << VERSION_MAJOR_SHIFT) | minor; the core writes
 * both before the sidecar starts and the sidecar only reads them.
 *
 * Turn discipline. The turn word says which side may write its slot. The
 * holder fills its slot, then stores the other side's value into the turn
 * word with release ordering; the waiter loads the turn word with acquire
 * ordering until it reads its own value, then reads the other side's slot.
 * The core creates the block with the turn given to the sidecar. The sidecar
 * writes Hello before the first instruction and hands over; the core answers
 * RunUntil or, when the versions differ, Shutdown. From then on the sidecar
 * writes Read, Write, or Stopped and the core answers Reply or RunUntil, or
 * Shutdown at any point. Never more than one message is in flight.
 *
 * Slot fields. kind is one of the KIND values and zero when the slot is
 * empty. width is the access width in bits, 8, 16, 32, or 64, as the Rust
 * side's rigless::soc::Width renders it. reason is one of the REASON
 * values on Stopped. address is the guest address of an access.
 * value is the value written on Write, the value read on Reply, and the
 * version packed as above on Hello. instruction_count is icount_get_raw():
 * on Read and Write it includes the accessing instruction, the access being
 * that instruction's last act, and on Stopped it is the instructions retired
 * before the stop. virtual_time_ns is
 * qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), the clock deadline_ns is in, which
 * runs ahead of the instruction count by every idle warp and is the core's
 * virtual time. program_counter is the address of the accessing instruction.
 * deadline_ns is the virtual time the sidecar runs to on RunUntil and re-arms
 * to on Reply; the run stops at the first instruction boundary at or past it,
 * less than one instruction late when the deadline is not a multiple of the
 * icount step, so the core treats virtual_time_ns >= deadline_ns as reached.
 * A field a message does not use is written as zero.
 *
 * Interrupt levels. One bit per line the proxy drives, set by the core: word
 * index / 64, bit index % 64. The proxy applies whatever differs from what it
 * last drove on every return from a crossing, before the next instruction.
 */

#ifndef RIGLESS_PROXY_PROTOCOL_H
#define RIGLESS_PROXY_PROTOCOL_H

#include <limits.h>
#include <stddef.h>
#include <stdint.h>

#define RIGLESS_PROXY_MAGIC 0x424353534C474952ULL
#define RIGLESS_PROXY_VERSION_MAJOR 1
#define RIGLESS_PROXY_VERSION_MINOR 0
#define RIGLESS_PROXY_VERSION_MAJOR_SHIFT 16

#define RIGLESS_PROXY_CACHE_LINE 64
#define RIGLESS_PROXY_PAGE 4096

#define RIGLESS_PROXY_CONTROL_BLOCK_SIZE 4096
#define RIGLESS_PROXY_SLOT_SIZE 64

#define RIGLESS_PROXY_MAGIC_OFFSET 0
#define RIGLESS_PROXY_VERSION_OFFSET 8
#define RIGLESS_PROXY_TURN_OFFSET 64
#define RIGLESS_PROXY_SIDECAR_SLOT_OFFSET 128
#define RIGLESS_PROXY_CORE_SLOT_OFFSET 192
#define RIGLESS_PROXY_INTERRUPT_LEVELS_OFFSET 256

#define RIGLESS_PROXY_INTERRUPT_LINES 1024
#define RIGLESS_PROXY_INTERRUPT_WORDS 16

#define RIGLESS_PROXY_SLOT_KIND_OFFSET 0
#define RIGLESS_PROXY_SLOT_WIDTH_OFFSET 4
#define RIGLESS_PROXY_SLOT_REASON_OFFSET 5
#define RIGLESS_PROXY_SLOT_ADDRESS_OFFSET 8
#define RIGLESS_PROXY_SLOT_VALUE_OFFSET 16
#define RIGLESS_PROXY_SLOT_INSTRUCTION_COUNT_OFFSET 24
#define RIGLESS_PROXY_SLOT_PROGRAM_COUNTER_OFFSET 32
#define RIGLESS_PROXY_SLOT_VIRTUAL_TIME_NS_OFFSET 40
#define RIGLESS_PROXY_SLOT_DEADLINE_NS_OFFSET 48

#define RIGLESS_PROXY_TURN_SIDECAR 0
#define RIGLESS_PROXY_TURN_CORE 1

#define RIGLESS_PROXY_KIND_HELLO 1
#define RIGLESS_PROXY_KIND_READ 2
#define RIGLESS_PROXY_KIND_WRITE 3
#define RIGLESS_PROXY_KIND_STOPPED 4
#define RIGLESS_PROXY_KIND_RUN_UNTIL 5
#define RIGLESS_PROXY_KIND_REPLY 6
#define RIGLESS_PROXY_KIND_SHUTDOWN 7

#define RIGLESS_PROXY_REASON_DEADLINE 1
#define RIGLESS_PROXY_REASON_IDLE 2

#define RIGLESS_PROXY_SPIN_WINDOW_US 50
#define RIGLESS_PROXY_SLEEP_QUANTUM_US 20

struct rigless_proxy_slot {
    _Alignas(RIGLESS_PROXY_CACHE_LINE) uint32_t kind;
    uint8_t width;
    uint8_t reason;
    uint16_t pad0;
    uint64_t address;
    uint64_t value;
    uint64_t instruction_count;
    uint64_t program_counter;
    uint64_t virtual_time_ns;
    uint64_t deadline_ns;
    uint64_t pad1;
};

struct rigless_proxy_control_block {
    _Alignas(RIGLESS_PROXY_PAGE) uint64_t magic;
    uint32_t version;
    _Alignas(RIGLESS_PROXY_CACHE_LINE) uint32_t turn;
    struct rigless_proxy_slot sidecar_slot;
    struct rigless_proxy_slot core_slot;
    _Alignas(RIGLESS_PROXY_CACHE_LINE) uint64_t interrupt_levels[RIGLESS_PROXY_INTERRUPT_WORDS];
};

_Static_assert(sizeof(struct rigless_proxy_slot) == RIGLESS_PROXY_SLOT_SIZE,
               "slot size");
_Static_assert(offsetof(struct rigless_proxy_slot, kind) ==
                   RIGLESS_PROXY_SLOT_KIND_OFFSET,
               "slot kind offset");
_Static_assert(offsetof(struct rigless_proxy_slot, width) ==
                   RIGLESS_PROXY_SLOT_WIDTH_OFFSET,
               "slot width offset");
_Static_assert(offsetof(struct rigless_proxy_slot, reason) ==
                   RIGLESS_PROXY_SLOT_REASON_OFFSET,
               "slot reason offset");
_Static_assert(offsetof(struct rigless_proxy_slot, address) ==
                   RIGLESS_PROXY_SLOT_ADDRESS_OFFSET,
               "slot address offset");
_Static_assert(offsetof(struct rigless_proxy_slot, value) ==
                   RIGLESS_PROXY_SLOT_VALUE_OFFSET,
               "slot value offset");
_Static_assert(offsetof(struct rigless_proxy_slot, instruction_count) ==
                   RIGLESS_PROXY_SLOT_INSTRUCTION_COUNT_OFFSET,
               "slot instruction count offset");
_Static_assert(offsetof(struct rigless_proxy_slot, program_counter) ==
                   RIGLESS_PROXY_SLOT_PROGRAM_COUNTER_OFFSET,
               "slot program counter offset");
_Static_assert(offsetof(struct rigless_proxy_slot, virtual_time_ns) ==
                   RIGLESS_PROXY_SLOT_VIRTUAL_TIME_NS_OFFSET,
               "slot virtual time offset");
_Static_assert(offsetof(struct rigless_proxy_slot, deadline_ns) ==
                   RIGLESS_PROXY_SLOT_DEADLINE_NS_OFFSET,
               "slot deadline offset");

_Static_assert(sizeof(struct rigless_proxy_control_block) ==
                   RIGLESS_PROXY_CONTROL_BLOCK_SIZE,
               "control block size");
_Static_assert(offsetof(struct rigless_proxy_control_block, magic) ==
                   RIGLESS_PROXY_MAGIC_OFFSET,
               "magic offset");
_Static_assert(offsetof(struct rigless_proxy_control_block, version) ==
                   RIGLESS_PROXY_VERSION_OFFSET,
               "version offset");
_Static_assert(offsetof(struct rigless_proxy_control_block, turn) ==
                   RIGLESS_PROXY_TURN_OFFSET,
               "turn offset");
_Static_assert(offsetof(struct rigless_proxy_control_block, sidecar_slot) ==
                   RIGLESS_PROXY_SIDECAR_SLOT_OFFSET,
               "sidecar slot offset");
_Static_assert(offsetof(struct rigless_proxy_control_block, core_slot) ==
                   RIGLESS_PROXY_CORE_SLOT_OFFSET,
               "core slot offset");
_Static_assert(offsetof(struct rigless_proxy_control_block, interrupt_levels) ==
                   RIGLESS_PROXY_INTERRUPT_LEVELS_OFFSET,
               "interrupt levels offset");
_Static_assert(sizeof(((struct rigless_proxy_control_block *)0)->interrupt_levels) ==
                   RIGLESS_PROXY_INTERRUPT_LINES / CHAR_BIT,
               "interrupt levels size");

#endif