Compare commits

...

No commits in common. "e1db6c530f17e5a1de513db5bded379f0df9098d" and "4bec2c3c4203b091d5d63353e3318e1ca108d731" have entirely different histories.

5 changed files with 282 additions and 220 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) [year] [fullname]
Copyright (c) 2025 Dominic Hoeglinger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,18 +1,67 @@
```
▌ ╬ ▗▄▄▖ ▗▄▄▄▖▗▄▄▄▖
▐▐ ▐ ▐▌ ▐▌▐▌ █ Pocket Event Trace
▌ ▌▌ ▐▛▀▘ ▐▛▀▀▘ █ A tiny tracing library
═══▐══ ▐▌ ▐▙▄▄▖ █ ═════════════════════════════
▓▓▓▓▓ ░░░ ███████ ███████ ████████
▓ ▓ ▒ ░ ██ ██ ██ Streaming Event Trace
▓ ▓ ▓ ░░ ███████ █████ ██ A tiny signal tracing library
▓ ▓ ▒ ░ ██ ██ ██
▓▓▓▓▓ ░░░ ███████ ███████ ██ Version 1.0.1 Alpha
```
# Introduction
PET is a small (less than 1k implementation lines) tracing library written in C and is highly optimized for minimal data usage
and aims to be easy to set up and use while also being flexible enough to be integrated in hard real time systems.
PET is a small tracing library written in C99.
It is highly optimized for minimal data usage and aims to be easy to use
while also being flexible enough to be integrated in hard real time systems.
# How to use
PET requires the integrator to implement a few functions such as the timestamp and output functions in order to work.
Apart from that, it is up to the specific use case how to stream data.
In its simplest form, traces are processed with no real time demands on a dedicated interface.
For more advanced usage, this library allows packets to be written, as the time budget allows,
into any given buffer to send its contents via DMA to for instance a serial output.
Simple example on Arduino:
````c
#include "set.h"
// Basic implementation of output and timestamp retrieval
void set_out(const char* const str, size_t len) { Serial.write(str, len); }
uint32_t set_timestamp(void) { return micros(); }
// No concurrency protection needed
void set_crit_on(uint32_t *h) {}
void set_crit_off(const uint32_t h) {}
static const size_t s_tracesize = 256;
static set_trace_t s_tracebuf[256];
void setup()
{
set_init(s_tracebuf, s_tracesize);
set_enable(set_drop); // drop traces on full buffer
set_u8trace("Serial.Ready", 0, false);
Serial.begin(921600);
while (!Serial);
set_u8trace("Serial.Ready", 1, false);
}
void loop()
{
unsigned sawtooth = 0;
if (sawtooth < 0xFFFF) sawtooth++;
else sawtooth = 0;
set_u32trace("Main.Sawtooth", sawtooth, false);
if (sawtooth == 4711) Serial.write("I can still be used with regular text.");
set_output(0, true); //send unlimited traces compressed
set_diagtrace(); // add diagnostic traces
}
````
# Basic operating principle
@ -38,3 +87,14 @@ making the trace appear to coincide with the last trace which captured a timesta
The repository contains an example client application which captures packages and outputs the traces in a Value Change Dump.
This script uses the PyVCD package which is the only dependency.
Apart from that, this utility has every function integrated it'll need to decode a trace stream.
It expects data via stdin, and displays any non-tracing traffic.
Example usage tracing remotely via SSH:
````bash
# Open /dev/ttyUSB0 via SSH and record to "trace.vcd" with timescale of 1 microsecond with diagnostics enabled
ssh user@host -C "picocom -b 921600 /dev/ttyUSB0" | ./set_record.py -d trace.vcd -s ./Sources -t "1 us" --diagnostics
````
The resulting VCD can be inspected with dedicated programs such as GTKwave.

View File

