Add support for hex (and octal) values
[bitfield] / bitfield
1 #!/usr/bin/env python
2 #
3 # Utility to decode register values
4 # Copyright (c) 2006 Jeremy Kerr <jk@ozlabs.org>
5 # Released under the GNU General Public License version 2 or later
6 #
7 # Documentation and updates at: http://ozlabs.org/~jk/code/bitfield
8
9 import os
10 import sys
11 import pprint
12 from pyparsing import Literal, Word, ZeroOrMore, Group, Dict, Optional, \
13         printables, ParseException, restOfLine
14 from getopt import getopt, GetoptError
15
16 # List of paths to look for configuration files. If a directory is specified,
17 # it will be (recursively) scanned for .conf files.
18 configs = ["/etc/bitfield.d", "/etc/bitfield",
19                 os.path.join(os.getenv("HOME"), ".bitfield.d"),
20                 os.path.join(os.getenv("HOME"), ".bitfield.conf")]
21
22 class bitfield:
23         def __init__(self, bits, name):
24                 self.bits = bits
25                 self.name = name
26                 self.values = {}
27
28         def width(self):
29                 return len(self.bits)
30
31         def add_value(self, value, description):
32                 self.values[int(value, 0)] = description
33
34         def mask(self, reg_width, value):
35                 ret = 0
36                 out_len = len(self.bits)
37                 for out_bit in range(0, out_len):
38                         in_bit = self.bits[out_bit]
39                         # shift this bit down to the LSB (and mask the rest)
40                         i = (value >> (reg_width - in_bit - 1)) & 1
41                         # shift back to the output position in the field
42                         i <<= out_len - out_bit - 1
43                         ret |= i
44                 return ret
45
46         def value(self, value):
47                 if value in self.values:
48                         return self.values[value]
49                 return None
50
51         @staticmethod
52         def mask_and_shift_to_bits(width, mask, shift):
53                 bits = []
54                 val = mask << shift
55                 for i in range(0, width):
56                         if mask & (1 << i):
57                                 bits.insert(0, width - i - 1 - shift)
58                 return bits
59
60         @staticmethod
61         def mask_to_bits(width, mask):
62                 return bitfield.mask_and_shift_to_bits(width, mask, 0)
63
64         @staticmethod
65         def parse_bitfield(line, reg):
66                 a = line.split(None, 1)
67                 if len(a) != 2:
68                         return None
69                 (range_str, name) = a
70
71                 bits = []
72                 for s in range_str.split(','):
73                         if ':' in s:
74                                 (start, end) = map( \
75                                         lambda s: reg.bit_number(int(s)),
76                                         s.split(':'))
77                                 start = reg.bit_number(int(start))
78                                 end = reg.bit_number(int(end))
79                                 bits.extend(range(start, end + 1, 1))
80                         elif '<<' in s:
81                                 (mask, shift) = map(lambda s: int(s.strip()),
82                                         s.split('<<'))
83                                 bits.extend(bitfield.mask_and_shift_to_bits( \
84                                         reg.width, mask, shift))
85                         elif s.startswith('&'):
86                                 mask = int(s[1:], 0)
87                                 bits.extend(bitfield.mask_to_bits(reg.width, \
88                                                         mask))
89                         else:
90                                 bits.append(reg.bit_number(int(s)))
91
92                 return bitfield(bits, name)
93
94
95
96         @staticmethod
97         def parse_value(line):
98                 a = line.split(None, 1)
99                 if len(a) != 2:
100                         return None
101                 return a
102
103 class register:
104         bit_0_is_msb = 0
105         bit_0_is_lsb = 1
106
107         def __init__(self, id):
108                 self.id = id
109                 self.fields = []
110                 # set defaults
111                 self.name = None
112                 self.bit_order = self.bit_0_is_msb
113                 self.width = 64
114
115         def add_field(self, field):
116                 self.fields.append(field)
117
118         def decode(self, value, ignore_zero):
119                 field_width = (self.width + 3) / 4
120                 name_width = max(map(lambda f: len(f.name), self.fields))
121
122                 str = "0x%0*lx [%d]\n" % (field_width, value, value)
123
124                 for field in self.fields:
125                         v = field.mask(self.width, value);
126                         if ignore_zero and v == 0:
127                                 continue
128                         desc = field.value(v)
129                         if desc is not None:
130                                 str += "%*s: 0x%x [%s]\n" \
131                                         % (name_width, field.name, v, desc)
132                         else:
133                                 str += "%*s: 0x%x\n" \
134                                         % (name_width, field.name, v)
135                 return str
136
137         def bit_number(self, number):
138                 if self.bit_order == self.bit_0_is_lsb:
139                         number = self.width - 1 - number
140                 return number
141
142 def list_regs(regs):
143         for (id, r) in regs.iteritems():
144                 print "%18s : %s" % (id, r.name)
145
146 def search_regs(regs, str):
147         return dict((k, regs[k]) for k in regs \
148                         if str.lower() in regs[k].name.lower() + k.lower())
149
150 class ConfigurationError(Exception):
151         def __init__(self, file, message):
152                 self.file = file
153                 self.message = message
154
155 def parse_config(bnf, regs, file):
156         f = open(file)
157
158         tokens = bnf.parseString(f.read())
159
160         order_map = {'bit-0-is-lsb':    register.bit_0_is_lsb,
161                         'bit-0-is-msb': register.bit_0_is_msb,
162                         'ibm':          register.bit_0_is_msb,
163                         'default':      register.bit_0_is_msb}
164
165         for tok in tokens:
166                 ts = tok.asList()
167                 id = ts.pop(0)
168
169                 if regs.has_key(id):
170                         raise ConfigurationError(file,
171                                 "Register %s is already defined" % id)
172
173                 reg = register(id)
174
175                 alias_id = None
176                 fields = []
177
178                 for t in ts:
179                         if t[0] == 'name':
180                                 name = t[1]
181                                 reg.name = name.strip()
182                         elif t[0] == 'width':
183                                 reg.width = int(t[1])
184                         elif t[0] == 'field':
185                                 f = bitfield.parse_bitfield(t[1], reg)
186                                 if f is None:
187                                         raise ConfigurationError(file,
188                                                 "Invalid field in %s" % id)
189                                 fields.append(f)
190                         elif t[0] == 'value':
191                                 if len(fields) == 0:
192                                         raise ConfigurationError(file,
193                                                 "No field for value in %s" % id)
194                                 v = bitfield.parse_value(t[1])
195                                 if v is None:
196                                         raise ConfigurationError(file,
197                                                 "Invalid value in %s" % id)
198
199                                 fields[-1].add_value(v[0], v[1])
200                         elif t[0] == 'order':
201                                 if len(fields) != 0:
202                                         raise ConfigurationError(file,
203                                                 ("bit order defined after " \
204                                                 + "fields in %s") % id)
205
206                                 order_str = t[1].strip().lower()
207                                 order_str = order_str.replace(' ', '-')
208
209                                 if order_str not in order_map.keys():
210                                         raise ConfigurationError(file,
211                                                 "Invalid bit order %s in %s" % \
212                                                 (order_str, id))
213                                 reg.bit_order = order_map[order_str]
214
215                         elif t[0] == 'alias':
216                                 alias_id = t[1].strip()
217
218                 if alias_id is not None:
219                         if reg.name is not None or fields != []:
220                                 raise ConfigurationError(file, ("Definiton " \
221                                         + "for %s is an alias, but has other " \
222                                         + "attributes") % id)
223
224                         if not regs.has_key(alias_id):
225                                 raise ConfigurationError(file, "Aliasing "
226                                         "non-existent register %s (from %s)" \
227                                         % (alias_id, id))
228
229                         reg = regs[alias_id]
230                         continue
231
232                 if reg.name is None or reg.name == '':
233                         raise ConfigurationError(file,
234                                 "No name for entry %s" % id)
235
236                 if len(fields) == 0:
237                         raise ConfigurationError(file,
238                                 "Register %s has no fields" % id)
239
240                 for f in fields:
241                         reg.add_field(f)
242
243                 regs[id] = reg
244
245 def parse_config_dir(data, dir, fnames):
246         (bnf, regs) = data
247         for fname in fnames:
248                 full_fname = os.path.join(dir, fname)
249
250                 if os.path.isdir(full_fname):
251                         continue
252
253                 if fname.endswith('.conf'):
254                         parse_config(bnf, regs, full_fname)
255
256 def parse_all_configs(configs):
257         regs = {}
258
259         # set up the bnf to be used for each file
260         lbrack = Literal("[").suppress()
261         rbrack = Literal("]").suppress()
262         colon  = Literal(":").suppress()
263         semi   = Literal(";")
264
265         comment = semi + Optional(restOfLine)
266
267         nonrbrack = "".join([c for c in printables if c != "]"]) + " \t"
268         noncolon  = "".join([c for c in printables if c != ":"]) + " \t"
269
270         sectionDef = lbrack + Word(nonrbrack) + rbrack
271         keyDef = ~lbrack + Word(noncolon) + colon + restOfLine
272
273         bnf = Dict(ZeroOrMore(Group(sectionDef + ZeroOrMore(Group(keyDef)))))
274         bnf.ignore(comment)
275
276         # bundle into a single var that can be passed to os.path.walk
277         conf_data = (bnf, regs)
278
279         for conf in configs:
280                 if not os.path.exists(conf):
281                         continue
282                 if os.path.isdir(conf):
283                         os.path.walk(conf, parse_config_dir, conf_data)
284                 else:
285                         parse_config(bnf, regs, conf)
286         return regs
287
288 def usage(prog):
289         print "Usage: %s <-l> | <-s pattern> | [-n] register [value...]" % prog
290
291 def decode_value(reg, value, options):
292         try:
293                 i = long(value, 0)
294         except ValueError, e:
295                 print "error: invalid value '%s'" % value
296                 return
297
298         if i > ((1 << reg.width) - 1):
299                 print ("error: value '%s' is too large " + \
300                         "for %d-bit register '%s'") % (value, reg.width, reg.id)
301                 return
302
303         print reg.decode(i, options['non-zero'])
304
305
306 def main():
307         try:
308                 (opts, args) = getopt(sys.argv[1:], "hlns:", \
309                         ["help", "list", "non-zero", "search="])
310         except GetoptError:
311                 usage(sys.argv[0])
312                 return 1
313
314         try:
315                 regs = parse_all_configs(configs)
316         except ConfigurationError, e:
317                 print "Error parsing configuration file %s:\n\t%s" % \
318                         (e.file, e.message)
319                 return 1
320
321         if regs == {}:
322                 print "No configuration available"
323                 return 1
324
325         options = {}
326         options['non-zero'] = False
327
328         for o, a in opts:
329                 if o in ("-h", "--help"):
330                         usage(sys.argv[0])
331                         return
332
333                 if o in ("-l", "--list"):
334                         list_regs(regs)
335                         return
336
337                 if o in ("-s", "--search"):
338                         list_regs(search_regs(regs, a))
339                         return
340
341                 if o in ("-n", "--non-zero"):
342                         options['non-zero'] = True
343
344         if not args:
345                 usage(sys.argv[0])
346                 return 1
347
348         reg_id = args.pop(0)
349         if not regs.has_key(reg_id):
350                 print "No such register '%s'" % reg_id
351                 return 1
352
353         reg = regs[reg_id]
354         print "decoding as %s" % reg.name
355
356         if args:
357                 value_iter = args.__iter__()
358         else:
359                 value_iter = iter(sys.stdin.readline, '')
360
361         try:
362                 for value in value_iter:
363                         decode_value(reg, value.strip(), options)
364         except KeyboardInterrupt, e:
365                 pass
366
367         return 0
368
369 if __name__ == "__main__":
370         sys.exit(main())