Do you differentiate between a glitch and data that is present for a
clock cycle? Andy
> http://paddy3118.blogspot.com/2008/03/writing-vcd-to-toggle-count-generator.html
>
> - Paddy.
In my application that is not required, so has not been added. I am
under a time constraint so was careful to do the minimum, get that
working, and only add features when neccessary.
I have already thought about adding a glitch rejecter as a separate
stage that would change base VCD to glitch-removed VCD so you could
run it as a separate process as the front end of a pipe feeding the
toggle generating code. That way I might make better use of our multi-
core compute cluster and keep the code complexity of the individual
programs down (they could also share a lot of the parsing code
directly via module import).
- Paddy.
I recently trialled your VCD toggle count code and came across a
problem with it. I use ModelSim so the VCD output may be a little
different to what your simulator produces. Anyway the problem
occurred on $var lines, the example I have has the following syntax:
$var reg 1 " clk $end
$var reg 8 # data [7:0] $end
The first line is parsed okay but the second one caused problems. I
changed the vcd_var function to take into account the extra bus
information. I have included the changes below, you may be able to
think of a better way as my Python knowledge is fairly limited.
def vcd_var(tokeniser, keyword):
var_options = tuple(takewhile(lambda x: x != "$end", tokeniser))
if len(var_options) == 4 :
var_type, size, identifier_code, reference = var_options;
else :
var_type, size, identifier_code, reference, bus_size =
var_options;
reference = vcd.scope + [('var', reference)]
vcd.idcode2references[identifier_code].append( (var_type, size,
reference))
vcd.reference2idcode[tuple(reference)] = identifier_code
vcd.id2stats[identifier_code] = IdStats(size)
One other problem I noticed with your code is when reporting which
signals in a bus where toggling it reversed the bit order.
I have also expanded your code to get a transition count of all the
signals in a simulation as I wanted to use this figure to generate a
guesstimate of the power required. If you would like this code let me
know and I will email it to you.
You also mentioned you made some updates to the code, have you posted
these onto the web anywhere?
Cheers,
Mark
ps. If you see a similar email to this one, its because I thought the
original post did not work.
Mark,
I'll try re-visiting this code this weekend. If you have any more
updates I'll take a look and maybe post the whole thing on Google code
as a project.
I had started to work on a parallel version to make use of multiple
cores (which I must get for home), and compute farms (LSF and
GridEngine). I haven't got the parallel one working yet though.
Anyone else interested?
- Paddy.
Here is the code with my changes inserted.
Cheers,
Mark
#######################################################
#!python
'''
Extract toggle count from vcd file
Refer to IEEE standard 1364 2001
(http://inst.eecs.berkeley.edu/~cs150/ProtectedDocs/verilog-ieee.pdf)
Author Donald 'Paddy' McCarthy (C) 24 March 2008
'''
from __future__ import with_statement
from itertools import dropwhile, takewhile, izip
from collections import defaultdict
from pprint import pprint as pp
vcdfile = r"dump.vcd"
class VCD(object):
def __init__(self):
self.scope = []
self.idcode2references = defaultdict(list)
self.reference2idcode = dict()
self.enddefinitions = False
self.id2stats = dict() # Maps id to its accumulated
statistics
def textstats(self):
total, updown, uponly, downonly = 0,0,0,0
total_transistions = 0
out = []
for ref in sorted(self.reference2idcode.keys()):
id = self.reference2idcode[ref]
stats = self.id2stats[id]
if stats.size == 1 :
total +=1
total_transistions = total_transistions +
stats.transistions
if stats.zero2one and stats.one2zero:
updown +=1
covered = 'PASS'
elif stats.zero2one:
uponly +=1
covered = 'FAIL0'
elif stats.one2zero:
downonly +=1
covered = 'FAIL1'
else:
covered = 'FAIL10'
out.append( " %-50s %s" % ( '"'+".".join(x[1] for x
in ref)+'":', (covered, stats.zero2one, stats.one2zero)) )
else:
total += stats.size
for count, (one2zero, zero2one, transistions) in
enumerate(izip(stats.one2zero, stats.zero2one, stats.transistions)) :
total_transistions = total_transistions +
transistions
if zero2one and one2zero:
updown +=1
covered = 'PASS'
elif zero2one:
uponly +=1
covered = 'FAIL0'
elif stats.one2zero:
downonly +=1
covered = 'FAIL1'
else:
covered = 'FAIL10'
name = ".".join( x[1] for x in (ref+
(('BIT:','<'+str(count)+'>'),)) )
out.append( " %-50s %s" % ( '"'+name+'":',
(covered, zero2one, one2zero)) )
header = "# TOGGLE REPORT: %g %%, %i / %i covered. %i up-only,
%i down-only." % (
updown/1.0/total*100, updown, total, uponly, downonly )
body = "toggle={\n" + "\n".join(out) + '\n }'
trans = []
trans.append( " \"transistion_count
\": (%d)" % ( total_transistions) )
trans_count = "total_transistions={\n" + "\n".join(trans) +
'\n }'
return header, body, trans_count
def scaler_value_change(self, value, id):
if value in '01' :
stats = self.id2stats[id]
if not stats.value:
stats.value = value
elif stats.value != value:
stats.value = value
stats.transistions = stats.transistions + 1;
if value == '0':
stats.one2zero +=1
else:
stats.zero2one +=1
def vector_value_change(self, format, number, id):
if format == 'b':
stats = self.id2stats[id]
extend = stats.size - len(number)
if extend:
number = ('0' if number[0] == '1' else
number[0])*extend + number
newdigit, newone2zero, newzero2one, newtransistions = [],
[],[],[]
for digit, olddigit, one2zero, zero2one, transistions in
izip(number,
stats.value,
stats.one2zero,
stats.zero2one,
stats.transistions) :
if digit in '01' and olddigit and olddigit != digit:
transistions = transistions + 1;
if digit == '0':
one2zero +=1
else:
zero2one +=1
elif digit not in '01':
digit = olddigit
newdigit.append(digit)
newone2zero.append(one2zero)
newzero2one.append(zero2one)
newtransistions.append(transistions)
stats.value, stats.one2zero, stats.zero2one,
stats.transistions = newdigit, newone2zero, newzero2one,
newtransistions
class IdStats(object):
def __init__(self, size):
size = int(size)
self.size = size
if size == 1 :
self.value = ''
self.zero2one = 0
self.one2zero = 0
self.transistions = 0
else :
# stats for each bit
self.value = ['' for x in range(size)]
self.zero2one = [0 for x in range(size)]
self.one2zero = [0 for x in range(size)]
self.transistions = [0 for x in range(size)]
def __repr__(self):
return "<IdStats: " + repr((self.size, self.value,
self.zero2one, self.one2zero, self.transistions)) + ">"
vcd = VCD()
def parse_error(tokeniser, keyword):
raise "Don't understand keyword: " + keyword
def drop_declaration(tokeniser, keyword):
dropwhile(lambda x: x != "$end", tokeniser).next()
def save_declaration(tokeniser, keyword):
vcd.__setattr__(keyword.lstrip('$'),
" ".join( takewhile(lambda x: x != "$end",
tokeniser)) )
vcd_date = save_declaration
vcd_timescale = save_declaration
vcd_version = save_declaration
def vcd_enddefinitions(tokeniser, keyword):
vcd.enddefinitions = True
drop_declaration(tokeniser, keyword)
def vcd_scope(tokeniser, keyword):
vcd.scope.append( tuple(takewhile(lambda x: x != "$end",
tokeniser)))
def vcd_upscope(tokeniser, keyword):
vcd.scope.pop()
tokeniser.next()
def vcd_var(tokeniser, keyword):
# var_type, size, identifier_code, reference =
tuple(takewhile(lambda x: x != "$end", tokeniser))
var_options = tuple(takewhile(lambda x: x != "$end", tokeniser))
if len(var_options) == 4 :
var_type, size, identifier_code, reference = var_options;
else :
var_type, size, identifier_code, reference, bus_size =
var_options;
reference = vcd.scope + [('var', reference)]
vcd.idcode2references[identifier_code].append( (var_type, size,
reference))
vcd.reference2idcode[tuple(reference)] = identifier_code
vcd.id2stats[identifier_code] = IdStats(size)
def vcd_dumpall(tokeniser, keyword): pass
def vcd_dumpoff(tokeniser, keyword): pass
def vcd_dumpon(tokeniser, keyword): pass
def vcd_dumpvars(tokeniser, keyword): pass
def vcd_end(tokeniser, keyword):
if not vcd.enddefinitions:
parse_error(tokeniser, keyword)
keyword2handler = {
# declaration_keyword ::=
"$comment": drop_declaration,
"$date": vcd_date,
"$enddefinitions": vcd_enddefinitions,
"$scope": vcd_scope,
"$timescale": vcd_timescale,
"$upscope": vcd_upscope,
"$var": vcd_var,
"$version": vcd_version,
# simulation_keyword ::=
"$dumpall": vcd_dumpall,
"$dumpoff": vcd_dumpoff,
"$dumpon": vcd_dumpon,
"$dumpvars": vcd_dumpvars,
"$end": vcd_end,
}
keyword2handler = defaultdict(parse_error, keyword2handler)
def vcd_toggle_count(vcdfile):
fp = open(vcdfile)
tokeniser = (word for line in fp for word in line.split() if word)
for count,token in enumerate(tokeniser):
if not vcd.enddefinitions:
# definition section
if token != '$var':
print token
keyword2handler[token](tokeniser, token)
else:
if count % 10000 == 0:
print count, "\r",
c, rest = token[0], token[1:]
if c == '$':
# skip $dump* tokens and $end tokens in sim section
continue
elif c == '#':
vcd.now = rest
elif c in '01xXzZ':
vcd.scaler_value_change(value=c, id=rest)
elif c in 'bBrR':
vcd.vector_value_change(format=c.lower(), number=rest,
id=tokeniser.next())
else:
raise "Don't understand: %s After %i words" % (token,
count)
print count
fp.close()
vcd_toggle_count(vcdfile)
header, body, transistion_count = vcd.textstats()
print '\n'+header+'\n\n'+body+'\n\n'+transistion_count+'\n'