pyhvlpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · pyhvl 0.3 1 introduction pyhvl is a...

37
PyHVL 0.3 PyHVL A verification tool developed by tom loftus ( [email protected] ) ben smith ( [email protected] ) jim greene ([email protected] ) xiang wu ( [email protected] ) original idea by tom scheffler ( [email protected] )

Upload: others

Post on 09-Jul-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

PyHVLA verification tool

developed by

tom loftus ( [email protected] )

ben smith ( [email protected] )

jim greene ([email protected] )

xiang wu ( [email protected] )

original idea by

tom scheffler ( [email protected] )

Page 2: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

Table of Contents

1 Introduction...............................................31.1 Why Python?.....................................41.2 Related Projects.................................51.3 Structure of this Document...............6

2 Examples...................................................62.1 Other Examples.................................82.2 Considerations in Implementing$pyhvl......................................................82.3 Memory Allocation and InstanceWorkareas................................................92.4 Mapping VPI structures to PythonData Structures........................................92.5 Callbacks in Python.........................112.6 New Callback Facility in Version0.11........................................................12

3 Framework Facilities..............................133.1 Configuration..................................133.2 Bit Vectors......................................143.3 Save/Restore (Persistence)..............15

4 PyHVL and Generators...........................164.1 A Brief Description of PythonGenerator Functions..............................174.2 Using a Generator in a verilogsimulation..............................................18

5 PyHVL Classes.......................................195.1 Simulation Time..............................195.2 Bitvector..........................................195.3 Signal..............................................195.4 Event...............................................205.5 Task.................................................20

6 Reasons...................................................216.1 sigchange(sig).................................216.2 posedge(sig)....................................216.3 negedge(sig)....................................216.4 timeout(t).........................................216.5 waitevent(e).....................................226.6 status(task).......................................226.7 vpireason(cbreason).........................23

7 Task Functions........................................237.1 currenttask().....................................237.2 currenttime()....................................247.3 currentsystf()...................................247.4 currentreason().................................247.5 currentreasonindex()........................25

8 Other Functions.......................................258.1 taskmsg(s)........................................258.2 error(s).............................................258.3 warning(s)........................................258.4 vpi_print(s)......................................258.5 traceon()/traceoff()..........................258.6 simstop()..........................................268.7 simfinish().......................................268.8 simreset().........................................26

9 Using PyHVL..........................................269.1 Building the top-level......................27

9.1.1 For each output port.................279.1.2 For each input port...................279.1.3 For each inout port...................279.1.4 For internal nodes....................28

9.2 Connecting to Verilog Nets.............289.3 Tasks and Typical Patterns..............299.4 Composing Tasks to FormTransactions...........................................309.5 Using Classes to Group Tasks.........31

10 Advanced Topics...................................3210.1 VPI Underneath.............................3210.2 Generating Tasks Dynamically.....3210.3 Generating Reasons Dynamically .3310.4 Synthesizing SynchronizationPrimitives..............................................33

11 Conclusion............................................3312 References.............................................3513 Addenda................................................36

13.1 Development Environment............3613.2 Documentation Tool......................3713.3 You Can Help................................37

Page 3: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

1 IntroductionPyHVL is a framework for writing Verilog PLI applications in Python. PLI applications areforeign C routines that are linked into a Verilog simulator to add new functions ("systemfunctions") to a Verilog program. Simple PLI applications can be as trivial as one that addsa single C math function ("cos" for example) to Verilog. More complex PLI applicationsadd entire verification languages or waveform analysys to Verilog. PyHVL links thePython interpreter as a PLI application and exposes the VPI interface to the Python PLIApplication writer.

The C-based VPI interface is very general, but complicated enough that it can be achallenge to implement even the simplest PLI Application. PyHVL does much to simplifythe process by introducing an object-oriented framework over the basic PLI interface. InPyHVL, each Verilog instance of a call to the $pyhvl system function is mapped to aninstance of the "pyhvl.systf" class in Python. The base class defines empty methodscorresponding to simulation events that result in function callbacks. Most applications arewritten by simply overriding the base class definitions in an extension class. Mostcommonly, the “calltf” method is overridden. This method is called whenever the $pyhvlinstance is called by the Verilog code.

Here is an example of a very simple PyHVL application.

helloworld.v

module top; initial $pyhvl("example", "helloworld", "hw"); endmodule

helloworld.py

from PyHVL import *

class hw(systf): def calltf(): print "Hello World!\n"

The Python module "helloworld" defines a class "hw" whose base class is PyHVL's systf.The calltf method is over-ridden to print the now famous string. In Verilog, we call $pyhvlwith a unique identifying name ("example" here), the name of the module ("helloworld")and the name of the class of which to create an instance ("hw"). PyHVL initializes the

Page 4: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

instance of the Python hw class and does all of the work of arranging for its calltf methodto be called whenever Verilog execution reaches the $pyhvl call.

PyHVL does more than just map VPI callbacks to method calls. It exposes the entire VPIprogramming interface to Python. VPI object pointers and pointers to VPI structs aremapped to opaque data types in Python. VPI functions for traversing the Verilog hierarchyor accessing and setting values are available, as are functions for creating simulationcallback events. All VPI #define values are mapped to constants in the PyHVL module. Asan application framework, PyHVL also provides a 4-valued bit-vector data type, apredefined mechanism for error and warning handling, and a structured method toconfigure $pyhvl instances from configuration files and Verilog +plusargs. Thus, it is morethan a simple Python wrapper for VPI.

Because of the simplicity that PyHVL brings to writing a PLI application, it opens upinteresting possibilities for projects that might be considered difficult if starting fromscratch in C. Here are a few ideas.

● Rapid prototyping of a simplified reference model may be accomplished in Pythonearly in the design of a Verification testbench so that testbench development canproceed before RTL is available.

● Highly mathematical algorithms are more easily explored in languages other thanVerilog. Before committing to RTL, using Python's infinite length integers or add-on numerical libraries may prove to be a more agile environment for exploringalgorithmic tradeoffs.

● Verilog is not necessarily a good language for implementing job reporting,manipulating files and formatting text. These things are easy in Python. One caneven imagine a sophisticated reporting system that logs all simulation data to arelational database on another machine using a database connection. Python'sdatabase connection facility makes this easy.

● Custom GUI applications for displaying simulation progress become attractivewhen you can code them in a few hours. Python's Tkinter interface to Tk/Tcl isappropriate for this.

● Sometimes it is desirable to collect information about the system a job is running onin a large distributed simulation farm and adding it to the log file. Such a task istrivial in Python but not easy in Verilog.

● Very large simulations ( eg simulations of gate netlists) have very long compiletimes. It can be convenient to have debug capability through the VPI withoutwaiting out the long recompile time.

1.1 Why Python?

Python is an object-oriented interpreted language with a rich set of built in datatypes. Italso comes with a large library of useful modules for doing things like compressing files,

Page 5: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

producing HTML, serving data over pipes or opening up database connections. It canintegrate with Tk/TCL for graphical applications. Contributed libraries are available froman enormous number of sources. At this stage of its development, Python is a stablecomputing platform with a huge community of users. It has been in development for over10 years.

While the Python programming model is not necessarily compatible with any of thefeatures of Verilog, as a replacement for C for orchestrating VPI calls it presents a veryfriendly environment. Python offers a great prototyping environment, and modernprogramming constructs like exceptions help users write clear code. Being byte-compiled itoffers good performance for many applications.

