Add 64 bit integers and doubles

This commit is contained in:
Dominic Höglinger 2025-05-18 12:45:56 +02:00
parent 27f06ba208
commit 85a628da2b
4 changed files with 132 additions and 22 deletions

View File

@ -6,7 +6,7 @@ ST is a small tracing library written in C99.
It is 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.
Signal types supported are all integers up to 32 bit as well as their arrays, floats, events and strings.
Signal types supported are all integers up to 64 bit, integer arrays up to 32 bit, floats, doubles, events and strings.
![Demo of st_record.py in TUI mode displaying various traced signals and diagnostic information regarding ST](.media/demo-tui.gif "Demo of st_record.py in TUI mode")

43
st.c
View File

@ -50,11 +50,13 @@ typedef enum
st_v1 = 0x21,
st_v2 = 0x22,
st_v4 = 0x24,
st_v8 = 0x28,
st_a1 = 0x31,
st_a2 = 0x32,
st_a4 = 0x34,
st_s4 = 0x44,
st_f4 = 0x54,
st_f8 = 0x58,
st_ev = 0x60,
} st_type_t;
@ -72,8 +74,10 @@ typedef struct
typedef union
{
uint32_t u32value;
uint64_t u64value;
#if (!ST_FLOAT_DISABLE)
float f32value;
double f64value;
#endif
} st_convert_t;
@ -387,6 +391,16 @@ void st_u32trace(const char* const tag, const uint32_t value, const bool skip_ti
}
}
void st_u64trace(const char* const tag, const uint64_t value, const bool skip_time)
{
if (s_enabled == true)
{
uint32_t ts = skip_time ? 0UL : st_timestamp();
tracebuffer_add(ts, st_v8, (uint32_t)(value & 0xFFFFFFFF), 0U, tag);
tracebuffer_add(ts, st_v8, (uint32_t)(value >> 32), 1U, tag);
}
}
void st_s8trace(const char* const tag, const int8_t value, const bool skip_time)
{
if (s_enabled == true)
@ -403,7 +417,7 @@ void st_s16trace(const char* const tag, const int16_t value, const bool skip_tim
}
}
void st_s4trace(const char* const tag, const int32_t value, const bool skip_time)
void st_s32trace(const char* const tag, const int32_t value, const bool skip_time)
{
if (s_enabled == true)
{
@ -411,6 +425,16 @@ void st_s4trace(const char* const tag, const int32_t value, const bool skip_time
}
}
void st_s64trace(const char* const tag, const int64_t value, const bool skip_time)
{
if (s_enabled == true)
{
uint32_t ts = skip_time ? 0UL : st_timestamp();
tracebuffer_add(ts, st_v8, (uint32_t)(value & 0xFFFFFFFF), 0U, tag);
tracebuffer_add(ts, st_v8, (uint32_t)(value >> 32), 1U, tag);
}
}
void st_au8trace(const char* const tag, const uint8_t value[const], const uint8_t size, const bool skip_time)
{
if (s_enabled == true)
@ -512,12 +536,23 @@ void st_strtrace(const char* const tag, const char* const str, const bool skip_t
#if (!ST_FLOAT_DISABLE)
void st_f32trace(const char* const tag, const float value, const bool skip_time)
{
st_convert_t converter = {.f32value = value };
st_convert_t converter = { .f32value = value };
if (s_enabled == true)
{
tracebuffer_add(skip_time ? 0UL : st_timestamp(), st_f4, converter.u32value, 0U, tag);
}
}
void st_f64trace(const char* const tag, const double value, const bool skip_time)
{
st_convert_t converter = { .f64value = value };
if (s_enabled == true)
{
uint32_t ts = skip_time ? 0UL : st_timestamp();
tracebuffer_add(ts, st_f8, (uint32_t)(converter.u64value & 0xFFFFFFFF), 0U, tag);
tracebuffer_add(ts, st_f8, (uint32_t)(converter.u64value >> 32), 1U, tag);
}
}
#endif
static size_t frame_preamble(char buffer[], const size_t buffer_size)
@ -567,6 +602,8 @@ static size_t pack_frame(char packet_bytes[], const st_trace_t* const p_packet)
case st_s4:
case st_f4:
case st_d4:
case st_v8:
case st_f8:
packet_bytes[packet_size++] = ST_BYTE(p_packet->m_value, 0);
packet_bytes[packet_size++] = ST_BYTE(p_packet->m_value, 1);
packet_bytes[packet_size++] = ST_BYTE(p_packet->m_value, 2);
@ -580,6 +617,8 @@ static size_t pack_frame(char packet_bytes[], const st_trace_t* const p_packet)
case st_a2:
case st_a4:
case st_s4:
case st_v8:
case st_f8:
packet_bytes[packet_size++] = (char)p_packet->m_sub;
break;
}

25
st.h
View File

@ -184,6 +184,14 @@ void st_u16trace(const char* const tag, const uint16_t value, const bool skip_ti
*/
void st_u32trace(const char* const tag, const uint32_t value, const bool skip_time);
/** @brief Trace an u64 value
* @param[in] tag Signal name of the trace
* @param[in] value Value to trace
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void st_u64trace(const char* const tag, const uint64_t value, const bool skip_time);
/** @brief Trace an s8 value
* @param[in] tag Signal name of the trace
* @param[in] value Value to trace
@ -208,6 +216,14 @@ void st_s16trace(const char* const tag, const int16_t value, const bool skip_tim
*/
void st_s32trace(const char* const tag, const int32_t value, const bool skip_time);
/** @brief Trace an s64 value
* @param[in] tag Signal name of the trace
* @param[in] value Value to trace
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void st_s64trace(const char* const tag, const int64_t value, const bool skip_time);
/** @brief Trace an array of u8 values
* @param[in] tag Signal name of the trace
* @param[in] value Value array to trace
@ -272,7 +288,6 @@ void st_as32trace(const char* const tag, const int32_t value[const], const uint8
void st_strtrace(const char* const tag, const char* const str, const bool skip_time);
#if (!ST_FLOAT_DISABLE)
/** @brief Trace an f32 value
* @param[in] tag Signal name of the trace
* @param[in] value Value to trace
@ -280,6 +295,14 @@ void st_strtrace(const char* const tag, const char* const str, const bool skip_t
* @return None
*/
void st_f32trace(const char* const tag, const float value, const bool skip_time);
/** @brief Trace an f64 value
* @param[in] tag Signal name of the trace
* @param[in] value Value to trace
* @param[in] skip_time Use last recorded timestamp for this trace
* @return None
*/
void st_f64trace(const char* const tag, const double value, const bool skip_time);
#endif
#endif

View File

@ -170,7 +170,7 @@ def fastlz_decompress_lv1(datain, doutlen):
def scan_for_signals(directory, predefined_signals):
library_files = ['st.c', 'st.h']
rx_events = re.compile(r'st_evtrace\(\"([^\"]+)\"')
rx_scalars = re.compile(r'st_([usf])(8|16|32)trace\(\"([^\"]+)\"')
rx_scalars = re.compile(r'st_([usf])(8|16|32|64)trace\(\"([^\"]+)\"')
rx_arrays = re.compile(r'st_a([us])(8|16|32)trace\(\"([^\"]+)\"\,\s*[^,]+,\s*((?:0x)?[a-zA-Z0-9]+)')
rx_strings = re.compile(r'st_strtrace\(\"([^\"]+)\"')
signals = {}
@ -227,11 +227,13 @@ class Filter:
0x21 : "V1",
0x22 : "V2",
0x24 : "V4",
0x28 : "V8",
0x31 : "A1",
0x32 : "A2",
0x34 : "A4",
0x44 : "S4",
0x54 : "F4",
0x58 : "F8",
0x60 : "EV"
}
@ -291,7 +293,6 @@ class Filter:
return
tag = self.TAGCODE_LUT[tagcode]
value = None
offset = 1
match tag:
@ -301,11 +302,11 @@ class Filter:
case "D2"|"V2"|"A2":
value = decode_binstr(packet[offset:offset+2])
offset += 2
case "D4"|"V4"|"A4"|"F4"|"S4":
case "D4"|"V4"|"V8"|"A4"|"F4"|"F8"|"S4":
value = decode_binstr(packet[offset:offset+4])
offset += 4
sub = None
if tag[0] == 'A' or tag[0] == 'S':
if tag[0] == 'A' or tag[0] == 'S' or tag == 'V8' or tag == 'F8':
sub = decode_binstr(packet[offset:offset+1])
offset += 1
@ -315,7 +316,6 @@ class Filter:
except Exception as ex:
self.packets_dropped += 1
return
self.process_value(hashtag, value, sub, tag)
self.packet_counter += 1
@ -370,6 +370,7 @@ class VcdSink:
def __init__(self, fs, signals, timescale='1 us'):
self.writer = VCDWriter(fs, timescale=timescale, date=datetime.datetime.now().isoformat(), version=f"ST v1.0.2")
self.skalars = {}
self.mw_skalars = {}
self.arrays = {}
self.strings = {}
self.varnames = {}
@ -393,6 +394,12 @@ class VcdSink:
case 'f32':
dtype = 'real'
dsize = 32
case 'f64':
dtype = 'real'
dsize = 64
case 'u64'|'s64':
dtype = 'integer'
dsize = 64
case 'u32'|'s32':
dtype = 'integer'
dsize = 32
@ -416,6 +423,8 @@ class VcdSink:
self.arrays[hvar] = vars
elif dtype == 'string':
self.strings[hvar] = [self.writer.register_var(hier, name, dtype, size=dsize), ""]
elif vtype in ['u64', 's64', 'f64']:
self.mw_skalars[hvar] = [self.writer.register_var(hier, name, dtype, size=dsize), 0]
else:
self.skalars[hvar] = self.writer.register_var(hier, name, dtype, size=dsize)
@ -472,6 +481,36 @@ class VcdSink:
self.packets_dropped += 1
self.strings[tag][1] = ""
self._emit(timestamp, tag, string, None)
elif datatag in ['V8', 'F8']:
timestamp = self.timestamp
commit = False
commit_value = 0
if sub == 0:
self.mw_skalars[tag][1] = 0
self.mw_skalars[tag][1] |= (value << (32 * sub))
if sub == 1:
commit = True
if datatag == 'V8':
commit_value = self.mw_skalars[tag][1]
elif datatag == 'F8':
try:
commit_value = struct.unpack("<d", struct.pack("<Q", self.mw_skalars[tag][1]))[0]
except Exception as e:
print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
if commit:
try:
self.writer.change(self.mw_skalars[tag][0], timestamp, commit_value)
except ValueError:
print(f"### {timestamp:012} : {self.varnames[tag]} <= {commit_value} [VAL_ERR] ", flush=True)
self.packets_dropped += 1
except writer.VCDPhaseError:
print(f"### {timestamp:012} : {self.varnames[tag]} <= {commit_value} [PHA_ERR] ", flush=True)
self.packets_dropped += 1
except:
print(f"### {timestamp:012} : {self.varnames[tag]} <= {commit_value} [ERR] ", flush=True)
self.packets_dropped += 1
self._emit(timestamp, tag, commit_value, None)
# skalar values
elif (datatag == 'EV') or (datatag[0] == 'V') or (datatag[0] == 'F'):
@ -558,10 +597,7 @@ def main():
# start recording
if enable_tui:
if enable_verbose_trace:
print("warning: verbose trace is not avaialble in TUI mode")
print()
tui_record(signals, packet_filter, vcd_sink, timescale)
tui_record(signals, packet_filter, vcd_sink, enable_verbose_trace, timescale)
else:
record(signals, packet_filter, vcd_sink, enable_verbose_trace)
@ -657,7 +693,7 @@ class SignalTable:
not_enough_time_passed = (time - self.lastupdate[signal]) < self.update_interval
is_array = signal in self.arraycache
is_last_array_index = is_array and (sub == (len(self.arraycache[signal])))
if not_enough_time_passed and is_last_array_index:
if not_enough_time_passed and not(is_array):
return
self.lastupdate[signal] = time
@ -671,7 +707,9 @@ class SignalTable:
value_str = f"x{value:04X}"
case "u32"|"s32":
value_str = f"x{value:08X}"
case "f32":
case "u64"|"s64":
value_str = f"x{value:016X}"
case "f32"|"f64":
value_str = str(value)
case "string":
value_str = value.rstrip("\r\n")
@ -686,7 +724,7 @@ class SignalTable:
self.values[signal] = value_str
def tui_record(signals, packet_filter, vcd_sink, timescale):
def tui_record(signals, packet_filter, vcd_sink, enable_verbose_trace, timescale):
try:
from rich.console import Console
from rich.text import Text
@ -699,6 +737,12 @@ def tui_record(signals, packet_filter, vcd_sink, timescale):
print("error: TUI mode requires the rich package")
exit()
if not(enable_verbose_trace):
print("Signals:")
for var in signals:
print(f" - {var}")
print()
console = Console()
noise_buffer = NoiseLineBuffer(lambda text: console.print(f"[blue]{text}"))
trace_text = Text("")
@ -724,6 +768,7 @@ def tui_record(signals, packet_filter, vcd_sink, timescale):
render_time_tm = TotalMaximumProgressUpdater(diag_progress, render_time)
comp_time_tm = TotalMaximumProgressUpdater(diag_progress, comp_time)
if enable_verbose_trace:
# set up table layout and signal view
# set its update interval to 0.2 seconds in ticks
signal_update_interval = int(0.2/timescale)
@ -765,7 +810,10 @@ def tui_record(signals, packet_filter, vcd_sink, timescale):
for bstr in sys.stdin.buffer:
for b in bstr:
packet_filter.process(b)
if enable_verbose_trace:
live_status.update(generate_table(diag_progress, signal_values))
else:
live_status.update(diag_progress)
except KeyboardInterrupt:
diag_progress.stop()