diff options
| -rwxr-xr-x | bin/analyze-archive.py | 36 | ||||
| -rw-r--r-- | lib/dfatool.py | 28 | 
2 files changed, 57 insertions, 7 deletions
| diff --git a/bin/analyze-archive.py b/bin/analyze-archive.py index 770e5d5..f7847c5 100755 --- a/bin/analyze-archive.py +++ b/bin/analyze-archive.py @@ -21,6 +21,22 @@ Options:      parameters. Also plots the corresponding measurements.      If gplearn function is set, it is plotted using dashed lines. +--export-traces=<directory> +    Export power traces of all states and transitions to <directory>. +    Creates a JSON file for each state and transition. Each JSON file +    lists all occurences of the corresponding state/transition in the +    benchmark's PTA trace. Each occurence contains the corresponding PTA +    parameters (if any) in 'parameter' and measurement results in 'offline'. +    As measurements are typically run repeatedly, 'offline' is in turn a list +    of measurements: offline[0]['uW'] is the power trace of the first +    measurement of this state/transition, offline[1]['uW'] corresponds t the +    second measurement, etc. Values are provided in microwatts. +    For example, TX.json[0].offline[0].uW corresponds to the first measurement +    of the first TX state in the benchmark, and TX.json[5].offline[2].uW +    corresponds to the third measurement of the sixth TX state in the benchmark. +    WARNING: Several GB of RAM and disk space are required for complex measurements. +             (JSON files may grow very large -- we trade efficiency for easy handling) +  --param-info      Show parameter names and values @@ -220,6 +236,7 @@ if __name__ == '__main__':      show_quality = []      pta = None      energymodel_export_file = None +    trace_export_dir = None      xv_method = None      xv_count = 10 @@ -227,6 +244,7 @@ if __name__ == '__main__':          optspec = (              'plot-unparam= plot-param= param-info show-models= show-quality= '              'ignored-trace-indexes= discard-outliers= function-override= ' +            'export-traces= '              'filter-param= '              'cross-validate= '              'with-safe-functions hwmodel= export-energymodel=' @@ -275,10 +293,26 @@ if __name__ == '__main__':          print(err)          sys.exit(2) -    raw_data = RawData(args) +    raw_data = RawData(args, with_traces=('export-traces' in opts))      preprocessed_data = raw_data.get_preprocessed_data() +    if 'export-traces' in opts: +        uw_per_sot = dict() +        for trace in preprocessed_data: +            for state_or_transition in trace['trace']: +                name = state_or_transition['name'] +                if name not in uw_per_sot: +                    uw_per_sot[name] = list() +                for elem in state_or_transition['offline']: +                    elem['uW'] = list(elem['uW']) +                uw_per_sot[name].append(state_or_transition) +        for name, data in uw_per_sot.items(): +            target = f"{opts['export-traces']}/{name}.json" +            print(f'exporting {target} ...') +            with open(target, 'w') as f: +                json.dump(data, f) +      if raw_data.preprocessing_stats['num_valid'] == 0:          print('No valid data available. Abort.')          sys.exit(2) diff --git a/lib/dfatool.py b/lib/dfatool.py index 177bd7b..8fb41a5 100644 --- a/lib/dfatool.py +++ b/lib/dfatool.py @@ -332,7 +332,7 @@ class CrossValidator:  def _preprocess_mimosa(measurement):      setup = measurement['setup'] -    mim = MIMOSA(float(setup['mimosa_voltage']), int(setup['mimosa_shunt'])) +    mim = MIMOSA(float(setup['mimosa_voltage']), int(setup['mimosa_shunt']), with_traces=measurement['with_traces'])      try:          charges, triggers = mim.load_data(measurement['content'])          trigidx = mim.trigger_edges(triggers) @@ -489,14 +489,16 @@ class RawData:      Expects a specific trace format and UART log output (as produced by the      dfatool benchmark generator). Loads data, prunes bogus measurements, and -    provides preprocessed data suitable for PTAModel. +    provides preprocessed data suitable for PTAModel. Results are cached on the +    file system, making subsequent loads near-instant.      """ -    def __init__(self, filenames): +    def __init__(self, filenames, with_traces=False):          """          Create a new RawData object. -        Each filename element corresponds to a measurement run. It must be a tar archive with the following contents: +        Each filename element corresponds to a measurement run. +        It must be a tar archive with the following contents:          Version 0: @@ -540,8 +542,12 @@ class RawData:            `.opt.configs`: ....          * EnergyTrace log files (`*.etlog`) as specified in `.opt.files` +        If a cached result for a file is available, it is loaded and the file +        is not preprocessed, unless `with_traces` is set. +          tbd          """ +        self.with_traces = with_traces          self.filenames = filenames.copy()          self.traces_by_fileno = []          self.setup_by_fileno = [] @@ -562,7 +568,8 @@ class RawData:                      break          self.set_cache_file() -        self.load_cache() +        if not with_traces: +            self.load_cache()      def set_cache_file(self):          cache_key = hashlib.sha256('!'.join(self.filenames).encode()).hexdigest() @@ -580,6 +587,8 @@ class RawData:                  self.preprocessed = True      def save_cache(self): +        if self.with_traces: +            return          try:              os.mkdir(self.cache_dir)          except FileExistsError: @@ -920,6 +929,7 @@ class RawData:      def get_preprocessed_data(self, verbose=True):          """          Return a list of DFA traces annotated with energy, timing, and parameter data. +        The list is cached on disk, unless the constructor was called with `with_traces` set.          Each DFA trace contains the following elements:           * `id`: Numeric ID, starting with 1 @@ -1000,6 +1010,7 @@ class RawData:                                  'fileno': i,                                  'info': member,                                  'setup': self.setup_by_fileno[i], +                                'with_traces': self.with_traces,                              })              elif version == 1: @@ -1049,6 +1060,7 @@ class RawData:                                  'setup': self.setup_by_fileno[j],                                  'repeat_id': repeat_id,                                  'expected_trace': ptalog['traces'][j], +                                'with_traces': self.with_traces,                              })                  self.filenames = new_filenames @@ -2507,7 +2519,7 @@ class MIMOSA:      Resulting data is a list of state/transition/state/transition/... measurements.      """ -    def __init__(self, voltage: float, shunt: int, verbose=True): +    def __init__(self, voltage: float, shunt: int, verbose=True, with_traces=False):          """          Initialize MIMOSA loader for a specific voltage and shunt setting. @@ -2518,6 +2530,7 @@ class MIMOSA:          self.voltage = voltage          self.shunt = shunt          self.verbose = verbose +        self.with_traces = with_traces          self.r1 = 984  # "1k"          self.r2 = 99013  # "100k"          self.errors = list() @@ -2855,6 +2868,9 @@ class MIMOSA:                  'us': (idx - previdx) * 10,              } +            if self.with_traces: +                data['uW'] = range_ua * self.voltage +              if 'states' in substates:                  data['substates'] = substates                  ssum = np.sum(list(map(lambda x: x['duration'], substates['states']))) | 
