Initial import from website release
[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 conf = os.path.join(os.getenv("HOME"), ".bitfields.conf")
17
18 class bitfield:
19         def __init__(self, start_bit, end_bit, name):
20                 self.start_bit = start_bit
21                 self.end_bit = end_bit
22                 self.name = name
23                 self.values = {}
24
25         def start_bit(self):
26                 return self.start_bit
27
28         def end_bit(self):
29                 return self.end_bit
30
31         def width(self):
32                 return 1 + self.end_bit - self.start_bit
33
34         def add_value(self, value, description):
35                 self.values[int(value)] = description
36
37         def mask(self, reg_width, value):
38                 shift = (reg_width - 1) - self.end_bit
39                 return (((2 ** self.width() - 1) << (shift))
40                         & value) >> shift
41
42         def value(self, value):
43                 if value in self.values:
44                         return self.values[value]
45                 return None
46
47         def __str__(self):
48                 return "[%2d:%-2d] %s 0x%x" % (int(self.start_bit),
49                         int(self.end_bit), self.name, self.mask())
50
51         @staticmethod
52         def parse_bitfield(line):
53                 a = line.split(None, 1)
54                 if len(a) != 2:
55                         return None
56                 (range_str, name) = a
57
58                 range = (None,None)
59                 if range_str.find(':') != -1:
60                         r = range_str.split(":")
61                         range = (int(r[0]),int(r[1]))
62                 else:
63                         range = (int(range_str),int(range_str))
64
65                 return bitfield(range[0], range[1], 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, name, width):
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):
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                         desc = field.value(v)
90                         if desc is not None:
91                                 str += "%*s: 0x%s [%s]\n" \
92                                         % (name_width, field.name, v, desc)
93                         else:
94                                 str += "%*s: 0x%x\n" \
95                                         % (name_width, field.name, v)
96                 return str
97
98         def __str__(self):
99                 str = self.name + "\n"
100                 for f in self.fields:
101                         str += "\t%s\n" % f
102                 return str
103
104 def list_regs(regs):
105         for (id, r) in regs.iteritems():
106                 print "%18s : %s" % (id, r.name)
107
108 def search_regs(regs, str):
109         return dict((k, regs[k]) for k in regs \
110                         if str.lower() in regs[k].name.lower() + k.lower())
111
112 class ConfigurationError(Exception):
113         def __init__(self, file, message):
114                 self.file = file
115                 self.message = message
116
117 def parse_config(file):
118         lbrack = Literal("[").suppress()
119         rbrack = Literal("]").suppress()
120         colon  = Literal(":").suppress()
121         semi   = Literal(";")
122
123         comment = semi + Optional( restOfLine )
124
125         nonrbrack = "".join( [ c for c in printables if c != "]" ] ) + " \t"
126         nonequals = "".join( [ c for c in printables if c != ":" ] ) + " \t"
127
128         sectionDef = lbrack + Word( nonrbrack ) + rbrack
129         keyDef = ~lbrack + Word( nonequals ) + colon + restOfLine
130
131         bnf = Dict(ZeroOrMore(Group(sectionDef + ZeroOrMore(Group(keyDef)))))
132         bnf.ignore(comment)
133
134         f = open(file)
135
136         tokens = bnf.parseString("".join(f.readlines()))
137
138         regs = {}
139
140         for tok in tokens:
141                 ts = tok.asList()
142                 id = ts.pop(0)
143
144                 # default to 64 bit registers
145                 width = 64
146                 name = None
147                 fields = []
148
149                 for t in ts:
150                         if t[0] == 'name':
151                                 name = t[1]
152                                 name = name.strip()
153                         elif t[0] == 'width':
154                                 width = int(t[1])
155                         elif t[0] == 'field':
156                                 f = bitfield.parse_bitfield(t[1])
157                                 if f is None:
158                                         raise ConfigurationError(file,
159                                                 "Invalid field in %s" % id)
160                                 fields.append(f)
161                         elif t[0] == 'value':
162                                 f = fields[-1]
163                                 if f is None:
164                                         raise ConfigurationError(file,
165                                                 "No field for value in %s" % id)
166                                 v = bitfield.parse_value(t[1])
167                                 if v is None:
168                                         raise ConfigurationError(file,
169                                                 "Invalid value in %s" % id)
170                                 f.add_value(v[0], v[1])
171
172                 if name is None or name == '':
173                         raise ConfigurationError(file,
174                                 "No name for entry %s" %id)
175
176                 if len(fields) == 0:
177                         raise ConfigurationError(file,
178                                 "Register %s has no fields" % id)
179
180                 r = register(name, width)
181                 for f in fields:
182                         r.add_field(f)
183
184                 regs[id] = r
185
186         return regs
187
188
189 def usage(prog):
190         print "Usage: %s <-l> | <-s pattern> | register [value...]" % prog
191
192 def main():
193         try:
194                 (opts, args) = getopt(sys.argv[1:], "hls:", \
195                         ["help", "list", "search="])
196         except GetoptError:
197                 usage(sys.argv[0])
198                 return 1
199
200         try:
201                 regs = parse_config(conf)
202
203         except ConfigurationError, e:
204                 print "Error parsing configuration file %s:\n\t%s" % \
205                         (e.file, e.message)
206                 return 1
207
208         for o, a in opts:
209                 if o in ("-h", "--help"):
210                         usage(sys.argv[0])
211                         return
212
213                 if o in ("-l", "--list"):
214                         list_regs(regs)
215                         return
216
217                 if o in ("-s", "--search"):
218                         list_regs(search_regs(regs, a))
219                         return
220
221
222         if not args:
223                 usage(sys.argv[0])
224                 return 1
225
226         a = args.pop(0)
227         if not regs.has_key(a):
228                 print "No such register '%s'. Valid regs are:" % a
229                 list_regs(regs)
230                 return 1
231
232         r = regs[a]
233         print "decoding as %s" % r.name
234
235         if args:
236                 values = args
237         else:
238                 try:
239                         values = sys.stdin.readlines()
240                 except KeyboardInterrupt, e:
241                         return
242
243         for value in values:
244                 i = long(value.strip(), 0)
245                 print r.decode(i)
246
247         return 0
248
249 if __name__ == "__main__":
250         sys.exit(main())