File: vcdreader.py

package info (click to toggle)
simulavr 1.0.0%2Bgit20160221.e53413b-1
  • links: PTS
  • area: main
  • in suites: buster
  • size: 5,748 kB
  • sloc: cpp: 35,491; python: 6,991; ansic: 3,567; makefile: 1,072; sh: 653; asm: 414; tcl: 320
file content (402 lines) | stat: -rw-r--r-- 11,173 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# Modul zum Einlesen von VCD-Dateien
from re import compile
from types import FloatType

class VCDError(Exception): pass

class VCDInternalError(VCDError): pass

class VCDParserError(VCDError):
  
  def __init__(self, parser, msg):
    self.__vcdname = parser._filename
    self.__lineno = parser._lineno
    self.__msg = msg
    
  def __str__(self):
    return self.__vcdname + ":" + str(self.__lineno) + ": " + self.__msg
  
class VCDResult(object): pass

class VCDTime(object):
  
  def __init__(self, value, conv):
    self.__time = value
    self.__conv = conv
    self.__edges = list()
    
  def add(self, edge):
    self.__edges.append(edge)
    
  @property
  def internalTime(self): return self.__time
  
class VCDEdge(object):
  
  def __init__(self, var, time, value, isInit):
    self.__init = isInit
    self.__size = var.size
    self.__value = self.__expandValue(value)
    self.__time = time
    self.__var = var
    self.__intern = time.internalTime
    
  def __expandValue(self, v):
    l = len(v)
    if l > self.__size:
      return v[l - self.__size:]
    elif l == self.__size:
      return v
    if v[0] in ("0", "1"):
      f = "0"
    else:
      f = v[0]
    return f * (self.__size - l) + v

  def bit(self, bitnum, width = 1):
    if bitnum >= self.__size:
      raise VCDInternalError, "invalid bit number, max is %d" % self.__size - 1
    if (bitnum + width) > self.__size:
      raise VCDInternalError, "invalid width, bitnum + width exeeded size, max is %d" % self.__size
    l = list(self.__value)
    l.reverse()
    l = l[bitnum:bitnum + width]
    l.reverse()
    return "".join(l)
    
  @property
  def value(self): return self.__value
  
  @property
  def intValue(self):
    try:
      return int(self.__value, 2)
    except ValueError:
      raise VCDError, "can't convert value to integer"
      
  @property
  def hasUnknown(self): return "x" in self.__value
  
  @property
  def valueUnknown(self): return ("x" * self.__size) == self.__value
  
  @property
  def hasTristate(self): return "z" in self.__value
  
  @property
  def valueTristate(self): return ("z" * self.__size) == self.__value
  
  @property
  def internalTime(self): return self.__intern
  
  @property
  def time(self): return self.__time
  
  @property
  def variable(self): return self.__variable
  
  @property
  def isInit(self): return self.__init
  
  def analyseWire(self, bitnum):
    def checkBit(e):
      if e.bit(bitnum) not in ("0", "1"):
        raise VCDError, "bit value isn't valid to analyse it"
    result = VCDResult()
    checkBit(self)
    c = self.__var.vcdInstance.conv2sec
    t0 = c(self.internalTime)
    e1 = self.__var.getNextEdge(self)
    checkBit(e1)
    t1 = c(e1.internalTime)
    e2 = self.__var.getNextEdge(e1)
    checkBit(e2)
    t2 = c(e2.internalTime)
    result.edges = [self, e1, e2]
    result.period = t2 - t0
    result.frequency = 1 / result.period
    result.pattern = self.bit(bitnum) + e1.bit(bitnum) + e2.bit(bitnum)
    if self.bit(bitnum) == "0":
      result.hightime = t2 - t1
      result.lowtime = t1 - t0
    else:
      result.hightime = t1 - t0
      result.lowtime = t2 - t1
    result.dutty = result.hightime / result.period
    return result
    
