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