#!/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 = ["/etc/bitfield.d", "/etc/bitfield", os.path.join(os.getenv("HOME"), ".bitfield.d"), os.path.join(os.getenv("HOME"), ".bitfield.conf")] 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 mask_and_shift_to_bits(width, mask, shift): bits = [] val = mask << shift for i in range(0, width): if mask & (1 << i): bits.append(width - i - 1 - shift) return bits @staticmethod def parse_bitfield(line, reg): 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) = map( \ lambda s: reg.bit_number(int(s)), s.split(':')) start = reg.bit_number(int(start)) end = reg.bit_number(int(end)) bits.extend(range(start, end + 1, 1)) elif '<<' in s: (mask, shift) = map(lambda s: int(s.strip()), s.split('<<')) bits.extend(bitfield.mask_and_shift_to_bits( \ reg.width, mask, shift)) else: bits.append(reg.bit_number(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: bit_0_is_msb = 0 bit_0_is_lsb = 1 def __init__(self, id): self.id = id self.fields = [] # set defaults self.name = None self.bit_order = self.bit_0_is_msb self.width = 64 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 bit_number(self, number): if self.bit_order == self.bit_0_is_lsb: number = self.width - 1 - number return number 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()) order_map = {'bit-0-is-lsb': register.bit_0_is_lsb, 'bit-0-is-msb': register.bit_0_is_msb, 'ibm': register.bit_0_is_msb, 'default': register.bit_0_is_msb} 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) reg = register(id) alias_id = None fields = [] for t in ts: if t[0] == 'name': name = t[1] reg.name = name.strip() elif t[0] == 'width': reg.width = int(t[1]) elif t[0] == 'field': f = bitfield.parse_bitfield(t[1], reg) 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]) elif t[0] == 'order': if len(fields) != 0: raise ConfigurationError(file, ("bit order defined after " \ + "fields in %s") % id) order_str = t[1].strip().lower() order_str = order_str.replace(' ', '-') if order_str not in order_map.keys(): raise ConfigurationError(file, "Invalid bit order %s in %s" % \ (order_str, id)) reg.bit_order = order_map[order_str] elif t[0] == 'alias': alias_id = t[1].strip() if alias_id is not None: if reg.name is not None or fields != []: raise ConfigurationError(file, ("Definiton " \ + "for %s is an alias, but has other " \ + "attributes") % id) if not regs.has_key(alias_id): raise ConfigurationError(file, "Aliasing " "non-existent register %s (from %s)" \ % (alias_id, id)) reg = regs[alias_id] continue if reg.name is None or reg.name == '': raise ConfigurationError(file, "No name for entry %s" % id) if len(fields) == 0: raise ConfigurationError(file, "Register %s has no fields" % id) for f in fields: reg.add_field(f) regs[id] = reg 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())