Writing a VCD to toggle-count generator in Python

P

Paddy3118

Guest
http://paddy3118.blogspot.com/2008/03/writing-vcd-to-toggle-count-generator.html

- Paddy.
 
Paddy3118 wrote:

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.
 
On Mar 25, 9:16 pm, Andy Botterill <a...@plymouth2.demon.co.uk> wrote:
Paddy3118 wrote:

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-gen...

- 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.
 
Hi 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.
 
On May 6, 9:40 am, mark.new...@microemissive.com wrote:
Hi 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.
 
Hi 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'
 
Hi Mark,

Thanks for modifying the Paddy's code. The modification you have done works properly but the only problem is related to running vcd files bigger than 4.5 Gbytes. For this capacity I received the following error in the terminal. It will be great if you guide me about this issue.

Traceback (most recent call last):
File "VCD2ToggleCount.py", line 225, in <module>
vcd_toggle_count(vcdfile)
File "VCD2ToggleCount.py", line 221, in vcd_toggle_count
raise "Don't understand: %s After %i words" % (token, count)
MemoryError

Sincerely,
Artemis
 
Hi Mark,

Thanks for modifying the Paddy's code. The modification you have done works properly but the only problem is related to running vcd files bigger than 4.5 Gbytes. For this capacity I received the following error in the terminal. It will be great if you guide me about this issue.

Traceback (most recent call last):
File "VCD2ToggleCount.py", line 225, in <module>
vcd_toggle_count(vcdfile)
File "VCD2ToggleCount.py", line 221, in vcd_toggle_count
raise "Don't understand: %s After %i words" % (token, count)
MemoryError

Sincerely,
Artemis
 
Hi Paddy,

Thanks for sharing your script and your experience. when I ran the python file, the following result is appeared for all the wires which shows that the script is not work well for my vcd files because there are lots of zero2one and one2zero for each wire, but the covered value is equal to FAIL10 for all of them. Could you please tell me what is the exact problem?

"TOP.chip_top.Rocket.ioNetwork.ClientTileLinkNetworkPort.io_network_grant_bits_payload_manager_xact_id": ('FAIL10', 0, 0)

Sincerely,
Artemis
 

Welcome to EDABoard.com

Sponsor

Back
Top