├── sides └── power_meas_utils │ ├── check_at_model.py │ ├── check_run.py │ ├── check_pico.py │ ├── measurments_utils.h │ ├── l2_bw_autotiler.py │ ├── editlog.py │ ├── log_to_csv.py │ └── ps4444Measure.py ├── .gitmodules └── README.md /sides/power_meas_utils/check_at_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | import csv 3 | import sys 4 | import numpy as np 5 | import pickle 6 | import re 7 | 8 | if len(sys.argv) != 2: 9 | print("Wrong number of arguments!") 10 | exit(1) 11 | 12 | file_at = str(sys.argv[1]) 13 | 14 | with open(file_at, newline='') as f: 15 | out_log = f.readlines() 16 | row = [] 17 | for line in out_log: 18 | if 'Execution aborted' in line: 19 | print('Detected') 20 | sys.exit(1) 21 | 22 | sys.exit(0) 23 | -------------------------------------------------------------------------------- /sides/power_meas_utils/check_run.py: -------------------------------------------------------------------------------- 1 | import os 2 | import csv 3 | import sys 4 | import numpy as np 5 | import pickle 6 | import re 7 | 8 | if len(sys.argv) != 2: 9 | print("Wrong number of arguments!") 10 | exit(1) 11 | 12 | file_at = str(sys.argv[1]) 13 | 14 | with open(file_at, newline='') as f: 15 | out_log = f.readlines() 16 | row = [] 17 | for line in out_log: 18 | if 'Graph constructor exited with error' in line: 19 | print('Detected') 20 | sys.exit(1) 21 | 22 | sys.exit(0) 23 | -------------------------------------------------------------------------------- /sides/power_meas_utils/check_pico.py: -------------------------------------------------------------------------------- 1 | 2 | import ctypes 3 | from picosdk.ps4000a import ps4000a as ps 4 | from picosdk.functions import assert_pico_ok 5 | 6 | # Create chandle and status ready for use 7 | chandle = ctypes.c_int16() 8 | status = {} 9 | 10 | # Open 4000 series PicoScope 11 | # Returns handle to chandle for use in future API functions 12 | status["openunit"] = ps.ps4000aOpenUnit(ctypes.byref(chandle), None) 13 | 14 | try: 15 | assert_pico_ok(status["openunit"]) 16 | except: # PicoNotOkError: 17 | powerStatus = status["openunit"] 18 | 19 | if powerStatus == 286: 20 | status["changePowerSource"] = ps.ps4000aChangePowerSource(chandle, powerStatus) 21 | elif powerStatus == 282: 22 | status["changePowerSource"] = ps.ps4000aChangePowerSource(chandle, powerStatus) 23 | else: 24 | raise 25 | 26 | assert_pico_ok(status["changePowerSource"]) 27 | 28 | print("PicoScope Status OK !!!") -------------------------------------------------------------------------------- /sides/power_meas_utils/measurments_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __measurments_utils_H__ 2 | #define __measurments_utils_H__ 3 | 4 | #if defined(GPIO_MEAS) && !defined(__EMUL__) 5 | #ifdef __GAP9__ 6 | #define GPIO_MEAS_PIN PI_GPIO_A89 7 | #else 8 | #define GPIO_MEAS_PIN PI_GPIO_A02 9 | #endif 10 | struct pi_device gpio_meas_port; 11 | pi_gpio_e gpio_meas_pin_o; 12 | #define OPEN_GPIO_MEAS() \ 13 | struct pi_gpio_conf gpio_conf = {0}; \ 14 | gpio_meas_pin_o = GPIO_MEAS_PIN; /* PI_GPIO_A02-PI_GPIO_A05 */ \ 15 | pi_gpio_conf_init(&gpio_conf); \ 16 | pi_open_from_conf(&gpio_meas_port, &gpio_conf); \ 17 | gpio_conf.port = (gpio_meas_pin_o & PI_GPIO_NUM_MASK) / 32; \ 18 | int errors = pi_gpio_open(&gpio_meas_port); \ 19 | if (errors) \ 20 | { \ 21 | printf("Error opening GPIO %d\n", errors); \ 22 | pmsis_exit(errors); \ 23 | } \ 24 | pi_gpio_pin_configure(&gpio_meas_port, gpio_meas_pin_o, PI_GPIO_OUTPUT); \ 25 | pi_gpio_pin_write(&gpio_meas_port, gpio_meas_pin_o, 0); 26 | 27 | #define GPIO_HIGH() pi_gpio_pin_write(&gpio_meas_port, gpio_meas_pin_o, 1) 28 | #define GPIO_LOW() pi_gpio_pin_write(&gpio_meas_port, gpio_meas_pin_o, 0) 29 | #else 30 | #define OPEN_GPIO_MEAS() 31 | #define GPIO_HIGH() 32 | #define GPIO_LOW() 33 | #endif 34 | 35 | #endif -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "main_courses/occupancy_management"] 2 | path = main_courses/occupancy_management 3 | url = git@github.com:GreenWaves-Technologies/occupancy_management.git 4 | [submodule "ingredients/MobilenetV1_Pytorch"] 5 | path = ingredients/MobilenetV1_Pytorch 6 | url = git@github.com:GreenWaves-Technologies/MobilenetV1_Pytorch.git 7 | [submodule "main_courses/FaceReID"] 8 | path = main_courses/FaceReID 9 | url = git@github.com:GreenWaves-Technologies/FaceReID.git 10 | [submodule "starters/face_detection"] 11 | path = starters/face_detection 12 | url = git@github.com:GreenWaves-Technologies/face_detection.git 13 | [submodule "starters/body_detection"] 14 | path = starters/body_detection 15 | url = git@github.com:GreenWaves-Technologies/body_detection.git 16 | [submodule "starters/people_spotting"] 17 | path = starters/people_spotting 18 | url = git@github.com:GreenWaves-Technologies/people_spotting.git 19 | [submodule "ingredients/image_classification_networks"] 20 | path = ingredients/image_classification_networks 21 | url = git@github.com:GreenWaves-Technologies/image_classification_networks.git 22 | [submodule "ingredients/kws"] 23 | path = ingredients/kws 24 | url = git@github.com:GreenWaves-Technologies/kws.git 25 | [submodule "starters/vehicle_spotting"] 26 | path = starters/vehicle_spotting 27 | url = git@github.com:GreenWaves-Technologies/vehicle_spotting.git 28 | [submodule "starters/licence_plate_recognition"] 29 | path = starters/licence_plate_recognition 30 | url = git@github.com:GreenWaves-Technologies/licence_plate_recognition.git 31 | [submodule "starters/keyword_spotting"] 32 | path = starters/keyword_spotting 33 | url = git@github.com:GreenWaves-Technologies/keyword_spotting.git 34 | [submodule "ingredients/blaze_face"] 35 | path = ingredients/blaze_face 36 | url = git@github.com:GreenWaves-Technologies/blaze_face.git 37 | [submodule "ingredients/mcunet"] 38 | path = ingredients/mcunet 39 | url = git@github.com:GreenWaves-Technologies/mcunet.git 40 | [submodule "sides/cifar10_quant_import"] 41 | path = sides/cifar10_quant_import 42 | url = git@github.com:GreenWaves-Technologies/cifar10_quant_import.git 43 | [submodule "starters/tiny_denoiser"] 44 | path = starters/tiny_denoiser 45 | url = git@github.com:GreenWaves-Technologies/tiny_denoiser.git 46 | -------------------------------------------------------------------------------- /sides/power_meas_utils/l2_bw_autotiler.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import sys 3 | import matplotlib.pyplot as plt 4 | import os 5 | import pandas as pd 6 | import re 7 | 8 | PLOT = True 9 | 10 | matches = { 11 | "L2 Budget": re.compile(r'\s+L2 Memory size \(Bytes\)\s+: Given:\s+(?P[0-9]+), Used:\s+[0-9]+\n'), 12 | "L2 Memory bandwidth for 1 graph run": re.compile(r'L2 Memory bandwidth for 1 graph run\s+\:\s+(?P[0-9]+)\s+Bytes\n'), 13 | "Percentage of baseline BW for L2": re.compile(r'Percentage of baseline BW for L2\s+\:\s+(?P[0-9.]+)\s+\%\n'), 14 | "L3 Memory bandwidth for 1 graph run": re.compile(r'L3 Memory bandwidth for 1 graph run\s+\:\s+(?P[0-9]+)\s+Bytes\n'), 15 | "Percentage of baseline BW for L3": re.compile(r'Percentage of baseline BW for L3\s+\:\s+(?P[0-9.]+)\s+\%\n'), 16 | "Tiling Bandwith overhead": re.compile(r'Tiling Bandwith overhead\s+\:\s+(?P[0-9.]+)\s+Move/KerArgSize\n'), 17 | "Sum of all Kernels arguments size": re.compile(r'Sum of all Kernels arguments size\s+\:\s+(?P[0-9]+)\s+Bytes\n'), 18 | "Sum of all Kernels operations": re.compile(r'Sum of all Kernels operations\s+\:\s+(?P[0-9]+)\s+Operations\n'), 19 | "Total amount of flash coefficients": re.compile(r'Total amount of flash coefficients\s+\:\s+(?P[0-9]+)\s+Bytes\n'), 20 | } 21 | 22 | def main(): 23 | l2_min = int(sys.argv[1]) if len(sys.argv) > 1 else 100 24 | l2_max = int(sys.argv[2]) if len(sys.argv) > 2 else 1500 25 | extra_flags = sys.argv[3] if len(sys.argv) > 3 else "" 26 | 27 | out_dict = {n: [] for n in matches.keys()} 28 | for L2_MEM in range(l2_min, l2_max, 50): 29 | stream = os.popen(f'make clean_at_model model MODEL_L2_MEMORY={L2_MEM*1000} {extra_flags}') 30 | 31 | out_log = "".join(stream.readlines()) 32 | #print(out_log) 33 | for name, match in matches.items(): 34 | m = match.findall(out_log) 35 | if m: 36 | out_dict[name].append(float(m[0])) 37 | else: 38 | if name == "L2 Budget": 39 | out_dict[name].append(L2_MEM * 1000) 40 | else: 41 | out_dict[name].append(0.0) 42 | 43 | df = pd.DataFrame(out_dict) 44 | print(df) 45 | 46 | if PLOT: 47 | l2_budget = df["L2 Budget"].to_numpy() / 1000 48 | l3_bw = df["L3 Memory bandwidth for 1 graph run"].to_numpy() / 1024 49 | l3_bw_per = df["Percentage of baseline BW for L3"].to_numpy() 50 | 51 | fig, ax1 = plt.subplots() 52 | line1 = ax1.plot(l2_budget, l3_bw, color='red', label="L3 Memory bandwidth for 1 graph run") 53 | ax2 = ax1.twinx() 54 | line2 = ax2.plot(l2_budget, l3_bw_per, "--", color='blue', label="Percentage of baseline BW for L3") 55 | 56 | ax1.set_xlabel('L2 MEM [KB]', fontweight ='bold') 57 | ax1.set_ylabel('KB') 58 | ax2.set_ylabel('%') 59 | lns = line1 + line2 60 | labs = [l.get_label() for l in lns] 61 | ax1.legend(lns, labs, loc=0) 62 | plt.show() 63 | 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NN Menu 2 | 3 | The **Neural Network Menu\*** is a collection of software that implements Neural Networks on Greenwaves Application Processors (GAP). This repository contains common mobile and edge NN archtecture examples, NN sample applications and full flagged reference designs. Our tools maps a TFLITE model (quantized or unquantized) onto gap. There is also a flow in the ingredients directory showing how to hand map from a Pytorch Model onto GAP. 4 | 5 | For a detailed description of the content of each project please refer to the readme inside the project folder. 6 | 7 | ## Getting Started 8 | 9 | To run the code in this repository you need to install and source the [gap sdk](https://github.com/GreenWaves-Technologies/gap_sdk). 10 | 11 | Once the sdk is installed source the sourceme.sh in the root sdk folder and retrieve this repository with a: 12 | 13 | ``` 14 | git clone --recurse-submodules -j4 git@github.com:GreenWaves-Technologies/nn_menu.git 15 | ``` 16 | 17 | ## Content of the repository 18 | 19 | The repository is divided into 4 different folders: 20 | 21 | #### **ingredients** 22 | Optimized models for common mobile and edge use cases. This is a playground to start with, it shows how well known networks topologies are mapped onto gap. 23 | 24 | Content of the folder: 25 | - Image Classification Networks (several versions of Mobilenet V1, V2, V3 minimalistic, full V3 to come) 26 | - Blaze Face (Face Detector + Facial Landmarks, from Google Media Pipe) 27 | - kws (Google Keyword Spotting) 28 | - Mobilenet V1 from Pytorch Model 29 | - MCUNet 30 | 31 | These examples take image from file with semihosting in input and output the results through the shell. 32 | 33 | #### **starters** 34 | Use Case specific examples that use specific datasets and shows nn running for a specific task. 35 | 36 | Content of the folder: 37 | - Body Detection (custom CNN) 38 | - Face Detection (custom CNN) 39 | - Keyword Spotting ([speech_commands](https://www.tensorflow.org/datasets/catalog/speech_commands) from Tensorflow) 40 | - Licence Plate Recognition (Mobilenet SSDLite + LPRNet) 41 | - People Spotting (NN from [MIT Visual Wakeup Words](https://github.com/mit-han-lab/VWW)) 42 | - Tiny Denoiser (Custon NN), only for GAP9 43 | - Vehicle Spotting (Customization and embedding of a deep learning pipeline for visual object spotting) 44 | 45 | These applications take an input from file with semihosting and output the results trough shell, they also run on our boards to be tested with input from drivers. For specific cameras configrations please check the readme in within each projects folder. 46 | 47 | #### **main courses** 48 | Full flagged applications (aka reference designs) running on [GAPoC series boards](https://greenwaves-technologies.com/store/). 49 | 50 | Content of the folder: 51 | - ReID (on GAPoC A) 52 | - Occupancy Management (on GAPoC B) 53 | 54 | These applications take image from file with semihosting in input and output the results trough shell, these mode run in gvsoc and all boards. Then they have flags in makefile to enable input/output with driver. Each of them is designed for a specific board as a reference design. 55 | 56 | 57 | #### **sides** 58 | 59 | This folders contains tools related to measurments and NNTool usage 60 | 61 | Content of the folder: 62 | - power_meas_utils: a project to batch measure energy consumption using a Picoscope® 63 | - cifar10: This project shows how to train and load post training quantization statistics from Pytorch through ONNX and Tensorflow through TFLite into NNTool and deploy on GAP. 64 | 65 | 66 | ## Futures Releases 67 | 68 | We are actively working in the area of RNN, LSTM and GRU. Next releases will contain new repository that will be demoing it. If you have any suggestion or willing please contact us at 69 | 70 | 71 | \* We are a french Company, so we care about food. The team is composed from people all over the world, that's why we can laugh about it :-) 72 | -------------------------------------------------------------------------------- /sides/power_meas_utils/editlog.py: -------------------------------------------------------------------------------- 1 | import os 2 | import csv 3 | import sys 4 | import numpy as np 5 | import pickle 6 | import re 7 | 8 | if len(sys.argv) != 5: 9 | print("Wrong number of arguments!") 10 | exit(1) 11 | 12 | file_csv = str(sys.argv[1]) 13 | file_at = str(sys.argv[2]) 14 | file_log = str(sys.argv[3]) 15 | file_name = str(sys.argv[4]) 16 | 17 | csv.field_size_limit(20000000) 18 | 19 | # log file 20 | at_log = {} 21 | 22 | 23 | # parse power measurements 24 | with open(file_csv, newline='') as f: 25 | reader = csv.reader(f) 26 | for r, row in enumerate(reader): 27 | print(row[0], float(row[1])) 28 | if len(row) == 2: 29 | at_log[row[0]] = float(row[1]) 30 | 31 | 32 | # parse at file 33 | L1_MEM_USAGE = re.compile(r'\s*(?PShared L1 Memory size) \(Bytes\)\s+\: Given\:\s+(?P[0-9]+)\,\sUsed\:\s+(?P[0-9]+)') 34 | L2_MEM_USAGE = re.compile(r'\s*(?PL2 Memory size) \(Bytes\)\s+\: Given\:\s+(?P[0-9]+)\,\sUsed\:\s+(?P[0-9]+)') 35 | L3_MEM_USAGE = re.compile(r'\s*(?PL3 Memory size) \(Bytes\)\s+\: Given\:\s+(?P[0-9]+)\,\sUsed\:\s+(?P[0-9]+)') 36 | L3_MEM_BW = re.compile(r'(?PL3 Memory bandwidth for 1 graph run)\s+\:\s+(?P[0-9]+)\s+Bytes\n') 37 | L2_MEM_BW = re.compile(r'(?PL2 Memory bandwidth for 1 graph run)\s+\:\s+(?P[0-9]+)\s+Bytes\n') 38 | KER_ARGS = re.compile(r'(?PSum of all Kernels arguments size)\s+\:\s+(?P[0-9]+)\s+Bytes\n') 39 | TIL_OH = re.compile(r'(?PTiling Bandwith overhead)\s+\:\s+(?P[0-9.]+)\s+Move/KerArgSize\n') 40 | L2_MEM_BW_PER = re.compile(r'(?PPercentage of baseline BW for L2)\s+\:\s+(?P[0-9.]+)\s+\%\n') 41 | L3_MEM_BW_PER = re.compile(r'(?PPercentage of baseline BW for L3)\s+\:\s+(?P[0-9.]+)\s+\%\n') 42 | KER_OPS = re.compile(r'(?PSum of all Kernels operations)\s+\:\s+(?P[0-9]+)\s+Operations\n') 43 | TOT_COEFFS = re.compile(r'(?PTotal amount of flash coefficients)\s+\:\s+(?P[0-9]+)\s+Bytes\n') 44 | 45 | matches = [L3_MEM_BW, L2_MEM_BW, TIL_OH, KER_ARGS, L2_MEM_BW_PER, L3_MEM_BW_PER, KER_OPS, TOT_COEFFS,L1_MEM_USAGE, L2_MEM_USAGE, L3_MEM_USAGE] 46 | 47 | with open(file_at, newline='') as f: 48 | out_log = f.readlines() 49 | row = [] 50 | for line in out_log: 51 | for match in matches: 52 | m = match.search(line) 53 | if m: 54 | metric = m['column'] 55 | if "Memory size" in metric: 56 | value= float(m['used']) 57 | print(m['column'], float(m['used'])) 58 | else: 59 | value= float(m['value']) 60 | print(m['column'], float(m['value'])) 61 | at_log[metric] = value 62 | 63 | continue 64 | 65 | 66 | # parse board measurements 67 | # Total: Cycles: 74947815, Operations: 571351529, Operations/Cycle: 7.623325 68 | # Set FC Frequency as 239616468 MHz, CL Frequency = 239616468 MHz, PERIIPH Frequency = 239616468 MHz 69 | # Voltage: 650mV 70 | 71 | with open(file_log, newline='') as f: 72 | out_log = f.readlines() 73 | row = [] 74 | for line in out_log: 75 | if 'Set FC Frequency' in line: 76 | # print(line) 77 | parse = line.replace(',','').replace(':','').replace('=','').split() 78 | fre_fc = parse[4] 79 | fre_cl = parse[8] 80 | fre_pe = parse[12] 81 | at_log['FREQ_FC'] = fre_fc 82 | at_log['FREQ_CL'] = fre_cl 83 | at_log['FREQ_PE'] = fre_pe 84 | print(fre_fc,fre_cl) 85 | # for item in parse: 86 | # print(item) 87 | continue 88 | elif 'Total' in line: 89 | # print(line) 90 | parse = line.replace(',','').replace(':','').split() 91 | cycles = parse[2] 92 | op_cycles = parse[6] 93 | at_log['cycles'] = cycles 94 | at_log['op_cycles'] = op_cycles 95 | print(cycles,op_cycles) 96 | # for item in parse: 97 | # print(item) 98 | 99 | continue 100 | # for match in matches: 101 | # m = match.search(line) 102 | # if m: 103 | # metric = m['column'] 104 | # value= float(m['value']) 105 | # at_log[metric] = value 106 | # print(m['column'], float(m['value'])) 107 | # continue 108 | 109 | print(at_log) 110 | with open(file_name+".csv", "w", newline='') as file: 111 | writer = csv.writer(file, delimiter='\t') 112 | for item in at_log.keys(): 113 | print(item, at_log[item]) 114 | writer.writerow([item,at_log[item]]) 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /sides/power_meas_utils/log_to_csv.py: -------------------------------------------------------------------------------- 1 | import os 2 | from glob import glob 3 | import pandas as pd 4 | import sys 5 | import re 6 | import csv 7 | 8 | MEAS_SETTINGS = { 9 | "CH_A_Avg": ["SoC Before DCDC [mW]", 1.8, 1], 10 | "CH_B_Avg": ["SoC After DCDC [mW]", None, 0.5], 11 | #"CH_C_Avg": ["Trigger", None, 0.5], 12 | "CH_D_Avg": ["IO [mW]", 1.8, 0.25], 13 | } 14 | 15 | def convert_meas_to_mW(dataframe: pd.DataFrame, settings): 16 | for name, setting in settings.items(): 17 | voltage = setting[1] if setting[1] is not None else dataframe["Voltage [V]"] 18 | dataframe[name] = dataframe[name] * voltage / setting[2] 19 | dataframe = dataframe.rename(columns={name: setting[0]}) 20 | dataframe.insert(dataframe.columns.get_loc("SoC After DCDC [mW]")+1, "Efficiency DCDC", dataframe["SoC After DCDC [mW]"] / dataframe["SoC Before DCDC [mW]"]) 21 | dataframe.insert(dataframe.columns.get_loc("Latency [ms]")+1, "Energy [uJ]", dataframe["Latency [ms]"] * dataframe["SoC Before DCDC [mW]"]) 22 | return dataframe 23 | 24 | # parse at file 25 | L1_MEM_USAGE = re.compile(r'\s*(?PShared L1 Memory size) \(Bytes\)\s+\: Given\:\s+(?P[0-9]+)\,\sUsed\:\s+(?P[0-9]+)') 26 | L2_MEM_USAGE = re.compile(r'\s*(?PL2 Memory size) \(Bytes\)\s+\: Given\:\s+(?P[0-9]+)\,\sUsed\:\s+(?P[0-9]+)') 27 | L3_MEM_USAGE = re.compile(r'\s*(?PL3 Memory size) \(Bytes\)\s+\: Given\:\s+(?P[0-9]+)\,\sUsed\:\s+(?P[0-9]+)') 28 | L3_MEM_BW = re.compile(r'(?PL3 Memory bandwidth for 1 graph run)\s+\:\s+(?P[0-9]+)\s+Bytes\n') 29 | L2_MEM_BW = re.compile(r'(?PL2 Memory bandwidth for 1 graph run)\s+\:\s+(?P[0-9]+)\s+Bytes\n') 30 | KER_ARGS = re.compile(r'(?PSum of all Kernels arguments size)\s+\:\s+(?P[0-9]+)\s+Bytes\n') 31 | TIL_OH = re.compile(r'(?PTiling Bandwith overhead)\s+\:\s+(?P[0-9.]+)\s+Move/KerArgSize\n') 32 | L2_MEM_BW_PER = re.compile(r'(?PPercentage of baseline BW for L2)\s+\:\s+(?P[0-9.]+)\s+\%\n') 33 | L3_MEM_BW_PER = re.compile(r'(?PPercentage of baseline BW for L3)\s+\:\s+(?P[0-9.]+)\s+\%\n') 34 | KER_OPS = re.compile(r'(?PSum of all Kernels operations)\s+\:\s+(?P[0-9]+)\s+Operations\n') 35 | TOT_COEFFS = re.compile(r'(?PTotal amount of flash coefficients)\s+\:\s+(?P[0-9]+)\s+Bytes\n') 36 | 37 | def main(): 38 | matches = [L3_MEM_BW, L2_MEM_BW, TIL_OH, KER_ARGS, L2_MEM_BW_PER, L3_MEM_BW_PER, KER_OPS, TOT_COEFFS,L1_MEM_USAGE, L2_MEM_USAGE, L3_MEM_USAGE] 39 | 40 | if len(sys.argv) != 4: 41 | print("Wrong number of arguments!") 42 | exit(1) 43 | file_pow = str(sys.argv[1]) 44 | file_at = str(sys.argv[2]) 45 | out_file = str(sys.argv[3]) 46 | 47 | # log file 48 | at_log = {} 49 | # parse power measurements 50 | with open(file_pow, newline='') as f: 51 | reader = csv.reader(f) 52 | for r, row in enumerate(reader): 53 | print(row[0], float(row[1])) 54 | if len(row) == 2: 55 | at_log[row[0]] = [float(row[1])] 56 | 57 | # parse AT measures 58 | with open(file_at, newline='') as f: 59 | out_log = f.readlines() 60 | row = [] 61 | for line in out_log: 62 | for match in matches: 63 | m = match.search(line) 64 | if m: 65 | metric = m['column'] 66 | if "Memory size" in metric: 67 | value= float(m['used']) 68 | print(m['column'], float(m['used'])) 69 | else: 70 | value= float(m['value']) 71 | print(m['column'], float(m['value'])) 72 | at_log[metric] = [value] 73 | continue 74 | print(at_log) 75 | print("--------------------") 76 | 77 | filename = os.path.splitext(file_pow)[0] 78 | model_name = filename.split("_")[1] 79 | freq = int(filename.split("_")[-2]) 80 | voltage = int(filename.split("_")[-1]) / 1000 81 | mode = "NE16" if "NE16" in filename else ("FP16" if "FP16" in filename else "SQ8") 82 | mode += "_HWC" if "HWC" in filename else "" 83 | df = pd.DataFrame.from_dict(at_log) 84 | df.insert(0, "Model", model_name) 85 | df.insert(1, "Mode", mode) 86 | df.insert(1, "Frequency [MHz]", freq) 87 | df.insert(1, "Voltage [V]", voltage) 88 | df = df.sort_values(["Mode", "Voltage [V]"]) 89 | df = df.rename( 90 | columns={ 91 | "n_Points": "Latency [ms]"}) 92 | df["Latency [ms]"] /= 1000 93 | total_cyc = df["Latency [ms]"] * df["Frequency [MHz]"] * 1000 94 | ops_over_cyc = df["Sum of all Kernels operations"] / total_cyc 95 | df.insert(df.columns.get_loc("Latency [ms]")+1, "Cyc", total_cyc) 96 | df.insert(df.columns.get_loc("Cyc")+1, "Ops/Cyc", ops_over_cyc) 97 | df = convert_meas_to_mW(df, MEAS_SETTINGS) 98 | print(df) 99 | if os.path.exists(out_file): 100 | df_before = pd.read_csv(out_file) 101 | df = df_before.append(df) 102 | df.to_csv(out_file, index=False) 103 | 104 | if __name__ == '__main__': 105 | main() 106 | -------------------------------------------------------------------------------- /sides/power_meas_utils/ps4444Measure.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2018 Pico Technology Ltd. See LICENSE file for terms. 3 | # 4 | # PS4824 BLOCK MODE EXAMPLE 5 | # This example opens a 4000a driver device, sets up two channels and a trigger then collects a block of data. 6 | # This data is then plotted as mV against time in ns. 7 | 8 | import sys 9 | import ctypes 10 | import numpy as np 11 | from picosdk.ps4000a import ps4000a as ps 12 | import matplotlib.pyplot as plt 13 | from picosdk.functions import adc2mV, assert_pico_ok 14 | import picosdk.discover as dut 15 | import csv 16 | 17 | if len(sys.argv) != 2: 18 | print("Wrong number of arguments!") 19 | exit(1) 20 | 21 | file_name = str(sys.argv[1]) 22 | 23 | 24 | 25 | # Create chandle and status ready for use 26 | chandle = ctypes.c_int16() 27 | status = {} 28 | 29 | 30 | 31 | # Open 4000 series PicoScope 32 | # Returns handle to chandle for use in future API functions 33 | status["openunit"] = ps.ps4000aOpenUnit(ctypes.byref(chandle), None) 34 | 35 | try: 36 | assert_pico_ok(status["openunit"]) 37 | except: # PicoNotOkError: 38 | powerStatus = status["openunit"] 39 | 40 | if powerStatus == 286: 41 | status["changePowerSource"] = ps.ps4000aChangePowerSource(chandle, powerStatus) 42 | elif powerStatus == 282: 43 | status["changePowerSource"] = ps.ps4000aChangePowerSource(chandle, powerStatus) 44 | else: 45 | raise 46 | 47 | assert_pico_ok(status["changePowerSource"]) 48 | 49 | 50 | #### Ranges 51 | # pico_d9_bnc_10mv = 0 52 | # pico_d9_bnc_20mv = 1 53 | # pico_d9_bnc_50mv = 2 54 | # pico_d9_bnc_100mv = 3 55 | # pico_d9_bnc_200mv = 4 56 | # pico_d9_bnc_500mv = 5 57 | # pico_d9_bnc_1v = 6 58 | # pico_d9_bnc_2v = 7 59 | # pico_d9_bnc_5v = 8 60 | # pico_d9_bnc_10v = 9 61 | # pico_d9_bnc_20v = 10 62 | # pico_d9_bnc_50v = 11 63 | # pico_d9_bnc_100v = 12 64 | # pico_d9_bnc_200v = 13 65 | 66 | ###### Set up channel A: Cluster ############# 67 | # handle = chandle 68 | # channel = PS4000a_CHANNEL_A = 0 69 | # enabled = 1 70 | # coupling type = PS4000a_DC = 1 71 | # range = PS4000a_2V = 7 72 | # analogOffset = 0 V 73 | chARange = 3 74 | status["setChA"] = ps.ps4000aSetChannel(chandle, 0, 1, 1, chARange, 0) 75 | assert_pico_ok(status["setChA"]) 76 | 77 | ###### Set up channel B: Soc ############# 78 | # Set up channel B 79 | # handle = chandle 80 | # channel = PS4000a_CHANNEL_B = 1 81 | # enabled = 1 82 | # coupling type = PS4000a_DC = 1 83 | # range = PS4000a_2V = 7 84 | # analogOffset = 0 V 85 | chBRange = 3 86 | status["setChB"] = ps.ps4000aSetChannel(chandle, 1, 1, 1, chBRange, 0) 87 | assert_pico_ok(status["setChB"]) 88 | 89 | ###### Set up channel C: Trigger ############# 90 | # handle = chandle 91 | # channel = PS4000a_CHANNEL_C = 2 92 | # enabled = 1 93 | # coupling type = PS4000a_DC = 1 94 | # range = PS4000a_2V = 7 95 | # analogOffset = 0 V 96 | chCRange = 8 #5V 97 | status["setChC"] = ps.ps4000aSetChannel(chandle, 2, 1, 1, chCRange, 0) 98 | assert_pico_ok(status["setChC"]) 99 | 100 | ###### Set up channel D: Memory ############# 101 | # handle = chandle 102 | # channel = PS4000a_CHANNEL_D = 3 103 | # enabled = 1 104 | # coupling type = PS4000a_DC = 1 105 | # range = PS4000a_2V = 7 106 | # analogOffset = 0 V 107 | chDRange = 3 108 | status["setChD"] = ps.ps4000aSetChannel(chandle, 3, 1, 1, chDRange, 0) 109 | assert_pico_ok(status["setChD"]) 110 | 111 | # Set up single trigger 112 | # handle = chandle 113 | # enabled = 1 114 | # source = PS4000a_CHANNEL_D = 3 115 | # threshold = 1024 ADC counts 116 | # direction = PS4000a_RISING = 2 117 | # delay = 0 s 118 | # auto Trigger = 1000 ms 119 | status["trigger"] = ps.ps4000aSetSimpleTrigger(chandle, 1, 2, 1024, 2, 0, 0) 120 | assert_pico_ok(status["trigger"]) 121 | 122 | # Set number of pre and post trigger samples to be collected 123 | preTriggerSamples = 0 124 | postTriggerSamples = 2000000 125 | maxSamples = preTriggerSamples + postTriggerSamples 126 | 127 | # Get timebase information 128 | # handle = chandle 129 | # timebase = 8 = timebase 130 | # noSamples = maxSamples 131 | # pointer to timeIntervalNanoseconds = ctypes.byref(timeIntervalns) 132 | # pointer to maxSamples = ctypes.byref(returnedMaxSamples) 133 | # segment index = 0 134 | timebase = 52 135 | timeIntervalns = ctypes.c_float() 136 | returnedMaxSamples = ctypes.c_int32() 137 | oversample = ctypes.c_int16(1) 138 | status["getTimebase2"] = ps.ps4000aGetTimebase2(chandle, timebase, maxSamples, ctypes.byref(timeIntervalns), ctypes.byref(returnedMaxSamples), 0) 139 | assert_pico_ok(status["getTimebase2"]) 140 | 141 | # Run block capture 142 | # handle = chandle 143 | # number of pre-trigger samples = preTriggerSamples 144 | # number of post-trigger samples = PostTriggerSamples 145 | # timebase = 3 = 80 ns = timebase (see Programmer's guide for mre information on timebases) 146 | # time indisposed ms = None (not needed in the example) 147 | # segment index = 0 148 | # lpReady = None (using ps4000aIsReady rather than ps4000aBlockReady) 149 | # pParameter = None 150 | status["runBlock"] = ps.ps4000aRunBlock(chandle, preTriggerSamples, postTriggerSamples, timebase, None, 0, None, None) 151 | assert_pico_ok(status["runBlock"]) 152 | 153 | # Check for data collection to finish using ps4000aIsReady 154 | ready = ctypes.c_int16(0) 155 | check = ctypes.c_int16(0) 156 | while ready.value == check.value: 157 | status["isReady"] = ps.ps4000aIsReady(chandle, ctypes.byref(ready)) 158 | 159 | # Create buffers ready for assigning pointers for data collection 160 | bufferAMax = (ctypes.c_int16 * maxSamples)() 161 | bufferAMin = (ctypes.c_int16 * maxSamples)() # used for downsampling which isn't in the scope of this example 162 | bufferBMax = (ctypes.c_int16 * maxSamples)() 163 | bufferBMin = (ctypes.c_int16 * maxSamples)() # used for downsampling which isn't in the scope of this example 164 | bufferCMax = (ctypes.c_int16 * maxSamples)() 165 | bufferCMin = (ctypes.c_int16 * maxSamples)() # used for downsampling which isn't in the scope of this example 166 | bufferDMax = (ctypes.c_int16 * maxSamples)() 167 | bufferDMin = (ctypes.c_int16 * maxSamples)() # used for downsampling which isn't in the scope of this example 168 | 169 | # Set data buffer location for data collection from channel A 170 | # handle = chandle 171 | # source = PS4000a_CHANNEL_A = 0 172 | # pointer to buffer max = ctypes.byref(bufferAMax) 173 | # pointer to buffer min = ctypes.byref(bufferAMin) 174 | # buffer length = maxSamples 175 | # segementIndex = 0 176 | # mode = PS4000A_RATIO_MODE_NONE = 0 177 | status["setDataBuffersA"] = ps.ps4000aSetDataBuffers(chandle, 0, ctypes.byref(bufferAMax), ctypes.byref(bufferAMin), maxSamples, 0 , 0) 178 | assert_pico_ok(status["setDataBuffersA"]) 179 | 180 | # Set data buffer location for data collection from channel B 181 | # handle = chandle 182 | # source = PS4000a_CHANNEL_B = 1 183 | # pointer to buffer max = ctypes.byref(bufferBMax) 184 | # pointer to buffer min = ctypes.byref(bufferBMin) 185 | # buffer length = maxSamples 186 | # segementIndex = 0 187 | # mode = PS4000A_RATIO_MODE_NONE = 0 188 | status["setDataBuffersB"] = ps.ps4000aSetDataBuffers(chandle, 1, ctypes.byref(bufferBMax), ctypes.byref(bufferBMin), maxSamples, 0 , 0) 189 | assert_pico_ok(status["setDataBuffersB"]) 190 | 191 | # Set data buffer location for data collection from channel C 192 | # handle = chandle 193 | # source = PS4000a_CHANNEL_C = 1 194 | # pointer to buffer max = ctypes.byref(bufferCMax) 195 | # pointer to buffer min = ctypes.byref(bufferCMin) 196 | # buffer length = maxSamples 197 | # segementIndex = 0 198 | # mode = PS4000A_RATIO_MODE_NONE = 0 199 | status["setDataBuffersC"] = ps.ps4000aSetDataBuffers(chandle, 2, ctypes.byref(bufferCMax), ctypes.byref(bufferCMin), maxSamples, 0 , 0) 200 | assert_pico_ok(status["setDataBuffersC"]) 201 | 202 | # Set data buffer location for data collection from channel D 203 | # handle = chandle 204 | # source = PS4000a_CHANNEL_D = 3 205 | # pointer to buffer max = ctypes.byref(bufferDMax) 206 | # pointer to buffer min = ctypes.byref(bufferDMin) 207 | # buffer length = maxSamples 208 | # segementIndex = 0 209 | # mode = PS4000A_RATIO_MODE_NONE = 0 210 | status["setDataBuffersD"] = ps.ps4000aSetDataBuffers(chandle, 3, ctypes.byref(bufferDMax), ctypes.byref(bufferDMin), maxSamples, 0 , 0) 211 | assert_pico_ok(status["setDataBuffersD"]) 212 | 213 | 214 | # create overflow loaction 215 | overflow = ctypes.c_int16() 216 | # create converted type maxSamples 217 | cmaxSamples = ctypes.c_int32(maxSamples) 218 | 219 | # Retried data from scope to buffers assigned above 220 | # handle = chandle 221 | # start index = 0 222 | # pointer to number of samples = ctypes.byref(cmaxSamples) 223 | # downsample ratio = 0 224 | # downsample ratio mode = PS4000a_RATIO_MODE_NONE 225 | # pointer to overflow = ctypes.byref(overflow)) 226 | status["getValues"] = ps.ps4000aGetValues(chandle, 0, ctypes.byref(cmaxSamples), 0, 0, 0, ctypes.byref(overflow)) 227 | assert_pico_ok(status["getValues"]) 228 | 229 | 230 | # find maximum ADC count value 231 | # handle = chandle 232 | # pointer to value = ctypes.byref(maxADC) 233 | maxADC = ctypes.c_int16(32767) 234 | 235 | 236 | # convert ADC counts data to mV 237 | adc2mVChAMax = adc2mV(bufferAMax, chARange, maxADC) 238 | adc2mVChBMax = adc2mV(bufferBMax, chBRange, maxADC) 239 | adc2mVChCMax = adc2mV(bufferCMax, chCRange, maxADC) 240 | adc2mVChDMax = adc2mV(bufferDMax, chDRange, maxADC) 241 | 242 | ## detect the last point 243 | last_point = postTriggerSamples 244 | for i,item in enumerate(adc2mVChCMax[10:]): 245 | if item < 1000: 246 | last_point = i+10 247 | break 248 | print("Last point is ", last_point, "\nTime duration is = ", (last_point*timeIntervalns.value)/(1000*1000) ,' msec') 249 | 250 | 251 | # Compute Average 252 | CH_A_Avg = np.average(adc2mVChAMax[:last_point]) 253 | CH_B_Avg = np.average(adc2mVChBMax[:last_point]) 254 | CH_D_Avg = np.average(adc2mVChDMax[:last_point]) 255 | n_Points = last_point 256 | print("Avg values {} {} {} over {} points!".format(CH_A_Avg,CH_B_Avg,CH_D_Avg,n_Points)) 257 | 258 | with open(file_name+".csv", "w", newline='') as file: 259 | writer = csv.writer(file, delimiter=',') 260 | # writer.writerow(adc2mVChAMax[:last_point]) 261 | # writer.writerow(adc2mVChBMax[:last_point]) 262 | # writer.writerow(adc2mVChCMax[:last_point]) 263 | writer.writerow(['CH_A_Avg',CH_A_Avg]) 264 | writer.writerow(['CH_B_Avg',CH_B_Avg]) 265 | writer.writerow(['CH_D_Avg',CH_D_Avg]) 266 | writer.writerow(['n_Points',n_Points]) 267 | 268 | ## Create time data 269 | # time = np.linspace(0, (cmaxSamples.value) * timeIntervalns.value, cmaxSamples.value) 270 | # # plot data from channel A and B 271 | # plt.plot(time[:last_point], adc2mVChAMax[:last_point], label="Ch A") 272 | # plt.plot(time[:last_point], adc2mVChBMax[:last_point], label="Ch B") 273 | # plt.plot(time[:last_point], adc2mVChCMax[:last_point], label="Ch C") 274 | # plt.plot(time[:last_point], adc2mVChDMax[:last_point], label="Ch D") 275 | # plt.legend() 276 | # plt.xlabel('Time (ns)') 277 | # plt.ylabel('Voltage (mV)') 278 | # plt.show() 279 | 280 | # Stop the scope 281 | # handle = chandle 282 | status["stop"] = ps.ps4000aStop(chandle) 283 | assert_pico_ok(status["stop"]) 284 | 285 | 286 | # Close unitDisconnect the scope 287 | # handle = chandle 288 | #status["close"] = ps.ps4000aCloseUnit(chandle) 289 | #assert_pico_ok(status["close"]) 290 | 291 | #print("Closed") 292 | 293 | # display status returns 294 | print(status) 295 | --------------------------------------------------------------------------------