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