X-Git-Url: https://git.ozlabs.org/?p=bitfield;a=blobdiff_plain;f=bitfield;h=345ee9fe060ac394b95e20e6736adbd1ca946609;hp=d470c7ce2958d5f7a22eff2e7f42ba4aba31d064;hb=HEAD;hpb=f4cfd030ce9a387e0512e165090f91946d18fc3e diff --git a/bitfield b/bitfield old mode 100644 new mode 100755 index d470c7c..345ee9f --- a/bitfield +++ b/bitfield @@ -1,4 +1,4 @@ -#!/usr/bin/python2.4 +#!/usr/bin/env python3 # # Utility to decode register values # Copyright (c) 2006 Jeremy Kerr @@ -9,60 +9,93 @@ import os import sys import pprint +import math from pyparsing import Literal, Word, ZeroOrMore, Group, Dict, Optional, \ printables, ParseException, restOfLine from getopt import getopt, GetoptError -conf = os.path.join(os.getenv("HOME"), ".bitfields.conf") +# 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, start_bit, end_bit, name): - self.start_bit = start_bit - self.end_bit = end_bit + def __init__(self, bits, name): + self.bits = bits self.name = name self.values = {} - def start_bit(self): - return self.start_bit - - def end_bit(self): - return self.end_bit - def width(self): - return 1 + self.end_bit - self.start_bit + return len(self.bits) def add_value(self, value, description): - self.values[int(value)] = description + self.values[int(value, 0)] = description + + def mask(self, reg, value): + ret = 0 + out_len = len(self.bits) + + if reg.bit_order == reg.bit_0_is_msb: + bit_pairs = zip(self.bits, range(0, out_len)) + else: + bit_pairs = zip(self.bits, range(out_len - 1, -1, -1)) - def mask(self, reg_width, value): - shift = (reg_width - 1) - self.end_bit - return (((2 ** self.width() - 1) << (shift)) - & value) >> shift + for (in_bit, out_bit) in bit_pairs: + # 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 - def __str__(self): - return "[%2d:%-2d] %s 0x%x" % (int(self.start_bit), - int(self.end_bit), self.name, self.mask()) + @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.insert(0, width - i - 1 - shift) + return bits + + @staticmethod + def mask_to_bits(width, mask): + return bitfield.mask_and_shift_to_bits(width, mask, 0) @staticmethod - def parse_bitfield(line): + def parse_bitfield(line, reg): a = line.split(None, 1) if len(a) != 2: return None (range_str, name) = a - range = (None,None) - if range_str.find(':') != -1: - r = range_str.split(":") - range = (int(r[0]),int(r[1])) - else: - range = (int(range_str),int(range_str)) + bits = [] + for s in range_str.split(','): + if ':' in s: + (start, end) = map( \ + lambda s: reg.bit_number(int(s)), + s.split(':')) + 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)) + elif s.startswith('&'): + mask = int(s[1:], 0) + bits.extend(bitfield.mask_to_bits(reg.width, \ + mask)) + else: + bits.append(reg.bit_number(int(s))) + + return bitfield(bits, name) + - return bitfield(range[0], range[1], name) @staticmethod def parse_value(line): @@ -72,38 +105,47 @@ class bitfield: return a class register: - def __init__(self, name, width): - self.name = name - self.width = width + 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,): + def add_field(self, field): self.fields.append(field) - def decode(self, value): - field_width = (self.width + 3) / 4 + def decode(self, value, ignore_zero): + field_width = math.floor((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); + v = field.mask(self, value); + if ignore_zero and v == 0: + continue desc = field.value(v) if desc is not None: - str += "%*s: 0x%s [%s]\n" \ + 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 __str__(self): - str = self.name + "\n" - for f in self.fields: - str += "\t%s\n" % f - return str + def bit_number(self, number): + if self.bit_order == self.bit_0_is_lsb: + number = self.width - number - 1 + return number def list_regs(regs): - for (id, r) in regs.iteritems(): - print "%18s : %s" % (id, r.name) + for (id, r) in regs.items(): + print("%18s : %s" % (id, r.name)) def search_regs(regs, str): return dict((k, regs[k]) for k in regs \ @@ -114,97 +156,180 @@ class ConfigurationError(Exception): self.file = file self.message = message -def parse_config(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" - nonequals = "".join( [ c for c in printables if c != ":" ] ) + " \t" - - sectionDef = lbrack + Word( nonrbrack ) + rbrack - keyDef = ~lbrack + Word( nonequals ) + colon + restOfLine - - bnf = Dict(ZeroOrMore(Group(sectionDef + ZeroOrMore(Group(keyDef))))) - bnf.ignore(comment) - +def parse_config(bnf, regs, file): f = open(file) - tokens = bnf.parseString("".join(f.readlines())) + tokens = bnf.parseString(f.read()) - regs = {} + 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) - # default to 64 bit registers - width = 64 - name = None + if id in regs: + 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] - name = name.strip() + reg.name = name.strip() elif t[0] == 'width': - width = int(t[1]) + reg.width = int(t[1]) elif t[0] == 'field': - f = bitfield.parse_bitfield(t[1]) + 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': - f = fields[-1] - if f is None: + 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) - f.add_value(v[0], v[1]) - if name is None or name == '': + 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) + "No name for entry %s" % id) if len(fields) == 0: raise ConfigurationError(file, "Register %s has no fields" % id) - r = register(name, width) for f in fields: - r.add_field(f) + reg.add_field(f) - regs[id] = r + regs[id] = reg - return regs +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): + for c in os.walk(conf): + parse_config_dir(conf_data, c[0], c[2]) + else: + parse_config(bnf, regs, conf) + return regs def usage(prog): - print "Usage: %s <-l> | <-s pattern> | register [value...]" % prog + print("Usage: %s <-l> | <-s pattern> | [-n] register [value...]" % prog) + +def decode_value(reg, value, options): + try: + i = int(value, 0) + except ValueError: + 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:], "hls:", \ - ["help", "list", "search="]) + (opts, args) = getopt(sys.argv[1:], "hlns:", \ + ["help", "list", "non-zero", "search="]) except GetoptError: usage(sys.argv[0]) return 1 try: - regs = parse_config(conf) + regs = parse_all_configs(configs) + except ConfigurationError as e: + print("Error parsing configuration file %s:\n\t%s" % \ + (e.file, e.message)) + return 1 - except ConfigurationError, e: - print "Error parsing configuration file %s:\n\t%s" % \ - (e.file, e.message) + 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]) @@ -218,31 +343,31 @@ def main(): 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 - a = args.pop(0) - if not regs.has_key(a): - print "No such register '%s'. Valid regs are:" % a - list_regs(regs) + reg_id = args.pop(0) + if reg_id not in regs: + print("No such register '%s'" % reg_id) return 1 - r = regs[a] - print "decoding as %s" % r.name + reg = regs[reg_id] + print("decoding as %s" % reg.name) if args: - values = args + value_iter = args.__iter__() else: - try: - values = sys.stdin.readlines() - except KeyboardInterrupt, e: - return + value_iter = iter(sys.stdin.readline, '') - for value in values: - i = long(value.strip(), 0) - print r.decode(i) + try: + for value in value_iter: + decode_value(reg, value.strip(), options) + except KeyboardInterrupt: + pass return 0