From ebf6ba36ee84adc5b2713e2a2fa5904fd6f311ce Mon Sep 17 00:00:00 2001 From: Romain TRILLING Date: Sun, 5 Jul 2026 14:53:10 +0200 Subject: [PATCH 1/2] Added Support STLINKV3_POWER using SoftMarkers as a replacement of D7 HW Timestamps --- benchmark/runner/devices_kws_ic_vww.yaml | 9 + benchmark/runner/devices_sww.yaml | 9 + benchmark/runner/main.py | 75 ++++--- .../runner/power_manager/power_manager.py | 10 +- .../power_manager_stlinkv3pwr.py | 183 ++++++++++++++++++ benchmark/runner/script.py | 39 +++- 6 files changed, 286 insertions(+), 39 deletions(-) create mode 100644 benchmark/runner/power_manager/power_manager_stlinkv3pwr.py diff --git a/benchmark/runner/devices_kws_ic_vww.yaml b/benchmark/runner/devices_kws_ic_vww.yaml index 8a4c2264..c004eeac 100644 --- a/benchmark/runner/devices_kws_ic_vww.yaml +++ b/benchmark/runner/devices_kws_ic_vww.yaml @@ -23,6 +23,15 @@ voltage: 1.8 # <-- Voltage for DUT usb: 0x0483: 0x5740 # Ensures detection by VID/PID (1155 / 22336) +- name: stlinkv3pwr + type: power + baud: 3686400 + preference: 3 + echo: false + voltage: 1.8 + raw_sampling_rate: 100000 + usb: + 0x0483: 0x3757 # Ensures detection by VID/PID (1155 / 22336) - name: l4r5zi type: dut baud: diff --git a/benchmark/runner/devices_sww.yaml b/benchmark/runner/devices_sww.yaml index 53acf5fc..29c60d42 100644 --- a/benchmark/runner/devices_sww.yaml +++ b/benchmark/runner/devices_sww.yaml @@ -23,6 +23,15 @@ voltage: 3.3 # <-- Voltage for DUT usb: 0x0483: 0x5740 # Ensures detection by VID/PID (1155 / 22336) +- name: stlinkv3pwr + type: power + baud: 3686400 + preference: 3 + echo: false + voltage: 1.8 + raw_sampling_rate: 100000 + usb: + 0x0483: 0x3757 # Ensures detection by VID/PID (1155 / 22336) - name: l4r5zi type: dut baud: diff --git a/benchmark/runner/main.py b/benchmark/runner/main.py index 2445311b..999fc69a 100644 --- a/benchmark/runner/main.py +++ b/benchmark/runner/main.py @@ -169,36 +169,57 @@ def print_energy_results(l_results, energy_sampling_freq=1000, req_cycles=5, res for inf_num,res in enumerate(l_results): plt.clf() energy_samples = res['power']['samples'] # assume 'output type' is set to energy + hw_timestamps = res['power'].get('timestamps', []) + soft_markers = res['power'].get('soft_markers', []) - # timestamps (or 'events' per LPM01a wording are recorded as - # (counter, num_samples) where - # counter: 0,1,.. for the 1st, 2nd, etc, to test if you lost a timestamp - # num_samples: number of samples captured when the timestamp happened - ts_counters = [int(ts[0]) for ts in res['power']['timestamps']] - ts_samp_nums = np.array([int(ts[1]) for ts in res['power']['timestamps']]) - ts_seconds = ts_samp_nums/energy_sampling_freq - - if np.any(np.diff(ts_counters) != 1): - raise RuntimeError(f"Timestamps are not consecutive:\n{res['power']['timestamps']}") - - t_implicit = np.arange(len(energy_samples))/energy_sampling_freq + t_implicit = np.arange(len(energy_samples)) / energy_sampling_freq plt.plot(t_implicit, energy_samples, 'b') - y_min = np.min(energy_samples) - y_max = np.max(energy_samples) + y_min = np.min(energy_samples) if len(energy_samples) else 0 + y_max = np.max(energy_samples) if len(energy_samples) else 1 + + idx_start = None + idx_stop = None + + if len(hw_timestamps) >= 2: + ts_counters = [int(ts[0]) for ts in hw_timestamps] + ts_samp_nums = np.array([int(ts[1]) for ts in hw_timestamps]) + ts_seconds = ts_samp_nums/energy_sampling_freq - for ts in ts_seconds: - plt.plot([ts, ts], [y_min, y_max], 'r') - plt.grid(True) - plt.savefig(os.path.join(results_dir, f"energy_inf_{inf_num:03d}.png")) + if np.any(np.diff(ts_counters) != 1): + raise RuntimeError(f"Timestamps are not consecutive:\n{hw_timestamps}") + + for ts in ts_seconds: + plt.plot([ts, ts], [y_min, y_max], 'r') + plt.grid(True) - # There is sometimes some extra activity on the timestamp pin at the - # beginning, so take the last two - t_start = ts_seconds[-2] - t_stop = ts_seconds[-1] - idx_start = ts_samp_nums[-2] - idx_stop = ts_samp_nums[-1] - - elapsed_time = t_stop-t_start + idx_start = ts_samp_nums[-2] + idx_stop = ts_samp_nums[-1] + + elif len(soft_markers) >= 2: + marker_dict = {} + for tag, idx in soft_markers: + marker_dict[tag] = idx + + if "start" in marker_dict and "stop" in marker_dict: + idx_start = marker_dict["start"] + idx_stop = marker_dict["stop"] + + t_start = idx_start / energy_sampling_freq + t_stop = idx_stop / energy_sampling_freq + plt.plot([t_start, t_start], [y_min, y_max], 'g') + plt.plot([t_stop, t_stop], [y_min, y_max], 'g') + plt.grid(True) + + else: + idx_start = 0 + idx_stop = len(energy_samples) + + plt.savefig(os.path.join(results_dir, f"energy_inf_{inf_num:03d}.png")) + + if idx_start is None or idx_stop is None or idx_stop <= idx_start: + raise RuntimeError(f"Invalid energy integration window for trial {inf_num}: start={idx_start}, stop={idx_stop}") + + elapsed_time = (idx_stop - idx_start) / energy_sampling_freq inference_energy_samples = np.array(energy_samples[idx_start:idx_stop]) total_inference_energy = np.sum(inference_energy_samples) num_inferences = res['infer']['iterations'] @@ -377,4 +398,4 @@ def summarize_result(result, power, mode, results_file=None): print(f"Session logged in file {log_filename}") results_data_file = os.path.join(os.path.dirname(log_filename), "results.json") with open(results_data_file, 'w') as fpo: - json.dump(result, fpo) \ No newline at end of file + json.dump(result, fpo) diff --git a/benchmark/runner/power_manager/power_manager.py b/benchmark/runner/power_manager/power_manager.py index 90e07c6d..0bf5d4bd 100644 --- a/benchmark/runner/power_manager/power_manager.py +++ b/benchmark/runner/power_manager/power_manager.py @@ -26,6 +26,10 @@ def __init__(self, device_type, port_device=None, baud_rate=None, voltage=3.3, e from .power_manager_lpm import LPMCommands # only import this file if we're using this device self._port = SerialDevice(port_device, baud_rate, "ack|error", "\r\n", echo=echo) self._commands = LPMCommands(self, self._port) + elif device_type == "stlinkv3pwr": + from .power_manager_stlinkv3pwr import STLinkV3PWRCommands + self._port = SerialDevice(port_device, baud_rate, "", "\r\n", echo=echo) + self._commands = STLinkV3PWRCommands(self, self._port) elif device_type == "js220": from .power_manager_js220 import JoulescopeCommands if js_device is None: @@ -40,8 +44,8 @@ def __init__(self, device_type, port_device=None, baud_rate=None, voltage=3.3, e def __enter__(self): self._port.__enter__() - self._start_read_thread() # ✅ move this up - self._commands.setup() # ✅ now it's safe like the old code + self._start_read_thread() # ? move this up + self._commands.setup() # ? now it's safe like the old code return self def __exit__(self, *args): @@ -61,7 +65,7 @@ def _stop_read_thread(self): def get_results(self): while not self._data_queue.empty(): yield self._data_queue.get() - + def should_stop(self): return not self._running diff --git a/benchmark/runner/power_manager/power_manager_stlinkv3pwr.py b/benchmark/runner/power_manager/power_manager_stlinkv3pwr.py new file mode 100644 index 00000000..6d7a5c1d --- /dev/null +++ b/benchmark/runner/power_manager/power_manager_stlinkv3pwr.py @@ -0,0 +1,183 @@ +# power_manager_stlinkv3pwr.py + +import re +import sys +from .power_manager_lpm import LPMCommands + + +class STLinkV3PWRCommands(LPMCommands): + PROMPT = "stlp > " + + def __init__(self, manager, port): + super().__init__(manager, port) + self._sample_count = 0 + + def setup(self): + self._send_command("htc") + self.power_off() + self.configure_trigger("inf", 0, "sw") + self.configure_output("energy", "ascii_dec", "1k") + self.configure_voltage(self.m._voltage) + + def tear_down(self): + try: + self.stop() + except Exception: + pass + try: + self.power_off() + except Exception: + pass + try: + self._send_command("hrc") + except Exception: + pass + + def read_loop(self): + in_summary = False + + while self.m._running: + line = self._port.read_line(timeout=0.25) + if line is None: + continue + if not line: + continue + + temp = self._strip_prompt(line) + if not temp: + continue + + if temp == "summary beg": + in_summary = True + self.m._message_queue.put(temp) + continue + + if in_summary: + self.m._message_queue.put(temp) + if temp == "summary end": + in_summary = False + continue + + if re.fullmatch(r"\d{4}[+-]\d{2}", temp): + value = self._decode_ascii_dec_value(temp) + self.m._data_queue.put(value) + self._sample_count += 1 + elif re.fullmatch(r"RecID\s+\d+", temp): + self.m._data_queue.put(temp) + elif temp == "end": + self.m._message_queue.put("Acquisition completed") + else: + self.m._message_queue.put(temp) + + def configure_trigger(self, acqtime, trigdelay, trigsrc): + self._send_command(f"acqtime {acqtime}") + self._send_command(f"trigdelay {trigdelay}") + self._send_command(f"trigsrc {trigsrc}") + + def power_on(self): + return self._send_command("pwr on nostatus") + + def power_off(self): + return self._send_command("pwr off") + + def get_board_id(self): + if not self.m._board_id: + result, output = self._send_command("whoami") + self.m._board_id = output if result else None + return self.m._board_id + + def set_lcd(self, *args): + return [None, None] + + def mark_soft_event(self, tag): + self.m._data_queue.put(f"soft_event {tag} {self._sample_count}") + + def stop(self): + self._port.write_line("stop") + while True: + line = self.m._message_queue.get() + temp = self._strip_prompt(line) + if temp == "Acquisition completed" or temp == "end": + break + return True + + def _strip_prompt(self, line): + line = line.strip() + if line.startswith(self.PROMPT): + line = line[len(self.PROMPT):].strip() + return line + + def _send_command(self, command, expect_output=False, err_message=None): + self._purge_messages() + self._port.write_line(command) + lines = self._read_response(command) + + result = "ack" in lines + output = [line for line in lines if line not in ("ack", "err")] + + if not result: + print("Power Manager did not acknowledge. PM Response:") + print(lines) + output = self._read_error_output() + if err_message and output: + print(f"{err_message}: {output[0]}", file=sys.stderr) + elif expect_output: + output = list(self._read_output()) + + return result, output if not output or len(output) != 1 else output[0] + + def _read_response(self, command): + out_lines = [] + + while True: + line = self.m._message_queue.get() + temp = self._strip_prompt(line) + + if not temp: + continue + + if temp.startswith("ack"): + remainder = temp[3:].strip() + out_lines.append("ack") + if remainder: + if remainder.startswith(command): + tail = remainder[len(command):].strip() + if tail.startswith(":"): + tail = tail[1:].strip() + if tail: + out_lines.append(tail) + else: + out_lines.append(remainder) + break + + elif temp.startswith("err"): + remainder = temp[3:].strip() + out_lines.append("err") + if remainder: + out_lines.append(remainder) + break + + else: + out_lines.append(temp) + + return out_lines + + def _read_output(self): + while True: + line = self.m._message_queue.get() + temp = self._strip_prompt(line) + if temp == "": + return + yield temp + + def _read_error_output(self): + errors = [] + while not self.m._message_queue.empty(): + line = self.m._message_queue.get() + temp = self._strip_prompt(line) + if temp: + errors.append(temp) + return errors if errors else ["Unknown STLINK-V3PWR error"] + + def _decode_ascii_dec_value(self, s): + return float(s.replace("+", "e+").replace("-", "e-")) diff --git a/benchmark/runner/script.py b/benchmark/runner/script.py index d273db94..4660551c 100644 --- a/benchmark/runner/script.py +++ b/benchmark/runner/script.py @@ -143,7 +143,7 @@ def run(self, io, dut, dataset, mode): if watchdog_flag["timeout"]: print("[WATCHDOG] Loop timeout. Restarting current loop iteration.") watchdog_flag["timeout"] = False # reset for next iteration - r = cmd.run(io, dut, dataset, mode) + for cmd in self._commands: r = cmd.run(io, dut, dataset, mode) if r is not None: @@ -182,7 +182,15 @@ def __init__(self, iterations=1, warmups=0, skip_time=0, loop_count=None): def run(self, io, dut, dataset, mode): # mode passed to run + # SW Fallback used for STLINK-V3PWR + if mode == "e" and dut.power_manager and hasattr(dut.power_manager._commands, "mark_soft_event"): + dut.power_manager._commands.mark_soft_event("start") + raw_result = dut.infer(self._iterations, self._warmups) + + if mode == "e" and dut.power_manager and hasattr(dut.power_manager._commands, "mark_soft_event"): + dut.power_manager._commands.mark_soft_event("stop") + print(f"Running inference with {self._warmups}/{self._iterations} (warmup/measured) iterations ... ", end="") infer_results = _ScriptInferStep._gather_infer_results(raw_result, mode) print(f" done") @@ -192,16 +200,17 @@ def run(self, io, dut, dataset, mode): # mode passed to run result = dict(infer=infer_results) if mode == "e": - timestamps, samples = _ScriptInferStep._gather_power_results(dut.power_manager) - print(f" Captured samples:{len(samples)} timestamps:{len(timestamps)}") - # Read power values from the log file using timestamps + timestamps, samples, soft_markers = _ScriptInferStep._gather_power_results(dut.power_manager) + print(f" Captured samples:{len(samples)} timestamps:{len(timestamps)} soft_markers:{len(soft_markers)}") + # Read power values from the log file using timestamps or Softmarker power_values = None result.update(power=dict(samples=samples, timestamps=timestamps, + soft_markers=soft_markers, extracted_power=power_values # <-- NEW: Power values from the file ) - ) + ) else: self._print_AP_results(infer_results,mode) @@ -213,6 +222,7 @@ def _gather_power_results(power): samples = [] timeStamps = [] # this is what the EEMBC runner calls "timestamps" clock_ticks = [] # this is what the LPM01a calls "timestamps" + soft_markers = [] if power: for x in power.get_results(): if isinstance(x, str): @@ -227,9 +237,20 @@ def _gather_power_results(power): match = re.match(r"event (\d+) ris", x) event_num = match.group(1) timeStamps.append((event_num, len(samples))) + elif x.startswith("RecID "): + # STLINK-V3PWR metadata for stream resynchronization / overflow info. + # Not used in current energy windowing logic. + pass + elif x.startswith("soft_event "): + # We intentionally ignore the absolute index encoded in the message + # and convert the marker into a local index, relative to the samples + # of THIS inference only. + match = re.match(r"soft_event (\w+)(?:\s+\d+)?", x) + if match: + soft_markers.append((match.group(1), len(samples))) else: samples.append(x) - return timeStamps, samples + return timeStamps, samples, soft_markers @staticmethod def _gather_infer_results(cmd_results, mode): @@ -343,14 +364,14 @@ def run(self, io, dut, dataset, mode): ) if mode == "e": - timestamps, samples = _ScriptInferStep._gather_power_results(dut.power_manager) - print(f"samples:{len(samples)} timestamps:{len(timestamps)}") - + timestamps, samples, soft_markers = _ScriptInferStep._gather_power_results(dut.power_manager) + print(f"samples:{len(samples)} timestamps:{len(timestamps)} soft_markers:{len(soft_markers)}") # Read power values from the log file using timestamps power_values = None result.update(power=dict(samples=samples, timestamps=timestamps, + soft_markers=soft_markers, extracted_power=power_values ) ) From abe9fa24183a9df2e5b6bcd4b9e3df9340ef4c12 Mon Sep 17 00:00:00 2001 From: Romain TRILLING Date: Mon, 6 Jul 2026 16:31:27 +0200 Subject: [PATCH 2/2] STLINKv3Power Support: removed soft_markers. Use TGI to collect HW events --- benchmark/runner/devices_ad.yaml | 9 +++ benchmark/runner/devices_kws_ic_vww.yaml | 2 +- benchmark/runner/main.py | 75 +++++++------------ .../runner/power_manager/power_manager_lpm.py | 2 +- .../power_manager_stlinkv3pwr.py | 47 +----------- benchmark/runner/script.py | 39 +++------- 6 files changed, 48 insertions(+), 126 deletions(-) diff --git a/benchmark/runner/devices_ad.yaml b/benchmark/runner/devices_ad.yaml index 31b89299..40da40b8 100644 --- a/benchmark/runner/devices_ad.yaml +++ b/benchmark/runner/devices_ad.yaml @@ -23,6 +23,15 @@ voltage: 1.8 # <-- Voltage for DUT usb: 0x0483: 0x5740 # Ensures detection by VID/PID (1155 / 22336) +- name: stlinkv3pwr + type: power + baud: 3686400 + preference: 3 + echo: false + voltage: 1.8 + raw_sampling_rate: 100000 + usb: + 0x0483: 0x3757 # Ensures detection by VID/PID (1155 / 22336) - name: l4r5zi type: dut baud: diff --git a/benchmark/runner/devices_kws_ic_vww.yaml b/benchmark/runner/devices_kws_ic_vww.yaml index c004eeac..8c97a88e 100644 --- a/benchmark/runner/devices_kws_ic_vww.yaml +++ b/benchmark/runner/devices_kws_ic_vww.yaml @@ -27,7 +27,7 @@ type: power baud: 3686400 preference: 3 - echo: false + echo: False voltage: 1.8 raw_sampling_rate: 100000 usb: diff --git a/benchmark/runner/main.py b/benchmark/runner/main.py index 999fc69a..2445311b 100644 --- a/benchmark/runner/main.py +++ b/benchmark/runner/main.py @@ -169,57 +169,36 @@ def print_energy_results(l_results, energy_sampling_freq=1000, req_cycles=5, res for inf_num,res in enumerate(l_results): plt.clf() energy_samples = res['power']['samples'] # assume 'output type' is set to energy - hw_timestamps = res['power'].get('timestamps', []) - soft_markers = res['power'].get('soft_markers', []) - t_implicit = np.arange(len(energy_samples)) / energy_sampling_freq - plt.plot(t_implicit, energy_samples, 'b') - y_min = np.min(energy_samples) if len(energy_samples) else 0 - y_max = np.max(energy_samples) if len(energy_samples) else 1 - - idx_start = None - idx_stop = None - - if len(hw_timestamps) >= 2: - ts_counters = [int(ts[0]) for ts in hw_timestamps] - ts_samp_nums = np.array([int(ts[1]) for ts in hw_timestamps]) - ts_seconds = ts_samp_nums/energy_sampling_freq - - if np.any(np.diff(ts_counters) != 1): - raise RuntimeError(f"Timestamps are not consecutive:\n{hw_timestamps}") + # timestamps (or 'events' per LPM01a wording are recorded as + # (counter, num_samples) where + # counter: 0,1,.. for the 1st, 2nd, etc, to test if you lost a timestamp + # num_samples: number of samples captured when the timestamp happened + ts_counters = [int(ts[0]) for ts in res['power']['timestamps']] + ts_samp_nums = np.array([int(ts[1]) for ts in res['power']['timestamps']]) + ts_seconds = ts_samp_nums/energy_sampling_freq - for ts in ts_seconds: - plt.plot([ts, ts], [y_min, y_max], 'r') - plt.grid(True) - - idx_start = ts_samp_nums[-2] - idx_stop = ts_samp_nums[-1] - - elif len(soft_markers) >= 2: - marker_dict = {} - for tag, idx in soft_markers: - marker_dict[tag] = idx - - if "start" in marker_dict and "stop" in marker_dict: - idx_start = marker_dict["start"] - idx_stop = marker_dict["stop"] - - t_start = idx_start / energy_sampling_freq - t_stop = idx_stop / energy_sampling_freq - plt.plot([t_start, t_start], [y_min, y_max], 'g') - plt.plot([t_stop, t_stop], [y_min, y_max], 'g') - plt.grid(True) - - else: - idx_start = 0 - idx_stop = len(energy_samples) + if np.any(np.diff(ts_counters) != 1): + raise RuntimeError(f"Timestamps are not consecutive:\n{res['power']['timestamps']}") + t_implicit = np.arange(len(energy_samples))/energy_sampling_freq + plt.plot(t_implicit, energy_samples, 'b') + y_min = np.min(energy_samples) + y_max = np.max(energy_samples) + + for ts in ts_seconds: + plt.plot([ts, ts], [y_min, y_max], 'r') + plt.grid(True) plt.savefig(os.path.join(results_dir, f"energy_inf_{inf_num:03d}.png")) - - if idx_start is None or idx_stop is None or idx_stop <= idx_start: - raise RuntimeError(f"Invalid energy integration window for trial {inf_num}: start={idx_start}, stop={idx_stop}") - - elapsed_time = (idx_stop - idx_start) / energy_sampling_freq + + # There is sometimes some extra activity on the timestamp pin at the + # beginning, so take the last two + t_start = ts_seconds[-2] + t_stop = ts_seconds[-1] + idx_start = ts_samp_nums[-2] + idx_stop = ts_samp_nums[-1] + + elapsed_time = t_stop-t_start inference_energy_samples = np.array(energy_samples[idx_start:idx_stop]) total_inference_energy = np.sum(inference_energy_samples) num_inferences = res['infer']['iterations'] @@ -398,4 +377,4 @@ def summarize_result(result, power, mode, results_file=None): print(f"Session logged in file {log_filename}") results_data_file = os.path.join(os.path.dirname(log_filename), "results.json") with open(results_data_file, 'w') as fpo: - json.dump(result, fpo) + json.dump(result, fpo) \ No newline at end of file diff --git a/benchmark/runner/power_manager/power_manager_lpm.py b/benchmark/runner/power_manager/power_manager_lpm.py index cf936f01..a4443825 100644 --- a/benchmark/runner/power_manager/power_manager_lpm.py +++ b/benchmark/runner/power_manager/power_manager_lpm.py @@ -33,7 +33,7 @@ def read_loop(self): values = self._extract_current_values(line) for v in values: self.m._data_queue.put(v) - elif re.match(r"event \d+ ris", line): + elif re.match(r"event \d+ (ris|fal)", line): self.m._data_queue.put(line) else: self.m._message_queue.put(line) diff --git a/benchmark/runner/power_manager/power_manager_stlinkv3pwr.py b/benchmark/runner/power_manager/power_manager_stlinkv3pwr.py index 6d7a5c1d..081b7ad8 100644 --- a/benchmark/runner/power_manager/power_manager_stlinkv3pwr.py +++ b/benchmark/runner/power_manager/power_manager_stlinkv3pwr.py @@ -10,7 +10,6 @@ class STLinkV3PWRCommands(LPMCommands): def __init__(self, manager, port): super().__init__(manager, port) - self._sample_count = 0 def setup(self): self._send_command("htc") @@ -19,19 +18,6 @@ def setup(self): self.configure_output("energy", "ascii_dec", "1k") self.configure_voltage(self.m._voltage) - def tear_down(self): - try: - self.stop() - except Exception: - pass - try: - self.power_off() - except Exception: - pass - try: - self._send_command("hrc") - except Exception: - pass def read_loop(self): in_summary = False @@ -61,36 +47,23 @@ def read_loop(self): if re.fullmatch(r"\d{4}[+-]\d{2}", temp): value = self._decode_ascii_dec_value(temp) self.m._data_queue.put(value) - self._sample_count += 1 - elif re.fullmatch(r"RecID\s+\d+", temp): + elif re.fullmatch(r"event \d+ (ris|fal)", temp): self.m._data_queue.put(temp) elif temp == "end": self.m._message_queue.put("Acquisition completed") else: self.m._message_queue.put(temp) - def configure_trigger(self, acqtime, trigdelay, trigsrc): - self._send_command(f"acqtime {acqtime}") - self._send_command(f"trigdelay {trigdelay}") - self._send_command(f"trigsrc {trigsrc}") def power_on(self): return self._send_command("pwr on nostatus") - def power_off(self): - return self._send_command("pwr off") - def get_board_id(self): if not self.m._board_id: result, output = self._send_command("whoami") self.m._board_id = output if result else None return self.m._board_id - def set_lcd(self, *args): - return [None, None] - - def mark_soft_event(self, tag): - self.m._data_queue.put(f"soft_event {tag} {self._sample_count}") def stop(self): self._port.write_line("stop") @@ -107,24 +80,6 @@ def _strip_prompt(self, line): line = line[len(self.PROMPT):].strip() return line - def _send_command(self, command, expect_output=False, err_message=None): - self._purge_messages() - self._port.write_line(command) - lines = self._read_response(command) - - result = "ack" in lines - output = [line for line in lines if line not in ("ack", "err")] - - if not result: - print("Power Manager did not acknowledge. PM Response:") - print(lines) - output = self._read_error_output() - if err_message and output: - print(f"{err_message}: {output[0]}", file=sys.stderr) - elif expect_output: - output = list(self._read_output()) - - return result, output if not output or len(output) != 1 else output[0] def _read_response(self, command): out_lines = [] diff --git a/benchmark/runner/script.py b/benchmark/runner/script.py index 4660551c..68a2420c 100644 --- a/benchmark/runner/script.py +++ b/benchmark/runner/script.py @@ -143,7 +143,7 @@ def run(self, io, dut, dataset, mode): if watchdog_flag["timeout"]: print("[WATCHDOG] Loop timeout. Restarting current loop iteration.") watchdog_flag["timeout"] = False # reset for next iteration - + r = cmd.run(io, dut, dataset, mode) for cmd in self._commands: r = cmd.run(io, dut, dataset, mode) if r is not None: @@ -182,15 +182,7 @@ def __init__(self, iterations=1, warmups=0, skip_time=0, loop_count=None): def run(self, io, dut, dataset, mode): # mode passed to run - # SW Fallback used for STLINK-V3PWR - if mode == "e" and dut.power_manager and hasattr(dut.power_manager._commands, "mark_soft_event"): - dut.power_manager._commands.mark_soft_event("start") - raw_result = dut.infer(self._iterations, self._warmups) - - if mode == "e" and dut.power_manager and hasattr(dut.power_manager._commands, "mark_soft_event"): - dut.power_manager._commands.mark_soft_event("stop") - print(f"Running inference with {self._warmups}/{self._iterations} (warmup/measured) iterations ... ", end="") infer_results = _ScriptInferStep._gather_infer_results(raw_result, mode) print(f" done") @@ -200,14 +192,13 @@ def run(self, io, dut, dataset, mode): # mode passed to run result = dict(infer=infer_results) if mode == "e": - timestamps, samples, soft_markers = _ScriptInferStep._gather_power_results(dut.power_manager) - print(f" Captured samples:{len(samples)} timestamps:{len(timestamps)} soft_markers:{len(soft_markers)}") - # Read power values from the log file using timestamps or Softmarker + timestamps, samples = _ScriptInferStep._gather_power_results(dut.power_manager) + print(f" Captured samples:{len(samples)} timestamps:{len(timestamps)}") + # Read power values from the log file using timestamps power_values = None result.update(power=dict(samples=samples, timestamps=timestamps, - soft_markers=soft_markers, extracted_power=power_values # <-- NEW: Power values from the file ) ) @@ -222,7 +213,6 @@ def _gather_power_results(power): samples = [] timeStamps = [] # this is what the EEMBC runner calls "timestamps" clock_ticks = [] # this is what the LPM01a calls "timestamps" - soft_markers = [] if power: for x in power.get_results(): if isinstance(x, str): @@ -234,23 +224,12 @@ def _gather_power_results(power): expected_samples = clock_ticks[-2][1] + 1000 print(f"At time {ts}: expected {expected_samples}, but we have {clock_ticks[-1][1]}.") elif x.startswith("event"): - match = re.match(r"event (\d+) ris", x) + match = re.match(r"event (\d+) (ris|fal)", x) event_num = match.group(1) timeStamps.append((event_num, len(samples))) - elif x.startswith("RecID "): - # STLINK-V3PWR metadata for stream resynchronization / overflow info. - # Not used in current energy windowing logic. - pass - elif x.startswith("soft_event "): - # We intentionally ignore the absolute index encoded in the message - # and convert the marker into a local index, relative to the samples - # of THIS inference only. - match = re.match(r"soft_event (\w+)(?:\s+\d+)?", x) - if match: - soft_markers.append((match.group(1), len(samples))) else: samples.append(x) - return timeStamps, samples, soft_markers + return timeStamps, samples @staticmethod def _gather_infer_results(cmd_results, mode): @@ -364,14 +343,14 @@ def run(self, io, dut, dataset, mode): ) if mode == "e": - timestamps, samples, soft_markers = _ScriptInferStep._gather_power_results(dut.power_manager) - print(f"samples:{len(samples)} timestamps:{len(timestamps)} soft_markers:{len(soft_markers)}") + timestamps, samples = _ScriptInferStep._gather_power_results(dut.power_manager) + print(f"samples:{len(samples)} timestamps:{len(timestamps)}") + # Read power values from the log file using timestamps power_values = None result.update(power=dict(samples=samples, timestamps=timestamps, - soft_markers=soft_markers, extracted_power=power_values ) )