Performance can be acceptable for many applications if the user sticks to one simplepremise: try not to call python on every edge of the primary system clock. Instead, gatherup some data using verilog, package it up, and exchange it with the python code only onhigher level boundaries, such as packet boundaries for networking applications.

1.2 Related Projects

There have been only a few projects attempting to integrate Python and Verilog. One iscalled ScriptSim by Dave Nelson [http://nelsim.com]. This approach runs Python as aseparate process and communicates with Verilog via a pipe. The communication protocolallows object values to be transferred back and forth, and allows Python to synchronizewith the simulator and schedule callbacks. ScriptSim has a nice Tk/TCL interface as well.

Another effort is the ScriptEDA project at Berkeley by Pinhong Chen [http://www-cad.eecs.berkeley.edu/~pinhong/scriptEDA]. This effort explored using SWIG (a powerfulpackage for mapping and linking C libraries into interpreters) [http://swig.org] to map theVPI interface into Python. The project did not add much structure or value beyond thistranslation.

Another related project is "Pivot", a commercial product that integrates Perl with Verilogthrough the PLI 1.0 interface [http://www.greenl.com/]. Pivot has a software layer thatseems similar to PyHVL but also provides many higher-level constructs on top of that layerto allow the design of complex testbenches in Perl.

The c++ verification world has a recent new open source toolset called truss/teal( http://www.trusster.com ), which addresses many of the same issues as PyHVL. The toolauthors have a book out based around their tool, and have plans to reimplement it in systemverilog.

EDA industry efforts to link programming languages into Verilog for test generation andmodeling include Vera, Specman and System C. All of these efforts are motivated by thefact that descrbing anything other than hardware is difficult in Verilog, whereas mosttestbenches are large software efforts. These languages all add features for describing"tasks" and synchronization primitives in a meaningful way, and hide the underlying PLI

Page 6: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

interface. In constrast, PyHVL opens up the VPI interface to the Python programmer.PyHVL is the higher-level software layer that presents a simplified abstraction of tasks andsynchronization.

1.3 Structure of this Document

The next section presents a slightly more full-featured example. The example is a“memory” model implemented using a Python hash table. The example shows a fairlyuseful application implemented in just a page of code. A similar application coded in Cwould be much more complicated. Understanding this example is a good place to start toget familiar with PyHVL.

Section 3 discusses the implementation of PyHVL. This may be of interest to thosefamiliar with implementing VPI applications, or those wondering how to embed Pythoninto a simulator. IThis section may be too low-level for those just wanting to know thebasics of PyHVL and wanting to trying out some examples.

Section 4 discusses the “Framework” facilities provided by PyHVL. While most of theexamples just show how simple applications are constructed, in a full-featured PLIapplication, there is a need for uniform ways to obtain configuration information andpresent error and warning messages. PyHVL provides the basics for further development.

Section 5 summarizes this article and presents some directions for future work. Finally,Section 6 presents some miscellaneous notes about the PyHVL/PyHVL project.

2 ExamplesThe following example demonstrates a sparse memory implemented using a hash table.The application would be called from Verilog with three Verilog register argumentsindicating a command (write or read), an address and the data. When called, the applicationattempts to turn the address value into a long integer. The long integer is used as the key tothe memory hash table, "mydata." If the conversion to along fails because there are x's or z's in the address, the call returns 0 and fails.

The rest of the application is straightforward. If a write command is requested, the datavalue is retrieved as a four-valued binary string and stored in the hash table. If a read isrequested, the memory location is looked up and applied to the data register using"put_value."

verilog code

reg mem;

Page 7: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

on (posedge clk) $pyhvl(mem, "memex", "memex", cmd, addr, data);

memex.py

class memex(systf):

def soscb(self): self.cmd = self.vobjs[0] self.addr = self.vobjs[1] self.data = self.vobjs[2]

self.mydata = { } # empty hash table

self.fmt = pack_s_vpi_value(vpiBinStringval, "01x")

def calltf(self)

addr_t = get_value_like(self.addr, self.fmt) addr_s = addr_t[1] cmd_t = get_value_like(self.cmd, self.fmt) cmd_s = cmd_t[1]

# parse as two-valued logic, return error is Xs or Zs try: addr_l = long(addr_s, 2) except: return 0

if cmd_s == "1": # write data_s = get_value_like(self.data, self.fmt) self.mydata[addr_l] = data_s

elif cmd_s == "0": # read try: data_s = self.mydata[addr_l] except: return 0 del = pack_s_vpi_time(vpiSimTime, 0, 0, 0.0) put_value(self.data, data_s, del, vpiTransportDelay) return 1

else: print "Unknown command\n" return 0

Page 8: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

2.1 Other Examples

PyHVL is distributed with a number of example applications that illustrate the basiccapabilities of the module and how to interact with the VPI interface through Python.These are described very briefly below. These examples are provided with the Pythoncode, a small Verilog top-level and a shell script that shows how to run the example withIcarus Verilog, Cver and NC-Verilog. Icarus and Cver are available for free.

PyHVL_mem - a sparse memory package, slightly different than the one shown above.

PyHVL_delay - a delay element showing how to use callbacks schedule events.

PyHVL_sr - a "stimulus/response" package. A stimulus/response file lists event times,stimuli to apply to nets and responses to check. This illustrates complex interactions withthe Verilog scheduler. An extension of this example could form the basis for a testgeneration package.

plusargs - how to parse and use plusargs.

shownets - show all of the nets in a given module.

checker - a task that looks for a sequence of integers over time. This example uses aPython "generator" function as a lightweight process so that the checker is properlyreentrant.

tkserver - demonstration of using Python's Tkinter interface to Tk/Tcl to build a simple Xwindow-based message facility for Verilog programs. This might be useful in a computeserver farm to allow simulation runs to send messages to a user's workstation.

rusage – print the execution hostname and resource statistics (CPU, IO, Swap) in the logfile.

These examples show that PyHVL can implement fairly complex behavior with not a lot ofcode. In fact, this is one of the main strengths of Python. With its full-featured built-in datatypes, many applications can be written without needing to define new classes. If you arenew to VPI or PyHVL, studying these examples should give a good understanding of thebasics of both.

2.2 Considerations in Implementing $pyhvl

The VPI interface is very powerful, but presents a number of challenges to implementingPLI applications. This section discusses a few of those challenges and describes howPyHVL handles them. For those not familiar with VPI, a good reference is[http://www.verilog.com/1364-2005_D3.pdf].

Page 9: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

2.3 Memory Allocation and Instance Workareas

Most Verilog implementations separate compile, elaborate and run phases of Verilogexecution. When writing a PLI application, the user is able to specify a compile-timefunction to be called for the application and a run-time (calltf) function to be called for theapplication. The user is severely limited as to the operations that can be performed atcompile time because compilation may be implemented as a separate Unix process bysome Verilog implementations. Thus, even implementing memory on behalf of the runtimeapplication is not permitted.

One operation that is allowed is to request that the simulator execute a callback on behalfof the application. PyHVL registers a start-of-simulation callback that is guaranteed to runjust before time 0 in the run phase of execution. It is in this callback that memory isallocated for each instance of an $pyhvl call. The requirement for a callback at start-of-simulation seems so basic that it is surprising that VPI does not provide this directly.

Each $pyhvl instance requires a separate workarea to maintain its instance-specificinformation. The most recent VPI specification describes two functions for this purpose:vpi_put_userdata and vpi_get_userdata. Most versions of Verilog now have thissupport, but some do not. For those versions of Verilog that do not yet have thesefunctions, PyHVL implements a simple closed growable hashtable to map system functionhandles to the workareas. The option is selectable at compile time by setting a few #definevariables.

2.4 Mapping VPI structures to Python Data Structures

The VPI interface defines a few C structures that must be manipulated by the user toperform operations in VPI. These are limited to just a few types and describe only simplestructures. The s_vpi_time struct is a good example. It is shown here.

struct s_vpi_time { unsigned int type, hi, lo; double simtime; };

When using the VPI interface in C, a programmer typically loads these structures withvalues to pass to VPI functions. To obtain values, the programmer passes a pointer to oneof these structures, and VPI fills it with values that can then be examined by theprogrammer. This use pattern does not map well to Python.

Instead, we implemented opaque pointers to these structures and provide a "pack" functionin Python that constructs a new s_vpi_time struct from a Python tuple and an "unpack"function that takes an opaque pointer and returns a tuple. From Python, an s_vpi_timeobject is an immutable object. This notion fits much better with Python's garbage

Page 10: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

collection.

The use pattern is pretty simple. When a Python VPI function requires an s_vpi_time type,the user creates one on the fly with a function that packs base types into a newly allocatedstruct.

t = pack_s_vpi_time(vpiSimTime, 10, 0, 0.0) put_value(vpi_h, value, t, vpiTransportDelay)

Such structs are also used as templates for result containes. This other use of these structs ismapped into Python in a slightly curious way. The s_vpi_time struct is a good example.The first field is a type tag that may take on either the value vpiSimTime orvpiScaledRealTime. If it is vpiSimTime then the low and high fields are used to describethe 64-bit simulation time. If the type field is vpiScaledRealTime, then the "real" fielddescribes time as a double.

To get the current time, in C the programmer sets the type field of an s_vpi_time struct andpasses it to get_time.

s_vpi_time t; t.type = vpiSimTime t.low = 0; t.high = 0; t.real = 0.0; vpi_get_time(vpiHandle, &t) /* use the return value somehow */ newtime = t.low + ...

Rather than adopt this directly and allow the VPI interface to modify allocated structures,in Python we chose to do something a little different. The user first creates a "template"s_vpi_time object and then passes it to a function that uses the template but returns a newstruct as the value.

t1 = pack_s_vpi_time(vpiSimTime, 0, 0, 0.0) t2 = get_time_like(vpiHandle, t1) (typ, lo, hi, real) = unpack_s_vpi_time(t2)

The value returned is a *new* s_vpi_time object that can be "unpacked" to query the returnvalue. This use pattern is sufficient to perform all required tasks, and is simple enough tolearn quickly. Its drawback is a greater impact on the memory allocation and collectionsystem.

Some VPI structs have union fields with variant field names for each of a number of fixednumber of different types: integer, double, string. When packing such a struct, PyHVLinfers the type field from the type of the Python argument. When unpacking the struct,PyHVL uses the type tag to infer the variant field to read. This mechanism is very simple

Page 11: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

but has been sufficient in practice.

2.5 Callbacks in Python

The implementation of callbacks in PyHVL deserves a discussion. PyHVL provides ansystem-function instance-specific callback, while VPI associates a callback with aparticular C function, but not a Verilog system-function instance.

VPI does however, allow a user to attach an identifying "char*" to each callback, and it isthis mechanism that we chose to use for our callback dispatch mechanism. The VPIstandard is not exactly clear whether the payload can be an arbitrary pointer or that it mustbe a string pointer. We chose to be very conservative and assume only that it is a stringpointer and create unique strings to identify callback instances.

The PyHVL systf class defines two methods for arbitrary callbacks: method"callmeback" schedules a callback for the instance, and arranges for the "callback" methodto be called.

class systf: def callmeback(self, reason, object, time, value, userdata): ...

def callback(self, cbdata, userdata): ...

When a user calls the callmeback method, PyHVL constructs a unique string (like agensym in Lisp) for the call, s. The string s is put in a dispatch table (represented as aPython dictionary) mapped to the calling instance object with copies of the callbackarguments. PyHVL then calls the VPI vpi_register_callback function with a pointer to thestatic PyHVL callback function, and the userdata field set to the string, s. When thesimulator calls the static PyHVL C callback function with string s, PyHVL looks up thecalling instance and data from the dispatch table and calls the "callback" method.

This implementation provides an object-oriented callback mechanism on top of the VPIcalls. It seems simple when explained here, but was not simple to arrive at. One source ofconfusion is that the VPI interface allows each callback request to specify an "obj" (object)field that the callback is associated with. In most simulators this field is relevant only if thecallback is associated with a structure like a wire or register and not an arbitrary systemfunction instance. The dispatch table implementation with uniques strings is an efficientand simple approach to the problem.

One drawback of this approach is that the size of the dispatch table could grow as thesimulation proceeds, since it stores information for every callback performed by PyHVL.This occurs because PyHVL cannot automatically determine which callbacks occur onlyonce and which may persist throughout the simulation. To address this problem, we requireusers to specify when a callback is "persistent", meaning that the callback may be executed

Page 12: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

many times. For calls that are not identified as "persistent", we remove their entry from thedispatch table after they are executed. This simple policy helps keep memory fromgrowing unacceptably.

2.6 New Callback Facility in Version 0.11

PyHVL Version 0.11 provides the existing callmeback/callback methods for each systfinstance, but also provides a more general mechanism that is not tied to a particular systfinstance. This streamlined interface is modeled after more traditional callback facilities andis much more flexible in that an instance of any callable Python type may be be the targetof a callback.

The PyHVL module function schedule_cb schedules a new VPI callback and returns ahandle. An example of the use of schedule_cb follows.

def mycbfn(reason, object, time, value, userdata): print “I am being called back for reason: %d\n” % reason

.... notime = pack_s_vpi_time(vpiSimTime, 0, 0, 0.0) noval = pack_s_vpi_value(vpiIntVal, 0)

h = schedule_cb(mycbfn, cbNextSimTime, vpih, notime, novalue, “”)

In the example above, we defined a callback function named “mycbfn.” All Python VPIcallback functions must have the formal parameter list shown above. These argumentsmirror those required by native C callback functions. When scheduling the callback withthe PyHVL function schedule_cb, the user gives the VPI reason, the object (a VPIhandle, if any, None if the C pointer should be NULL), a simulation time object, a VPIvalue object and any optional user data. When Verilog schedules the callback, it calls theuser function with the arguments shown.

If the callback has not yet transpired, it can be cancelled as shown below. This functionboth removes the entry from the dispatch table, and calls the VPI remove_cb andfree_object functions on the corresponding C-level callback object. cancel_cb(h)

Note that any callable Python object may be used as the callback function. Bound methodcalls are especially useful in this role. A short example follows. The interesting part ishighlighted in bold.

class myclass: def cb_example(self, reason, object, time, value, userdata): print "I am a bound method callback for: ", name

Page 13: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

inst = myclass() ...

h = schedule_cb(inst.cb_example, cbAfterDelay, vpih, t2, value, "")

This callback mechanism subsumes and extends the previous mechanism based on thecallmeback and callack methods. Any method of the same instance can be the target of acallback. The very short example below shows how a method of one class can scheduleanother method as the target of a callback. Following this design pattern ensures that anynumber of instances of a class can be created and the callbacks of each is restricted to themembers of the same instance.

class myclass: def meth1(self): ... self.h = schedule_cb(self.meth2, .... )

def meth2(self, reason, object, time, value, userdata): pass

3 Framework FacilitiesPyHVL offers facilities beyond that of simple interaction with the VPI interface. Thissection discusses some of those features.

3.1 Configuration

The first argument to the $pyhvl call is the "name" of the instance. Users of PyHVL shouldensure that each name in the system is unique. This name may be a string or a Verilogobject (usually a register). If it is a string, then the string is the name of the call. If theargument is a Verilog object, then its pathname becomes the name of the call.

Using a Verilog object makes it possible to write Verilog modules containing $pyhvl callsin a way that each instance will have a unique name. Consider the following example.

module PyHVL_module(i, o); in [0:0] i; out [0:0] o; reg [0:0] o_drv; reg id; assign o = o_drv; initial $pyhvl(id, "foo", "bar", i, o_drv); endmodule

Page 14: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

The path name of the id register will become the name of each $pyhvl instance.

The instance name is used for such mundane things as printing a welcome banner andoccasional debugging messages. However, the unique name becomes important for PyHVLapplications that require (or provide) configuration.

PyHVL uses Python's native ConfigParser class to look for configuration files in standardplaces (current directory, home directory, system install directory). Python configurationfiles have sections with named parameters and resemble ".ini" files that are found on somesystems. PyHVL adopts the convention that an instance looks for its configurationparameters in the section with its instance name. Consider the example below for aninstance that was given the unique name "top.config_example.id." The application can lookup its configuration parameter "myval" using a standard method.

file.py

from PyHVL import * def config_example(systf):

def soscb(): self.myval = self.get_config("myval")

PyHVL.cfg

[top.config_example.id] myval: 45

PyHVL also allows instance parameters to be specified on the Verilog command line withplus-args. PyHVL adopts the convention that a plusarg of the form "name:param" ismapped to the instance with the given name. If using the example above, a user couldoverride the configuration file default with a different value.

% verilog +top.config_example.id:myval=46 ...

This simple combination of unique instance names and structured configurationmechanisms provides a useful feature for writing reusable applications.

3.2 Bit Vectors

As a convenience, PyHVL provides a bit vector class and functions for getting the value of

Page 15: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

a Verilog variable as a bit vector and for setting the value of a Verilog variable. The bitvector class overrides standard Python operators (+, *, <<, >>, subscripting, etc) to makeoperations on bit vectors clear and convenient. Bit vectors can be initialized from integers,or four-valued strings in Verilog syntax.

x = BV("4'b01xz")

The example below shows A PyHVL application that computes a function on two Veriloginput variables and then schedules the result value to be applied two time units in thefuture. The list "self.vobjs" is provided by PyHVL for all instances: it is the list of VPIhandles to the objects in the $pyhvl call following the class name. Functions bv_from_vpiand bv_to_vpi get bit vectors from and send bit vectors to objects referenced by VPIhandles.

bitvect.v

$pyhvl("bvex", "bitvect", "bitvect_example", in0, in1, out)

bitvect.py

from PyHVL import * class bitvect_example(systf):

def calltf(self: val0 = bv_from_vpi(self.vobjs[0]) val1 = bv_from_vpi(self.vobjs[1]) delay = 2.0

retval = (val0 << 1) & (val1 >> 1) bv_to_vpi(self.vobjs[2], retval, delay)

The bit vector class represents its values as simple four-valued strings. Thus, computationson bit vectors are straightforward but not necessarily as efficient as they could be. Theconvenience of using the class may outweigh the performance consideration. In the future,we may consider trying to optimize this class.

3.3 Save/Restore (Persistence)

Writing a PLI application is tough enough. Writing one that plays well with the VerilogSave/Restore mechanism is even harder. PyHVL provides a nice solution to the problemlargely because it is based on Python. The "shelve" module is a standard part of a Pythondistribution defining a dictionary-like object that is mapped to a file. Users can save any

Page 16: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

type of Python structure (most nested and even recursive structures are supported) in thedictionary and when the shelve object is written to a file, the Python objects are "pickled"(serialized) so that they can be saved.

The standard PyHVL systf class defines empty Save/Restart simulation event methods, andtwo methods to help save and restore data from the shelve object. PyHVL calls the method"save_app_data" when Verilog does a system save, and calls method "restore_app_data"upon a restart. These methods default to empty function bodies. Implementing persistenceis as simple as what is shown in the example below.

class myclass(systf):

def save_app_data(self): self.save_data(self.mydata)

def restore_app_data(self): self.mydata = self.restore_data()

The methods "save_data" and "restore_data" simply put data in the shelve object and get itout using the systf instance name as the dictionary key. Other parts of PyHVL handleopening and closing the shelve object and reloading Python on Restart.

While many aspects of state can be saved this way, other things require more complicatedrestart operations. One example might be a PLI application that has registered callbackevents on certain signal changes. These callbacks would have to be re-registered uponrestart.

The name of the shelve file defaults to "PyHVL.gdbm", but can be set with configurationfile or plusargs using the key "PyHVL:shelve_file".

4 PyHVL and Generators

In PyHVL Python generator functions are a key component of the native language. In aregular python program generators are nothing more than functions that have somepersistence. That is, when a normal function returns to the caller, all local variables aredestroyed. However, a generator function, distinguished by the appearance of the 'yield'statement somewhere in the function, does not destroy its local variables. The variablesremain on the stack, so that the next time the function is called, it can ( and does) resumefrom the point of the yield statement.

In PyHVL, these Python generators are used to model simulation tasks in a cooperativemultitasking style. A typical PyHVL task suspends its execution with a yield statementlisting the reasons the task should be resumed. Typical reasons map directly to VPI

Page 17: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

callback reasons. PyHVL does not implement a scheduler of its own, rather, it relies onVerilog for all event scheduling through the use of callback functions. PyHVL is a simple,Python-based verification language.

4.1 A Brief Description of Python Generator Functions

This section is a detour into a very short explanation of Python generator functions forthose who have not encountered them before. Python generators are a very limited form ofcoroutine. This form of coroutine only returns execution to its caller and is sometimescalled a "semicoroutine" for this reason. They are most often used to produce sequences ofvalues, without creating a list of all the values at once.

The example below shows how to write a generator function that produces the Fibonaccisequence, one value at a time. The fib function is written as an infinite loop that producesone value of the sequence each time it resumes execution. The yield statement causes thestate of the generator function (fib) to be saved and returns the value to the caller.

Example 2

def fib(): a = 0 b = 1 while 1: yield b a = b b = a+1

# example of getting the first 10 values gen = fib() for i in range(10): val = gen.next() print val

Generator functions have many applications. They are useful in lexical analysis, to producethe tokens of an input stream only as needed. They are also a convenient way to writefunctions that represent state machines. PyHVL uses them as a very simple and lightweightsimulation task.

In PyHVL, the ultimate caller of all tasks is the Verilog scheduler itself - layered through aseries of C and Python function calls. When A PyHVL task invokes the "yield" statement,it suspends its execution at at the yield statement, and its state is saved in the Python heap.Because resumption "reasons" are due to the actions of the Verilog scheduler, a taskresumes as the "callee" of the scheduler.

Page 18: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

This approach is simple, low-overhead (generator functions have almost no additionaloverhead in Python), and elegant. The fact that the Verilog scheduler is the "caller" of alltasks, means that once the programmer understands the Verilog scheduler, he or sheunderstands all of the scheduling interactions that may occur in A PyHVL program. OtherVerilog verification lanagues often interpose an additional scheduler, and the interactionsbetween the two schedulers can lead to confusion.

4.2 Using a Generator in a verilog simulation

Because the Python interpreter is embedded in the Verilog simulator using the VPIinterface, testbench programs and simulation models written using PyHVL run with fullaccess to the Verilog simulation netlist database and can access and change signal values.The resulting system is essentially a single simulator, with the Verilog schedulerscheduling both Verilog and Python tasks. Synchronization modeling issues are simplifiedbecause a single scheduling algorithm unifies both languages.

A short example illustrates some basic capabilities such as how A PyHVL applicationattaches to Verilog nets, how it watches for value changes, and how it can schedule delays(“timeouts”).

Example 1

from PyHVL import *

def watcher(sig): print "Watching signal %s" % sig while 1:

# wait until either 'sig' changes, or 10 time units elapse yield sigchange(sig), timeout(10)

if currentreasonindex() == 0: print "New value for signal %s is %s" % (sig, sig.get())

if currentreasonindex() == 1: print "Timeout has elapsed. Wait again."

def main(systf):

s = signal("top.mysignal") # attach to verilog net t = task(watcher, s) # spawn a task to watch signal s

yield timeout(0) return

Page 19: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

The example above illustrates a simple task that watches a Verilog signal. The task'watcher' loops indefinitely, suspending its execution with a yield statement that lists thereasons it should be resumed. Here, the task waits until either the signal value changes, or a10 simulation tick timeout occurs ... whichever comes first. When the task resumes, it usesthe 'currentreasonindex' function to determine whether the first or second reason in theyield statement was triggered. The task prints an informative message and then loops.

The main task first creates a new signal object by attaching to the Verilog net or wire"top.mysignal." It then spawns the 'watcher' task, passing the signal as an initializationparameter. The main task yields with a null timeout and then exits, leaving its child taskrunning. The child 'watcher' task continues executing until the simulation completes on theVerilog side.

5 PyHVL Classes

The PyHVL system introduces only a few new classes and functions. Because it is built asA PyHVL application, all of VPI is available as well, but a knowledge of VPI is notnecessary to use PyHVL. This section gives a very brief overview of the major componentsof PyHVL. A good approach to using the information in this section is to examine theexamples in the distributrion and then read the information presented here.

5.1 Simulation Time

In PyHVL, all simulation times are long integer values. These are converted directly into64-bit vpiSimTime structures for Verilog as needed.

5.2 Bitvector

The BV class provides a four-valued bitvector. Operations on bitvectors include typicalbitwise AND and OR operations, as well as shifts, concatenation and other operations. TheBV class also provides a variety methods for creating random bitvectors and comparing“expect” values in simulation.

5.3 Signal

A PyHVL signal is a wrapper object for a VPI handle that can have a vector value.Typically, a signal is attached to a Verilog wire or reg. PyHVL can watch for valueschanges on a signal. A signal also has two main methods: get and set.

The get method retrieves the current value of the Verilog object and returns it as a four-

Page 20: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

valued bitvector instance of class BV. The set method takes a bitvector value and assignsit to the Verilog object at the current simulation time. The set method also optionallytakes a second parameter, a simulation time delay, which will cause the assignment to bescheduled at a future simulation time. By default, these scheduled assignments use thevpiPureTransportDelay method.

5.4 Event

Tasks may signal each other through event objects. An event is a simple object that has onemethod: post. A task may yield with the reason waitevent(e) to suspend itsexecution until another task performs a post operation on the event. Multiple tasks maywait on the posting of an event. The post method accepts an optional value, which isattached to the event at the time of posting. A waking task can examine the val field of anevent if it is expecting a value to be attached.

5.5 Task

The task is the main worker class in PyHVL. A task is initialized with a reference to agenerator, and the generator must yield with one or more instances of the reason class. Atask is most naturally viewed as a thread of execution in the Verilog scheduler.

The arguments to the task constructor are the generator function or method and thearguments that are to be passed to the generator when the task starts. A small example isshown below in which a task is started that prints a message after a simulation time delayof 10 ticks.

def genfn(msg, delaytime):

# Print the message after a delay yield timeout(delaytime) print “This is the message:”, msg

new_task = task(genfn, “Hello World!”, 10)

When a task yields, the function's execution is suspended and a VPI callback is scheduledfor each of its yield reasons. Whichever callback occurs first wakes the suspended task andcauses it to resumed. Other callbacks – even if they eventually occur – are simply ignoredby the task. Thus, a task links together a chain of VPI callbacks through a generatorfunction.

A task supports only one publicly usable method: kill. A running task can beprematurely terminated by another task using its kill() method.

A task also has a current status, which is available in its status instance variable. Thosestates currently implemented are: BORN , RUNNING, WAITING, EXITED and KILLED.

Page 21: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

A BORN task is one that has been initialized but has not started executing yet because itsfirst callback has not yet occurred. A task is RUNNING if it has resumed because of acallback. Only one task is ever in the RUNNING state. A WAITING task is one that hasyielded (suspended) and is waiting for a callback. An EXITED task has exited byexecuting a return statement, and a KILLED task is one whose kill() method has beencalled.

6 ReasonsReasons are the objects used with a yield statement to suspend the execution of a task.Reasons are so named because most map directly to VPI callback reasons:cbValueChange, cbAfterDelay, etc, and are the "reason" a task should resumeexecution. The family of reasons are listed below.

6.1 sigchange(sig)

The sigchange reason is used to indicate that the task wishes to be resumed upon anysignal change to the signal object, sig. A sigchange reason causes a task to resume in acbReadWriteSynch region (see a Verilog scheduling algorighm description fordetails).

6.2 posedge(sig)

The posedge reason is used to indicate that the task wishes to remain suspended until thevalue of the signal transitions from a 0 to a 1. It is a special case of the sigchangereason.

6.3 negedge(sig)

The negedge reason is used to indicate that the task wishes to remain suspended until thevalue of the signal transitions from a 1 to a 0. It is also a special case of the sigchangereason.

6.4 timeout(t)

The timeout reason is used to indicate that the task wishes to be resumed after the time,t, has elapsed. It maps directly to a VPI cbAfterDelay callback. Note that this callbackwill resume the task at the beginning of a Verilog timestep, before other events have beenprocessed. This behavior is appropriate for tasks that initiate signal changes immediatelyfollowing a timeout, like the clock generator example below.

Page 22: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

def clockgen(sig_d): yield timeout(cycle_time/2) sig_d.set(BV(“1'b1”)) yield timeout(cycle_time – (cycle_time/2)) sig_d.set(BV(“1'b0”))

6.5 waitevent(e)

The waitevent reason is used with an event, e, to indicate that the task should beresumed when another task performs a post operation on the event e. The post actioncauses the task to be immediately resumed. The example below shows how an event couldbe used to synchronize two free-running tasks.

def task1(e): yield timeout(10) print “Posting the event in task1.” e.post()

def task2(e) yield waitevent(e) print “Event seen in task2.”

# The parent creates a new event and starts both tasks. e = event() t1 = task(task1, e) t2 = task(task2, e)

The user may also attach a value to the event for passing values between the posting andthe waiting task.

6.6 status(task)

The status reason is used with a task to indicate that the task should be resumed whenthe argument task undergoes a status change. Currently, the only status changeimplemented is when a task exits. Its status transitions to EXITED. The pattern for startinga task and waiting for it to exit is the following.

def taskparent(): ... t = task(taskchild, args ...) yield status(t) # wait for child to complete

Page 23: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

6.7 vpireason(cbreason)

Arbitrary VPI callbacks may be scheduled using vpireason. Typical callback reasonsare cbNextSimTime, or cbReadOnlySynch. This class is intended for users withsome knowledge of the VPI interface. A simple example using this type of reason is toconstruct a “strobe”-type synchronization point. When dumping signals, it is important thatthe dumping function gets the final-final value of the net it wants to dump. To do this, thetask must first wait for a value change, and then must wait until all updates to that signalhave been processes. The following shows how to do this in PyHVL.

def dumper(sig): print "Dumping signal %s" % sig while 1:

# wait until 'sig' changes yield sigchange(sig)

# wait again for ReadOnlySynch region yield vpireason(PyHVL.cbReadOnlySynch)

print “Value: “, sig.get()

Of cousre, a more useful dumping function would print the signal name and the currentsimulation time, but these details have been omitted here.

7 Task FunctionsA running task can query the environment to obtain information about itself or the reasonthat was triggered in a yield statement. The following module-level functions are availablefor this purpose.

7.1 currenttask()

This returns the task object corresponding to the currently executing task. Instancevariables of interest to programmers are the id of a task, its __name__,and its parenttask. Converting a task to a string produces a printable reprentation of the task thatuniquely identifies it.

def task_ex(x): yield ... me = current_task() id = me.id name = me.__name__ parent = me.parent # another task pid = parent.id print str(me) # print representation of me

Page 24: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

7.2 currenttime()

This returns the current Verilog simulation time as a long integer. The time is obtainedfrom the VPI callback responsible for resuming execution.

7.3 currentsystf()

This returns the PyHVL systf object corresponding to the currently executing PyHVLapplication. (Though not typical, there may be more than one PyHVL application runningsimultaneously.) It is through this object that configuration information may be obtained,for example. See the PyHVL documentation for a description of the methods of the systfclass.

A typical use for this function, would be to access configuration parameters for the PyHVLinstance of which this task is part.

def task_ex(): yield ... stf = currentsystf() # which PyHVL.systf instance param = stf.my_config_param_string("userparam")

It is typical to see code such in the main startup task of A PyHVL application.

7.4 currentreason()

After a task resumes, this function returns a reference to the triggered reason that causedthe task to resume. This value can be used to determine why the task resumed byexamining its type, for instance.

def task_ex(s): ... # resume if the signal changes, or after 10 time units yield sigchange(s), timeout(10)

# when the task resumes, x is one of the reason objects above x = currentreason()

if isinstance(x, sigchange): # if x is the signal change reason pass

if isinstance(x, timeout): # if x is the timeout reason pass

Page 25: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

7.5 currentreasonindex()

The reasons in a yield clause are assigned identifying numbers beginning at 0 when theyield statement is executed. When a task resumes, this function can be used to get theindex of the current reason. In the example of the previous section, the sigchange reason isassigned an index of 0 in the yield clause, and the timeout reason is assigned an index of 1.

8 Other FunctionsA few other functions are available to structure message output and error reporting.

8.1 taskmsg(s)

This function prints a message to the log file (using PyHVL.vpi_print) with the currenttime and task id in a nice format.

8.2 error(s)

This is the PyHVL error function. It is used to indicate a simulation error. The globalPyHVL error counter is incremented and the message, s, is printed to the log file. IfPyHVL has been configured to link this error count to a Verilog variable, then the count issynchronized with the global Verilog counter.

8.3 warning(s)

This is the PyHVL warning function. It is used to indicate a simulation warning. Theglobal PyHVL warning counter is incremented and the message, s, is printed to the logfile. If PyHVL has been configured to link this error count to a Verilog variable, then thecount is synchronized with the global Verilog counter.

8.4 vpi_print(s)

This is the Python interface to the raw VPI print function which normally prints to both thescreen and the logfile. This function does not prepend the simulation time or a taskidentifier.

8.5 traceon()/traceoff()

Verbose tracing of the event processing performed by PyHVL may be turned on and offwith these functions. Viewing event tracing is useful for debugging and for understandingimplementation details of PyHVL. What you may notice by running with tracing on is that

Page 26: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

the PyHVL examples create an enormous number of tasks on the fly for even the simplestof programs.

8.6 simstop()

Call the simulator's $stop function. Normally, this returns the user to an interactive prompt.

8.7 simfinish()

Call the simulator's $finish function. Normally, this ends the simulation. Because PyHVLhas registered an end-of-simulation callback, the PyHVL error and warning report also getprinted.

8.8 simreset()

Call the simulator's $reset function. While this restarts the simulation at time 0, it does notreset PyHVL.

9 Using PyHVLThis section gives a few pointers for using PyHVL. The examples here discuss some moreinteresting aspects of the package.

Because PyHVL is A PyHVL application, the first step is to build PyHVL and make sure itis operating properly by running a few PyHVL examples.

PyHVL is most typically integrated with Verilog in the following way. In the top level,add the AVPM call that creates A PyHVL instance and give it the name "oro."

initial begin reg [31:0] res; res = $pyhvl("oro", "PyHVL", "PyHVL"); end

PyHVL requires two configuration parameters to name the Python module and function ofthe main user task. These are most typically given on the Verilog command line asplusargs, but could also be configuration file parameters. A typical example follows.

verilog ... +oro:module=mymodule +oro:task=mymain

The function mymain must be a generator function, and this leads to a strangerequirement. The main function must have a "yield" statement in it even if it serves nopurpose. The main function is passed a single argument: the PyHVL systf instance

Page 27: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

corresponding to the root PyHVL instance. The user can use methods of this instance toretrieve configuration parameters, among other things.

For example, if your application requires a command line parameter, you can get it in yourmain function as shown in the following example.

def mymain(systf):

fooval = systf.my_config_param_string("foo") t = task(main_worker_task, fooval) yield timeout(0) return

And on the command line, the foo parameter is given as a plusarg.

verilog ... +oro:foo=value ...

As a convenience, PyHVL allows you to set the global Python random number generatorfrom the command line as well. If you desire multiple random number generators, you're inluck. Python provides a number of random number generation services - and their use hasnothing to do with Verification, specifically, so they aren't described here. verilog ... +oro:seed=N

9.1 Building the top-level

Following a few suggestions helps simplify the top-level simulation harness for yourVerilog module. At this point in time, PyHVL offers no automatic support for creatingVerilog drivers or wires, but adopting this straightforward methodology makes it simple.

9.1.1 For each output port

It is sufficient to define a wire and attach it to the output port. A PyHVL signal can attachto this wire to read values using the get method.

9.1.2 For each input port

It is sufficient to define a reg and attach it to the input port. A PyHVL signal can attach tothis reg to drive values using the set method.

9.1.3 For each inout port

It is best to create a wire and a reg. For a port called foo, create wire 'foo' and reg 'foo_d'(driver). In Verilog, use an assign statement like this.

wire foo; reg foo_d; initial foo_d = 1'bz;

Page 28: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

assign foo = foo_d;

Attach the wire to the module under test. Then, in your PyHVL code, create a read-onlysignal for 'foo' and a write-only signal for 'foo_d'. s = signal("top.foo") s_d = signal("top.foo_d")

When you do not want to drive values, set the driver value to all Zs.

s_d.set(BV("1'bz"))

9.1.4 For internal nodes

Consider these the same as other "ports" and define top-level entities for them, followingthe conventions above. For instance, if there is a node called "node" with verilog path"`NODEPATH" buried in the design that you wish to observe and drive, create thisharness.

`define NODEPATH top.dut.x.y.node wire node; reg node_d; initial node_d = 1'bz; assign node = NODEPATH; assign NODEPATH = node_d;

The use of the `define slightly simplifies the code and makes it possible for peoplefamiliar with Verilog (but not Python) to keep these paths up to date. PyHVL code thenneeds to only refer to the invariant paths "top.node" and "top.node_p" when it createssignals.

9.2 Connecting to Verilog Nets

Two methods are available for connecting to Verilog objects. The first method is to simplyuse the full pathname of the verilog object as the initializer to a signal object.

s0 = signal(“top.sig0”) s1 = signal(“top.sig1”) s2 = signal(“top.sig2_d”)

A second method is to use the PyHVL.systf object passed to the main task to accessverilog objects that are passed as arguments in the $pyhvl call that spawned PyHVL.

module top; ...

Page 29: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

res = $pyhvl(“oro”, “PyHVL”, “PyHVL”, sig0, sig1, sig2_d) ... endmodule

In the Python main task, the VPI handles for these objects can be accessed through thevobjs list in the PyHVL.systf object. These handles may also be used as initializersfor the signal class.

main(systf): s0 = signal(systf.vobjs[0]) s1 = signal(systf.vobjs[1]) s2 = signal(systf.vobjs[2]) ...

The first method is the more flexible of the two and should be used if simplicity andflexibility are key. The second method has advantages in terms of modularity and possiblysimulation performance. If the PyHVL application is internal to a module that isinstantiated multiple times, then this approach may simplify connecting the right signals tothe right PyHVL instance.

module top; ... res1 = $pyhvl(“oro1”, “PyHVL”, “PyHVL”, sig0, sig1, sig2_d) res2 = $pyhvl(“oro2”, “PyHVL”, “PyHVL”, sig3, sig4, sig5_d) ... endmodule

Most importantly, some Verilog compilers perform optimizations based on knowledge ofwhich nets are accessed by the Verilog code and PLI applications. In the first approach,global access to all Verilog objects must be enabled (since any object is accessible throughits pathname) and this may disable code optimizations. If the second approach is usedconsistently, the Verilog compiler has complete knowledge of the nets accessed and canapply full optimization.

9.3 Tasks and Typical Patterns

A task is a Python generator function that yields with a reason, tuple of reasons or list ofreasons. Such a generator function becomes a running simulation thread when it is used asan initializer for a new task instance. When a new task is spawned, it becomes a free-running thread. The child thread does not begin execution until after its parent yields.

To wait for completion of a child task, the parent must yield with a status reason. Intypical use, a parent that wishes to spawn a child and wait for its completion uses the

Page 30: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

following pattern.

child = task(childtask, args, ...) yield status(child)

This pattern can be shortened to the following if a reference to the task object is not neededexcept as an argument to status. yield status(task(childtask, args, ...)

A task may suspend execution and resume again at the same simulation time by using thefollowing construct. yield timeout(0)

This is equivalent to introducing a #0 delay in a task in Verilog. The task will suspend itsexecution, but the Verilog scheduler will resume it again in the same time step.

9.4 Composing Tasks to Form Transactions

Tasks and the status reason are used together to compose sequences of tasks, or tocompose tasks into larger transactions. The following example shows the definition ofsome low-level tasks and how they might be built up to form a transaction.

Example 3

def cycle(clk): yield posedge(clk) yield negedge(clk)

def cmdpacket(clk, cmd_d, opcode): cmd_d.set(opcode) for i in range(2): yield status(task(cycle, clk)) cmd_d.set(BV("5'bzzzzz"))

def datapacket(data_d, data): ...

def transaction(clk, cmd_d, opcode, data_d, data): yield status(task(cmdpacket, clk, cmd_d, opcode)) for i in range(4): yield status(task(cycle, clk)) yield status(task(datapacket, data_d, data)) for i in range(4): yield status(task(cycle, clk))

Page 31: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

The task “cycle” is a simple task that waits for two edges of a clock and exits aligned to the“negedge” of the clock. This is significant, because a waiting task following this one willawake synchronized with that edge. The task “cmdpacket” places an opcode on a signaldriver “cmd_d” for two cycles and then ceases driving the driver by placing Zs on it. Thedefinition of “datapacket” is omitted, but its function is to drive data on a signal “data_d”for some number of cycles. The top level “transaction” task drives a command packet,waits for four cycles, drives a data packet, and then waits another four cycles.

9.5 Using Classes to Group Tasks

Classes can be used to group together tasks and transactions that apply to a particular set ofsignals. This is a good way to structure BFMs. For each port group, a BFM object isinitialized with the signals that comprise the port. Because Python generator functions maybe method bodies, the BFM class can contain both simple methods and task methods.

Example 4

class reg_bfm:

def __init__(self, cyc_c, rw_d, addr_d, data, data_d): self.cyc_d = cyc_d # register cycle driver self.wr_d = wr_d # write/read select self.addr_d = addr_d # address drive self.data = data # data sample self.data_d = data_d # data drive

def write(self, waddr, wdata): yield timeout(10) self.cyc_d.set(BV(“1'b1”)) self.wr_d.set(BV(“1'b1”)) self.addr_d.set(waddr) self.data_d.set(wdata) ... other details of write BFM omitted

def read(self, raddr, rdatamut): yield timeout(10) ... details of read BFM omitted rdatamut[0] = self.data.get() # rdatamut is a mutable object

def main(systf): # create one register interface over one port group reg0 = reg_bfm(top.cyc0, ....) # create another register interface over another port group reg1 = reg_bfm(top.cyc1, ....)

Page 32: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

# example of calling one of the operations yield status(task(reg0.write, addr, data))

In this example, the register interface BFM supports two methods: write and read. Theclass initializer connects the instance to a particular set of register interface signals.Multiple register interfaces may be instantiated over different ports with no ambiguity. Thewrite task accepts an address and data and applies them to the ports. The read task acceptsan address and a list in which to place the return data. When the read task samples the readdata, it places it in this mutable object. At that point in time the caller can examine the readdata.

10 Advanced TopicsPython is a dynamic interpreted language. With PyHVL, tasks and reasons are simplyobjects that may be generated on the fly. The dynamic nature of Python allows for veryflexible code. This section briefly touches on some aspects of this topic.

10.1 VPI Underneath

Because nearly all of VPI is available through PyHVL, A PyHVL application may querythe Verilog netlist database to obtain information about simulation objects. For example, ageneral-purpose testbench could be written that sizes itself to the width of a particular port. from PyHVL import * from PyHVL import *

s = signal("top.port") width = vpi_get(vpiSize, s.vpih) s.set(BV("1'bx") * width) # set to all X ...

10.2 Generating Tasks Dynamically

A task is spawned by initializing a new task object. Consider the watcher task shownearlier. Given a list of signals, ss, a list of watcher tasks could be spawned as follows.This example uses Python list comprehension syntax. tt = [ task(watcher, x) for x in ss ]

This construct would start a new watcher for each signal in the list.

Page 33: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

10.3 Generating Reasons Dynamically

The argument to the yield statement in a task must be a reason, tuple of reasons or list ofreasons. For readability, reasons are often written in a comma-separated list. (This comma-separated list is actually just a literal Python tuple.) However, the list of reasons could begenerated dynamically.

For example, to yield waiting on a status change of any of the tasks spawned above, wecould do the following. reasons = [ status(x) for x in tt ] yield reasons

This construct gives the same result as writing out yield status(tt[0]), status(tt[1]), status(tt[2]), ...

10.4 Synthesizing Synchronization Primitives

The dynamic capabilities of Python and PyHVL create some interesting possibilities forcreating new synchronizataion primitives. Most multitasking simulation languages offersome sort of fork/join primitive. In PyHVL, it is not difficult to synthesize our own.

In the example below, the argument to the join task is a list of tasks. The join task loopsuntil all of the tasks in the list have exited and then it exits itself. def join(tasks):

reasons = [ status(x) for x in tasks ] while (len(reasons) != 0): yield reasons tasks.remove(currentreason().t) reasons = [ status(x) for x in tasks ] return

Given the list of tasks created above, a task would use join in the following way. yield status(task(join, tt))

This yield statement spawns a new join task whose arguments are the list of running tasks,tt. The yield statement resumes after the join task exits.

11 Conclusionhis first release of PyHVL presents a framework for writing PLI applications in Python.The object oriented features of Python help to simplify the call back model of VPI and"systf" instances unify issues such as instance data handling, configuration andcheckpoint/restart. PyHVL automates much of the tedium of writing a new PLI application

Page 34: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

and allows the programmer to focus on the interesting parts.

Many aspects of the VPI interface are unchanged in PyHVL. Traversing the Verilogdatabase requires the same use of tags and iterators one would use in C. Creating time andvalue structures also requires knowing the corresponding C struct layout. Higher levels ofabstraction layered on top of PyHVL might help simplify some of these issues one day, butall of the functionality required for full-featured applications is present in PyHVL now.

The examples provided here and in the release illustrate a variety of tasks that can beaccomplished relatively easily using PyHVL. These include generating tests andinteracting with the scheduler, traversing the database, implementing a checker andinstrumenting Verilog with a Tk/Tcl interface.

This first implementation of PyHVL is an experiment in using Python generators as asimple mechanism for representing Verilog simulation tasks. In contrast to the capabilitiesof more general threading packages, a generator is a a very restrictive kind of thread thatcan only suspend its execution when its stack is empty. While limiting, the construct isshown to be useful in practice. Whether this is useful enough for serious work remains tobe seen.

Future work will add features of more developed "verification languages" to PyHVLthrough class libraries. Constrained random generation is usually considered one suchfeature. Python already has a sophisticated package of random number generators, and themost straightforward way of adding constrained random constructs for Verification isthrough a class library. Instrumentation that aids in tracing and viewing complex sequencesof tasks (ala transactions) would be helpful, and so would a class library that providesconstructs for collecting run-time coverage data. Ideally, coverage would implemented as aPython wrapper around an existing C-based coverage library. Temporal expressions forimplementing checkers might also be defined as a class library at some point. Because ofthe abilities to create tasks dynamically and to store execution state in generators, temporalexpressions seem to be an interesting next step. Class libraries can sometimes becumbersome, however, and the languages that support these features as first-class languageconstructs may prove to be more convenient to use.

At this stage of its development, PyHVL is a robust base platform for the development ofmore advanced features. It provides the basic mechanisms needed to interact with a runningVerilog simulation. With the addition of the right collection of class libraries, it couldbecome a powerful Verification environment.

[email protected]

[email protected]

[email protected]

Page 35: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

12 References

The following have been useful, stimulating or are somehow relevant.

http://www-106.ibm.com/developerworks/linux/library/l-pythrd.html“Charming Python: Implementing "weightless threads" with Python generators”This article gives a good description of using generators as ultra-lightweight threads andmakes a case for why they have almost no overhead. Understanding this article was the"ah-ha" needed to figure out how to use Python generators with the Verilog scheduler.

IEEE P1364-2005 Draft 3This is the proposed 2005 Verilog standard. Besides the usual language reference manual,this document includes a detailed description of the Verilog scheduling algorithm and acomplete reference for VPI.

http://www.sutherland-hdl.com/publications.shtmlStuart Sutherland's 806 page book "The Verilog PLI Handbook: A Tutorial and ReferenceManual on the Verilog Programming Language Interface,Second Edition" is the bestreference on VPI I have found that includes detailed examples.

http://www.pragmatic-c.com/gpl-cver GPLCVER is a version of the CVER verilog simulator that is freely distributable.GPLCVER is an interpreted Verilog simulator. Both PyHVL and PyHVL work well withit.

http://www.icarus.com/eda/verilogIcarus is an open source verilog compiler and runtime system. It first compiles Verilog tobytecodes and then runs the bytecode result. It has a very good VPI implementation and iscompatible with PyHVL and PyHVL.

http://www.open-vera.comVera is a testbench and simulation language that can be used with Verilog. Vera code isnormally compiled and then run in a PLI run time system that is linked with a Verilogsimulator.

http://www.testbuilder.netTestBuilder is a very sophisticated testbench system written in C++. It can be used withboth Verilog and VHDL. TestBuilder uses either pthreads or QuickThreads as itsunderlying mechanism for representing simulation tasks.

Page 36: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

http://www.ieee1647.orgThe Specman "e" language is now an IEEE standard, P1647. This site explains some of thefeatures of e that make it an excellent verification language.

http://PyHVL.sourceforge.netPyHVL embeds the Python interpreter in Verilog using the VPI interface. PyHVL isdistributed with PyHVL as an example application.

http://jandecaluwe.com/Tools/MyHDL/Overview.htmlMyHDL is an interesting project that advocates using Python as a Hardware DescriptionLanguage. While the focus is on creating synthesizable Python, it can also be be used as ahardware verification language. MyHDL presents a use of generators and the yieldstatement very similar to the one presented here.

http://simpy.sourceforge.netSimPy is an event driven simulation system written in Python. It too represents “tasks”using Python generators.

13 AddendaThese are some miscellaneous notes regarding this project.

13.1 Development Environment

PyHVL has been developed using Python, Icarus Verilog, GPL Cver, and Tkinter onRedhat 9. PyHVL has also been demonstrated using NC-Verilog, VCS and Modelsim. Theexamples included with the distribution are written so as to work properly in the subset ofthe VPI interface provide by Icarus [http://icarus.com/eda/verilog]. Another promising freeVerilog simulator is GPLCVER [http://www.pragmatic-c.com/gpl-cver]. As of thiswriting, PyHVL works with GPLCVER1.10i. Universal compatibility with all Verilogsimulators is the eventual goal.

Page 37: PyHVLpyhvl.sourceforge.net/pyhvl.pdf · 2007-08-31 · PyHVL 0.3 1 Introduction PyHVL is a framework for writing Verilog PLI applications in Python. PLI applications are foreign C

PyHVL 0.3

13.2 Documentation Tool

I have adopted OpenOffice.org as my main documentation tool. The program seems tohave anything one might want, but it is complicated. My mastery of this program isminimal, so I have difficulty with even the simplest things – like properly numberingsections. Getting to know this tool has been a great learning experience.

13.3 You Can Help

If you find this project or tool interesting, you might be able to help make it better. Onearea that needs improvement is the configure/build process, which is largely manual rightnow. The correct use of the Python "distutils" package should help. Some installations alsorequire setting Python environment variables, and this can be very difficult. Developing abetter way to get all of this right would be an enormous step forward.