8aab6f8b617bd868264f50f12a392f4bf7c43285
[bitfield] / bitfield
1 #!/usr/bin/python2.4
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 = [os.path.join(os.getenv("HOME"), ".bitfields.conf"),
19                 os.path.join(os.getenv("HOME"), ".bitfields.d")]
20
21 class bitfield:
22         def __init__(self, bits, name):
23                 self.bits = bits
24                 self.name = name
25                 self.values = {}
26
27         def width(self):
28                 return len(self.bits)
29
30         def add_value(self, value, description):
31                 self.values[int(value)] = description
32
33         def mask(self, reg_width, value):
34                 ret = 0
35                 out_len = len(self.bits)
36                 for out_bit in range(0, out_len):
37                         in_bit = self.bits[out_bit]
38                         # shift this bit down to the LSB (and mask the rest)
39                         i = (value >> (reg_width - in_bit - 1)) & 1
40                         # shift back to the output position in the field
41                         i <<= out_len - out_bit - 1
42                         ret |= i
43                 return ret
44
45         def value(self, value):
46                 if value in self.values:
47                         return self.values[value]
48                 return None
49
50         @staticmethod
51         def parse_bitfield(line):
52                 a = line.split(None, 1)
53                 if len(a) != 2:
54                         return None
55                 (range_str, name) = a
56
57                 bits = []
58                 for s in range_str.split(','):
59                         if ':' in s:
60                                 (start, end) = s.split(':')
61                                 bits.extend(range(int(start), int(end) + 1, 1))
62                         else:
63                                 bits.append(int(s))
64  
65                 return bitfield(bits, name)
66
67         @staticmethod
68         def parse_value(line):
69                 a = line.split(None, 1)
70                 if len(a) != 2:
71                         return None
72                 return a
73
74 class register:
75         def __init__(self, id, name, width):
76                 self.id = id
77                 self.name = name
78                 self.width = width
79                 self.fields = []
80
81         def add_field(self, field):
82                 self.fields.append(field)
83
84         def decode(self, value, ignore_zero):
85                 field_width = (self.width + 3) / 4
86                 name_width = max(map(lambda f: len(f.name), self.fields))
87
88                 str = "0x%0*lx [%d]\n" % (field_width, value, value)
89
90                 for field in self.fields:
91                         v = field.mask(self.width, value);
92                         if ignore_zero and v == 0:
93                                 continue
94                         desc = field.value(v)
95                         if desc is not None:
96                                 str += "%*s: 0x%x [%s]\n" \
97                                         % (name_width, field.name, v, desc)
98                         else:
99                                 str += "%*s: 0x%x\n" \
100                                         % (name_width, field.name, v)
101                 return str
102
103 def list_regs(regs):
104         for (id, r) in regs.iteritems():
105                 print "%18s : %s" % (id, r.name)
106
107 def search_regs(regs, str):
108         return dict((k, regs[k]) for k in regs \
109                         if str.lower() in regs[k].name.lower() + k.lower())
110
111 class ConfigurationError(Exception):
112         def __init__(self, file, message):
113                 self.file = file
114                 self.message = message
115
116 def parse_config(bnf, regs, file):
117         f = open(file)
118
119         tokens = bnf.parseString(f.read())
120
121         for tok in tokens:
122                 ts = tok.asList()
123                 id = ts.pop(0)
124
125                 if regs.has_key(id):
126                         raise ConfigurationError(file,
127                                 "Register %s is already defined" % id)
128
129                 # default to 64 bit registers
130                 width = 64
131                 name = None
132                 fields = []
133
134                 for t in ts:
135                         if t[0] == 'name':
136                                 name = t[1]
137                                 name = name.strip()
138                         elif t[0] == 'width':
139                                 width = int(t[1])
140                         elif t[0] == 'field':
141                                 f = bitfield.parse_bitfield(t[1])
142                                 if f is None:
143                                         raise ConfigurationError(file,
144                                                 "Invalid field in %s" % id)
145                                 fields.append(f)
146                         elif t[0] == 'value':
147                                 if len(fields) == 0:
148                                         raise ConfigurationError(file,
149                                                 "No field for value in %s" % id)
150                                 v = bitfield.parse_value(t[1])
151                                 if v is None:
152                                         raise ConfigurationError(file,
153                                                 "Invalid value in %s" % id)
154
155                                 fields[-1].add_value(v[0], v[1])
156
157                 if name is None or name == '':
158                         raise ConfigurationError(file,
159                                 "No name for entry %s" % id)
160
161                 if len(fields) == 0:
162                         raise ConfigurationError(file,
163                                 "Register %s has no fields" % id)
164
165                 r = register(id, name, width)
166                 for f in fields:
167                         r.add_field(f)
168
169                 regs[id] = r
170
171 def parse_config_dir(data, dir, fnames):
172         (bnf, regs) = data
173         for fname in fnames:
174                 full_fname = os.path.join(dir, fname)
175
176                 if os.path.isdir(full_fname):
177                         continue
178
179                 if fname.endswith('.conf'):
180                         parse_config(bnf, regs, full_fname)
181
182 def parse_all_configs(configs):
183         regs = {}
184
185         # set up the bnf to be used for each file
186         lbrack = Literal("[").suppress()
187         rbrack = Literal("]").suppress()
188         colon  = Literal(":").suppress()
189         semi   = Literal(";")
190
191         comment = semi + Optional(restOfLine)
192
193         nonrbrack = "".join([c for c in printables if c != "]"]) + " \t"
194         noncolon  = "".join([c for c in printables if c != ":"]) + " \t"
195
196         sectionDef = lbrack + Word(nonrbrack) + rbrack
197         keyDef = ~lbrack + Word(noncolon) + colon + restOfLine
198
199         bnf = Dict(ZeroOrMore(Group(sectionDef + ZeroOrMore(Group(keyDef)))))
200         bnf.ignore(comment)
201
202         # bundle into a single var that can be passed to os.path.walk
203         conf_data = (bnf, regs)
204
205         for conf in configs:
206                 if not os.path.exists(conf):
207                         continue
208                 if os.path.isdir(conf):
209                         os.path.walk(conf, parse_config_dir, conf_data)
210                 else:
211                         parse_config(bnf, regs, conf)
212         return regs
213
214 def usage(prog):
215         print "Usage: %s <-l> | <-s pattern> | [-n] register [value...]" % prog
216
217 def decode_value(reg, value, options):
218         try:
219                 i = long(value, 0)
220         except ValueError, e:
221                 print "error: invalid value '%s'" % value
222                 return
223
224         if i > ((1 << reg.width) - 1):
225                 print ("error: value '%s' is too large " + \
226                         "for %d-bit register '%s'") % (value, reg.width, reg.id)
227                 return
228
229         print reg.decode(i, options['non-zero'])
230
231
232 def main():
233         try:
234                 (opts, args) = getopt(sys.argv[1:], "hlns:", \
235                         ["help", "list", "non-zero", "search="])
236         except GetoptError:
237                 usage(sys.argv[0])
238                 return 1
239
240         try:
241                 regs = parse_all_configs(configs)
242         except ConfigurationError, e:
243                 print "Error parsing configuration file %s:\n\t%s" % \
244                         (e.file, e.message)
245                 return 1
246
247         if regs == {}:
248                 print "No configuration available"
249                 return 1
250
251         options = {}
252         options['non-zero'] = False
253
254         for o, a in opts:
255                 if o in ("-h", "--help"):
256                         usage(sys.argv[0])
257                         return
258
259                 if o in ("-l", "--list"):
260                         list_regs(regs)
261                         return
262
263                 if o in ("-s", "--search"):
264                         list_regs(search_regs(regs, a))
265                         return
266
267                 if o in ("-n", "--non-zero"):
268                         options['non-zero'] = True
269
270         if not args:
271                 usage(sys.argv[0])
272                 return 1
273
274         reg_id = args.pop(0)
275         if not regs.has_key(reg_id):
276                 print "No such register '%s'" % reg_id
277                 return 1
278
279         reg = regs[reg_id]
280         print "decoding as %s" % reg.name
281
282         if args:
283                 value_iter = args.__iter__()
284         else:
285                 value_iter = iter(sys.stdin.readline, '')
286                 
287         try:
288                 for value in value_iter:
289                         decode_value(reg, value.strip(), options)
290         except KeyboardInterrupt, e:
291                 pass
292
293         return 0
294
295 if __name__ == "__main__":
296         sys.exit(main())