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