class VCDVar(object):
  
  def __init__(self, vcd, typ, size, name):
    self.__vcd = vcd
    self.__type = typ
    self.__size = size
    self.__name = name
    self.__edges = list()
    
  def add(self, edge):
    self.__edges.append(edge)
    
  @property
  def size(self): return self.__size
  
  @property
  def name(self): return self.__name
  
  @property
  def edgecount(self): return len(self.__edges) - 1
  
  @property
  def vcdInstance(self): return self.__vcd
  
  @property
  def initedge(self):
    if len(self.__edges) < 1:
      raise VCDError, "no init edge available"
    return self.__edges[0]
  
  @property
  def firstedge(self):
    if len(self.__edges) < 2:
      raise VCDError, "no edges available"
    return self.__edges[1]
  
  @property
  def lastedge(self):
    if len(self.__edges) < 2:
      raise VCDError, "no edges available"
    return self.__edges[-1]
  
  def getEdges(self, starttime = None, endtime = None):
    if not starttime == None and type(starttime) == FloatType:
      starttime = self.__vcd.conv2intern(starttime)
    if not endtime == None and type(endtime) == FloatType:
      endtime = self.__vcd.conv2intern(endtime)
    for e in self.__edges:
      if starttime is not None and e.internalTime < starttime: continue
      if endtime is not None and e.internalTime > endtime: break
      yield e
    
  def getPrevEdge(self, timeOrEdge):
    if isinstance(timeOrEdge, VCDEdge):
      # edge-Mode
      idx = self.__edges.index(timeOrEdge)
      if idx < 2:
        raise VCDError, "edge hasn't previous edge"
      return self.__edges[idx - 1]
    elif type(timeOrEdge) == FloatType:
      time = self.__vcd.conv2intern(timeOrEdge)
    else:
      time = timeOrEdge
    e0 = self.__edges[0]
    if e0.internalTime > time:
      raise VCDError, "no previous edge found, because start time is higher"
    for e in self.__edges[1:]:
      if e.internalTime >= time:
        return e0
      e0 = e
    return self.__edges[-1]
    
  def getNextEdge(self, timeOrEdge):
    if isinstance(timeOrEdge, VCDEdge):
      # edge-Mode
      idx = self.__edges.index(timeOrEdge)
      if idx >= (len(self.__edges) - 1):
        raise VCDError, "edge hasn't next edge"
      return self.__edges[idx + 1]
    elif type(timeOrEdge) == FloatType:
      time = self.__vcd.conv2intern(timeOrEdge)
    else:
      time = timeOrEdge
    for e in self.__edges[:-1]:
      if e.internalTime >= time:
        return e
    raise VCDError, "no next edge found, because end time to low"
  
