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