Skip to content
Snippets Groups Projects
Select Git revision
  • 4c2035d55e0a5c013677a8e83193e37d51000793
  • passt default
  • master
  • pu
  • todo
  • next
  • maint
  • v2.8.0-rc1
  • v2.8.0-rc0
  • v2.7.2
  • v2.7.1
  • v2.7.0
  • v2.6.5
  • v2.7.0-rc3
  • v2.7.0-rc2
  • v2.7.0-rc1
  • v2.7.0-rc0
  • v2.6.4
  • v2.6.3
  • v2.6.2
  • v2.6.1
  • v2.3.10
  • v2.5.4
  • v2.4.10
  • v2.6.0
  • v2.6.0-rc3
  • v2.5.3
27 results

git-gui

Blame
  • bench_input_energy.py 7.57 KiB
    #! /usr/bin/env python3
    
    import argparse as ap
    import os
    import sys
    import time
    import numpy
    import h5py
    import numpy as np
    import pexpect
    import pylab
    from time import sleep
    from struct import unpack
    from multiprocessing import Process, Pipe
    
    from Helper import Benchmark, Log, Result, GenE, MSO
    import Backends
    from Backends import Patmos, XMC4500
    
    def get_argparser() -> ap.ArgumentParser:
        parser = ap.ArgumentParser(prog=sys.argv[0] + " bench_input_energy",
            formatter_class=ap.ArgumentDefaultsHelpFormatter,
            description='Subfunction bench_input_energy: ' + desc)
    
        parser.add_argument('--samples',  metavar='count',    type=int, default=1000,        help='Number of samples used for benchmarking')
        parser.add_argument('--entry',    metavar='function', type=str, default='gene_main', help='Entry used for analysis/simulation')
        parser.add_argument('--init',     metavar='function', type=str, default='gene_init', help='Function used to initialize data for entry')
        parser.add_argument('--opt',      metavar='opt',      type=int, default=0,           help='Optimization level')
        parser.add_argument('--cache',    metavar='file',     type=str, default=None,        help='Cache file to be used for faster operation')
    
        parser.add_argument('-sr, --shunt-resistance', metavar='ohms', dest='sr', type=float, default=None, required=True, help='Shunt resistance [ohms]')
    
        GenE.add_arguments(parser)
    
        global BACKEND, SUBBACKEND
        BACKEND, SUBBACKEND = Backends.add_arguments(parser)
    
        return parser
    
    
    def decode_data(data_per_channel):
        times = []
        volts = {}
    
        for ch in data_per_channel.keys():
            data, ymult, yzero, yoff, xincr = data_per_channel[ch]
    
            headerlen = 2 + int(data[1])
            header    = data[:headerlen]
            ADC_wave  = data[headerlen:-1]
    
            no_samples = len(ADC_wave) / 2
            fmt_string = '>%dH' % no_samples
    
            Log.debug("fmt_string: {}".format(fmt_string))
            ADC_wave = np.array(unpack(fmt_string, ADC_wave))
    
            volts[ch] = (ADC_wave - yoff) * ymult + yzero
            times.append( np.arange(0, xincr * len(volts[ch]), xincr) )
    
            Log.debug("Got {} entries for {}".format(len(volts[ch]), ch))
    
        return (times[0], volts)
    
    
    def find_benchmark(times, volts, idx):
        stime = times[1] - times[0]
        Log.debug("Time between 2 Samples: {:.2f}ns -> {:.2f}MHz".format( 1000*1000*1000*stime, 1/stime/1000/1000 ))
    
        # find start of measurement (based on idx)
        LIMIT = 1.0
    
        tail = 50 * 10**-6 # 50us
        tail_samples = int(tail / stime)
        Log.debug("tail_samples = {}".format(tail_samples))
    
        start = 0
        end   = 0
    
        try:
            start = next(i for i, U in enumerate(volts[idx]) if U > LIMIT)
        except StopIteration:
            Log.fail("Failed to find start")
    
        try:
            end   = next(start + i for i, U in enumerate(volts[idx][start:]) if U < LIMIT)
        except StopIteration:
            Log.fail("Failed to find end in {} samples".format(len(volts[idx])))
            pylab.plot(times, volts["CH1"], '-r')
            pylab.plot(times, volts[idx], '-y')
            pylab.axvline(x=times[start])
            pylab.show()
    
    
        assert start < len(times)
        assert end   < len(times)
        assert start < end
    
        end += tail_samples
        Log.debug("Measurement in range [{}, {}], {} samples ({:.2f}% of total measurement length: {} samples)".format(start, end, end - start, 100 * (end - start) / len(times), len(times)))
    
        times = times[start:end]
        for ch, curr in volts.items():
            volts[ch] = curr[start:end]
    
        return (times, volts)
    
    
    def hdf_store(hdf, dset_name, times, volts):
        for curr in volts.values():
            assert len(curr) == len(times)
    
        dset = hdf.create_dataset(dset_name, (1 + len(volts), len(times)), dtype='float64', compression="gzip", compression_opts=9)
    
        dset[0] = times
        for i, curr in  enumerate(volts.values()):
            dset[1 + i] = curr
    
        return dset
    
    
    def calculate_consumption(times, Ushunt, Usys, Rshunt):
        stime = times[1] - times[0]
    
        I = Ushunt / Rshunt
    
        P = np.multiply(I, Usys)     # [VA] = [W]
        Pint = np.trapz(P, dx=stime) # [Ws] = [J]
    
        us = 1000*1000*(times[-1] - times[0]) # us
    
        return (Pint, us)
    
    
    def gdb_start(binary, remote):
        Log.debug("Spawning gdb...")
        fout = open('gdb.log','wb')
        gdb = pexpect.spawn("arm-none-eabi-gdb --se='{}' -ex 'target remote {}'".format(binary, remote), logfile=fout)
    
        gdb.expect("Remote debugging using .*\r\n")
        gdb.expect("0x.* in .*\r\n")
        gdb.expect("\(gdb\) ")
    
        Log.debug("Done spawning gdb.")
    
        #gdb.sendline("c")
        #gdb.expect("(gdb) ")
    
        return gdb
    
    def gdb_measure(gdb, inp):
        gdb.sendline("set variable input = {}".format(inp))
        gdb.expect("\(gdb\) ")
        gdb.sendline("si")
        gdb.expect("\(gdb\) ")
        gdb.sendline("c")
        gdb.expect("\(gdb\) ")
        gdb.sendline("p result")
        gdb.expect(r'\$[0-9]+ = ([0-9]+)')
        cycles = int(gdb.match.group(1).decode('utf-8'))
        sleep(0.1)
        return cycles
    
    
    def analyze_thread(odir, pipe, hdf, Rshunt):
        while True:
            measurement = pipe.recv()
            if measurement is None:
                break
    
            inp, cycles, data_per_channel = measurement
    
            times, volts = decode_data(data_per_channel)
            times, volts = find_benchmark(times, volts, "CH4")
            dset = hdf_store(hdf, "input_{}".format(inp), times, volts)
    
            Pint, us = calculate_consumption(times, volts["CH1"], volts["CH2"] - volts["CH1"], Rshunt)
            W  = Pint / (times[-1] - times[0])
            mJ = Pint * 1000 # mJ
    
            dset.attrs["power_W"]        = W
            dset.attrs["consumption_mJ"] = mJ
            dset.attrs["etime_us"]       = us
            dset.attrs["cycles"]         = cycles
    
            Log.info("Input {}: {:.2f}mJ in {:.2f}us ({}cy, {:.2f}mW)".format(inp, mJ, us, cycles, W * 1000))
            hdf.flush()
    
    
    def run(args):
        Log.info("Using backend " + BACKEND.NAME)
        config = BACKEND.get_config(args)
    
        gene_rev, gene_mod = GenE.revision()
    
        subcmd = os.path.splitext(os.path.basename(__file__))[0]
        odir = Result.get_dirname(subcmd + "_" + args.backend, args.budget, args.seed, args.bits, args.samples, gene_rev, args.suite, args.opt, config.eic, args.ofactor)
    
        if not os.path.exists(odir):
            os.makedirs(odir)
    
        Log.set_logfile(odir + '/aladdin.log')
    
        if gene_mod:
            Log.warn("Your GenE repo is modified!")
    
        rm, devs = MSO.find_devices()
        mso = MSO.MSO(rm, devs[0])
    
        # TODO
        gdb = gdb_start("/home/chris/i4/palladium/results/bench_input_xmc4500.plain_budget20000_seed1431655765_bits32_samples10000_opt0_ofactor25_revff42678d10acef590c184a5fa68c5b9b4ded0e63+_suitedevice/bench.axf", ":2331")
        inputs = Benchmark.get_input(args.bits, args.seed, args.samples - 1, 1, crafted = False)[0]
    
        hdf = h5py.File('{}/measurement.hdf5'.format(odir), 'w')
        hdf.attrs["rshunt_ohm"] = args.sr
    
        # start analysis process
        pipe_parent, pipe_child = Pipe()
        p = Process(target=analyze_thread, args=(odir, pipe_child, hdf, args.sr))
        p.start()
    
        try:
            for i, inp in enumerate(inputs):
                Log.info("({}/{}) Starting measurement for input {}".format(i, len(inputs), inp))
                mso.start()
    
                cycles = gdb_measure(gdb, inp)
                while not mso.is_triggered():
                    Log.warn(" - Repeating Measurement for {}".format(inp))
                    cycles = gdb_measure(gdb, inp)
    
                data_per_channel = mso.get_values()
                pipe_parent.send( (inp, cycles, data_per_channel) )
        except Exception as e:
            Log.fail("Caught exception during sampling:\n{}".format(e))
    
        pipe_parent.send(None)
        p.join()
    
        hdf.close()
    
    desc = "Determine energy consumption via shunt-based measurement"