@ -23,14 +23,14 @@
#include "pet.h"
#define PET_NIBBLE_TO_HEX(nibble) ((nibble) < 10 ? (nibble) + '0' : (nibble) - 10 + 'A')
#define PET_NIBBLE(value, nibble) ((value >> nibble) & 0xF)
#define PET_BYTE(value, offset) (char)(((value >> (8 * offset)) & 0xFF))
#define PET_ENCODE(value, nibble) (char)(PET_NIBBLE_TO_HEX(PET_NIBBLE(value, nibble)))
#define PET_NELEMS(a) (sizeof(a) / sizeof(a[0]))
#define PET_MACROPACK_FRAME_SIZE (2 * 8)
#define PET_MACROPACK_SIZE(n) (PET_MACROPACK_FRAME_SIZE * n + (PET_MACROPACK_FRAME_SIZE * n) / 254 + 1 + n - 1)
#define PET_MAX_RENDER_SIZE(n) (PET_MACROPACK_SIZE(n) + (PET_MACROPACK_SIZE(n) / 254) + 1)
#define SET_NIBBLE_TO_HEX(nibble) ((nibble) < 10 ? (nibble) + '0' : (nibble) - 10 + 'A')
#define SET_NIBBLE(value, nibble) ((value >> nibble) & 0xF)
#define SET_BYTE(value, offset) (char)(((value >> (8 * offset)) & 0xFF))
#define SET_ENCODE(value, nibble) (char)(SET_NIBBLE_TO_HEX(SET_NIBBLE(value, nibble)))
#define SET_NELEMS(a) (sizeof(a) / sizeof(a[0]))
#define SET_MACROPACK_FRAME_SIZE (2 * 8)
#define SET_MACROPACK_SIZE(n) (SET_MACROPACK_FRAME_SIZE * n + (SET_MACROPACK_FRAME_SIZE * n) / 254 + 1 + n - 1)
#define SET_MAX_RENDER_SIZE(n) (SET_MACROPACK_SIZE(n) + (SET_MACROPACK_SIZE(n) / 254) + 1)
#define FASTLZ1_MAX_COPY 32
#define FASTLZ1_MAX_LEN 264 /* 256 + 8 */
@ -44,43 +44,43 @@
typedef enum
{
pet_d1 = 0x11,
pet_d2 = 0x12,
pet_d4 = 0x14,
pet_v1 = 0x21,
pet_v2 = 0x22,
pet_v4 = 0x24,
pet_a1 = 0x31,
pet_a2 = 0x32,
pet_a4 = 0x34,
pet_s4 = 0x44,
pet_f4 = 0x54,
pet_ev = 0x60,
} pet_type_t;
set_d1 = 0x11,
set_d2 = 0x12,
set_d4 = 0x14,
set_v1 = 0x21,
set_v2 = 0x22,
set_v4 = 0x24,
set_a1 = 0x31,
set_a2 = 0x32,
set_a4 = 0x34,
set_s4 = 0x44,
set_f4 = 0x54,
set_ev = 0x60,
} set_type_t;
typedef enum
{
pet_packet_z = (1 << 0),
} pet_packetflags_t;
set_packet_z = (1 << 0),
} set_packetflags_t;
typedef struct
{
const char* m_tag;
uint16_t m_hash;
} pet_hashcache_t;
} set_hashcache_t;
typedef union
{
uint32_t u32value;
#if (!PET_FLOAT_DISABLE)
#if (!SET_FLOAT_DISABLE)
float f32value;
#endif
} pet_convert_t;
} set_convert_t;
static bool s_enabled = false;
static uint32_t s_last_time = 0;
pet_overflow_policy_t s_policy = pet_overwrite;
static pet_trace_t* s_tracebuffer;
set_overflow_policy_t s_policy = set_overwrite;
static set_trace_t* s_tracebuffer;
static size_t s_tracebuffer_size = 0;
static size_t s_tracebuffer_head = 0;
static size_t s_tracebuffer_tail = 0;
@ -100,39 +100,39 @@ static uint32_t s_diag_avg_ctime_n = 0;
static uint32_t s_diag_avg_rtime_total = 0;
static uint32_t s_diag_avg_rtime_n = 0;
#if (PET_HASHCACHE_LINES > 0)
static pet_hashcache_t s_hashcache[PET_HASHCACHE_LINES] = {0};
#if (SET_HASHCACHE_LINES > 0)
static set_hashcache_t s_hashcache[SET_HASHCACHE_LINES] = {0};
static size_t s_hashcache_index = 0;
#endif
size_t render_macropacket_payload(char buffer[], const size_t buffer_size, size_t n);
static size_t frame_preamble(char buffer[], const size_t buffer_size);
static size_t frame_epilouge(char buffer[], const size_t buffer_size);
static size_t pack_frame(char packet_bytes[], const pet_trace_t* const p_packet);
static size_t pack_frame(char packet_bytes[], const set_trace_t* const p_packet);
static size_t ttyize_payload(char packet_buffer[], const size_t packet_size, const size_t buffer_size);
static uint16_t bsd_hash(const char* const str, size_t n);
static bool populate_time_delta_packet(const uint32_t timestamp, pet_trace_t* const p_trace);
static void tracebuffer_add(const uint32_t timestamp, const pet_type_t type, const uint32_t value, const uint8_t sub, char const *tag);
static bool populate_time_delta_packet(const uint32_t timestamp, set_trace_t* const p_trace);
static void tracebuffer_add(const uint32_t timestamp, const set_type_t type, const uint32_t value, const uint8_t sub, char const *tag);
size_t cobs_encode(uint8_t* dst, size_t dst_buf_len, const uint8_t* ptr, int len, uint8_t delim);
static int fastlz1_compress(const void* input, int length, void* output);
void pet_init(pet_trace_t tracebuffer[], size_t ntraces)
void set_init(set_trace_t tracebuffer[], size_t ntraces)
{
s_tracebuffer = tracebuffer;
s_tracebuffer_size = ntraces;
}
size_t pet_get_buffer_items(void)
size_t set_get_buffer_items(void)
{
return s_tracebuffer_items;
}
void pet_set_tty_mode(const bool enable)
void set_tty_mode(const bool enable)
{
s_tty_enabled = enable;
}
size_t pet_tty_rubout(char buffer[], const size_t buffer_size)
size_t set_tty_rubout(char buffer[], const size_t buffer_size)
{
const size_t rubout_packet_size = 3 + s_tty_rubout_len + 3;
size_t buffer_pos = 0;
@ -153,25 +153,25 @@ size_t pet_tty_rubout(char buffer[], const size_t buffer_size)
return buffer_pos;
}
size_t pet_output(size_t n, const bool compress)
size_t set_output(size_t n, const bool compress)
{
const size_t render_number_items = 4;
const size_t render_max_size = PET_MAX_RENDER_SIZE(render_number_items);
char packet_bytes[PET_MAX_RENDER_SIZE(4)] = {0};
const size_t render_max_size = SET_MAX_RENDER_SIZE(render_number_items);
char packet_bytes[SET_MAX_RENDER_SIZE(4)] = {0};
size_t packet_size = 0;
size_t i = 0;
n = (n == 0) ? s_tracebuffer_size : n;
while ((s_tracebuffer_full || (s_tracebuffer_tail != s_tracebuffer_head)) && (i < n))
{
packet_size = pet_render(packet_bytes, render_max_size, render_number_items, compress);
packet_size = set_render(packet_bytes, render_max_size, render_number_items, compress);
i += render_number_items;
pet_out(packet_bytes, packet_size);
set_out(packet_bytes, packet_size);
}
packet_size = pet_tty_rubout(packet_bytes, render_max_size);
pet_out(packet_bytes, packet_size);
packet_size = set_tty_rubout(packet_bytes, render_max_size);
set_out(packet_bytes, packet_size);
return i;
}
@ -181,7 +181,7 @@ size_t render_macropacket_payload(char buffer[], const size_t buffer_size, size_
const size_t max_render_size = (8 + 1 + 1) * 2 - 1;
char payload_buffer[8] = {0};
char delimimter = 0x00;
pet_trace_t timestamp_pack = {0};
set_trace_t timestamp_pack = {0};
size_t payload_size = 0;
size_t i = 0;
size_t buffer_pos = 0;
@ -220,20 +220,20 @@ size_t render_macropacket_payload(char buffer[], const size_t buffer_size, size_
return buffer_pos;
}
size_t pet_render(char buffer[], const size_t buffer_size, const size_t n, const bool compress)
size_t set_render(char buffer[], const size_t buffer_size, const size_t n, const bool compress)
{
const char delimimter = '\033';
size_t n_elems = (n == 0) ? s_tracebuffer_items : n;
n_elems = (s_tty_enabled && (n > 4)) ? 4 : n_elems;
size_t render_max_size = PET_MAX_RENDER_SIZE(n_elems);
uint32_t ticks_render_start = pet_timestamp();
size_t render_max_size = SET_MAX_RENDER_SIZE(n_elems);
uint32_t ticks_render_start = set_timestamp();
char *final_payload_buffer = NULL;
size_t final_payload_size = 0;
while ((buffer_size < (1 + (render_max_size * 2))) && (n_elems > 0))
{
n_elems--;
render_max_size = PET_MAX_RENDER_SIZE(n_elems);
render_max_size = SET_MAX_RENDER_SIZE(n_elems);
}
if (n_elems == 0) return 0;
@ -245,11 +245,11 @@ size_t pet_render(char buffer[], const size_t buffer_size, const size_t n, const
size_t compressed_size = macropack_payload_size + (macropack_payload_size*15)/100;
char *compressed_buffer = &buffer[buffer_size - compressed_size];
uint32_t ticks_comp_start = pet_timestamp();
uint32_t ticks_comp_start = set_timestamp();
compressed_size = fastlz1_compress(buffer, macropack_payload_size, compressed_buffer);
final_payload_buffer = compressed_buffer;
final_payload_size = compressed_size;
s_diag_avg_ctime_total += pet_timestamp() - ticks_comp_start;
s_diag_avg_ctime_total += set_timestamp() - ticks_comp_start;
s_diag_avg_ctime_n++;
if (macropack_payload_size > 0)
@ -272,7 +272,7 @@ size_t pet_render(char buffer[], const size_t buffer_size, const size_t n, const
size_t out_pos = frame_preamble(buffer, buffer_size);
uint8_t flags = 0;
if (compress) flags |= (uint8_t)pet_packet_z;
if (compress) flags |= (uint8_t)set_packet_z;
buffer[out_pos] = flags;
out_pos++;
@ -290,54 +290,54 @@ size_t pet_render(char buffer[], const size_t buffer_size, const size_t n, const
out_pos += payload_size;
out_pos += frame_epilouge(&buffer[out_pos], buffer_size - out_pos);
s_diag_avg_rtime_total += pet_timestamp() - ticks_render_start;
s_diag_avg_rtime_total += set_timestamp() - ticks_render_start;
s_diag_avg_rtime_n++;
return out_pos;
}
void pet_enable(const pet_overflow_policy_t policy)
void set_enable(const set_overflow_policy_t policy)
{
s_policy = policy;
s_enabled = true;
}
void pet_disable(void)
void set_disable(void)
{
s_enabled = false;
}
void pet_clear(void)
void set_clear(void)
{
s_tracebuffer_tail = s_tracebuffer_head;
s_tracebuffer_full = false;
}
void pet_diagtrace(void)
void set_diagtrace(void)
{
const uint32_t buffer_health = ((s_tracebuffer_size - s_tracebuffer_items) * 0xFF)/s_tracebuffer_size;
pet_u32trace("PET.BufferItems", s_tracebuffer_items, false);
pet_u8trace("PET.BufferHealth", (uint8_t)buffer_health, true);
set_u32trace("PET.BufferItems", s_tracebuffer_items, false);
set_u8trace("PET.BufferHealth", (uint8_t)buffer_health, true);
if (s_diag_avg_compression_n > 0)
{
const uint32_t average = s_diag_avg_compression_total / s_diag_avg_compression_n;
pet_u8trace("PET.CompressionLevel", (uint8_t)average, true);
set_u8trace("PET.CompressionLevel", (uint8_t)average, true);
s_diag_avg_compression_total = 0;
s_diag_avg_compression_n = 0;
}
if (s_diag_items_sent > 0)
{
pet_u32trace("PET.ItemsSent", s_diag_items_sent, true);
set_u32trace("PET.ItemsSent", s_diag_items_sent, true);
s_diag_items_sent = 0;
}
if (s_diag_avg_ctime_n > 0)
{
const uint32_t average = s_diag_avg_ctime_total / s_diag_avg_ctime_n;
pet_u8trace("PET.CompressionTime", (uint8_t)average, true);
set_u8trace("PET.CompressionTime", (uint8_t)average, true);
s_diag_avg_ctime_total = 0;
s_diag_avg_ctime_n = 0;
}
@ -346,147 +346,147 @@ void pet_diagtrace(void)
if (s_diag_avg_rtime_n > 0)
{
const uint32_t average = s_diag_avg_rtime_total / s_diag_avg_rtime_n;
pet_u8trace("PET.RenderTime", (uint8_t)average, true);
set_u8trace("PET.RenderTime", (uint8_t)average, true);
s_diag_avg_rtime_total = 0;
s_diag_avg_rtime_n = 0;
}
}
void pet_evtrace(const char* const tag, const bool skip_time)
void set_evtrace(const char* const tag, const bool skip_time)
{
if (s_enabled == true)
{
tracebuffer_add(skip_time ? 0UL : pet_timestamp(), pet_ev, 1UL, 0U, tag);
tracebuffer_add(skip_time ? 0UL : set_timestamp(), set_ev, 1UL, 0U, tag);
}
}
void pet_u8trace(const char* const tag, const uint8_t value, const bool skip_time)
void set_u8trace(const char* const tag, const uint8_t value, const bool skip_time)
{
if (s_enabled == true)
{
tracebuffer_add(skip_time ? 0UL : pet_timestamp(), pet_v1, (uint32_t)value, 0U, tag);
tracebuffer_add(skip_time ? 0UL : set_timestamp(), set_v1, (uint32_t)value, 0U, tag);
}
}
void pet_u16trace(const char* const tag, const uint16_t value, const bool skip_time)
void set_u16trace(const char* const tag, const uint16_t value, const bool skip_time)
{
if (s_enabled == true)
{
tracebuffer_add(skip_time ? 0UL : pet_timestamp(), pet_v2, (uint32_t)value, 0U, tag);
tracebuffer_add(skip_time ? 0UL : set_timestamp(), set_v2, (uint32_t)value, 0U, tag);
}
}
void pet_u32trace(const char* const tag, const uint32_t value, const bool skip_time)
void set_u32trace(const char* const tag, const uint32_t value, const bool skip_time)
{
if (s_enabled == true)
{
tracebuffer_add(skip_time ? 0UL : pet_timestamp(), pet_v4, (uint32_t)value, 0U, tag);
tracebuffer_add(skip_time ? 0UL : set_timestamp(), set_v4, (uint32_t)value, 0U, tag);
}
}
void pet_s8trace(const char* const tag, const int8_t value, const bool skip_time)
void set_s8trace(const char* const tag, const int8_t value, const bool skip_time)
{
if (s_enabled == true)
{
tracebuffer_add(skip_time ? 0UL : pet_timestamp(), pet_v1, (uint32_t)value, 0U, tag);
tracebuffer_add(skip_time ? 0UL : set_timestamp(), set_v1, (uint32_t)value, 0U, tag);
}
}
void pet_s16trace(const char* const tag, const int16_t value, const bool skip_time)
void set_s16trace(const char* const tag, const int16_t value, const bool skip_time)
{
if (s_enabled == true)
{
tracebuffer_add(skip_time ? 0UL : pet_timestamp(), pet_v2, (uint32_t)value, 0U, tag);
tracebuffer_add(skip_time ? 0UL : set_timestamp(), set_v2, (uint32_t)value, 0U, tag);
}
}
void pet_s4trace(const char* const tag, const int32_t value, const bool skip_time)
void set_s4trace(const char* const tag, const int32_t value, const bool skip_time)
{
if (s_enabled == true)
{
tracebuffer_add(skip_time ? 0UL : pet_timestamp(), pet_v4, (uint32_t)value, 0U, tag);
tracebuffer_add(skip_time ? 0UL : set_timestamp(), set_v4, (uint32_t)value, 0U, tag);
}
}
void pet_au8trace(const char* const tag, const uint8_t value[const], const uint8_t size, const bool skip_time)
void set_au8trace(const char* const tag, const uint8_t value[const], const uint8_t size, const bool skip_time)
{
if (s_enabled == true)
{
const size_t ts = skip_time ? 0UL : pet_timestamp();
const size_t ts = skip_time ? 0UL : set_timestamp();
for (size_t i = 0; i < size; ++i)
{
tracebuffer_add(ts, pet_a1, (uint32_t)value[i], (uint8_t)i, tag);
tracebuffer_add(ts, set_a1, (uint32_t)value[i], (uint8_t)i, tag);
}
}
}
void pet_au16trace(const char* const tag, const uint16_t value[const], const uint8_t size, const bool skip_time)
void set_au16trace(const char* const tag, const uint16_t value[const], const uint8_t size, const bool skip_time)
{
if (s_enabled == true)
{
const size_t ts = skip_time ? 0UL : pet_timestamp();
const size_t ts = skip_time ? 0UL : set_timestamp();
for (size_t i = 0; i < size; ++i)
{
tracebuffer_add(ts, pet_a2, (uint32_t)value[i], (uint8_t)i, tag);
tracebuffer_add(ts, set_a2, (uint32_t)value[i], (uint8_t)i, tag);
}
}
}
void pet_au32trace(const char* const tag, const uint32_t value[const], const uint8_t size, const bool skip_time)
void set_au32trace(const char* const tag, const uint32_t value[const], const uint8_t size, const bool skip_time)
{
if (s_enabled == true)
{
const size_t ts = skip_time ? 0UL : pet_timestamp();
const size_t ts = skip_time ? 0UL : set_timestamp();
for (size_t i = 0; i < size; ++i)
{
uint16_t sub = (i) | (size << 8);
tracebuffer_add(ts, pet_a4, (uint32_t)value[i], (uint8_t)i, tag);
tracebuffer_add(ts, set_a4, (uint32_t)value[i], (uint8_t)i, tag);
}
}
}
void pet_as8trace(const char* const tag, const int8_t value[const], const uint8_t size, const bool skip_time)
void set_as8trace(const char* const tag, const int8_t value[const], const uint8_t size, const bool skip_time)
{
if (s_enabled == true)
{
const size_t ts = skip_time ? 0UL : pet_timestamp();
const size_t ts = skip_time ? 0UL : set_timestamp();
for (size_t i = 0; i < size; ++i)
{
tracebuffer_add(ts, pet_a1, (uint32_t)value[i], (uint8_t)i, tag);
tracebuffer_add(ts, set_a1, (uint32_t)value[i], (uint8_t)i, tag);
}
}
}
void pet_as16trace(const char* const tag, const int16_t value[const], const uint8_t size, const bool skip_time)
void set_as16trace(const char* const tag, const int16_t value[const], const uint8_t size, const bool skip_time)
{
if (s_enabled == true)
{
const size_t ts = skip_time ? 0UL : pet_timestamp();
const size_t ts = skip_time ? 0UL : set_timestamp();
for (size_t i = 0; i < size; ++i)
{
tracebuffer_add(ts, pet_a2, (uint32_t)value[i], (uint8_t)i, tag);
tracebuffer_add(ts, set_a2, (uint32_t)value[i], (uint8_t)i, tag);
}
}
}
void pet_as32trace(const char* const tag, const int32_t value[const], const uint8_t size, const bool skip_time)
void set_as32trace(const char* const tag, const int32_t value[const], const uint8_t size, const bool skip_time)
{
if (s_enabled == true)
{
const size_t ts = skip_time ? 0UL : pet_timestamp();
const size_t ts = skip_time ? 0UL : set_timestamp();
for (size_t i = 0; i < size; ++i)
{
uint16_t sub = (i) | (size << 8);
tracebuffer_add(ts, pet_a4, (uint32_t)value[i], (uint8_t)i, tag);
tracebuffer_add(ts, set_a4, (uint32_t)value[i], (uint8_t)i, tag);
}
}
}
void pet_strtrace(const char* const tag, const char* const str, const bool skip_time)
void set_strtrace(const char* const tag, const char* const str, const bool skip_time)
{
if (s_enabled == true)
{
const size_t ts = skip_time ? 0UL : pet_timestamp();
const size_t ts = skip_time ? 0UL : set_timestamp();
uint32_t packword = 0;
uint8_t packindex = 0;
for (size_t i = 0; str[i] != '\0'; ++i)
@ -495,24 +495,24 @@ void pet_strtrace(const char* const tag, const char* const str, const bool skip_
packword |= (str[i] << (8 * packindex));
if (packindex == 3)
{
tracebuffer_add(ts, pet_s4, packword, (uint8_t)str[i + 1] == '\0', tag);
tracebuffer_add(ts, set_s4, packword, (uint8_t)str[i + 1] == '\0', tag);
packword = 0;
}
}
if (packindex != 3)
{
tracebuffer_add(ts, pet_s4, packword, (uint8_t)true, tag);
tracebuffer_add(ts, set_s4, packword, (uint8_t)true, tag);
}
}
}
#if (!PET_FLOAT_DISABLE)
void pet_f32trace(const char* const tag, const float value, const bool skip_time)
#if (!SET_FLOAT_DISABLE)
void set_f32trace(const char* const tag, const float value, const bool skip_time)
{
pet_convert_t converter = {.f32value = value };
set_convert_t converter = {.f32value = value };
if (s_enabled == true)
{
tracebuffer_add(skip_time ? 0UL : pet_timestamp(), pet_f4, converter.u32value, 0U, tag);
tracebuffer_add(skip_time ? 0UL : set_timestamp(), set_f4, converter.u32value, 0U, tag);
}
}
#endif
@ -541,56 +541,56 @@ static size_t frame_epilouge(char buffer[], const size_t buffer_size)
return pack_pos;
}
static size_t pack_frame(char packet_bytes[], const pet_trace_t* const p_packet)
static size_t pack_frame(char packet_bytes[], const set_trace_t* const p_packet)
{
size_t packet_size = 0;
packet_bytes[packet_size++] = (char)p_packet->m_type;
switch ((pet_type_t)p_packet->m_type)
switch ((set_type_t)p_packet->m_type)
{
case pet_v1:
case pet_a1:
case pet_d1:
packet_bytes[packet_size++] = PET_BYTE(p_packet->m_value, 0);
case set_v1:
case set_a1:
case set_d1:
packet_bytes[packet_size++] = SET_BYTE(p_packet->m_value, 0);
break;
case pet_v2:
case pet_a2:
case pet_d2:
packet_bytes[packet_size++] = PET_BYTE(p_packet->m_value, 0);
packet_bytes[packet_size++] = PET_BYTE(p_packet->m_value, 1);
case set_v2:
case set_a2:
case set_d2:
packet_bytes[packet_size++] = SET_BYTE(p_packet->m_value, 0);
packet_bytes[packet_size++] = SET_BYTE(p_packet->m_value, 1);
break;
case pet_v4:
case pet_a4:
case pet_s4:
case pet_f4:
case pet_d4:
packet_bytes[packet_size++] = PET_BYTE(p_packet->m_value, 0);
packet_bytes[packet_size++] = PET_BYTE(p_packet->m_value, 1);
packet_bytes[packet_size++] = PET_BYTE(p_packet->m_value, 2);
packet_bytes[packet_size++] = PET_BYTE(p_packet->m_value, 3);
case set_v4:
case set_a4:
case set_s4:
case set_f4:
case set_d4:
packet_bytes[packet_size++] = SET_BYTE(p_packet->m_value, 0);
packet_bytes[packet_size++] = SET_BYTE(p_packet->m_value, 1);
packet_bytes[packet_size++] = SET_BYTE(p_packet->m_value, 2);
packet_bytes[packet_size++] = SET_BYTE(p_packet->m_value, 3);
break;
}
switch ((pet_type_t)p_packet->m_type)
switch ((set_type_t)p_packet->m_type)
{
case pet_a1:
case pet_a2:
case pet_a4:
case pet_s4:
case set_a1:
case set_a2:
case set_a4:
case set_s4:
packet_bytes[packet_size++] = (char)p_packet->m_sub;
break;
}
switch ((pet_type_t)p_packet->m_type)
switch ((set_type_t)p_packet->m_type)
{
case pet_d1:
case pet_d2:
case pet_d4:
case set_d1:
case set_d2:
case set_d4:
break;
default:
const uint16_t hash_tag = bsd_hash(p_packet->m_tag, PET_MAX_TAG_LEN);
packet_bytes[packet_size++] = PET_BYTE(hash_tag, 0);
packet_bytes[packet_size++] = PET_BYTE(hash_tag, 1);
const uint16_t hash_tag = bsd_hash(p_packet->m_tag, SET_MAX_TAG_LEN);
packet_bytes[packet_size++] = SET_BYTE(hash_tag, 0);
packet_bytes[packet_size++] = SET_BYTE(hash_tag, 1);
break;
}
@ -610,8 +610,8 @@ static size_t ttyize_payload(char packet_buffer[], const size_t packet_size, con
{
value = packet_buffer[(packet_size - i - 1)];
backpos = (packet_size - i - 1) * 2;
packet_buffer[backpos] = PET_ENCODE(value, 0);
packet_buffer[backpos + 1] = PET_ENCODE(value, 4);
packet_buffer[backpos] = SET_ENCODE(value, 0);
packet_buffer[backpos + 1] = SET_ENCODE(value, 4);
}
return tty_packet_size;
@ -622,10 +622,10 @@ static uint16_t bsd_hash(const char* const str, size_t n)
uint32_t checksum = 0;
if (str == NULL) return 0U;
#if (PET_HASHCACHE_LINES > 0)
for (size_t i = 0; i < PET_HASHCACHE_LINES; ++i)
#if (SET_HASHCACHE_LINES > 0)
for (size_t i = 0; i < SET_HASHCACHE_LINES; ++i)
{
size_t hash_index = (i + s_hashcache_index) % PET_HASHCACHE_LINES;
size_t hash_index = (i + s_hashcache_index) % SET_HASHCACHE_LINES;
if (s_hashcache[hash_index].m_tag == str)
{
return s_hashcache[hash_index].m_hash;
@ -638,8 +638,8 @@ static uint16_t bsd_hash(const char* const str, size_t n)
checksum += str[i];
checksum &= 0xFFFF;
}
#if (PET_HASHCACHE_LINES > 0)
s_hashcache_index = (s_hashcache_index + 1) % PET_HASHCACHE_LINES;
#if (SET_HASHCACHE_LINES > 0)
s_hashcache_index = (s_hashcache_index + 1) % SET_HASHCACHE_LINES;
s_hashcache[s_hashcache_index].m_hash = checksum;
s_hashcache[s_hashcache_index].m_tag = str;
#endif
@ -647,7 +647,7 @@ static uint16_t bsd_hash(const char* const str, size_t n)
return checksum;
}
static bool populate_time_delta_packet(const uint32_t timestamp, pet_trace_t* const p_trace)
static bool populate_time_delta_packet(const uint32_t timestamp, set_trace_t* const p_trace)
{
const uint32_t dt = timestamp - s_last_time;
uint16_t type = 0;
@ -658,15 +658,15 @@ static bool populate_time_delta_packet(const uint32_t timestamp, pet_trace_t* co
{
if (dt > 0xFFFF)
{
type = pet_d4;
type = set_d4;
}
else if (dt > 0xFF)
{
type = pet_d2;
type = set_d2;
}
else
{
type = pet_d1;
type = set_d1;
}
p_trace->m_type = type;
@ -682,18 +682,18 @@ static bool populate_time_delta_packet(const uint32_t timestamp, pet_trace_t* co
return false;
}
static void tracebuffer_add(const uint32_t timestamp, const pet_type_t type, const uint32_t value, const uint8_t sub, char const *tag)
static void tracebuffer_add(const uint32_t timestamp, const set_type_t type, const uint32_t value, const uint8_t sub, char const *tag)
{
const size_t insertion_pos = s_tracebuffer_head;
uint32_t critical_h = 0UL;
if (s_tracebuffer_full == false || s_policy == pet_overwrite)
if (s_tracebuffer_full == false || s_policy == set_overwrite)
{
pet_crit_on(&critical_h);
set_crit_on(&critical_h);
s_tracebuffer_head = (s_tracebuffer_head + 1) % s_tracebuffer_size;
s_tracebuffer_full = (s_tracebuffer_head == s_tracebuffer_tail);
s_tracebuffer_items++;
pet_crit_off(critical_h);
set_crit_off(critical_h);
s_tracebuffer[insertion_pos].m_type = (uint8_t)type;
s_tracebuffer[insertion_pos].m_value = value;
@ -758,7 +758,7 @@ static uint16_t flz_hash(uint32_t v) {
}
static void pet_memcpy(uint8_t* dest, const uint8_t* src, uint32_t count)
static void set_memcpy(uint8_t* dest, const uint8_t* src, uint32_t count)
{
for(size_t i = 0; i < count; ++i)
{
@ -776,14 +776,14 @@ static uint32_t flz_cmp(const uint8_t* p, const uint8_t* q, const uint8_t* r) {
static uint8_t* flz_literals(uint32_t runs, const uint8_t* src, uint8_t* dest) {
while (runs >= FASTLZ1_MAX_COPY) {
*dest++ = FASTLZ1_MAX_COPY - 1;
pet_memcpy(dest, src, FASTLZ1_MAX_COPY);
set_memcpy(dest, src, FASTLZ1_MAX_COPY);
src += FASTLZ1_MAX_COPY;
dest += FASTLZ1_MAX_COPY;
runs -= FASTLZ1_MAX_COPY;
}
if (runs > 0) {
*dest++ = runs - 1;
pet_memcpy(dest, src, runs);
set_memcpy(dest, src, runs);
dest += runs;
}
return dest;

View File

@ -21,28 +21,28 @@
* SOFTWARE.
*/
#ifndef PET_H_INCLUDED_
#define PET_H_INCLUDED_
#ifndef SET_H_INCLUDED_
#define SET_H_INCLUDED_
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
/** Maxmum signal name / tag length considered for the identifying hash */
#define PET_MAX_TAG_LEN (126)
#define SET_MAX_TAG_LEN (126)
/** Number of cached hashes */
#define PET_HASHCACHE_LINES (64)
#define SET_HASHCACHE_LINES (64)
/** Switch to enable or disable floating point support */
#define PET_FLOAT_DISABLE (false)
#define SET_FLOAT_DISABLE (false)
/** Overflow policy for the tracebuffer */
typedef enum
{
pet_overwrite, /**< Overwrite oldest samples */
pet_drop /**< Drop newest samples */
} pet_overflow_policy_t;
set_overwrite, /**< Overwrite oldest samples */
set_drop /**< Drop newest samples */
} set_overflow_policy_t;
/** Element used to allocate the tracebuffer */
typedef struct
@ -52,52 +52,52 @@ typedef struct
uint32_t m_value;
uint8_t m_type;
uint8_t m_sub;
} pet_trace_t;
} set_trace_t;
/** @brief Integration hook writing out a packet to the desired interface
* @param[in] str Packet data
* @param[in] len Packet data length
* @return None
*/
void pet_out(const char* const str, size_t len);
void set_out(const char* const str, size_t len);
/** @brief Integration hook for retrieving a timestamp
* @return Timestamp value
*/
uint32_t pet_timestamp(void);
uint32_t set_timestamp(void);
/** @brief Integration hook which locks the tracebuffer
* This can be for instance a mutex or interrupt lock.
* @param[out] h handle value which will be passed to the #pet_crit_off function
* @param[out] h handle value which will be passed to the #set_crit_off function
*/
void pet_crit_on(uint32_t *h);
void set_crit_on(uint32_t *h);
/** @brief Integration hook which locks the tracebuffer
* This can be for instance a mutex or interrupt lock.
* @param[in] h handle value set by #pet_crit_on
* @param[in] h handle value set by #set_crit_on
*/
void pet_crit_off(const uint32_t h);
void set_crit_off(const uint32_t h);
/** @brief Initializes PET with the provided trace buffer
* @param[in] str Trace buffer array
* @param[in] len Trace buffer array length
* @return None
*/
void pet_init(pet_trace_t tracebuffer[], size_t ntraces);
void set_init(set_trace_t tracebuffer[], size_t ntraces);
/** @brief Returns the current trace buffer utilization
* @return Number of items used in the trace buffer
*/
size_t pet_get_buffer_items(void);
size_t set_get_buffer_items(void);
/** @brief Enables or disables TTY mode
* TTY mode esures packet output is friendly for interactive console usage without a client.
* This means that at most 4 packets are sent at once, the data is encoded as hex digits
* and position is restored with each transmit. To erase a transmitted packet, see pet_tty_rubout.
* and position is restored with each transmit. To erase a transmitted packet, see set_tty_rubout.
* @param[in] enable If true, enable TTY mode
* @return None
*/
void pet_set_tty_mode(const bool enable);
void set_tty_mode(const bool enable);
/** @brief Creates a rubout packet clearing the maximum amount of data transmitted in TTY mode.
* This consists of a ANSI store, enough spaces to overwrite sent data and an ANSI restore.
@ -105,31 +105,31 @@ void pet_set_tty_mode(const bool enable);
* @param[in] buffer_size the size of the output buffer
* @return The packet size written to the buffer
*/
size_t pet_tty_rubout(char buffer[], const size_t buffer_size);
size_t set_tty_rubout(char buffer[], const size_t buffer_size);
/** @brief Enables tracing with the provided overflow policy
* @param[in] policy Either overwrite to overwrite oldest trace, or drop to drop the newest sample
* @return None
*/
void pet_enable(const pet_overflow_policy_t policy);
void set_enable(const set_overflow_policy_t policy);
/** @brief Disables tracing
* @return None
*/
void pet_disable(void);
void set_disable(void);
/** @brief Clears the trace buffer deleting all captures samples
* @return None
*/
void pet_clear(void);
void set_clear(void);
/** @brief Render a set amount of tracebuffer items and write them out using pet_out
/** @brief Render a set amount of tracebuffer items and write them out using set_out
* This function is for basic usage where the output function writes into a managed stream such as stdout
* @param[in] n Number of tracebuffer items to render, or 0 for the current size of the tracebuffer
* @param[in] compress if true, render the packets compressed
* @return None
*/
size_t pet_output(size_t n, const bool compress);
size_t set_output(size_t n, const bool compress);
/** @brief Render a set amount of tracebuffer items and store them in the provided buffer
* This advanced function is intended to be used with DMA buffers.
@ -139,7 +139,7 @@ size_t pet_output(size_t n, const bool compress);
* @param[in] compress if true, render the packets compressed
* @return None
*/
size_t pet_render(char buffer[], const size_t buffer_size, const size_t n, const bool compress);
size_t set_render(char buffer[], const size_t buffer_size, const size_t n, const bool compress);
/** @brief Adds diagnostic samples to the tracebuffer
* Signals include:
@ -151,14 +151,14 @@ size_t pet_render(char buffer[], const size_t buffer_size, const size_t n, const
* - PET.ItemsSent:u32 - The number of items sent since the last call to this function
* @return None
*/
void pet_diagtrace(void);
void set_diagtrace(void);
/** @brief Trace an event
* @param[in] tag Signal name of the trace
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_evtrace(const char* const tag, const bool skip_time);
void set_evtrace(const char* const tag, const bool skip_time);
/** @brief Trace an u8 value
* @param[in] tag Signal name of the trace
@ -166,7 +166,7 @@ void pet_evtrace(const char* const tag, const bool skip_time);
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_u8trace(const char* const tag, const uint8_t value, const bool skip_time);
void set_u8trace(const char* const tag, const uint8_t value, const bool skip_time);
/** @brief Trace an u16 value
* @param[in] tag Signal name of the trace
@ -174,7 +174,7 @@ void pet_u8trace(const char* const tag, const uint8_t value, const bool skip_tim
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_u16trace(const char* const tag, const uint16_t value, const bool skip_time);
void set_u16trace(const char* const tag, const uint16_t value, const bool skip_time);
/** @brief Trace an u32 value
* @param[in] tag Signal name of the trace
@ -182,7 +182,7 @@ void pet_u16trace(const char* const tag, const uint16_t value, const bool skip_t
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_u32trace(const char* const tag, const uint32_t value, const bool skip_time);
void set_u32trace(const char* const tag, const uint32_t value, const bool skip_time);
/** @brief Trace an s8 value
* @param[in] tag Signal name of the trace
@ -190,7 +190,7 @@ void pet_u32trace(const char* const tag, const uint32_t value, const bool skip_t
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_s8trace(const char* const tag, const int8_t value, const bool skip_time);
void set_s8trace(const char* const tag, const int8_t value, const bool skip_time);
/** @brief Trace an s16 value
* @param[in] tag Signal name of the trace
@ -198,7 +198,7 @@ void pet_s8trace(const char* const tag, const int8_t value, const bool skip_time
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_s16trace(const char* const tag, const int16_t value, const bool skip_time);
void set_s16trace(const char* const tag, const int16_t value, const bool skip_time);
/** @brief Trace an s32 value
* @param[in] tag Signal name of the trace
@ -206,7 +206,7 @@ void pet_s16trace(const char* const tag, const int16_t value, const bool skip_ti
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_s32trace(const char* const tag, const int32_t value, const bool skip_time);
void set_s32trace(const char* const tag, const int32_t value, const bool skip_time);
/** @brief Trace an array of u8 values
* @param[in] tag Signal name of the trace
@ -215,7 +215,7 @@ void pet_s32trace(const char* const tag, const int32_t value, const bool skip_ti
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_au8trace(const char* const tag, const uint8_t value[const], const uint8_t size, const bool skip_time);
void set_au8trace(const char* const tag, const uint8_t value[const], const uint8_t size, const bool skip_time);
/** @brief Trace an array of u16 values
* @param[in] tag Signal name of the trace
@ -224,7 +224,7 @@ void pet_au8trace(const char* const tag, const uint8_t value[const], const uint8
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_au16trace(const char* const tag, const uint16_t value[const], const uint8_t size, const bool skip_time);
void set_au16trace(const char* const tag, const uint16_t value[const], const uint8_t size, const bool skip_time);
/** @brief Trace an array of u32 values
* @param[in] tag Signal name of the trace
@ -233,7 +233,7 @@ void pet_au16trace(const char* const tag, const uint16_t value[const], const uin
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_au32trace(const char* const tag, const uint32_t value[const], const uint8_t size, const bool skip_time);
void set_au32trace(const char* const tag, const uint32_t value[const], const uint8_t size, const bool skip_time);
/** @brief Trace an array of s8 values
@ -243,7 +243,7 @@ void pet_au32trace(const char* const tag, const uint32_t value[const], const uin
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_as8trace(const char* const tag, const int8_t value[const], const uint8_t size, const bool skip_time);
void set_as8trace(const char* const tag, const int8_t value[const], const uint8_t size, const bool skip_time);
/** @brief Trace an array of s16 values
* @param[in] tag Signal name of the trace
@ -252,7 +252,7 @@ void pet_as8trace(const char* const tag, const int8_t value[const], const uint8_
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_as16trace(const char* const tag, const int16_t value[const], const uint8_t size, const bool skip_time);
void set_as16trace(const char* const tag, const int16_t value[const], const uint8_t size, const bool skip_time);
/** @brief Trace an array of s32 values
* @param[in] tag Signal name of the trace
@ -261,7 +261,7 @@ void pet_as16trace(const char* const tag, const int16_t value[const], const uint
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_as32trace(const char* const tag, const int32_t value[const], const uint8_t size, const bool skip_time);
void set_as32trace(const char* const tag, const int32_t value[const], const uint8_t size, const bool skip_time);
/** @brief Trace a string
* @param[in] tag Signal name of the trace
@ -269,9 +269,9 @@ void pet_as32trace(const char* const tag, const int32_t value[const], const uint
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_strtrace(const char* const tag, const char* const str, const bool skip_time);
void set_strtrace(const char* const tag, const char* const str, const bool skip_time);
#if (!PET_FLOAT_DISABLE)
#if (!SET_FLOAT_DISABLE)
/** @brief Trace an f32 value
* @param[in] tag Signal name of the trace
@ -279,7 +279,7 @@ void pet_strtrace(const char* const tag, const char* const str, const bool skip_
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void pet_f32trace(const char* const tag, const float value, const bool skip_time);
void set_f32trace(const char* const tag, const float value, const bool skip_time);
#endif
#endif

View File

@ -11,10 +11,11 @@ from vcd import VCDWriter, writer
from vcd.common import VarType
header = """
Pocket Event Trace
VCD Recorder Utility
CC BY-SA 2025 Dominic Höglinger
Streaming Event Trace
VCD Recorder Utility
(c) 2025 D.Hoeglinger
"""
@ -197,7 +198,7 @@ def scan_for_signals(directory, predefined_signals):
return list(signals.values()), valid
class PetFilter:
class Filter:
PREAMBLE = b"\x1b[s"
EPILOUGE = b"\x1b[u"
TAGCODE_LUT = {
@ -260,7 +261,6 @@ class PetFilter:
if tagcode not in self.TAGCODE_LUT:
self.packets_dropped += 1
print("LUT ERR", tagcode)
return
tag = self.TAGCODE_LUT[tagcode]
@ -325,7 +325,7 @@ class PetFilter:
else:
self.disassemble_packet(packet)
class PetRetagger:
class Retagger:
def __init__(self, on_value, tags=None):
tags = tags or []
self._tag_lut = {bsd_hash(tag):tag for tag in tags}
@ -412,11 +412,13 @@ class VcdSink:
timestamp = self.timestamp
# unpack
for i in range(0,4):
char = chr(value >> (i*8) & 0xFF)
self.strings[tag][1] += char
char = value >> (i*8) & 0xFF
if char != 0:
self.strings[tag][1] += chr(char)
# sub of 1 indicates end of string
if sub == 1:
try:
string = self.strings[tag][1]
#print(f"### {timestamp:012X} : {self.varnames[tag]} <= \"{self.strings[tag][1]}\"", flush=True)
self.writer.change(self.strings[tag][0], timestamp, self.strings[tag][1])
except ValueError:
@ -504,9 +506,9 @@ def main():
nfile = open(noisefile, 'wb')
process_noise_p = partial(process_noise, nfile)
petv = VcdSink(dfile, signals, timescale)
petr = PetRetagger(petv.process, tags)
petf = PetFilter(petr.process, process_noise_p)
vcd_sink = VcdSink(dfile, signals, timescale)
retagger = Retagger(vcd_sink.process, tags)
packet_filter = Filter(retagger.process, process_noise_p)
print("Signals:")
for var in signals:
print(f" - {var}")
@ -514,8 +516,8 @@ def main():
print(" === BEGIN NOISE ===")
try:
while True:
for b in sys.stdin.buffer.read(1):
for bstr in sys.stdin.buffer:
for b in bstr:
petf.process(b)
except KeyboardInterrupt:
pass
@ -524,14 +526,14 @@ def main():
print(" === END NOISE ===")
print()
petv.writer.close()
vcd_sink.writer.close()
dfile.close()
if nfile:
nfile.close()
print("Summary:")
packet_count = petf.packet_counter
drop_count = petf.packets_dropped + petv.packets_dropped
packet_count = packet_filter.packet_counter
drop_count = packet_filter.packets_dropped + vcd_sink.packets_dropped
trace_size = human_readable_size(os.path.getsize(tracefile))
print(f" - Packets received: {packet_count}")
print(f" - Packets dropped: {drop_count}")