diff --git a/.gitignore b/.gitignore index e2d49afe..4117b847 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.idea* *__pycache__* +venv +.python-version diff --git a/benchmark/reference_submissions/.gitignore b/benchmark/reference_submissions/.gitignore new file mode 100644 index 00000000..585f6892 --- /dev/null +++ b/benchmark/reference_submissions/.gitignore @@ -0,0 +1,3 @@ +*.zip* +main.cpp + diff --git a/benchmark/runner/.gitignore b/benchmark/runner/.gitignore new file mode 100644 index 00000000..9c687349 --- /dev/null +++ b/benchmark/runner/.gitignore @@ -0,0 +1,3 @@ +.idea +venv + diff --git a/benchmark/runner/README.md b/benchmark/runner/README.md new file mode 100644 index 00000000..1f6bd3bb --- /dev/null +++ b/benchmark/runner/README.md @@ -0,0 +1,11 @@ +[[_TOC_]] + +## Energy Test + +### Power Board (LPM01A) +![LPM01A Wiring](img/LPM01A.jpg) +### Interface Board (STM32H573I-DK) +![STM32H573I-DK Top Wiring](img/STM32H573I-DK-Top.jpg) +![STM32H573I-DK Bottom Wiring](img/STM32H573I-DK-Bottom.png) +### Device Under Test (L4R5ZI) +![DUT Wiring](img/L4R5ZI.png) \ No newline at end of file diff --git a/benchmark/runner/device_manager.py b/benchmark/runner/device_manager.py new file mode 100644 index 00000000..6c0805f1 --- /dev/null +++ b/benchmark/runner/device_manager.py @@ -0,0 +1,73 @@ +import yaml + +from serial.tools import list_ports + +from device_under_test import DUT +from io_manager import IOManager +from io_manager_enhanced import IOManagerEnhanced +from power_manager import PowerManager + + +class DeviceManager: + def __init__(self, devices): + self._device_defs = devices + + def __getitem__(self, item): + return self.__dict__[item] + + def __setitem__(self, key, value): + self.__dict__[key] = value + + def get(self, item, default=None): + return self.__dict__.get(item, default) + + def values(self): + return (a for a in self.__dict__.values() if isinstance(a, dict)) + + def _add_device(self, usb, definition): + type = definition.get("type") + if type and (not self.__dict__.get(type) + or definition.get("preference", 0) > self.__dict__[type].get("preference", 0)): + self.__dict__[type] = {k: v for k, v in definition.items()} + self.__dict__[type]["port"] = usb.device + + def _instantiate(self, definition): + args = { + "port_device": definition.get("port") + } + if definition.get("baud"): + args["baud_rate"] = definition.get("baud") + if definition.get("type") == "interface": + definition["instance"] = IOManagerEnhanced(**args) if definition.get("name") == "stm32h573i-dk" \ + else IOManager(**args) + elif definition.get("type") == "power": + definition["instance"] = PowerManager(**args) + elif definition.get("type") == "dut": + definition["instance"] = DUT(**args) + + def scan(self): + """Scan fpr USB serial devices + This scans the connected usb devices. It compares the device definitions + based on the USB vid and pid primarily and then by the text of the description. + """ + pending = [p for p in list_ports.comports(True) if p.vid] + matched = [] + for p in pending: + for d in self._device_defs: + found = False + for vid, pids in d.get("usb", {}).items(): + for pid in (pids if isinstance(pids, list) else [pids]): + if pid == p.pid and vid == p.vid: + self._add_device(p, d) + matched.append(p) + found = True + break + if found: break + for p in (a for a in pending if a not in matched): + for d in (d1 for d1 in self._device_defs if d1.get("usb_description", "zZzZzZzZ") in p.description): + self._add_device(p, d) + matched.append(p) + break + + for d in self.values(): + self._instantiate(d) diff --git a/benchmark/runner/device_under_test.py b/benchmark/runner/device_under_test.py new file mode 100644 index 00000000..8eb657cc --- /dev/null +++ b/benchmark/runner/device_under_test.py @@ -0,0 +1,100 @@ +import re +import sys +import time + +from interface_device import InterfaceDevice +from serial_device import SerialDevice + + +class DUT: + def __init__(self, port_device, baud_rate=115200, power_manager=None): + interface = port_device + if not isinstance(port_device, InterfaceDevice): + interface = SerialDevice(port_device, baud_rate, "m-ready", '%') + self._port = interface + self._power_manager = power_manager + self._profile = None + self._model = None + self._name = None + + def __enter__(self): + if self._power_manager: + self._power_manager.__enter__() + self._port.__enter__() + return self + + def __exit__(self, *args): + self._port.__exit__(*args) + if self._power_manager: + self._power_manager.__exit__() + + def _get_name(self): + for l in self._port.send_command("name"): + match = re.match(r'^m-(name)-dut-\[([^]]+)]$', l) + if match: + self.__setattr__(f"_{match.group(1)}", match.group(2)) + + def get_name(self): + if self._name is None: + self._get_name() + return self._name + + def _get_profile(self): + for l in self._port.send_command("profile"): + match = re.match(r'^m-(model|profile)-\[([^]]+)]$', l) + if match: + self.__setattr__(f"_{match.group(1)}", match.group(2)) + + def get_model(self): + if self._model is None: + self._get_profile() + return self._model + + def get_profile(self): + if self._profile is None: + self._get_profile() + return self._profile + + def timestamp(self): + return self._port.send_command("timestamp") + + def send_data(self, data): + size = len(data) + pass + + def load(self, data): + self._port.send_command(f"db load {len(data)}") + i = 0 + while i < len(data): + time.sleep(0.5) + # print(".", end='', file=sys.stderr) + cmd = f"db {''.join(f'{d:02x}' for d in data[i:i+32])}" + result = self._port.send_command(cmd) + print(f"{result} ({i})") + i += 32 + # print("", file=sys.stderr) + + def infer(self, number, warmups): + command = f"infer {number}" + if warmups: + command += f" {warmups}" + self._port.send_command(command) + return self._port.send_command("results") + + def get_help(self): + return self._port.send_command("help") + + """ + ULPMark for tinyML Firmware V0.0.1 + + help : Print this information + name : Print the name of the device + timestsamp : Generate a timetsamp + db SUBCMD : Manipulate a generic byte buffer + load N : Allocate N bytes and set load counter + db HH[HH]* : Load 8-bit hex byte(s) until N bytes + print [N=16] [offset=0] + : Print N bytes at offset as hex + infer N [W=0]: Load input, execute N inferences after W warmup loops + results : Return the result fp32 vector + """ diff --git a/benchmark/runner/devices.yaml b/benchmark/runner/devices.yaml new file mode 100644 index 00000000..d9451667 --- /dev/null +++ b/benchmark/runner/devices.yaml @@ -0,0 +1,20 @@ +- name: stm32h573i-dk + usb_description: STLINK + type: interface + preference: 2 + usb: + 0x0483: 0x374E +- name: arduino + port: auto + type: interface + preference: 1 + usb: + 0x2341: + - 0x0043 + - 0x0001 + 0x2a03: + - 0x0043 + - 0x0243 +- name: lpm01a + usb_description: PowerShield + type: power diff --git a/benchmark/runner/dut.yaml b/benchmark/runner/dut.yaml new file mode 100644 index 00000000..292b437d --- /dev/null +++ b/benchmark/runner/dut.yaml @@ -0,0 +1 @@ +voltage: 3000m \ No newline at end of file diff --git a/benchmark/runner/img/L4R5ZI.png b/benchmark/runner/img/L4R5ZI.png new file mode 100644 index 00000000..6a4f50c8 Binary files /dev/null and b/benchmark/runner/img/L4R5ZI.png differ diff --git a/benchmark/runner/img/LPM01A.jpg b/benchmark/runner/img/LPM01A.jpg new file mode 100644 index 00000000..f31fba78 Binary files /dev/null and b/benchmark/runner/img/LPM01A.jpg differ diff --git a/benchmark/runner/img/STM32H573I-DK-Bottom.png b/benchmark/runner/img/STM32H573I-DK-Bottom.png new file mode 100644 index 00000000..9b20a591 Binary files /dev/null and b/benchmark/runner/img/STM32H573I-DK-Bottom.png differ diff --git a/benchmark/runner/img/STM32H573I-DK-Top.jpg b/benchmark/runner/img/STM32H573I-DK-Top.jpg new file mode 100644 index 00000000..dbed0d6d Binary files /dev/null and b/benchmark/runner/img/STM32H573I-DK-Top.jpg differ diff --git a/benchmark/runner/interface_device.py b/benchmark/runner/interface_device.py new file mode 100644 index 00000000..8755b25c --- /dev/null +++ b/benchmark/runner/interface_device.py @@ -0,0 +1,3 @@ +class InterfaceDevice: + def send_command(self, command, end=None, echo=False): + pass diff --git a/benchmark/runner/io_manager.py b/benchmark/runner/io_manager.py new file mode 100644 index 00000000..dd58baad --- /dev/null +++ b/benchmark/runner/io_manager.py @@ -0,0 +1,62 @@ +from interface_device import InterfaceDevice +from serial_device import SerialDevice + + +class IOManager(InterfaceDevice): + def __init__(self, port_device, baud_rate=115200): + self.port = SerialDevice(port_device, baud_rate, "m-ready", '%') + self.entry_count = 0 + + def __enter__(self): + if not self.entry_count: + self.port.__enter__() + self.get_name() + self.entry_count += 1 + return self + + def __exit__(self, *args): + self.entry_count -= 1 + if not self.entry_count: + self.port.__exit__(*args) + + def get_name(self): + return self.port.send_command("name") + + def timestamp(self): + return self.port.send_command("timestamp") + + def get_help(self): + return self.port.send_command("help") + + def send_data(self, data): + size = len(data) + pass + + def send_command(self, command, end=None, echo=False): + resp = self.port.send_command(f"dut {command}") + if len(resp) != 2 or resp[1] != "m-ready": + return None + resp = None + lines = [] + while resp != 'm-ready': + resp = self.port.read_line() + resp = resp.replace("[dut]: ", "") + lines.append(resp) + return lines if len(lines) != 1 else lines[0] + + """ + help + name device name + res Reset polarity 0*,1 + enable-timer Enable timer ISR + disable-timer Disable ISR + dut DUT passthrough + i2c-enable N : Enable I2C slave-mode to send N bytes (mod16) + i2c-disable Disable above + load-vdata Load 16 random bytes into vdata + show-vdata Show vdata + et Start Emon + tm 0=fall/1*=change + tp [res] + version firmware version + """ diff --git a/benchmark/runner/io_manager_enhanced.py b/benchmark/runner/io_manager_enhanced.py new file mode 100644 index 00000000..1245207e --- /dev/null +++ b/benchmark/runner/io_manager_enhanced.py @@ -0,0 +1,16 @@ +from io_manager import IOManager + + +class IOManagerEnhanced(IOManager): + def __init__(self, port_device, baud_rate=921600): + IOManager.__init__(self, port_device, baud_rate) + + def get_files(self): + return self.port.send_command('ls') + + def get_waves(self): + return [x for x in self.get_files() if x.lower().endswith("wav") or x == "m-ready"] + + def play_wave(self, filename=None): + command = "play" + (f" {filename}" if filename else "") + return self.port.send_command(command) diff --git a/benchmark/runner/main.py b/benchmark/runner/main.py new file mode 100644 index 00000000..36203aa3 --- /dev/null +++ b/benchmark/runner/main.py @@ -0,0 +1,152 @@ +import argparse +import csv +import time + +from device_manager import DeviceManager +from device_under_test import DUT + +from io_manager_enhanced import IOManagerEnhanced +from power_manager import PowerManager +from io_manager import IOManager + +from contextlib import nullcontext, ExitStack +from serial.tools import list_ports +import yaml + + +def test_power(device): + with device as power: + pass + + +def run_dut_test(): + """ + power on + m-ready + -mode energy + imit-done + m-ready + :return: + dut passthrough: profile + """ + + +def test_dut(device): + if device: + with device as dut: + print(dut.get_name()) + print(dut.get_model()) + print(dut.get_profile()) + print(dut.timestamp()) + for l in dut.get_help(): + print(l) + + +def test_io_manager(device): + with device as io: + print(io.get_name()) + for l in io.get_help(): + print(l) + + +def test_io_manager_enhanced(device): + with device as io: + print(io.get_name()) + print(io.get_waves()) + print(io.play_wave()) + print(io.play_wave("spaceship.wav")) + # waves = io.list_waves() + # io.play_wave(); + # for w in waves: + # print(w) + # # io.play_wave(w) + + +def init_dut(device): + with device as dut: + time.sleep(2) + dut.get_name() + dut.get_model() + dut.get_profile() + +def identify_dut(manager): + interface = manager.get("interface", {}).get("instance") + power = manager.get("power", {}).get("instance") + if not manager.get("dut") and interface and power: + dut = DUT(interface, power_manager=power) + manager["dut"] = { + "instance": dut + } + else: + dut = manager.get("dut", {}).get("instance") + init_dut(dut) + + +def run_test(devices_config, dut_config, test_script): + manager = DeviceManager(devices_config) + manager.scan() + if manager.get("power", {}).get("instance") and dut_config and dut_config.get("voltage"): + manager["power"]["instance"].configure_voltage(dut_config["voltage"]) + identify_dut(manager) + dut = manager.get("dut", {}).get("instance") + test = test_script.get(dut.get_model()) + with open("/Users/sreckamp/eembc/runner/benchmarks/ulp-mlperf/datasets/ic01/y_labels.csv") as file: + reader = csv.DictReader(file, fieldnames=["file", "classes", "class"]) + file_name = reader.__next__()["file"] + with open(f"/Users/sreckamp/eembc/runner/benchmarks/ulp-mlperf/datasets/ic01/{file_name}", mode="rb") as file: + data = file.read() + with dut: + dut.load(data) + print(dut.infer(10, 1)) + # for _def, i in ((t, t.get("instance")) for t in manager.values() if t and t.get("instance")): + # if isinstance(i, PowerManager): + # test_power(i) + # elif isinstance(i, IOManagerEnhanced): + # test_io_manager_enhanced(i) + # elif isinstance(i, IOManager): + # test_io_manager(i) + # elif isinstance(i, DUT): + # test_dut(i) + + +def parse_device_config(device_list, device_yaml): + if device_yaml: + return yaml.load(device_yaml) + else: + with open(device_list) as dev_file: + return yaml.load(dev_file, Loader=yaml.CLoader) + + +def parse_dut_config(dut, dut_voltage, dut_baud): + config = {} + if dut: + with open(dut) as dut_file: + dut_config = yaml.load(dut_file, Loader=yaml.CLoader) + config.update(**dut_config) + if dut_voltage: + config.update(voltage=dut_voltage) + if dut_baud: + config.update(baud=dut_baud) + return config + + +def parse_test_script(test_script): + with open(test_script) as test_file: + return yaml.load(test_file, Loader=yaml.CLoader) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(prog="TestRunner") + parser.add_argument("-d", "--device_list", default="devices.yaml") + parser.add_argument("-y", "--device_yaml", required=False) + parser.add_argument("-u", "--dut", required=False) + parser.add_argument("-v", "--dut_voltage", required=False) + parser.add_argument("-b", "--dut_baud", required=False) + parser.add_argument("-t", "--test_script", default="tests.yaml") + args = parser.parse_args() + config = { + "devices_config": parse_device_config(args.device_list, args.device_yaml), + "dut_config": parse_dut_config(args.dut, args.dut_voltage, args.dut_baud), + "test_script": parse_test_script(args.test_script) + } + run_test(**config) diff --git a/benchmark/runner/power_manager.py b/benchmark/runner/power_manager.py new file mode 100644 index 00000000..b69c12af --- /dev/null +++ b/benchmark/runner/power_manager.py @@ -0,0 +1,390 @@ +import re +import sys +from queue import Queue +from serial_device import SerialDevice +from threading import Thread + +class PowerManager(SerialDevice): + PROMPT = "PowerShield > " + + def __init__(self, port_device, baud_rate=921600): + self._port = SerialDevice(port_device, baud_rate, "ack|error", "\r\n") + self._voltage = "3000m" + self._board_id = None + self._version = None + self._lcd = [None, None] + self._in_prograss = False + self._data_queue = Queue() + self._message_queue = Queue() + self._read_thread = None + self._running = True + + def __enter__(self): + self._port.__enter__() + self._start_read_thread() + self._setup() + return self + + def __exit__(self, *args): + self._tear_down() + self._stop_read_thread() + self._port.__exit__(*args) + + def _read_loop(self): + while self._running: + line = self._port.read_line(timeout=250) + if line is None: + continue + # print(f"RX: {line}") + if line.startswith("TimeStamp"): + print(line, file=sys.stderr) + self._data_queue.put(line) + elif re.match("\d\d\d\d[+-]\d\d", line): + print(line) + line = line.replace('+', 'e').replace('-', "e-") + line = line[:1] + "." + line[1:] + value = float(line) + self._data_queue.put(value) + else: + self._message_queue.put(line) + + def _start_read_thread(self): + self._running = True + self._read_thread = Thread(target=self._read_loop) + self._read_thread.start() + + def _stop_read_thread(self): + self._running = False + self._read_thread.join() + + def _setup(self): + # verify board + self._claim_remote_control() + print(f"LPM01A Power Monitor Board", file=sys.stderr) + print(f"BoardID: {self.get_board_id()}", file=sys.stderr) + print(f"Version: {self.get_version()}", file=sys.stderr) + print(f"Status: {self.get_status()}", file=sys.stderr) + self.set_lcd(" mlperf ", " monitor ") + self.power_off() + # Acquire infinitely + self.configure_trigger(0, 0, 'd7', 'fal') + self.configure_output('energy', 'ascii_dec', 1000) + self.set_voltage(self._voltage) + self.power_on() + + def _tear_down(self): + self._release_remote_control() + + def _claim_remote_control(self): + self._send_command("htc") + + def _release_remote_control(self): + self._send_command("hrc") + + def get_board_id(self): + if not self._board_id: + result, output = self._send_command("powershield", err_message="Error getting BoardId") + self._board_id = output if result else None + return self._board_id + + def get_version(self): + if not self._version: + result, output = self._send_command("version", err_message="Error getting version") + self._version = output[1] if result else None + return self._version + + def get_lcd(self): + return self._lcd + + def set_lcd(self, *args): + for i in range(len(args)): + if args[i] and args[i] != self._lcd[i]: + result, _ = self._send_command(f'lcd {i+1} "{args[i]}"', + err_message=f"Error setting LCD line {i+1} to \"{args[i]}\"") + self._lcd[i] = args[i] if result else self._lcd[i] + return self._lcd + + def configure_trigger(self, acquisition_time, trigger_delay, trigger_source, trigger_type): + self._send_command(f"acqtime {acquisition_time}", + err_message=f"Error setting acquisition_time to {acquisition_time}") + self._send_command(f"trigdelay {trigger_delay}", + err_message="Error setting trigger_delay to {trigger_delay}") + self._send_command(f"eventsrc {trigger_source} {trigger_type}", + err_message=f"Error setting trigger_source to {trigger_source} " + f"and trigger_type to {trigger_type}") + + def configure_output(self, output_type, output_format, samples_per_second): + self._send_command(f"output {output_type}", err_message=f"Error setting output_type to {output_type}") + self._send_command(f"format {output_format}", err_message=f"Error setting output_format to {output_format}") + self._send_command(f"freq {samples_per_second}", + err_message=f"Error setting samples_per_second to {samples_per_second}") + + def power_on(self, show_status=False): + self.set_lcd(None, f"{self._voltage : >14}V ") + self._send_command(f"pwr on {'' if show_status else 'no'}status", err_message=f"Error turning on power") + + def power_off(self): + self._send_command("pwr off", err_message=f"Error turning off power") + + def configure_voltage(self, voltage): + self._voltage = voltage + + def set_voltage(self, voltage): + self._send_command(f"volt {voltage}", err_message=f"Error setting voltage to {voltage}V") + + def acquire(self, samples=0): + self._in_prograss = True + print(self._send_command("start")) + for value in self._get_data(samples): + print(value) + self._port.write_line("stop") + while True: + line = self._message_queue.get() + if line: + print(line) + if line == "end": + break + self._in_prograss = False + + def get_status(self): + result, output = self._send_command("status", err_message="Error getting status") + return output if result else None + + def get_help(self): + result, output = self._send_command("help", True, err_message="Error getting help") + return output if result else None + + def _get_data(self, count=0): + idx = 0 + while self._in_prograss and (not count or idx < count): + line = self._data_queue.get() + if isinstance(line, str) and line.startswith("TimeStamp"): + print(line, file=sys.stderr) + elif isinstance(line, float): + yield line + if count: idx += 1 + else: + print(f"UNKNOWN: {line}", file=sys.stderr) + + def _read_response(self, command): + out_lines = [] + while True: + line = self._message_queue.get() + # print(f"RES: {line}") + temp = line.replace(PowerManager.PROMPT, "").strip() + if temp and command in temp and (temp.startswith('ack') or temp.startswith('error')): + out_lines.extend(r for r in temp.replace(command, "").split(" ", 2) if r) + break + elif temp and not temp.startswith("ack") and not temp.startswith("error"): + out_lines.append(temp) + return out_lines + + def _read_error_output(self): + while True: + line = self._message_queue.get() + line = line.replace(PowerManager.PROMPT, "").strip() + if line.startswith("Error detail"): + return [line.replace("Error detail:", "").strip()] + + def _read_output(self): + while True: + line = self._message_queue.get() + # print(f"OUT: {line}") + if line == PowerManager.PROMPT: + return + line = line.replace(PowerManager.PROMPT, "").strip() + if line: + yield line + + def _send_command(self, command, expect_output=False, err_message=None): + self._port.write_line(command) + lines = self._read_response(command) + result = lines and lines[0] == 'ack' + output = lines[1:] if lines and len(lines) > 1 else [] + if not result: + output = self._read_error_output() + if err_message is not None: + print(f"{err_message}: {output[0]}", file=sys.stderr) + elif expect_output: + output = [l for l in self._read_output()] + return result, output if not output or len(output) != 1 else output[0] + + """ + ################################################################################ + # PowerShield commands # + ################################################################################ + # Command # Description # + ################################################################################ + # Common operation # + ################################################################################ + # help # Displays list of commands. # + # # # + # echo # Loopback to check functionality of communication Rx and Tx. # + # # : String of characters # + # # # + # powershield # Check PowerShield device availability, can be used to scan # + # # on which serial port is connected the PowerShield. # + # # Response: 'PowerShield present' with board unique ID # + # # # + # version # Get PowerShield FW revision. # + # # Response: '
..' # + # # # + # status # Get PowerShield status. # + # # Response: 'ok' or 'error: ' # + # # # + # htc # Host take control (go from mode 'standalone' # + # # to mode 'controlled by host') # + # hrc # Host release control (go from mode 'controlled by host' # + # # to mode 'standalone') # + # # # + # lcd # Display a custom string on LCD display when PowerShield is # + # # controlled by host. # + # # : LCD line. Numerical value among list: {1, 2} # + # # : String to be displayed, surrounded by double quotes # + # # and with 16 characters maximum. # + # # Example: lcd 1 " custom display" # + # # # + # psrst # Reset PowerShield (hardware reset, # + # # host communication will have to be restored). # + # # # + ################################################################################ + # Measurement acquisition configuration # + ################################################################################ + # volt # Set or get power supply voltage level, unit: V. # + # # : Set voltage: Numerical value in range [1800m; 3300m] # + # # Default value: 3300mV # + # # Get voltage: String 'get' # + # # # + # freq # Set sampling frequency, unit: Hz. # + # # : Numerical value among list: # + # # {100k, 50k, 20k, 10k, 5k, 2k, 1k, 500, 200 # + # # 100, 50, 20, 10, 5, 2, 1} # + # # Default value: 100Hz # + # # # + # acqtime # Set acquisition time, unit: s. # + # # : For limited acquisition duration: # + # # Numerical value in range: [100u; 10] # + # # For infinite acquisition duration: # + # # Numerical value '0' or string 'inf' # + # # Caution: Maximum acquisition time depends on other # + # # parameters. Refer to table below. # + # # Default value: 10s # + # # # + # acqmode # Set acquisition mode: dynamic or static. # + # # dynamic: current can vary, range [100nA; 10mA] # + # # static: current must be constant, range [2nA; 200mA] # + # # : String among list: {dyn, stat} # + # # Default value: 'dyn' # + # # # + # funcmode # Set optimization of acquisition mode dynamic (applicable # + # # only with command 'output' set to parameter 'current'): # + # # optim: priority on current resolution (100nA-10mA), # + # # max sampling frequency at 100KHz. # + # # high: high current (30uA-10mA), high sampling frequency # + # # (50-100KHz), high resolution. # + # # : String among list: {optim, high} # + # # Default value: 'optim' # + # # # + # output # Set output type. # + # # current: instantaneous current # + # # energy: integrated energy, reset after each sample sent # + # # (integration time set by param 'freq', # + # # limited at 10kHz max (<=> 100us min)). # + # # : String among list: {current, energy} # + # # Default value: 'current' # + # # # + # format # Set measurement data format. # + # # Data format 1: ASCII, decimal basis. # + # # Format readable directly, but sampling # + # # frequency limited to 10kHz. # + # # Decoding: 6409-07 <=> 6409 x 10^-7 = 640.9uA # + # # Data format 2: Binary, hexadecimal basis. # + # # Format optimized data stream size. # + # # Decoding: 52A0 <=> (2A0)16 x 16^-5 = 640.9uA # + # # : String among list: {ascii_dec, bin_hexa} # + # # Caution: Data format depends on other # + # # parameters. Refer to table below. # + # # Default value: 'ascii_dec' # + # # # + # trigsrc # Set trigger source to start measurement acquisition: # + # # trigger source SW (immediate trig after SW start), # + # # trigger from external signal rising or falling edge on # + # # Arduino connector D7 (via solder bridge). # + # # : String among list: {sw, d7} # + # # Default value: 'sw' # + # # # + # trigdelay # Set trigger delay between target power-up and start # + # # measurement acquisition, unit: s, resolution: 1ms. # + # # : Numerical value in range [0; 30] # + # # Default value: 1m # + # # # + # currthres # Set current threshold to trig an event, unit: A. # + # # Event triggered when threshold exceeded: signal generated # + # # on Arduino connector D2 or D3 (via solder bridge) # + # # and LED4 (blue) turned on. # + # # : Numerical value in range [100n; 50m] # + # # or value '0' for threshold disable # + # # Default value: 1m # + # # # + # pwrend # Set target power supply to be applied after current # + # # measurement acquisition: power-on or power-off. # + # # : String among list: {on, off} # + # # Default value: 'on' # + # # # + ################################################################################ + # Measurement acquisition operation # + ################################################################################ + # start # Start acquisition (measurement of current or energy # + # # depending on configuration). # + # # # + # stop # Stop acquisition. If acquisition is set to a finite duration, # + # # it will stop when reaching the target duration. # + # # # + # targrst # Reset target by disconnecting power supply during # + # # a configurable duration, unit: s. # + # # Note: Can be performed during acquisition to monitor target # + # # transient current consumption during its power-up. # + # # : Numerical value in range [10m; 1] # + # # or value '0' to let target powered-down # + # # # + # temp # Get temperature from temperature sensor on PowerShield board, # + # # on unit: Degree Celsius or Fahrenheit. # + # # : String among list: {degc, degf} # + # # Default value: 'degc' # + # # # + ################################################################################ + # Board state operation # + ################################################################################ + # autotest # Perform board autotest and display autotest results. # + # # Note: Autotest is done at PowerShield power-up. # + # # : Optional: string among list: {start, status} # + # # or no argument equivalent to value: 'start' # + # # # + # calib # Perform board self-calibration. # + # # Note: New calibration should be performed when temperature # + # # shifts of more than 5 degC since the previous # + # # calibration. # + # # Note: Calibration is done at PowerShield power-up # + # # and after each update of power supply voltage level. # + # # # + ################################################################################ + # Note: Numerical values of arguments can be formatted either: # + # - Numerical characters only (possible when numbers are >= 1) # + # - Numerical characters with unit characters 'u', 'm', 'k', 'm' # + # - Numerical characters with power of ten '-xx' or '+xx' # + # (xx: number on 2 digits maximum) # + # Example: Value '2 milliseconds' can be entered with: '2m' or '2-3' # + ################################################################################ + # Table of maximum acquisition time possible in function of # + # sampling frequency and data format, # + # for a baudrate of 3686400 bauds: # + # ______________________________________________________________________ # + # | format freq | acqtime max (corresp. nb of samples) | # + # |____________________________________________________________________| # + # | ascii_dec | <= 5k | unlimited (unlimited) | # + # | ascii_dec | 10k | 1s ( 10 000) | # + # | ascii_dec | 20k | 500ms ( 5 000) | # + # | bin_hexa | <=100k | unlimited (unlimited) | # + # |____________________________________________________________________| # + ################################################################################ + """ diff --git a/benchmark/runner/serial_device.py b/benchmark/runner/serial_device.py new file mode 100644 index 00000000..a18fb86a --- /dev/null +++ b/benchmark/runner/serial_device.py @@ -0,0 +1,52 @@ +import serial +import time + + +class SerialDevice: + def __init__(self, port_device, baud_rate, end_of_response="", delimiter="\n"): + self.port = serial.Serial(port_device, baud_rate, timeout=0.1) + self.delimiter = delimiter + self.end_of_response = end_of_response + + def __enter__(self): + self.port.__enter__() + return self + + def __exit__(self, *args): + self.port.__exit__(*args) + + def write(self, text, echo=False): + self.port.write(text.encode()) + if echo: print(text, end='') + + def write_line(self, text, echo=False): + self.write(text, echo) + self.write(self.delimiter, echo=False) + if echo: print() + + def read_line(self, timeout=0): + result = "" + txt = None + start_time = round(time.time() * 1000) + while txt != "\n" and (not timeout or round(time.time() * 1000) - start_time < timeout): + txt = self.port.read(1).decode() + if txt and txt != "\n": + result = result + txt + else: + if txt != "\n": + return None + return result.replace('\r', '').replace('\0', '') + + def send_command(self, command, end=None, echo=False): + self.write_line(command, echo) + lines = [] + + while True: + resp = self.read_line() + end_of_resp = (end if end is not None else self.end_of_response) in resp + if resp: + lines.append(resp) + if end_of_resp: + break + + return lines if len(lines) != 1 else lines[0] diff --git a/benchmark/runner/tests.yaml b/benchmark/runner/tests.yaml new file mode 100644 index 00000000..bdd9133f --- /dev/null +++ b/benchmark/runner/tests.yaml @@ -0,0 +1,24 @@ +ad01: + name: anomoly_detection + model: ad01 + script: + - download + - a +ic01: + name: image_classification + model: ic01 + script: + - download + - a +kws01: + name: keyword_spotting + model: kws01 + script: + - download + - a +vww01: + name: person_detection + model: vww01 + script: + - download + - a diff --git a/benchmark/training/.gitignore b/benchmark/training/.gitignore new file mode 100644 index 00000000..dc1ca02e --- /dev/null +++ b/benchmark/training/.gitignore @@ -0,0 +1,2 @@ +perf_samples +y_labels.csv