Skip to content

Commit d145b10

Browse files
committed
Skeleton for CM3 additions and integration
1 parent 56c22da commit d145b10

File tree

13 files changed

+1856
-0
lines changed

13 files changed

+1856
-0
lines changed

examples/picocm3-profiling/README.md

Whitespace-only changes.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
from EventManager.Models.RunnerEvents import RunnerEvents
2+
from EventManager.EventSubscriptionController import EventSubscriptionController
3+
from ConfigValidator.Config.Models.RunTableModel import RunTableModel
4+
from ConfigValidator.Config.Models.FactorModel import FactorModel
5+
from ConfigValidator.Config.Models.RunnerContext import RunnerContext
6+
from ConfigValidator.Config.Models.OperationType import OperationType
7+
from ProgressManager.Output.OutputProcedure import OutputProcedure as output
8+
9+
from typing import Dict, List, Any, Optional
10+
from pathlib import Path
11+
from os.path import dirname, realpath
12+
13+
import os
14+
import signal
15+
import pandas as pd
16+
import time
17+
import subprocess
18+
import shlex
19+
20+
class RunnerConfig:
21+
ROOT_DIR = Path(dirname(realpath(__file__)))
22+
23+
# ================================ USER SPECIFIC CONFIG ================================
24+
"""The name of the experiment."""
25+
name: str = "new_picocm3_experiment"
26+
27+
"""The path in which Experiment Runner will create a folder with the name `self.name`, in order to store the
28+
results from this experiment. (Path does not need to exist - it will be created if necessary.)
29+
Output path defaults to the config file's path, inside the folder 'experiments'"""
30+
results_output_path: Path = ROOT_DIR / 'experiments'
31+
32+
"""Experiment operation type. Unless you manually want to initiate each run, use `OperationType.AUTO`."""
33+
operation_type: OperationType = OperationType.AUTO
34+
35+
"""The time Experiment Runner will wait after a run completes.
36+
This can be essential to accommodate for cooldown periods on some systems."""
37+
time_between_runs_in_ms: int = 1000
38+
39+
# Dynamic configurations can be one-time satisfied here before the program takes the config as-is
40+
# e.g. Setting some variable based on some criteria
41+
def __init__(self):
42+
"""Executes immediately after program start, on config load"""
43+
44+
EventSubscriptionController.subscribe_to_multiple_events([
45+
(RunnerEvents.BEFORE_EXPERIMENT, self.before_experiment),
46+
(RunnerEvents.BEFORE_RUN , self.before_run ),
47+
(RunnerEvents.START_RUN , self.start_run ),
48+
(RunnerEvents.START_MEASUREMENT, self.start_measurement),
49+
(RunnerEvents.INTERACT , self.interact ),
50+
(RunnerEvents.STOP_MEASUREMENT , self.stop_measurement ),
51+
(RunnerEvents.STOP_RUN , self.stop_run ),
52+
(RunnerEvents.POPULATE_RUN_DATA, self.populate_run_data),
53+
(RunnerEvents.AFTER_EXPERIMENT , self.after_experiment )
54+
])
55+
self.run_table_model = None # Initialized later
56+
output.console_log("Custom config loaded")
57+
58+
def create_run_table_model(self) -> RunTableModel:
59+
"""Create and return the run_table model here. A run_table is a List (rows) of tuples (columns),
60+
representing each run performed"""
61+
sampling_factor = FactorModel("sampling", [10, 50, 100, 200, 500, 1000])
62+
self.run_table_model = RunTableModel(
63+
factors = [sampling_factor],
64+
data_columns=['dram_energy', 'package_energy',
65+
'pp0_energy', 'pp1_energy']
66+
67+
)
68+
return self.run_table_model
69+
70+
def before_experiment(self) -> None:
71+
"""Perform any activity required before starting the experiment here
72+
Invoked only once during the lifetime of the program."""
73+
pass
74+
75+
def before_run(self) -> None:
76+
"""Perform any activity required before starting a run.
77+
No context is available here as the run is not yet active (BEFORE RUN)"""
78+
pass
79+
80+
def start_run(self, context: RunnerContext) -> None:
81+
"""Perform any activity required for starting the run here.
82+
For example, starting the target system to measure.
83+
Activities after starting the run should also be performed here."""
84+
pass
85+
86+
def start_measurement(self, context: RunnerContext) -> None:
87+
"""Perform any activity required for starting measurements."""
88+
sampling_interval = context.run_variation['sampling']
89+
90+
profiler_cmd = f'sudo energibridge \
91+
--interval {sampling_interval} \
92+
--max-execution 20 \
93+
--output {context.run_dir / "energibridge.csv"} \
94+
--summary \
95+
python3 examples/energibridge-profiling/primer.py'
96+
97+
#time.sleep(1) # allow the process to run a little before measuring
98+
energibridge_log = open(f'{context.run_dir}/energibridge.log', 'w')
99+
self.profiler = subprocess.Popen(shlex.split(profiler_cmd), stdout=energibridge_log)
100+
101+
def interact(self, context: RunnerContext) -> None:
102+
"""Perform any interaction with the running target system here, or block here until the target finishes."""
103+
104+
# No interaction. We just run it for XX seconds.
105+
# Another example would be to wait for the target to finish, e.g. via `self.target.wait()`
106+
output.console_log("Running program for 20 seconds")
107+
time.sleep(20)
108+
109+
def stop_measurement(self, context: RunnerContext) -> None:
110+
"""Perform any activity here required for stopping measurements."""
111+
self.profiler.wait()
112+
113+
def stop_run(self, context: RunnerContext) -> None:
114+
"""Perform any activity here required for stopping the run.
115+
Activities after stopping the run should also be performed here."""
116+
pass
117+
118+
def populate_run_data(self, context: RunnerContext) -> Optional[Dict[str, Any]]:
119+
"""Parse and process any measurement data here.
120+
You can also store the raw measurement data under `context.run_dir`
121+
Returns a dictionary with keys `self.run_table_model.data_columns` and their values populated"""
122+
123+
# energibridge.csv - Power consumption of the whole system
124+
df = pd.read_csv(context.run_dir / f"energibridge.csv")
125+
run_data = {
126+
'dram_energy' : round(df['DRAM_ENERGY (J)'].sum(), 3),
127+
'package_energy': round(df['PACKAGE_ENERGY (J)'].sum(), 3),
128+
'pp0_energy' : round(df['PP0_ENERGY (J)'].sum(), 3),
129+
'pp1_energy' : round(df['PP1_ENERGY (J)'].sum(), 3),
130+
}
131+
return run_data
132+
133+
def after_experiment(self) -> None:
134+
"""Perform any activity required after stopping the experiment here
135+
Invoked only once during the lifetime of the program."""
136+
pass
137+
138+
# ================================ DO NOT ALTER BELOW THIS LINE ================================
139+
experiment_path: Path = None
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import os
2+
import ctypes
3+
from picosdk.plcm3 import plcm3
4+
from platform import uname
5+
6+
7+
class PicoCM3(object):
8+
"""An integration of PicoTech CM3 current data logger (https://www.picotech.com/download/manuals/PicoLogCM3CurrentDataLoggerUsersGuide.pdf)"""
9+
SAMPLE_FREQUENCY = 1000 # in ms
10+
CHANNEL_SETTINGS = {
11+
12+
}
13+
14+
def __init__(self):
15+
# Check if a CM3 is present
16+
pass
17+
18+
def mode(self, runmode):
19+
pass
20+
21+
def log(self,timeout, logfile = None):
22+
pass
23+
24+
# Returns a tuple (scaled_value, unit_string)
25+
def apply_scaling(value, channel_mode):
26+
if channel_mode == plcm3.PLCM3DataTypes["PLCM3_OFF"]:
27+
return (0, "")
28+
elif channel_mode == plcm3.PLCM3DataTypes["PLCM3_1_MILLIVOLT"]:
29+
return (value / 1000, "A")
30+
elif channel_mode == plcm3.PLCM3DataTypes["PLCM3_10_MILLIVOLTS"]:
31+
return (value / 1000.0, "A")
32+
elif channel_mode == plcm3.PLCM3DataTypes["PLCM3_100_MILLIVOLTS"]:
33+
return (value, "mA")
34+
elif channel_mode == plcm3.PLCM3DataTypes["PLCM3_VOLTAGE"]:
35+
return (value / 1000.0, "mV")
36+
else:
37+
return (-1, "invalid mode")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#
2+
# Copyright (C) 2018 Pico Technology Ltd. See LICENSE file for terms.
3+
#

0 commit comments

Comments
 (0)