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 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. 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") ![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")

41
st.c
View File

@ -50,11 +50,13 @@ typedef enum
st_v1 = 0x21, st_v1 = 0x21,
st_v2 = 0x22, st_v2 = 0x22,
st_v4 = 0x24, st_v4 = 0x24,
st_v8 = 0x28,
st_a1 = 0x31, st_a1 = 0x31,
st_a2 = 0x32, st_a2 = 0x32,
st_a4 = 0x34, st_a4 = 0x34,
st_s4 = 0x44, st_s4 = 0x44,
st_f4 = 0x54, st_f4 = 0x54,
st_f8 = 0x58,
st_ev = 0x60, st_ev = 0x60,
} st_type_t; } st_type_t;
@ -72,8 +74,10 @@ typedef struct
typedef union typedef union
{ {
uint32_t u32value; uint32_t u32value;
uint64_t u64value;
#if (!ST_FLOAT_DISABLE) #if (!ST_FLOAT_DISABLE)
float f32value; float f32value;
double f64value;
#endif #endif
} st_convert_t; } 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) void st_s8trace(const char* const tag, const int8_t value, const bool skip_time)
{ {
if (s_enabled == true) 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) 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) void st_au8trace(const char* const tag, const uint8_t value[const], const uint8_t size, const bool skip_time)
{ {
if (s_enabled == true) if (s_enabled == true)
@ -518,6 +542,17 @@ void st_f32trace(const char* const tag, const float value, const bool skip_time)
tracebuffer_add(skip_time ? 0UL : st_timestamp(), st_f4, converter.u32value, 0U, tag); 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 #endif
static size_t frame_preamble(char buffer[], const size_t buffer_size) 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_s4:
case st_f4: case st_f4:
case st_d4: 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, 0);
packet_bytes[packet_size++] = ST_BYTE(p_packet->m_value, 1); packet_bytes[packet_size++] = ST_BYTE(p_packet->m_value, 1);
packet_bytes[packet_size++] = ST_BYTE(p_packet->m_value, 2); 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_a2:
case st_a4: case st_a4:
case st_s4: case st_s4:
case st_v8:
case st_f8:
packet_bytes[packet_size++] = (char)p_packet->m_sub; packet_bytes[packet_size++] = (char)p_packet->m_sub;
break; 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); 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 /** @brief Trace an s8 value
* @param[in] tag Signal name of the trace * @param[in] tag Signal name of the trace
* @param[in] value Value to 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); 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 /** @brief Trace an array of u8 values
* @param[in] tag Signal name of the trace * @param[in] tag Signal name of the trace
* @param[in] value Value array to 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); void st_strtrace(const char* const tag, const char* const str, const bool skip_time);
#if (!ST_FLOAT_DISABLE) #if (!ST_FLOAT_DISABLE)
/** @brief Trace an f32 value /** @brief Trace an f32 value
* @param[in] tag Signal name of the trace * @param[in] tag Signal name of the trace
* @param[in] value Value to 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 * @return None
*/ */
void st_f32trace(const char* const tag, const float value, const bool skip_time); 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
#endif #endif

View File

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