#!/usr/bin/python2.4 # # Utility to decode register values # Copyright (c) 2006 Jeremy Kerr # Released under the GNU General Public License version 2 or later # # Documentation and updates at: http://ozlabs.org/~jk/code/bitfield import os import sys import pprint from pyparsing import Literal, Word, ZeroOrMore, Group, Dict, Optional, \ printables, ParseException, restOfLine from getopt import getopt, GetoptError # List of paths to look for configuration files. If a directory is specified, # it will be (recursively) scanned for .conf files. configs = [os.path.join(os.getenv("HOME"), ".bitfield.conf"), os.path.join(os.getenv("HOME"), ".bitfield.d"), "/etc/bitfield.d", "/etc/bitfield", ] class bitfield: def __init__(self, bits, name): self.bits = bits self.name = name self.values = {} def width(self): return len(self.bits) def add_value(self, value, description): self.values[int(value)] = description def mask(self, reg_width, value): ret = 0 out_len = len(self.bits) for out_bit in range(0, out_len): in_bit = self.bits[out_bit] # shift this bit down to the LSB (and mask the rest) i = (value >> (reg_width - in_bit - 1)) & 1 # shift back to the output position in the field i <<= out_len - out_bit - 1 ret |= i return ret def value(self, value): if value in self.values: return self.values[value] return None @staticmethod def parse_bitfield(line): a = line.split(None, 1) if len(a) != 2: return None (range_str, name) = a bits = [] for s in range_str.split(','): if ':' in s: (start, end) = s.split(':') bits.extend(range(int(start), int(end) + 1, 1)) else: bits.append(int(s)) return bitfield(bits, name) @staticmethod def parse_value(line): a = line.split(None, 1) if len(a) != 2: return None return a class register: def __init__(self, id, name, width): self.id = id self.name = name self.width = width self.fields = [] def add_field(self, field): self.fields.append(field) def decode(self, value, ignore_zero): field_width = (self.width + 3) / 4 name_width = max(map(lambda f: len(f.name), self.fields)) str = "0x%0*lx [%d]\n" % (field_width, value, value) for field in self.fields: v = field.mask(self.width, value); if ignore_zero and v == 0: continue desc = field.value(v) if desc is not None: str += "%*s: 0x%x [%s]\n" \ % (name_width, field.name, v, desc) else: str += "%*s: 0x%x\n" \ % (name_width, field.name, v) return str def list_regs(regs): for (id, r) in regs.iteritems(): print "%18s : %s" % (id, r.name) def search_regs(regs, str): return dict((k, regs[k]) for k in regs \ if str.lower() in regs[k].name.lower() + k.lower()) class ConfigurationError(Exception): def __init__(self, file, message): self.file = file self.message = message def parse_config(bnf, regs, file): f = open(file) tokens = bnf.parseString(f.read()) for tok in tokens: ts = tok.asList() id = ts.pop(0) if regs.has_key(id): raise ConfigurationError(file, "Register %s is already defined" % id) # default to 64 bit registers width = 64 name = None fields = [] for t in ts: if t[0] == 'name': name = t[1] name = name.strip() elif t[0] == 'width': width = int(t[1]) elif t[0] == 'field': f = bitfield.parse_bitfield(t[1]) if f is None: raise ConfigurationError(file, "Invalid field in %s" % id) fields.append(f) elif t[0] == 'value': if len(fields) == 0: raise ConfigurationError(file, "No field for value in %s" % id) v = bitfield.parse_value(t[1]) if v is None: raise ConfigurationError(file, "Invalid value in %s" % id) fields[-1].add_value(v[0], v[1]) if name is None or name == '': raise ConfigurationError(file, "No name for entry %s" % id) if len(fields) == 0: raise ConfigurationError(file, "Register %s has no fields" % id) r = register(id, name, width) for f in fields: r.add_field(f) regs[id] = r def parse_config_dir(data, dir, fnames): (bnf, regs) = data for fname in fnames: full_fname = os.path.join(dir, fname) if os.path.isdir(full_fname): continue if fname.endswith('.conf'): parse_config(bnf, regs, full_fname) def parse_all_configs(configs): regs = {} # set up the bnf to be used for each file lbrack = Literal("[").suppress() rbrack = Literal("]").suppress() colon = Literal(":").suppress() semi = Literal(";") comment = semi + Optional(restOfLine) nonrbrack = "".join([c for c in printables if c != "]"]) + " \t" noncolon = "".join([c for c in printables if c != ":"]) + " \t" sectionDef = lbrack + Word(nonrbrack) + rbrack keyDef = ~lbrack + Word(noncolon) + colon + restOfLine bnf = Dict(ZeroOrMore(Group(sectionDef + ZeroOrMore(Group(keyDef))))) bnf.ignore(comment) # bundle into a single var that can be passed to os.path.walk conf_data = (bnf, regs) for conf in configs: if not os.path.exists(conf): continue if os.path.isdir(conf): os.path.walk(conf, parse_config_dir, conf_data) else: parse_config(bnf, regs, conf) return regs def usage(prog): print "Usage: %s <-l> | <-s pattern> | [-n] register [value...]" % prog def decode_value(reg, value, options): try: i = long(value, 0) except ValueError, e: print "error: invalid value '%s'" % value return if i > ((1 << reg.width) - 1): print ("error: value '%s' is too large " + \ "for %d-bit register '%s'") % (value, reg.width, reg.id) return print reg.decode(i, options['non-zero']) def main(): try: (opts, args) = getopt(sys.argv[1:], "hlns:", \ ["help", "list", "non-zero", "search="]) except GetoptError: usage(sys.argv[0]) return 1 try: regs = parse_all_configs(configs) except ConfigurationError, e: print "Error parsing configuration file %s:\n\t%s" % \ (e.file, e.message) return 1 if regs == {}: print "No configuration available" return 1 options = {} options['non-zero'] = False for o, a in opts: if o in ("-h", "--help"): usage(sys.argv[0]) return if o in ("-l", "--list"): list_regs(regs) return if o in ("-s", "--search"): list_regs(search_regs(regs, a)) return if o in ("-n", "--non-zero"): options['non-zero'] = True if not args: usage(sys.argv[0]) return 1 reg_id = args.pop(0) if not regs.has_key(reg_id): print "No such register '%s'" % reg_id return 1 reg = regs[reg_id] print "decoding as %s" % reg.name if args: value_iter = args.__iter__() else: value_iter = iter(sys.stdin.readline, '') try: for value in value_iter: decode_value(reg, value.strip(), options) except KeyboardInterrupt, e: pass return 0 if __name__ == "__main__": sys.exit(main())