diff --git a/myhdl/__init__.py b/myhdl/__init__.py index 1c37c334..9ac0e57f 100644 --- a/myhdl/__init__.py +++ b/myhdl/__init__.py @@ -60,5 +60,5 @@ from _Simulation import Simulation from _misc import instances, processes from _always_comb import always_comb from _enum import enum -from _trace_sigs import trace_sigs +from _traceSignals import traceSignals diff --git a/myhdl/_traceSignals.py b/myhdl/_traceSignals.py new file mode 100644 index 00000000..b9e929a0 --- /dev/null +++ b/myhdl/_traceSignals.py @@ -0,0 +1,242 @@ +# This file is part of the myhdl library, a Python package for using +# Python as a Hardware Description Language. +# +# Copyright (C) 2003 Jan Decaluwe +# +# The myhdl library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" myhdl traceSignals module. + +""" + +__author__ = "Jan Decaluwe " +__revision__ = "$Revision$" +__date__ = "$Date$" + +from __future__ import generators + +import sys +from inspect import currentframe, getframeinfo, getouterframes +import compiler +import re +import string +import time +from types import FunctionType +import os +path = os.path +import shutil + +from myhdl import _simulator, Signal, __version__ +from myhdl._util import _isGenSeq, _isgeneratorfunction + +_tracing = 0 + +class Error(Exception): + """ traceSignals Error""" + def __init__(self, arg=""): + self.arg = arg + def __str__(self): + msg = self.__doc__ + if self.arg: + msg = msg + ": " + str(self.arg) + return msg + +class TopLevelNameError(Error): + """result of traceSignals call should be assigned to a top level name""" + +class ArgTypeError(Error): + """traceSignals first argument should be a classic function""" + +class NoInstancesError(Error): + """traceSignals returned no instances""" + +re_assign = r"""^ + \s* + (?P\w[\w\d]*) + (?P\[.*\])? + \s* + = + """ + +rex_assign = re.compile(re_assign, re.X) + + +def traceSignals(dut, *args, **kwargs): + global _tracing + if _tracing: + return dut(*args, **kwargs) # skip + if not callable(dut): + raise ArgTypeError("got %s" % type(dut)) + if _isgeneratorfunction(dut): + raise ArgTypeError("got generator function") + _tracing = 1 + try: + outer = getouterframes(currentframe())[1] + name = _findInstanceName(outer) + if name is None: + raise TopLevelNameError + h = _HierExtr(name, dut, *args, **kwargs) + vcdpath = name + ".vcd" + if path.exists(vcdpath): + backup = vcdpath + '.' + str(path.getmtime(vcdpath)) + shutil.copyfile(vcdpath, backup) + os.remove(vcdpath) + vcdfile = open(vcdpath, 'w') + _simulator._tracing = 1 + _simulator._tf = vcdfile + _writeVcdHeader(vcdfile) + _writeVcdSigs(vcdfile, h.instances) + finally: + _tracing = 0 + return h.m + + +def _findInstanceName(framerec): + f = framerec[0] + c = framerec[4][0] + m = rex_assign.match(c) + name = None + if m: + basename, index = m.groups() + if index: + il = [] + for i in index[1:-1].split("]["): + try: + s = str(eval(i, f.f_globals, f.f_locals)) + except: + break + il.append(s) + else: + name = basename + '[' + "][".join(il) + ']' + else: + name = basename + return name + + +class _HierExtr(object): + + def __init__(self, name, dut, *args, **kwargs): + self.names = [name] + self.instances = instances = [] + self.level = 0 + sys.setprofile(self.extractor) + try: + _top = dut(*args, **kwargs) + finally: + sys.setprofile(None) + if not instances: + raise NoInstancesError + self.m = _top + instances.reverse() + instances[0][1] = name + + def extractor(self, frame, event, arg): + if event == "call": + outer = getouterframes(frame)[1] + name = _findInstanceName(outer) + self.names.append(name) + if name: + self.level += 1 + elif event == "return": + name = self.names.pop() + if name: + if _isGenSeq(arg): + sigdict = {} + for dict in (frame.f_locals, frame.f_globals): + for n, v in dict.items(): + if isinstance(v, Signal): + sigdict[n] = v + i = [self.level, name, sigdict] + self.instances.append(i) + self.level -= 1 + + +_codechars = "" +for i in range(33, 127): + _codechars += chr(i) +_mod = len(_codechars) + +def _genNameCode(): + n = 0 + while 1: + yield _namecode(n) + n += 1 + +def _namecode(n): + q, r = divmod(n, _mod) + code = _codechars[r] + while q > 0: + q, r = divmod(q, _mod) + code = _codechars[r] + code + return code + +def _writeVcdHeader(f): + print >> f, "$date" + print >> f, " %s" % time.asctime() + print >> f, "$end" + print >> f, "$version" + print >> f, " MyHDL %s" % __version__ + print >> f, "$end" + print >> f, "$timesscale" + print >> f, " 1ns" + print >> f, "$end" + print >> f + +def _writeVcdSigs(f, instances): + curlevel = 0 + namegen = _genNameCode() + siglist = [] + for level, name, sigdict in instances: + delta = curlevel - level + curlevel = level + assert(delta >= -1) + if delta >= 0: + for i in range(delta + 1): + print >> f, "$upscope $end" + print >> f, "$scope module %s $end" % name + for n, s in sigdict.items(): + if not s._tracing: + s._tracing = 1 + s._code = namegen.next() + siglist.append(s) + w = s._nrbits + if w: + if w == 1: + print >> f, "$var reg 1 %s %s $end" % (s._code, n) + else: + print >> f, "$var reg %s %s %s $end" % (w, s.code, n) + else: + print >> f, "$var real 1 %s %s $end" % (s._code, n) + for i in range(curlevel): + print >> f, "$upscope $end" + print >> f + print >> f, "$enddefinitions $end" + print >> f, "$dumpvars" + for s in siglist: + s._printVcd() # initial value + print >> f, "$end" + + + + + + + + + + + + + diff --git a/myhdl/test_all.py b/myhdl/test_all.py index 386b8482..d55ca2e2 100644 --- a/myhdl/test_all.py +++ b/myhdl/test_all.py @@ -24,10 +24,10 @@ __revision__ = "$Revision$" __date__ = "$Date$" import test_Simulation, test_Signal, test_intbv, test_Cosimulation, test_misc, \ - test_always_comb, test_bin, test_trace_sigs, test_enum, test_concat + test_always_comb, test_bin, test_traceSignals, test_enum, test_concat modules = (test_Simulation, test_Signal, test_intbv, test_misc, test_always_comb, - test_bin, test_trace_sigs, test_enum, test_concat + test_bin, test_traceSignals, test_enum, test_concat ) import unittest diff --git a/myhdl/test_traceSignals.py b/myhdl/test_traceSignals.py new file mode 100644 index 00000000..c08afdfe --- /dev/null +++ b/myhdl/test_traceSignals.py @@ -0,0 +1,162 @@ +# This file is part of the myhdl library, a Python package for using +# Python as a Hardware Description Language. +# +# Copyright (C) 2003 Jan Decaluwe +# +# The myhdl library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" Run the unit tests for bin """ + +__author__ = "Jan Decaluwe " +__revision__ = "$Revision$" +__date__ = "$Date$" + +from __future__ import generators + +import random +from random import randrange +random.seed(1) # random, but deterministic +import sys +import os +path = os.path + +import unittest +from unittest import TestCase +import shutil + +from myhdl import delay, Signal, Simulation +from myhdl._traceSignals import traceSignals, TopLevelNameError, ArgTypeError, \ + NoInstancesError + +QUIET=1 + +def gen(clk): + while 1: + yield delay(10) + clk.next = not clk + +def fun(): + clk = Signal(bool(0)) + inst = gen(clk) + return inst + +def dummy(): + clk = Signal(bool(0)) + inst = gen(clk) + return 1 + +def top(): + inst = traceSignals(fun) + return inst + +def top2(): + inst = [{} for i in range(4)] + j = 3 + inst[j-2]['key'] = traceSignals(fun) + return inst + + +class TestTraceSigs(TestCase): + + def setUp(self): + self.paths = paths = ["dut.vcd", "inst.vcd"] + for p in paths: + if path.exists(p): + os.remove(p) + + def tearDown(self): + for p in self.paths: + if path.exists(p): + os.remove(p) + + def testTopName(self): + p = "dut.vcd" + dut = traceSignals(fun) + try: + traceSignals(fun) + except TopLevelNameError: + pass + else: + self.fail() + + def testArgType1(self): + p = "dut.vcd" + try: + dut = traceSignals([1, 2]) + except ArgTypeError: + pass + else: + self.fail() + + def testArgType2(self): + p = "dut.vcd" + try: + dut = traceSignals(gen, Signal(0)) + except ArgTypeError: + pass + else: + self.fail() + + def testReturnVal(self): + p = "dut.vcd" + try: + dut = traceSignals(dummy) + except NoInstancesError: + pass + else: + self.fail() + + def testHierarchicalTrace1(self): + p = "inst.vcd" + top() + self.assert_(path.exists(p)) + + def testHierarchicalTrace2(self): + pdut = "dut.vcd" + psub = "inst.vcd" + dut = traceSignals(top) + self.assert_(path.exists(pdut)) + self.assert_(not path.exists(psub)) + + def testIndexedName(self): + p = "dut[1][0].vcd" + dut = [[None] * 3 for i in range(4)] + i, j = 0, 2 + dut[i+1][j-2] = traceSignals(top) + self.assert_(path.exists(p)) + os.remove(p) + + def testIndexedName2(self): + p = "inst[1][key].vcd" + top2() + self.assert_(path.exists(p)) + os.remove(p) + + def testBackupOutputFile(self): + p = "dut.vcd" + dut = traceSignals(fun) + Simulation(dut).run(1000, quiet=QUIET) + size = path.getsize(p) + pbak = p + '.' + str(path.getmtime(p)) + self.assert_(not path.exists(pbak)) + dut = traceSignals(fun) + self.assert_(path.exists(p)) + self.assert_(path.exists(pbak)) + self.assert_(path.getsize(pbak) == size) + self.assert_(path.getsize(p) < size) + os.remove(pbak) + +if __name__ == "__main__": + unittest.main()