Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ if(CONFIG_ARBITER)
${CMAKE_CURRENT_LIST_DIR}/lib/arbiter_accel.c
)

zephyr_library_sources_ifdef(CONFIG_ARBITER_TRACE_EXPORT
${CMAKE_CURRENT_LIST_DIR}/lib/arbiter_trace_export.c
)

zephyr_include_directories(${CMAKE_CURRENT_LIST_DIR}/include)

add_subdirectory_ifdef(CONFIG_ARBITER ${CMAKE_CURRENT_LIST_DIR}/subsys/arbiter)
Expand Down
67 changes: 67 additions & 0 deletions include/arbiter/arbiter_trace_export.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: MIT */

#ifndef ARBITER_TRACE_EXPORT_H_
#define ARBITER_TRACE_EXPORT_H_

#include <stdint.h>
#include <stddef.h>
#include <arbiter/arbiter_trace.h>

#ifdef __cplusplus
extern "C" {
#endif

/** Frame start marker for trace export protocol. */
#define ARBITER_TRACE_EXPORT_MARKER 0xAB

/** CRC-8 polynomial used by the trace export framing. */
#define ARBITER_TRACE_EXPORT_CRC8_POLY 0x07

/**
* Transport abstraction for remote trace export.
*
* The engine serialises each trace entry into a binary frame and
* calls @c send to push it to a transport (UART, network, etc.).
*/
struct ARBITER_trace_transport {
/**
* @brief Send a binary frame.
*
* @param buf Serialized frame bytes.
* @param len Length of @p buf in bytes.
* @param user_data Opaque pointer passed through from the transport.
* @return 0 on success, negative errno on failure.
*/
int (*send)(const uint8_t *buf, size_t len, void *user_data);

/** Opaque user data forwarded to @c send. */
void *user_data;
};

/**
* @brief Initialize the remote trace exporter.
*
* Registers the transport and resets the internal sequence counter.
*
* @param transport Transport to use for sending frames.
* @return 0 on success, -EINVAL if @p transport or its send callback is NULL.
*/
int ARBITER_trace_export_init(const struct ARBITER_trace_transport *transport);

/**
* @brief Export all entries from a trace buffer.
*
* Each entry is serialised into a binary frame:
* [0xAB][len_le16][seq_u16][rule_id_u16][fired_u8]
* [action_id_u16][n_facts_u8][fact_ids...][crc8]
*
* @param trace Trace buffer to export.
* @return 0 on success, negative errno on transport failure.
*/
int ARBITER_trace_export(const struct ARBITER_trace *trace);

#ifdef __cplusplus
}
#endif

#endif /* ARBITER_TRACE_EXPORT_H_ */
52 changes: 52 additions & 0 deletions include/arbiter/arbiter_zbus.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: MIT */

#ifndef ARBITER_ZBUS_H_
#define ARBITER_ZBUS_H_

#include <stdint.h>
#include <arbiter/arbiter_model.h>
#include <zephyr/zbus/zbus.h>

#ifdef __cplusplus
extern "C" {
#endif

/** Inbound message: set a fact value with timestamp. */
struct arbiter_facts_msg {
arbiter_index_t fact_id;
int32_t value;
uint32_t timestamp_ms;
};

/** Outbound message: evaluation result summary. */
struct arbiter_result_msg {
uint16_t mode;
uint32_t faults;
uint16_t action_count;
uint32_t op_count;
};

/* Channel declarations (defined in arbiter_zbus.c). */
ZBUS_CHAN_DECLARE(arbiter_facts_chan, arbiter_result_chan);

/**
* @brief Initialize the arbiter zbus integration.
*
* @param ctx Initialized arbiter context for fact updates.
* @return 0 on success, -EINVAL if @p ctx is NULL.
*/
int arbiter_zbus_init(struct ARBITER_ctx *ctx);

/**
* @brief Publish an evaluation result to the result channel.
*
* @param result Evaluation result to publish.
* @return 0 on success, negative errno on failure.
*/
int arbiter_zbus_publish_result(const struct ARBITER_result *result);

#ifdef __cplusplus
}
#endif

#endif /* ARBITER_ZBUS_H_ */
139 changes: 139 additions & 0 deletions lib/arbiter_trace_export.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/* SPDX-License-Identifier: MIT */

#include <arbiter/arbiter_trace_export.h>
#include <arbiter/arbiter.h>
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>

LOG_MODULE_DECLARE(arbiter, CONFIG_ARBITER_LOG_LEVEL);

/* ── Static state (no dynamic allocation) ─────────────────────── */

static const struct ARBITER_trace_transport *active_transport;
static uint16_t seq_counter;

/* ── CRC-8 (polynomial 0x07) ─────────────────────────────────── */

static uint8_t crc8_update(uint8_t crc, const uint8_t *__restrict data,
size_t len)
{
for (size_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t bit = 0; bit < 8; bit++) {
if (crc & 0x80) {
crc = (uint8_t)((crc << 1) ^ ARBITER_TRACE_EXPORT_CRC8_POLY);
} else {
crc = (uint8_t)(crc << 1);
}
}
}
return crc;
}

