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