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