/* ── Helpers ──────────────────────────────────────────────────── */

static void put_le16(uint8_t *__restrict buf, uint16_t val)
{
buf[0] = (uint8_t)(val & 0xFF);
buf[1] = (uint8_t)((val >> 8) & 0xFF);
}

/* ── Public API ───────────────────────────────────────────────── */

int ARBITER_trace_export_init(const struct ARBITER_trace_transport *transport)
{
if (transport == NULL || transport->send == NULL) {
return ARBITER_EINVAL;
}

active_transport = transport;
seq_counter = 0;

LOG_INF("Trace export initialized");
return ARBITER_OK;
}

int ARBITER_trace_export(const struct ARBITER_trace *trace)
{
if (trace == NULL) {
return ARBITER_EINVAL;
}

if (active_transport == NULL || active_transport->send == NULL) {
return ARBITER_EINVAL;
}

for (uint16_t i = 0; i < trace->count; i++) {
const struct ARBITER_trace_entry *e = &trace->entries[i];

/*
* Frame layout:
* [marker 1B][len_le16 2B][seq_le16 2B]
* [rule_id_le16 2B][fired 1B][action_id_le16 2B]
* [n_facts 1B][fact_ids n*2B][crc8 1B]
*
* Payload length = everything after len field, including crc.
*/
uint8_t n_facts = (uint8_t)e->input_fact_count;
size_t payload_len = 2 + 2 + 1 + 2 + 1 +
((size_t)n_facts * 2) + 1;
size_t frame_len = 1 + 2 + payload_len;

/*
* Stack-allocated frame buffer. Maximum frame size with
* CONFIG_ARBITER_MAX_TRACE_INPUTS = 8 is:
* 1 + 2 + 2 + 2 + 1 + 2 + 1 + 16 + 1 = 28 bytes.
* Use a generous upper bound.
*/
uint8_t frame[3 + 2 + 2 + 1 + 2 + 1 +
(CONFIG_ARBITER_MAX_TRACE_INPUTS * 2) + 1];
size_t pos = 0;

/* Marker */
frame[pos++] = ARBITER_TRACE_EXPORT_MARKER;

/* Payload length (LE16) */
put_le16(&frame[pos], (uint16_t)payload_len);
pos += 2;

/* Sequence number (LE16) — wraps at UINT16_MAX */
put_le16(&frame[pos], seq_counter);
pos += 2;
seq_counter++;

/* Rule ID (LE16) */
put_le16(&frame[pos], e->rule_id);
pos += 2;

/* Fired flag */
frame[pos++] = e->condition_result ? 1U : 0U;

/* Action ID (LE16) */
put_le16(&frame[pos], e->action_id);
pos += 2;

/* Number of input facts */
frame[pos++] = n_facts;

/* Input fact IDs (LE16 each) */
for (uint8_t f = 0; f < n_facts; f++) {
put_le16(&frame[pos], e->input_facts[f]);
pos += 2;
}

/* CRC-8 over everything after the marker */
uint8_t crc = crc8_update(0x00, &frame[1], pos - 1);

frame[pos++] = crc;

int ret = active_transport->send(frame, pos,
active_transport->user_data);
if (unlikely(ret < 0)) {
LOG_ERR("Trace export send failed: %d", ret);
return ret;
}
}

return ARBITER_OK;
}
2 changes: 2 additions & 0 deletions subsys/arbiter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ zephyr_library()
zephyr_library_sources_ifdef(CONFIG_ARBITER_SHELL arbiter_shell.c)
zephyr_library_sources_ifdef(CONFIG_ARBITER_RUNTIME_THREAD arbiter_runtime_thread.c)
zephyr_library_sources_ifdef(CONFIG_ARBITER_WATCHDOG arbiter_watchdog.c)
zephyr_library_sources_ifdef(CONFIG_ARBITER_ZBUS arbiter_zbus.c)
zephyr_library_sources_ifdef(CONFIG_ARBITER_METRICS arbiter_metrics.c)
31 changes: 31 additions & 0 deletions subsys/arbiter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,35 @@ config ARBITER_FPGA_OFFLOAD
to offload condition evaluation and expression execution
to FPGA fabric. No implementation is shipped in v1.

# ── Remote Trace Export (REQ-ARCH-038) ───────────────────────

config ARBITER_TRACE_EXPORT
bool "Enable remote trace export"
default n
help
Serialize evaluation trace entries into binary frames and
send them over a pluggable transport (UART, network, etc.)
for remote analysis and debugging.

# ── Zbus Integration (REQ-ARCH-042) ─────────────────────────

config ARBITER_ZBUS
bool "Enable zbus integration"
depends on ZBUS
default n
help
Publish evaluation results and subscribe to fact updates
via Zephyr zbus channels.

# ── Grafana Metrics (REQ-ARCH-042) ──────────────────────────

config ARBITER_METRICS
bool "Enable runtime metrics (Zephyr stats)"
depends on STATS
default n
help
Expose evaluation metrics (count, latency, faults, etc.)
through the Zephyr stats subsystem for Grafana or other
monitoring dashboards.

endif # ARBITER
Loading
Loading