class VCD(object):
  
  __rx_timescale = compile(r"^(\d+)\s*([pnum]?)s$")
  __timeunitmap = {
    "p": 0.000000000001,
    "n": 0.000000001,
    "u": 0.000001,
    "m": 0.001,
    "":  1.0,
  }
  __rx_edge = compile(r"^(([01zx])(\S)|b([01zx]+)\s(\S+))$")
  
  def __init__(self, filename):
    self.__time = None
    self.__starttime = None
    self.__timescale = None
    self.__namemap = dict()
    self.__scope = None
    self.__is_enddef = False
    self.__conv = None
    self.__timemap = list()
    self.__edgecount = 0
    self.__lineno = None
    self.__filename = None
    self.readByFilename(filename)
  
  def readByFilename(self, filename):
    self.__filename = filename
    try:
      stream = open(filename, "r")
    except IOError, e:
      raise VCDError, str(e)
    in_definition = False
    line = ""
    self.__lineno = 0
    for raw in stream.readlines():
      self.__lineno += 1
      if not raw.strip(): continue
      #print "[%s]" % raw
      if in_definition:
        # suche nach "$end"
        line += raw
        if raw.find("$end") >= 0:
          self.__read_def(line.strip())
          line = ""
          in_definition = False
      else:
        if raw.lstrip()[0] == "$":
          if raw.find("$end") >= 0:
            # einzeilige Def.
            self.__read_def(raw.strip())
          else:
            in_definition = True
            line = raw
        elif raw[0] == "#":
          try:
            t = long(raw[1:].strip())
          except ValueError:
            raise VCDParserError(self, "can't read timeslot, wrong time format")
          if self.__time == None:
            self.__starttime = t
          self.__time = VCDTime(t, self.__conv)
          self.__timemap.append(self.__time)
        else:
          self.__parse_edge(raw.strip())
    stream.close()
  
  def __read_def(self, defstring):
    defstring = defstring[1:-4].strip() # erstes "$" und letztes "$end" entfernen
    l = defstring.split(None, 1)
    if len(l) == 1:
      keyword, tail = l[0], None
    else:
      keyword, tail = l
    # bearbeiten der Keywords
    if keyword == "enddefinitions":
      if self.__scope:
        raise VCDParserError(self, "unresolved scope")
      self.__is_enddef = True
    elif keyword == "upscope":
      l = self.__scope.split(".")
      if len(l) > 1:
        self.__scope = ".".join(l[-1])
      else:
        self.__scope = None
    elif keyword == "scope":
      typ, name = tail.split()
      if self.__scope:
        self.__scope += "." + name
      else:
        self.__scope = name
    elif keyword == "timescale":
      self.__parse_timescale(tail)
    elif keyword == "var":
      if not self.__scope:
        raise VCDParserError(self, "var definition not into scope definition")
      self.__parse_var(tail)
    elif keyword == "version":
      pass # skip definition
    elif keyword == "date":
      pass # skip definition
    elif keyword == "dumpvars":
      if self.__starttime == None:
        raise VCDParserError(self, "dumpvars is defined before starttime")
      tail = tail.replace("\r\n", "\n")
      for value in tail.split("\n"):
        self.__parse_edge(value, True)
    else:
      raise VCDParserError(self, "unknown definition: %s = '%s'" % (keyword, tail))
    
  def __parse_timescale(self, value):
    m = self.__rx_timescale.match(value)
    if not m:
      raise VCDParserError(self, "parser error on timescale setting: '%s'" % value)
    n, u = m.groups()
    self.__conv = self.__timeunitmap[u] * long(n)
    
  def __parse_var(self, value):
    l = value.split()
    typ, size, vid, name = l[:4]
    size = int(size)
    self.__namemap[vid] = VCDVar(self, typ, size, self.__scope + "." + name)
    
  def __parse_edge(self, value, isDump = False):
    m = self.__rx_edge.match(value.strip())
    if not m:
      raise VCDParserError(self, "format error in change line: '%s'" % value)
    l = m.groups()
    if l[1] == None:
      v, i = l[3:5]
    else:
      v, i = l[1:3]
    if not i in self.__namemap:
      raise VCDParserError(self, "format error in change line: '%s': id not found" % value)
    var = self.__namemap[i]
    if var.size < len(v):
      raise VCDParserError(self, "format error in change line: '%s': size exceeded, expected <= %d, found = %d" % (value, var.size, len(v)))
    try:
      e = VCDEdge(var, self.__time, v, isDump)
    except VCDInternalError, e:
      raise VCDParserError(self, str(e))
    self.__time.add(e)
    var.add(e)
    if not isDump: self.__edgecount += 1
    
  @property
  def starttime(self): return self.__starttime
  
  @property
  def endtime(self): return self.__time.internalTime
  
  @property
  def edgecount(self): return self.__edgecount
  
  @property
  def timecount(self): return len(self.__timemap) - 1
  
  @property
  def _filename(self): return self.__filename or "<unknown>"
  
  @property
  def _lineno(self): return self.__lineno or -1
  
  @property
  def conv2sec(self):
    def _t2s(itime):
      return itime * self.__conv
    return _t2s
    
  @property
  def conv2intern(self):
    def _s2t(stime):
      return long(stime / self.__conv)
    return _s2t
    
  @property
  def variables(self):
    result = self.__namemap.values()
    result.sort(None, lambda x: x.name)
    return result
    
  def getVariable(self, name):
    for v in self.__namemap.values():
      if v.name == name: return v
    raise VCDError, "variable '%s' not found" % name
    
# EOF