def evaInit(args): # {{{ '''Read in EVC and VCD to create result directory like ./foo.eva/ ''' assert paths._INITIALIZED evc = loadEvc(args.info) checkEvc(evc) mkDirP(paths.outdir) cfg = initCfg(evc["config"], args.info) evcx = expandEvc(evc, cfg, args.info) # Fully read in and copy then clean input data. verb("Cleaning input VCD... ", end='') vcdClean(args.input, paths.fname_cln) verb("Done") # VCD-to-VCD: extract, interpolate, clean meaVcd(paths.fname_cln, evcx, cfg, args.info) #vcdClean(paths.fname_mea) # Reduce size of varIds vcdInfo = evaVcdInfo(paths.fname_mea) saveToml(vcdInfo, paths.fname_meainfo) # VCD-to-binaries meaDbFromVcd() # Identicons createIdenticons(vcdInfo) return 0
def loadEvc(infoFlag): # {{{ '''Read EVC file with only basic checking. ''' def infoEvc(evc): # {{{ '''Print information about EVC. ''' if not infoFlag: return for k, v in evc.get("config", {}).items(): msg = "%s = %s" % (k, v) info(msg, prefix="INFO:EVC:CONFIG: ") for m in evc.get("signal", []): msg = "%s <-- %s" % (m["name"], m["hook"]) info(msg, prefix="INFO:EVC:SIGNAL: ") # }}} def infoEvc verb("Loading EVC... ", end='') try: # NOTE: loadToml() appends ".toml" to fname. assert paths._INITIALIZED evc = toml.load(paths.fname_evc) except toml.decoder.TomlDecodeError as e: raise EVCError_TomlLoad(e) verb("Done") infoEvc(evc) return evc
def actionReset(device, _args): # {{{ verb("Reseting BytePipe FSM...", end='') bpReset(device) verb("Done") return # No return value
def checkEvcxWithVcd(evcx, vcd, infoFlag): # {{{ '''Check hooks exist in VCD. ''' def infoEvcxWithVcd(evcx): # {{{ '''Print information about EVCX. ''' if not infoFlag: return for nm, v in evcx.items(): msg = "%s %d %s <-- %s %s %s" % \ (v["type"], v["idx"], nm, v["hookVarId"], v["hookType"], v["hookBit"]) info(msg, prefix="INFO:EVCX/VCD: ") # }}} def infoEvcxWithVcd verb("Checking EVCX with VCD... ", end='') plainVarNames = [re.sub(r'\[.*$', '', x) for x in vcd.varNames] evcxx_ = {} for nm, v in evcx.items(): hk = v["hook"] # Plain hook in VCD, which doesn't have vector select. # NOTE: Only limited support. No vectored modules, no multidim. # hk -> hkPlain # module:TOP.foo.bar[3] -> module:TOP.foo.bar hkPlain = re.sub(r'\[.*$', '', hk) if hkPlain not in plainVarNames: raise EVCError_SignalName(hk, hkPlain) hkVarId = vcd.mapVarNameNovectorToVarId[hkPlain] hkType = vcd.mapVarIdToType[hkVarId] hkSize = vcd.mapVarIdToSize[hkVarId] # Check width of VCD signal contains numbered bit. if hkType in oneBitTypes and hk.endswith(']'): hkBit = int(hk[hk.rfind('['):].strip('[]'), 10) if hkBit >= hkSize: raise EVCError_SignalName(hk, hkBit) else: hkBit = None evcxx_[nm] = v evcxx_[nm].update({ "hookVarId": hkVarId, "hookType": hkType, "hookBit": hkBit, }) verb("Done") infoEvcxWithVcd(evcxx_) return evcxx_
def meaDbFromVcd(): # {{{ '''Apply post-processing steps to stage0. Extract changes from signals.vcd into fast-to-read binary form. signals.vcd has only 2 datatypes: bit, real Assume initial state for all measurements is 0.: All timestamps are 32b non-negative integers. Binary format for bit is different from that of real. bit: Ordered sequence of timestamps. real: Ordered sequence of (timestamp, value) pairs. All values are 32b IEEE754 floats, OR 32b(zext) fx. ''' verb("Creating binary database from VCD... ", end='') mkDirP(paths.dname_mea) with VcdReader(paths.fname_mea) as vcdi: # Stage0 file has bijective map between varId and varName by # construction, so take first (only) name for convenience. _mapVarIdToName = {varId: detypeVarName(nms[0]) \ for varId,nms in vcdi.mapVarIdToNames.items()} mapVarIdToName = {varId: nm \ for varId,nm in _mapVarIdToName.items() \ if isUnitIntervalMeasure(nm)} fds = {nm: open(joinP(paths.dname_mea, nm), 'wb') \ for varId,nm in mapVarIdToName.items()} prevValues = {varId: 0 for varId in mapVarIdToName.keys()} for newTime, changedVarIds, newValues in vcdi.timechunks: for varId, newValue in zip(changedVarIds, newValues): nm = mapVarIdToName.get(varId, None) if nm is None: continue tp, _, structFmt = meaDtype(nm) v, p = tp(newValue), prevValues[varId] if v != p: _packArgs = [newTime, v] if tp is float else [newTime] bs = struct.pack(structFmt, *_packArgs) fds[nm].write(bs) prevValues[varId] = v for _, fd in fds.items(): fd.close() verb("Done") return
def actionPut(device, args): # {{{ addr = abs(int(args.addr)) % 128 nBytes = abs(int(args.nBytes)) fname = args.file verb("Writing %dB @%d from %s..." % (nBytes, addr, fname), end='') with open(fname, 'rb') as fd: bpWriteAddr(device, addr, nBytes, fd.read(nBytes), args.record_time) verb("Done") return # No return value
def actionGet(device, args): # {{{ addr = abs(int(args.addr)) % 128 nBytes = abs(int(args.nBytes)) fname = args.file verb("Reading %dB @%d to %s..." % (nBytes, addr, fname), end='') with open(fname, 'wb') as fd: fd.write(bytes(bpReadAddr(device, addr, nBytes, args.record_time))) verb("Done") return # No return value
def actionDump(device, _args): # {{{ rd: Callable = functools.partial(bpReadSequential, device) wr: Callable = functools.partial(bpWriteSequential, device) mem: Callable = bpAddrValuesToMem verb("Reading all register locations...", end='') init0: BpMem = mem(rd(list(range(128)))) verb("Done") bpPrintMem("Dump", init0) return # No return value
def loadEvcx(): # {{{ '''Return dict of measurement names to VCD hook names. ''' assert paths._INITIALIZED verb("Loading EVCX... ", end='') evcx = toml.load(paths.fname_evcx) verb("Done") return evcx
def actionPeek(device, args): # {{{ rd: Callable = functools.partial(bpReadSequential, device) addr = abs(int(args.addr)) % 128 verb("Peeking @%d..." % addr, end='') addrValue: BpAddrValue = rd([addr])[0] rdAddr, value = addrValue assert addr == rdAddr, (addr, rdAddr) verb("Done") print("%02x" % value) return # No return value
def actionPoke(device, args): # {{{ wr: Callable = functools.partial(bpWriteSequential, device) addr = abs(int(args.addr)) % 128 value = abs(int(args.data)) % 256 assert 0 != addr, "Writing @0 reserved for burst." verb("Poking %d@%d..." % (value, addr), end='') addrValue: BpAddrValue = wr([(addr, value)])[0] rdAddr, value = addrValue assert addr == rdAddr, (addr, rdAddr) verb("Done") return # No return value
def loadCfg(): # {{{ '''Return config extracted from EVC and VCD. CFG is assumed to be sane, written by initCfg(). ''' assert paths._INITIALIZED verb("Loading CFG... ", end='') cfg = Bunch() cfg.__dict__.update(toml.load(paths.fname_cfg)) verb("Done") return cfg
def main(args) -> int: # {{{ ''' 1. Upload bitfile to TinyFPGA-BX (optional). 2. Open connection to device. 3. Discover writable bits. ''' locale.setlocale(locale.LC_ALL, '') if args.prog: bitfile = getBitfilePath(args.bitfile) verb("Uploading bitfile %s ..." % bitfile, end='') assert 0 == uploadBitfile(bitfile) verb("Done") # Allow OS time to enumerate USB before looking for device. nAttempts = 10 waitTime = 1 # seconds verb("Waiting up to %0.01fs..." % (nAttempts * waitTime), end='') maybeDevicePath_: Optional[str] = None for _ in range(nAttempts): time.sleep(waitTime) try: maybeDevicePath_ = getDevicePath(args.device) break except OSError: pass if maybeDevicePath_ is None: return 1 else: devicePath = maybeDevicePath_ verb("Done") else: devicePath = getDevicePath(args.device) # Keep lock on device to prevent other processes from accidentally messing # with the state machine. verb("Connecting to device %s" % devicePath) with serial.Serial(devicePath, timeout=1.0, write_timeout=1.0) as device: actions[args.action](device, args) return 0
def createIdenticons(vcdInfo): # {{{ '''Produce an identicon for each signal in VCD. ''' verb("Creating identicons... ", end='') mkDirP(paths.dname_identicon) measureNames = vcdInfo["unitIntervalVarNames"] for nm in measureNames: measureType, siblingType, baseName = measureNameParts(nm) svgStr = identiconSpriteSvg(baseName, fill="darkgray") fname = joinP(paths.dname_identicon, baseName + ".svg") with open(fname, 'w') as fd: fd.write(svgStr) verb("Done") return
def initCfg(evcConfig, infoFlag): # {{{ '''Fill in and save CFG. ''' def infoCfg(cfg): # {{{ '''Print information about CFG. ''' if not infoFlag: return for k, v in cfg.__dict__.items(): msg = "%s = %s" % (k, v) info(msg, prefix="INFO:CFG: ") # }}} def infoCfg verb("Initializing CFG... ", end='') cfg = Bunch() cfg.__dict__.update(loadToml(appPaths.configDefault)) cfg.__dict__.update(evcConfig) verb("Saving... ", end='') saveToml(cfg.__dict__, paths.fname_cfg) verb("Done") infoCfg(cfg) return cfg
def __enter__(self): exception_ = None for i in range(self.connectNAttempt): if 0 != i: time.sleep(self.connectTimeout) verb("Attempt %d/%d connecting SerialDevice %s ... " % \ (i+1, self.connectNAttempt, self.path), end='') try: self.port = serial.Serial(self.path, timeout=self.rdTimeout, write_timeout=self.wrTimeout, exclusive=self.exclusive) self.port.open() except Exception as e: exception_ = e if hasattr(self, "port") and self.port.is_open: verb("Success") break else: verb("Failure") if not (hasattr(self, "port") and self.port.is_open): raise exception_ return self.port
def info(src, showTime): # {{{ fnamei = fnameAppendExt(src, "vcd") verb("Opening file and reading header...", end='') with VcdReader(fnamei) as vd: verb("DONE") print("__version__", __version__) print("filename", vd.filename) vs = zip(vd.varIds, vd.varNames, vd.varSizes, vd.varTypes) print("<id> <name> <size> <type>:") for varId, varName, varSize, varType in vs: print(" ", varId, varName, varSize, varType) if showTime: print("<timechunkNumber> <#changes> <time>:") for i, tc in enumerate(vd.timechunks, start=1): newTime, changedVarIds, newValues = tc print(" ", i, len(changedVarIds), newTime) return 0
def runHttpDaemon(args, cfg): # {{{ '''Run local HTTP/HTML daemon serving data visualization pages on request. ''' verb("Starting HTTPD on TCP port %d..." % args.httpd_port, end='') httpd = EvaHTTPServer(('', args.httpd_port), EvaHTTPRequestHandler) verb("Running...") try: tm_start = time.time() httpd.serve_forever(args, cfg) except KeyboardInterrupt: tm_stop = time.time() verb("Stopped HTTPD server [%s]" % \ tmdiff(tm_stop - tm_start)) return
def main(args) -> int: # {{{ ''' 1. Open connection to device. 2. Read config RO registers. 3. Write config RW registers if --init-* is used. 4. Read/check config RW registers. 5. Record data with burst reads from pktfifo. ''' locale.setlocale(locale.LC_ALL, '') devicePath = getDevicePath(args.device) # Keep lock on device to prevent other processes from accidentally messing # with the state machine. with SerialDevice(devicePath, exclusive=True, rdTimeout=args.timeout, wrTimeout=args.timeout, connectNAttempt=50, connectTimeout=0.2) as device: rdBytePipe: Callable = functools.partial(bpReadSequential, device) wrBytePipe: Callable = functools.partial(bpWriteSequential, device) rd: Callable = functools.partial(hwReadRegs, rdBytePipe) wr: Callable = functools.partial(hwWriteRegs, wrBytePipe) verb("Detecting number of engines...", end='') nEngine: int = detectNEngine(rd) assert args.engine < nEngine, "--engine must be less than %d" % nEngine engineNum: int = args.engine verb("Done") verb("Reading RO registers...", end='') hwRegsRO: Dict[HwReg, Any] = rd(engineNum, ( HwReg.PktfifoDepth, HwReg.MaxWindowLengthExp, HwReg.WindowPrecision, HwReg.MaxSamplePeriodExp, HwReg.MaxSampleJitterExp, )) verb("Done") # Gather registers required for initialization. initRegsRW: Dict[HwReg, Any] = {} if args.init_windowLengthExp is not None: initRegsRW[HwReg.WindowLengthExp] = args.init_windowLengthExp if args.init_windowShape is not None: initRegsRW[HwReg.WindowShape] = args.init_windowShape if args.init_samplePeriodExp is not None: initRegsRW[HwReg.SamplePeriodExp] = args.init_samplePeriodExp if args.init_sampleJitterExp is not None: initRegsRW[HwReg.SampleJitterExp] = args.init_sampleJitterExp if args.init_pwmSelect is not None: initRegsRW[HwReg.PwmSelect] = args.init_pwmSelect if args.init_xSelect is not None: initRegsRW[HwReg.XSelect] = args.init_xSelect if args.init_ySelect is not None: initRegsRW[HwReg.YSelect] = args.init_ySelect if 0 < len(initRegsRW): verb("Initializing RW registers...", end='') wr(engineNum, initRegsRW) verb("Checking...", end='') hwRegsRW: Dict[HwReg, Any] = rd(engineNum, initRegsRW.keys()) assert all(initRegsRW[k] == v for k, v in hwRegsRW.items()), hwRegsRW verb("Done") if args.prng_seed is not None: seed: int = abs(args.prng_seed) verb("Initializing PRNG (xoshiro128+ %s)..." % hex(seed), end='') bpWriteAddr(device, HwReg.PrngSeed.value, 16, [0] * 16) bpWriteAddr(device, HwReg.PrngSeed.value, 4, [ (seed >> 3 * 8) & 0xff, (seed >> 2 * 8) & 0xff, (seed >> 1 * 8) & 0xff, (seed >> 0 * 8) & 0xff, ]) verb("Done") verb("Reading RW registers...", end='') hwRegsRW: Dict[HwReg, Any] = rd(engineNum, [ HwReg.WindowLengthExp, HwReg.WindowShape, HwReg.SamplePeriodExp, HwReg.SampleJitterExp, HwReg.PwmSelect, HwReg.XSelect, HwReg.YSelect, ]) verb("Done") try: verb("Recording...") nLinesWritten, wrSuccess = \ wrLines(args.output, pktLines(device, args.nWindows, engineNum, {**hwRegsRO, **hwRegsRW})) verb("Recording %s" % ("complete" if wrSuccess else "FAILURE nLinesWritten=%d" % nLinesWritten)) except KeyboardInterrupt: verb("KeyboardInterrupt. Exiting.") return 0
def main(args) -> int: # {{{ ''' 1. Upload bitfile to FPGA. 2. Open connection to device. 3. Read config RO registers. 4. Write config RW registers. 5. Read/check config RW registers. 6. Initialize TUI 7. TUI output loop: 1. Sleep for refresh period. 2. Read results RO registers. 2. Update results section. 8. TUI config loop: 1. Wait for <Enter> 2. Write config RW registers. 3. Read config RW registers, check they're what was written. 9. TUI input loop: 1. Wait for keypress. 2. Handle keypress by moving highlighted line or changing value. ''' locale.setlocale(locale.LC_ALL, '') if args.prog: bitfile = getBitfilePath(args.bitfile) verb("Uploading bitfile %s ..." % bitfile, end='') assert 0 == uploadBitfile(bitfile) verb("Done") # Allow OS time to enumerate USB before looking for device. nAttempts = 10 waitTime = 1 # seconds verb("Waiting up to %0.01fs..." % (nAttempts * waitTime), end='') maybeDevicePath_:Optional[str] = None for _ in range(nAttempts): time.sleep(waitTime) try: maybeDevicePath_ = getDevicePath(args.device) break except OSError: pass if maybeDevicePath_ is None: return 1 else: devicePath = maybeDevicePath_ verb("Done") else: devicePath = getDevicePath(args.device) # Keep lock on device to prevent other processes from accidentally messing # with the state machine. with SerialDevice(devicePath, exclusive=True, rdTimeout=args.timeout, wrTimeout=args.timeout, connectNAttempt=50, connectTimeout=0.2) as device: rdBytePipe:Callable = functools.partial(bpReadSequential, device) wrBytePipe:Callable = functools.partial(bpWriteSequential, device) rd:Callable = functools.partial(hwReadRegs, rdBytePipe) wr:Callable = functools.partial(hwWriteRegs, wrBytePipe) verb("Detecting number of engines...", end='') global nEngine_ nEngine_ = detectNEngine(rd) engineNum = args.engine if (args.engine < nEngine_) else 0 verb("Done") verb("Reading engine %d RO registers..." % engineNum, end='') hwRegsRO:Dict[HwReg, Any] = rd(engineNum, ( HwReg.PktfifoDepth, HwReg.MaxWindowLengthExp, HwReg.WindowPrecision, HwReg.MaxSamplePeriodExp, HwReg.MaxSampleJitterExp, )) verb("Done") # Fill in missing values of parameter domains. global mapTuiRegToDomain_ mapTuiRegToDomain_.update({ TuiReg.Engine: mapTuiRegToDomain_[TuiReg.Engine] % nEngine_, TuiReg.WindowLength: mapTuiRegToDomain_[TuiReg.WindowLength] % hwRegsRO[HwReg.MaxWindowLengthExp], TuiReg.SampleRate: mapTuiRegToDomain_[TuiReg.SampleRate] % hwRegsRO[HwReg.MaxSamplePeriodExp], TuiReg.SampleJitter: mapTuiRegToDomain_[TuiReg.SampleJitter] % (2**hwRegsRO[HwReg.MaxSampleJitterExp], hwRegsRO[HwReg.MaxSampleJitterExp]), }) # Gather registers required for TUI. initRegsRW:Dict[HwReg, Any] = { HwReg.WindowLengthExp: args.init_windowLengthExp, HwReg.WindowShape: args.init_windowShape, HwReg.SamplePeriodExp: args.init_samplePeriodExp, HwReg.SampleJitterExp: args.init_sampleJitterExp, HwReg.PwmSelect: args.init_pwmSelect, HwReg.XSelect: args.init_xSelect, HwReg.YSelect: args.init_ySelect, } if args.no_init: verb("Reading RW registers...", end='') hwRegsRW:Dict[HwReg, Any] = rd(engineNum, initRegsRW.keys()) verb("Done") else: verb("Initializing RW registers...", end='') wr(0, initRegsRW) verb("Checking...", end='') hwRegsRW:Dict[HwReg, Any] = rd(engineNum, initRegsRW.keys()) assert all(initRegsRW[k] == v for k,v in hwRegsRW.items()), hwRegsRW verb("Done") seed:int = abs(args.prng_seed) verb("Initializing PRNG (xoshiro128+ %s)..." % hex(seed), end='') bpWriteAddr(device, HwReg.PrngSeed.value, 16, [0]*16) bpWriteAddr(device, HwReg.PrngSeed.value, 4, [ (seed >> 3*8) & 0xff, (seed >> 2*8) & 0xff, (seed >> 1*8) & 0xff, (seed >> 0*8) & 0xff, ]) verb("Done") try: verb("Starting TUI (curses)...") curses.wrapper(tui, device.name, rd, wr, engineNum, {**hwRegsRO, **hwRegsRW}) verb("TUI Done") except KeyboardInterrupt: verb("KeyboardInterrupt. Exiting.") return 0
def main(args): # {{{ ''' ''' mkDirP(args.output_dir) fdir = args.output_dir + sep f = partial(cs0, gamma=args.gamma) verb("cs0...", end='') makePng(f, fdir + "cs0.png") verb("DONE") verb("cs0_invOrigin...", end='') makePng(partial(f, invOrigin=True), fdir + "cs0_invOrigin.png") verb("DONE") verb("cs0_magB...", end='') makePng(partial(f, gamma=args.gamma, magColor='b'), fdir + "cs0_magB.png") verb("DONE") verb("cs0_magG...", end='') makePng(partial(f, gamma=args.gamma, magColor='g'), fdir + "cs0_magG.png") verb("DONE") return 0
def actionBits(device, _args): # {{{ rd: Callable = functools.partial(bpReadSequential, device) wr: Callable = functools.partial(bpWriteSequential, device) mem: Callable = bpAddrValuesToMem verb("Writing ones to all register locations...", end='') _ = mem(wr(list((addr, 0xff) for addr in range(1, 128)))) verb("Done") verb("Writing zeros to all register locations...", end='') ones: BpMem = mem(wr(list((addr, 0x00) for addr in range(1, 128)))) verb("Done") verb("Reading all register locations...", end='') zeros: BpMem = mem(rd(list(range(1, 128)))) verb("Checking writable bits...", end='') symdiff: BpMem = cast(BpMem, tuple(o ^ z for o, z in zip(ones, zeros))) verb("Done") bpPrintMem("Writable bits", symdiff) return # No return value
def meaVcd(instream, evcx, cfg, infoFlag): # {{{ '''Filter input data to sanitized VCD (signals.vcd). Extract measurements of interest, at times of interest. Perform interpolation for normal measurements. Time becomes a straightforward sample index. Hierarchy shows raw measures and refl/rise/fall. NOTE: This initial extraction to filter/clean the dataset is probably the most complex part of eva! ''' assert paths._INITIALIZED def twoStateBool(v, hookbit): # {{{ if isinstance(v, int): ret = (0 != v) else: assert isinstance(v, str), (type(v), v) intValue = int(v, 2) if hookBit is None: ret = (intValue != 0) else: ret = ((intValue & 1 << hookBit) != 0) assert isinstance(ret, bool), type(ret) return ret # }}} def twoStateBool def vcdoVarlist(evcx): # {{{ '''Create varlist for vcdo.wrHeader from EVCX. structure: [ (<name:str>, <width:int>, <type:str>), ... ] example: [ ("aName", 1, "bit"), ... ] All signals are of either "bit" or "real" VCD type. ''' measuresEvent = (nm for nm, v in evcx.items() if "event" == v["type"]) measuresBstate = (nm for nm, v in evcx.items() if "bstate" == v["type"]) measuresThreshold = (nm for nm, v in evcx.items() if "threshold" == v["type"]) measuresNormal = (nm for nm, v in evcx.items() if "normal" == v["type"]) # Sibling measurements denoted by prefix. prefixesEvent = ("orig", ) prefixesBstate = ( "orig", "refl", "rise", "fall", ) prefixesNormal = ( "raw", "smooth", "orig", ) prefixesThreshold = ( "orig", "refl", "rise", "fall", ) namesEvent = \ ('.'.join(("event", pfx, nm)) \ for nm in sorted(measuresEvent) for pfx in prefixesEvent) namesBstate = \ ('.'.join(("bstate", pfx, nm)) \ for nm in sorted(measuresBstate) for pfx in prefixesBstate) namesThreshold = \ ('.'.join(("threshold", pfx, nm)) \ for nm in sorted(measuresThreshold) for pfx in prefixesThreshold) namesNormal = \ ('.'.join(("normal", pfx, nm)) \ for nm in sorted(measuresNormal) for pfx in prefixesNormal) varlist = [(nm, 1, "bit") \ for nms in (namesEvent, namesBstate, namesThreshold,) \ for nm in nms] + \ [(nm, 64, "real") \ for nms in (namesNormal,) \ for nm in nms] return varlist # }}} def vcdoVarlist def interpolateNormal(iVarId, oTime, mea, mapVarIdToHistory_, nq_, bq_, newValue=None): # {{{ nm = mea["name"] geq = mea["geq"] leq = mea["leq"] prevIpolTime, prevIpolValues_ = mapVarIdToHistory_[iVarId] for t in range(prevIpolTime + 1, oTime): assert t < oTime, (t, oTime) zs = [prevIpolValues_[0]] + prevIpolValues_ prevIpolValues_ = zs[:-1] assert len(zs) == len(cfg.fir), zs smoothValue = dotp(zs, cfg.fir) clipnormValue = clipNorm(smoothValue, geq, leq) bq_.append((t, "normal.smooth." + nm, smoothValue)) bq_.append((t, "normal.orig." + nm, clipnormValue)) zs = [prevIpolValues_[0] if newValue is None else newValue ] + prevIpolValues_ mapVarIdToHistory_[iVarId] = (oTime, zs[:-1]) assert len(zs) == len(cfg.fir), zs smoothValue = dotp(zs, cfg.fir) clipnormValue = clipNorm(smoothValue, geq, leq) nq_.append(("normal.smooth." + nm, smoothValue)) nq_.append(("normal.orig." + nm, clipnormValue)) # }}} def interpolateNormal # NOTE: VCD input may come from STDIN ==> only read once. with VcdReader(instream) as vcdi, VcdWriter(paths.fname_mea) as vcdo: evcxx = checkEvcxWithVcd(evcx, vcdi, infoFlag) verb("Extracting measurements to VCD ... ", end='') evcxVarIds = tuple(sorted(list(set(v["hookVarId"] \ for nm,v in evcxx.items())))) meaSortKey = (lambda mea: mea["name"]) mapVarIdToMeasures = \ {varId: sorted([{"name": nm, "type": v["type"], "hookType": v["hookType"], "hookBit": v["hookBit"], "geq": v.get("geq"), "leq": v.get("leq")} \ for nm,v in evcxx.items() \ if varId == v["hookVarId"]], key=meaSortKey) \ for varId in evcxVarIds} # Initialize previous values to 0. # {varId: (time, value), ...} mapVarIdToPrev_ = {varId: (0, 0) for varId in evcxVarIds} # Initialise previous values for normals to 0 for filter history. # {varId: (time, values), ...} mapVarIdToHistory_ = \ {varId: (0, [0.0 for _ in cfg.fir[1:]]) \ for varId in evcxVarIds \ if "normal" in [mea["type"] for mea in mapVarIdToMeasures[varId]]} vcdo.wrHeader(vcdoVarlist(evcx), comment=' '.join( (vcdi.vcdComment, "<<< Extracted by evaInit >>>")), date=vcdi.vcdDate, version=vcdi.vcdVersion, timescale=' '.join(vcdi.vcdTimescale)) # Forward (future) queue of speculative changes which may need to be # interleaved with timechunks from vcdi. # E.g. event->bit conversion inferring 1 then 0 in consecutive times. # Or rise/fall on bstate. # [ (time, name, value) ... ] # Initialise all measurements to 0, except reflections to 1. fq_ = [(0, nm, int(re.match(r"^[^\.]*\.refl\.", nm) is not None)) \ for nm in vcdo.varNames] lastTime_ = 0 # Work through vcdi timechunks putting values into vcdo. for iTc in vcdi.timechunks: iTime, iChangedVarIds, iNewValues = iTc if iTime < cfg.timestart: continue if cfg.timestop != 0 and iTime > cfg.timestop: break tQuotient, tRemainder = divmod(iTime, cfg.timestep) if 0 != tRemainder: continue # Index in EVent Sample (EVS) array of this time. oTime = (iTime - cfg.timestart) // cfg.timestep assert isinstance(oTime, int), type(oTime) assert 0 <= oTime, (oTime, iTime, cfg.timestart, cfg.timestep) # Current (now) queue of changes which may contain duplicate # varnames with different values. # Only the last appended will be used. # No time field is necessary, all use current timechunk (oTime). # [ (name, value) ... ] nq_ = [(nm, v) for t, nm, v in fq_ if t == oTime] # Extract proper changes from fq_ and put into current queue. # Changes are proper if they are for time before this timechunk. # fq_ may still contain future speculative changes. bq_ = [(t, nm, v) for t, nm, v in fq_ if t < oTime] fq_ = [(t, nm, v) for t, nm, v in fq_ if t > oTime] for iVarId, iNewValue in zip(iChangedVarIds, iNewValues): # {{{ if not iVarId in evcxVarIds: continue assert isinstance(iNewValue, str) # VcdReader only gives str. newValueClean = iNewValue.replace('x', '0').replace('z', '1') prevTime, prevValue = mapVarIdToPrev_[iVarId] # Always clean. assert prevTime <= oTime, (prevTime, oTime) # Each iVarId may refer to multiple measurements, such as # vectored wires or wires used in multiple ways. for mea in mapVarIdToMeasures[iVarId]: nm = mea["name"] tp = mea["type"] hookType = mea["hookType"] hookBit = mea["hookBit"] if "event" == tp: # {{{ if "event" == hookType: # vcdi implies event only occurring at this time. nq_.append(("event.orig." + nm, 1)) # Speculatively reset to 0 in next time. fq_.append((oTime + 1, "event.orig." + nm, 0)) elif hookType in oneBitTypes: newValue = int(twoStateBool( newValueClean, hookBit)) nq_.append(("event.orig." + nm, newValue)) else: # Event measure only made from VCD event, or # 2-state (bit), 4-state types (wire, reg, logic) assert False, hookType # }}} event elif "bstate" == tp: # {{{ if hookType in oneBitTypes: newValue = twoStateBool(newValueClean, hookBit) if prevValue != newValue: nq_.append( ("bstate.orig." + nm, int(newValue))) nq_.append( ("bstate.refl." + nm, int(not newValue))) if newValue: nq_.append(("bstate.rise." + nm, 1)) fq_.append( (oTime + 1, "bstate.rise." + nm, 0)) else: nq_.append(("bstate.fall." + nm, 1)) fq_.append( (oTime + 1, "bstate.fall." + nm, 0)) else: pass # No change else: # Bstate measure only made from VCD 2-state (bit) or # 4-state types (wire, reg, logic, etc) assert False, hookType # }}} bstate elif "threshold" == tp: # {{{ if (hookType in oneBitTypes and hookBit is None) or \ (hookType in ["real", "integer"]): geq = mea["geq"] leq = mea["leq"] newValueFloat = float(newValueClean) \ if "real" == hookType else \ float(int(newValueClean, 2)) if geq is None: # Is measurement under threshold? newValue = (newValueFloat <= leq) elif leq is None: # Is measurement over threshold? newValue = (geq <= newValueFloat) elif geq < leq: # Is measurement inside interval? newValue = \ (newValueFloat <= leq and \ geq <= newValueFloat) else: # Is measurement outside interval? newValue = \ (newValueFloat <= leq or \ geq <= newValueFloat) if prevValue != newValue: nq_.append( ("threshold.orig." + nm, int(newValue))) nq_.append(("threshold.refl." + nm, int(not newValue))) if newValue: nq_.append(("threshold.rise." + nm, 1)) fq_.append( (oTime + 1, "threshold.rise." + nm, 0)) else: nq_.append(("threshold.fall." + nm, 1)) fq_.append( (oTime + 1, "threshold.fall." + nm, 0)) else: pass # No change else: # Threshold (number to bstate) measure only made # from VCD 2-state (bit) vector, 4-state (wire, reg, # logic) vector, integer, or real. assert False, (hookType, hookBit) # }}} threshold elif "normal" == tp: # {{{ if (hookType in oneBitTypes and hookBit is None) or \ (hookType in ["real", "integer"]): newValue = float(newValueClean) \ if "real" == hookType else \ float(int(newValueClean, 2)) # NOTE: normal.raw values are not necessarily # in [0, 1]; rather than (-inf, +inf). nq_.append(("normal.raw." + nm, newValue)) interpolateNormal(iVarId, oTime, mea, mapVarIdToHistory_, nq_, bq_, newValue=newValue) else: # Normal (real number) measure only made # from VCD 2-state (bit) vector, 4-state (wire, reg, # logic) vector, integer, or real. assert False, (hookType, hookBit) # }}} normal else: assert False, tp # Track previous value in vcdi try: mapVarIdToPrev_[iVarId] = oTime, newValue except UnboundLocalError: pass # }}} for iVarId,iNewValue in zip(iChangedVarIds, iNewValues) # Interpolate normal/smooth values up to current timechunk for # measurements which aren't sampled in this timechunk. for iVarId, (prevIpolTime, prevIpolValues_) in mapVarIdToHistory_.items(): if prevIpolTime == oTime: continue for mea in mapVarIdToMeasures[iVarId]: if "normal" != mea["type"] or 0 >= oTime: continue interpolateNormal(iVarId, oTime, mea, mapVarIdToHistory_, nq_, bq_, newValue=None) # Resolve conflicts from fq_/bq_. # Forward queue is speculative so a proper value from the current # timechunk will take precedence. # I.e. Always use the last appended change. bq_.sort() for fqTime, fqGroup in groupby(bq_, key=(lambda x: x[0])): fqChangedVars, fqNewValues = \ list(zip(*[(nm,v) for _,nm,v in fqGroup])) assert fqTime < oTime, (fqTime, oTime) vcdo.wrTimechunk((fqTime, fqChangedVars, fqNewValues)) # Flush out current (now) queue. if 0 < len(nq_): dedupVars = [] for nm, v in nq_: dedupVars = appendNonDuplicate(dedupVars, (nm, v), replace=True) nqChangedVars, nqNewValues = zip(*dedupVars) vcdo.wrTimechunk((oTime, nqChangedVars, nqNewValues)) lastTime_ = oTime # Events from oneBitTypes cannot be interpolated until after last # timechunk. fq_ += [(lastTime_+1, nm, 0) \ for nm in vcdo.varNames \ if re.match(r"^event.orig\.", nm) is not None] # Flush out forward queue after input VCD has been fully processed. # Time is set to only one greater than last time in input VCD, # regardless of what the time in each fq_ item says. if 0 < len(fq_): dedupVars = [] for _, nm, v in fq_: dedupVars = appendNonDuplicate(dedupVars, (nm, v), replace=True) nqChangedVars, nqNewValues = zip(*dedupVars) vcdo.wrTimechunk((lastTime_ + 1, nqChangedVars, nqNewValues)) verb("Done") # with return
def actionTest(device, _args): # {{{ rd: Callable = functools.partial(bpReadSequential, device) wr: Callable = functools.partial(bpWriteSequential, device) mem: Callable = bpAddrValuesToMem verb("Reading all register locations...", end='') init0: BpMem = mem(rd(list(range(128)))) verb("Done") bpPrintMem("Initial values", init0) verb("Writing ones to all register locations...", end='') init1: BpMem = mem(wr(list((addr, 0xff) for addr in range(1, 128)))) verb("Done") bpPrintMem("Initial values (again)", init1) verb("Checking previous unchanged...", end='') allUnchanged: bool = all( (i0 == i1) for i0, i1 in zip(init0[1:], init1[1:])) verb("Done") if not allUnchanged: verb("Warning: Some values changed!") verb("Writing zeros to all register locations...", end='') ones: BpMem = mem(wr(list((addr, 0x00) for addr in range(1, 128)))) verb("Done") bpPrintMem("Ones", ones) verb("Reading all register locations...", end='') zeros: BpMem = mem(rd(list(range(1, 128)))) verb("Checking writable bits...", end='') symdiff: BpMem = cast(BpMem, tuple(o ^ z for o, z in zip(ones, zeros))) verb("Done") bpPrintMem("Zeros", zeros) bpPrintMem("Writable bits", symdiff) verb("Writing unique values to all register locations...", end='') _ = mem(wr(list((addr, addr + 10) for addr in range(1, 128)))) verb("Reading back...", end='') addrPlus10: BpMem = mem(rd(list(range(1, 128)))) verb("Done") bpPrintMem("mem[addr] <-- (addr+10)", addrPlus10) return # No return value
def expandEvc(evc, cfg, infoFlag): # {{{ '''Perform substitutions in EVC to create and save EVCX. Does not include config since that goes into a separate file. ''' def infoEvcx(evcx): # {{{ '''Print information about EVCX. ''' if not infoFlag: return for nm, v in evcx.items(): hk = v["hook"] geq = v.get("geq") leq = v.get("leq") if v["type"] in ["threshold", "normal"]: hk = hk if geq is None else ("%s <= " % geq + hk) hk = hk if leq is None else (hk + " <= %s" % leq) msg = "%s %d %s <-- %s" % (v["type"], v["idx"], nm, hk) info(msg, prefix="INFO:EVCX: ") # }}} def infoEvcx reInt = r"[+-]?\d+" reEvcRange = re.compile(r"^" + reInt + r"\s*\.\.\s*" + reInt + r"(\s*\.\.\s*" + reInt + r")?$") def evcSubstitute(instr, choices): # {{{ reEvcSubstitution = re.compile(r'({\d*})') ret_ = instr found = reEvcSubstitution.search(ret_) i = -1 while found is not None: found_str = ret_[found.start():found.end()].strip("{}") if len(found_str) > 0: i = int(found_str) else: i += 1 ret_ = reEvcSubstitution.sub(choices[i], ret_, count=1) found = reEvcSubstitution.search(ret_) return ret_ # }}} def evcSubstitute verb("Expanding EVC to EVCX... ", end='') evsIdxEvent_, evsIdxBstate_, evsIdxNormal_, evsIdxThreshold_ = 0, 0, 0, 0 evcx = {} signals = evc.get("signal", []) for m in signals: subs = m["subs"] if "subs" in m else [] tp = m["type"] if "normal" == tp: # Values of geq,leq define clipNorm interval. geq = m.get("geq", 0) leq = m.get("leq", 1) assert isinstance(geq, (int, float)), (type(geq), geq) assert isinstance(leq, (int, float)), (type(leq), leq) assert geq < leq, (geq, leq) elif "threshold" == tp: # At least one of geq, leq must be a number. # Values of geq,leq used for boundary checks. geq = m.get("geq", None) leq = m.get("leq", None) assert (geq is not None) or (leq is not None), (geq, leq) assert isinstance(geq, (int, float)) or geq is None, (type(geq), geq) assert isinstance(leq, (int, float)) or leq is None, (type(leq), leq) else: # Event and bstate don't use geq or leq. pass # `subs` guaranteed to be list of lists # Each `sub` guaranteed to be homogenous list. # Each `s` guaranteed to be string or int, but string may be a range. assert isinstance(subs, list) for sub in subs: assert isinstance(sub, list) for s in sub: # Unsure how practical other types are. assert isinstance(s, (int, str, unicode)) # Build up new list of lists of usable strings. subs_ = [] for sub in subs: sub_ = [] for s in sub: # Range <start>..<stop>..<step> if isinstance(s, (str, unicode)) and reEvcRange.match(s): sub_ += [str(i) for i in range(*[int(x) \ for x in s.split("..")])] else: s_ = str(s) sub_.append(s_) subs_.append(sub_) # `subs_` guaranteed to be list of lists # Each `sub_` guaranteed to be homogenous list. # Each `s_` guaranteed to be usable string. assert isinstance(subs_, list) for sub_ in subs_: assert isinstance(sub_, list) for s_ in sub_: assert isinstance(s_, str) subsProd = product(*subs_) for subsList in subsProd: fullName = evcSubstitute(m["name"], subsList) fullHook = cfg.vcdhierprefix + \ evcSubstitute(m["hook"], subsList) if "event" == tp: evsIdx = evsIdxEvent_ evsIdxEvent_ += 1 elif "bstate" == tp: evsIdx = evsIdxBstate_ evsIdxBstate_ += 1 elif "threshold" == tp: evsIdx = evsIdxThreshold_ evsIdxThreshold_ += 1 elif "normal" == tp: evsIdx = evsIdxNormal_ evsIdxNormal_ += 1 else: evsIdx = evsIdxEvent_ assert False, tp evcx[fullName] = { "hook": fullHook, "type": tp, "idx": evsIdx, } if tp in ["threshold", "normal"]: evcx[fullName]["geq"] = geq evcx[fullName]["leq"] = leq verb("Saving... ", end='') # Unittests don't setup everything. try: saveToml(evcx, paths.fname_evcx) except AttributeError: pass verb("Done") infoEvcx(evcx) return evcx
def main(args) -> int: # {{{ ''' 1. Read in all regexs and precompile filters into memory. 2. Read STDIN line by line. 3. If line does not match any regex then print on STDOUT. ''' verb("Reading and compiling regex filters ...", end='') regexLines:Iterable = \ rdLines(args.filterFile, commentLines=True, commentMark='#', expandTabs=True, deduplicateSpaces=True, leftStrip=True, rightStrip=True, caseFold=False, raiseIOError=True) regexes: List = [re.compile(line) for line in regexLines if len(line) > 0] verb("Done") verb("Opening STDIN with optional whitespace preprocessing ...", end='') inputLines:Iterable = \ rdLines(None, # STDIN commentLines=False, expandTabs=args.expand_tabs, deduplicateSpaces=args.deduplicate_spaces, leftStrip=args.left_strip, rightStrip=args.right_strip, caseFold=args.case_fold) verb("Done") verb("Filtering ...", end='') for line in inputLines: reMatch: bool = any(r.search(line) for r in regexes) if reMatch == args.invert_match: print(line, end='') verb("Done") return 0
def pktLines(device, nWindows: int, engineNum: int, hwRegs: Dict[HwReg, Any]) -> None: # {{{ '''Generator yielding lines to be written to output. - Display progress/status line. - Read up to 50 packets in each burst. - pktPerBurst = maxRdBurst // nBytesPerWindow - 50 = 255B // 5B - Update progress/status line after each burst. - Append to output after each burst. ''' rpt_ = ' '.join(( '#', "device=%s" % device.name, )) yield rpt_ rpt_ = ' '.join(( '#', "HwRegRO", "PktfifoDepth=%d" % hwRegs[HwReg.PktfifoDepth], "MaxWindowLengthExp=%d" % hwRegs[HwReg.MaxWindowLengthExp], "WindowPrecision=%d" % hwRegs[HwReg.WindowPrecision], "MaxSamplePeriodExp=%d" % hwRegs[HwReg.MaxSamplePeriodExp], "MaxSampleJitterExp=%d" % hwRegs[HwReg.MaxSampleJitterExp], )) yield rpt_ rpt_ = ' '.join(( '#', "HwRegRW", "WindowLengthExp=%d" % hwRegs[HwReg.WindowLengthExp], "SamplePeriodExp=%d" % hwRegs[HwReg.SamplePeriodExp], "SampleJitterExp=%d" % hwRegs[HwReg.SampleJitterExp], "PwmSelect=%s" % hwRegs[HwReg.PwmSelect].name, "XSelect=%d" % hwRegs[HwReg.XSelect], "YSelect=%d" % hwRegs[HwReg.YSelect], )) yield rpt_ #rdBytePipe:Callable = functools.partial(bpReadSequential, device) wrBytePipe: Callable = functools.partial(bpWriteSequential, device) #rd:Callable = functools.partial(hwReadRegs, rdBytePipe) wr: Callable = functools.partial(hwWriteRegs, wrBytePipe) nBytesPerWindow: int = 5 maxRdBurst: int = 255 # NOTE: The term "rate" is used instead of "frequency" to reinforce that the # duty cycle is unbalanced so Fourier analysis will have high power in the # upper frequencies. samplePeriod_cycles: int = 2**hwRegs[HwReg.SamplePeriodExp] samplePeriod_ms: float = samplePeriod_cycles / float(maxSampleRate_kHz) sampleRate_kHz: float = 1.0 / samplePeriod_ms rpt_ = ' '.join(( '#', "sampleStrobe", "averageRegularity=%d_cycles" % samplePeriod_cycles, "averagePeriod=%0.06f_ms" % samplePeriod_ms, "approximateRate=%0.06f_kHz" % sampleRate_kHz, )) yield rpt_ windowLength_samples: int = 2**hwRegs[HwReg.WindowLengthExp] windowPeriod_ms: float = windowLength_samples * samplePeriod_ms windowRate_kHz: float = 1.0 / windowPeriod_ms rpt_ = ' '.join(( '#', 'window', "length=%d_samples" % windowLength_samples, "period_ms=%0.06f_ms" % windowPeriod_ms, "rate=%0.06f_kHz" % windowRate_kHz, )) yield rpt_ totalNSamples: int = nWindows * windowLength_samples totalNBytes: int = nWindows * nBytesPerWindow totalTime_ms: float = nWindows * windowPeriod_ms totalBitrate_kbps: float = (8 * totalNBytes) / (1000 * totalTime_ms) rpt_ = ' '.join(( '#', 'dataset', "nWindows=%d" % nWindows, "time=%0.06f_ms" % totalTime_ms, "nSamples=%d" % totalNSamples, "pktfifoNBytes=%d" % totalNBytes, "pktfifoBitrate=%0.06f_kbps" % totalBitrate_kbps, )) yield rpt_ nWindowPerBurst: int = maxRdBurst // nBytesPerWindow burstTime_ms: float = nWindowPerBurst * windowPeriod_ms nBytesPerBurst: int = nWindowPerBurst * nBytesPerWindow yield "winNum,countX,countY,countIsect,countSymdiff" lineFmt: str = ','.join(["%03d"] * 5) # Flush the packet fifo. # The cycle this arrives at bpReg is the beginning of time for this dataset. verb("Flushing to begin dataset...", end='') wr(engineNum, {HwReg.PktfifoFlush: 1}) verb("Done") nWindowsRemaining_ = nWindows burstNum_ = 0 while (0 < nWindowsRemaining_): nBytesRemaining: int = nWindowsRemaining_ * nBytesPerWindow nBytesThisBurst: int = min(nBytesRemaining, nBytesPerBurst) assert ((nBytesThisBurst // nBytesPerWindow) == \ int(nBytesThisBurst / nBytesPerWindow)), \ (nBytesThisBurst, nBytesPerWindow) # Read burst from packet fifo. verb("Reading burst %d..." % burstNum_, end='') bs = bpReadAddr(device, HwReg.PktfifoRd.value, nBytesThisBurst) verb("Done") assert len(bs) == nBytesThisBurst, (len(bs), nBytesThisBurst) assert all(isinstance(b, int) for b in bs), bs assert all((0 <= b <= 255) for b in bs), bs # Translate bs to output line. for pkt in grouper(bs, nBytesPerWindow): assert len(pkt) == nBytesPerWindow, (len(pkt), nBytesPerWindow) assert all(isinstance(b, int) for b in pkt), pkt line = lineFmt % tuple(pkt) yield line nWindowsRemaining_ -= nWindowPerBurst burstNum_ += 1
def main(args) -> int: # {{{ ''' ''' ########################################################################### # 1. Setup plot ########################################################################### fignum = 0 # figsize used to set dimensions in inches. # ax.set_aspect() doesn't work for KDE where Y-axis is scaled. figsize = tuple(int(a) for a in args.figsize.split(',')) assert 2 == len(figsize) assert all(0 < i for i in figsize) fig = plt.figure(fignum, figsize=figsize) if args.title: plt.title(args.title) if args.xlabel: plt.xlabel(args.xlabel) if args.ylabel: plt.ylabel(args.ylabel) if args.xlim: xLo, xHi = args.xlim.split(',') plt.xlim(float(xLo), float(xHi)) if args.ylim: yLo, yHi = args.ylim.split(',') plt.ylim(float(yLo), float(yHi)) markers = list(args.markers) labels = list(l for l in args.labels.split(',') if 0 < len(l)) ########################################################################### # 2. Populate data ########################################################################### a = np.loadtxt(rdLines(args.input), skiprows=args.skiprows, delimiter=args.delimiter, unpack=True) x = a[0] if args.baseX: args.addX = x[0] * -1 if args.addX: verb("Add constant to X axis. (+ %0.05f)" % args.addX) x += args.addX if args.mulX: verb("Multiply X axis by constant. (* %0.05f)" % args.mulX) x *= args.mulX if args.intX: verb("Reduce X axis to integers.") x = x.astype(np.int) if args.product: prdX = np.copy(x) if args.diffX: verb("Product difference X axis.") tmpX = np.zeros(prdX.shape) tmpX[1:] = np.diff(prdX) prdX = tmpX if args.invX: verb("Product X**-1 axis.") prdX = prdX.astype(np.float) prdX **= -1 ys = a[1:] for i, y in enumerate(ys): if args.baseY: args.addY = y[0] * -1 if args.addY: verb("Add constant to Y axis[%d]. (+ %0.05f)" % (i, args.addY)) y += args.addY if args.mulY: verb("Multiply Y axis (%d) by constant. (%0.05f)" % (i, args.mulY)) y *= args.mulY if args.intY: verb("Reduce Y axis (%d) to integers.") y = y.astype(np.int) if args.product: prdY = np.copy(y) if args.diffY: verb("Product difference Y axis.") tmpY = np.zeros(prdY.shape) tmpY[1:] = np.diff(prdY) prdY = tmpY if args.invY: verb("Product Y**-1 axis.") prdY = prdY.astype(np.float) prdY **= -1 y = prdX * prdY ########################################################################### # 3. Draw plot ########################################################################### for i, y in enumerate(ys): marker = markers[i] if i < len(markers) else '' label = labels[i] if i < len(labels) else None kwargsPlot = {"marker": marker} if label is not None: kwargsPlot.update({"label": label}) plt.plot(x, y, **kwargsPlot) if 0 < len(labels): plt.legend() if args.vlines: for line in args.vlines.split(','): plt.axvline(y=float(line), color="green", linestyle='-', linewidth=1) if args.hlines: for line in args.hlines.split(','): plt.axhline(y=float(line), color="green", linestyle='-', linewidth=1) ########################################################################### # 4. Save plot to file ########################################################################### if args.pdf: plt.savefig(fnameAppendExt(args.output, "pdf"), bbox_inches="tight") else: plt.savefig(fnameAppendExt(args.output, "png"), bbox_inches="tight") plt.close() return 0
def main(args) -> int: # {{{ ''' ''' ########################################################################### # 1. Setup plot ########################################################################### fignum = 0 # figsize used to set dimensions in inches. # ax.set_aspect() doesn't work for KDE where Y-axis is scaled. figsize = tuple(int(a) for a in args.figsize.split(',')) assert 2 == len(figsize) assert all(0 < i for i in figsize) fig = plt.figure(fignum, figsize=figsize) if args.xlabel: plt.xlabel(args.xlabel) if args.ylabel: plt.ylabel(args.ylabel) if args.title: plt.title(args.title) if args.xlim: xLo, xHi = args.xlim.split(',') plt.xlim(float(xLo), float(xHi)) _xLo, _xHi = args.xlim.split(',') xLo, xHi = float(_xLo), float(_xHi) plt.xlim(xLo, xHi) if args.ylim: yLo, yHi = args.ylim.split(',') plt.ylim(float(yLo), float(yHi)) if args.title: plt.title(args.title) markers = list(args.markers) ########################################################################### # 2. Populate data ########################################################################### x = np.linspace(xLo, xHi, args.nsamples) ys = (np.apply_along_axis(eval("lambda x: " + e), 0, x) for e in args.expr) ########################################################################### # 3. Draw plot ########################################################################### for i, y in enumerate(ys): verb("Plotting `%s`" % args.expr[i]) marker = markers[i] if i < len(markers) else '' label = args.expr[i] kwargsPlot = {"marker": marker} if label is not None: kwargsPlot.update({"label": label}) plt.plot(x, y, **kwargsPlot) plt.legend() if args.vlines: for line in args.vlines.split(','): plt.axvline(y=float(line), color="green", linestyle='-', linewidth=1) if args.hlines: for line in args.hlines.split(','): plt.axhline(y=float(line), color="green", linestyle='-', linewidth=1) ########################################################################### # 4. Save plot to file ########################################################################### if args.pdf: plt.savefig(fnameAppendExt(args.output, "pdf"), bbox_inches="tight") else: plt.savefig(fnameAppendExt(args.output, "png"), bbox_inches="tight") plt.close() return 0
def do_GET(self): # {{{ # Remove leading / which is usually (always?) present. self.path = self.path.lstrip('/') # Send response. if len(self.path) and not self.path.startswith("?") and \ (self.path.endswith(".css") or self.path.endswith(".js")): # Strip off any relative paths to only find CSS and JS files. fname = joinP(appPaths.share, os.path.basename(self.path)) try: response = rdTxt(fname) self.send_response(200) if fname.endswith(".js"): self.send_header("Content-Type", "application/javascript; charset=utf-8") elif fname.endswith(".css"): self.send_header("Content-Type", "text/css; charset=utf-8") responseBytes = response.encode("utf-8") except: self.send_error(404, "Invalid JS or CSS GET request!") return elif len(self.path) and self.path.endswith("favicon.ico"): # Bug in Chrome requests favicon.ico with every request until it # gets 404'd. # https://bugs.chromium.org/p/chromium/issues/detail?id=39402 try: faviconFpath = joinP(appPaths.share, "eva_logo.png") responseBytes = open(faviconFpath, 'rb').read() self.send_response(200) self.send_header("Content-Type", "image/x-icon") except: self.send_error(404, "Cannot read favicon!") elif len(self.path) and not self.path.startswith("?"): # Unknown requests. self.send_response(404) return else: # Generate HTML string and send OK if inputs are valid. try: request = self.parseGetRequest(self.path) verb("Calculating {a,b}(x|y;u) <-- {%s,%s}(%s|%s;%s)..." % ( request['a'], request['b'], request['x'], request['y'], request['u'], ), end='') response = evaHtmlString(self.args, self.cfg, request) verb("DONE") self.send_response(200) self.send_header("Content-Type", "text/html; charset=utf-8") responseBytes = response.encode("utf-8") except EvaHTMLException: self.send_error(404, "Invalid GET request!") return # Send HTTP headers. # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers self.send_header("Content-Length", "%d" % len(responseBytes)) self.end_headers() # Send HTML. self.wfile.write(responseBytes) return