It has been a few years since there has been a new project embedding the Python interpreter into Verilog. There is a new one on the block, called “Cocotb” built with a definite focus on writing testbenches and running regressions. It is written in a modern dialect of Python and hosted on Git. It is well-documented. I’m impressed with what I’ve seen.
This article gives a little bit of a history of Python and Verilog and describes how Cocotb fits in.
A Brief History
ScriptEDA (2001) [http://embedded.eecs.berkeley.edu/Alumni/pinhong/scriptEDA/,
http://www.scribd.com/doc/3492141/scriptEDA-pinhong] was primarily an example of using SWIG to link a Python interpreter into a Verilog simulator using PLI/VPI. ScriptEDA handled TCK, Perl and Python in roughly the same way.
The SWIG wrapper exposed the constants and functions of VPI as top-level objects in the Python namespace. The “main” program of the embedded Python interpreter was called once and was responsible for everything, including registering callbacks from Verilog.
main.py ================ def setup_my_function(user_data): argv = get_args() arg0 = argv.pop(0) get_value.format = vpiStringVal vpi_get_value(arg0, get_value) print "value:", get_value.value_str vpi_register_systf(vpiSysTask, 0, "$my_function", setup_my_function, 0, 0, "USER_DATA")
ScriptEDA was an experiment in embedding and an illustration of the power of SWIG. It provided a very low-level interface, and it did not present the simulator in a very Pythonic way.
One insurmountable limitation of this interface is that the Python code cannot easily keep track of multiple instances of calls to the embedded Python function. For example, if a module includes a call to a Python function, and that module is instantiated multiple times, the Python function will not know which instance is involved. Workarounds can be devised using tricks on “user_data”, but it is not managable.
Scriptsim – by NelSim Software [http://www.nelsim.com/] Embeds Perl, Python and Tk into Verilog with a single PLI extension method called $scriptsim. Scriptsim considers the difference between instantiating a Python script and calling it.
instantiate ================ integer id; $scriptsim("my_prog.py", id, args, ...); use ================ $scriptsim(id, args ...);
Through the use of “id” management, multiple instances of scriptsim calls can be handled. Scriptsim abstracts the simulator into a simplified interface. It has been in use for a number of years and is well supported. Scriptsim will let you build Tk GUI interfaces directly into your simulation (which is cool).
Both of the preceding projects made it possible to implement functional models in Python, and to make use of the IO facilities of Python. Neither addressed how the Python functions interacted with the Verilog scheduler.
APVM/Oroboro (2004) were two related research projects of my own [http://tsheffler.com/site_media/software/apvm_025.pdf, http://tsheffler.com/site_media/software/oro_025.pdf]. APVM (“A Python VPI Module”) embedded the Python interpreter into Verilog using VPI and exposed each instance of a PLI call as an instance of a Python class called a “Systf”. In an object-oriented approach, by subclassing Systf and overriding default methods, a Python programmer could provide behaviors for the various phases of a PLI call’s lifetime. These phases included:
- inline callback
- scheduled callback
- save simulator state
- restore simulator state
There was a simple exploration of using Python to schedule callbacks to the Systf via the Verilog scheduler. Although functional, the way in which the callback mechanism and the scheduler interacted was not elegant.
Oroboro attempted to provide a cleaner representation of Verilog “tasks” in Python. Oroboro became an experiment in using Python Generators and the “Yield” statement to model the passage of simulator time. [Python Generators were proposed 2001 in PEP 255 and seemed to hit the mainstream in 2004.] The basic technique was the implementation a trampoline in Python that managed the callbacks from the Verilog scheduler and dispatched to the desired Python method. (The name “Oroboro” was a fanciful reference to the way the scheduler scheduled itself.) Oroboro essentially used the relatively new Python Generator construct as a semi-coroutine to simulate non-preemptive multitasking.
PyHVL [http://sourceforge.net/projects/pyhvl/] forked the APVM/Oroboro project and cleaned up the code. The authors have kept the project alive and updated it to work with evolving simulator linkage interfaces. (This last bit is really hard, actually.) It’s current status is unclear.
Cocotb [https://github.com/potentialventures/cocotb] is a completely new project that embeds the Python interpreter into Verilog with a focus on writing testbenches in Python and running regressions. It uses completely modern Python with a judicious use of decorators to make writing tests simple. For programmers coming from Python or Ruby, unit tests written using Cocotb will look somewhat familiar. People familiar with Jenkins (an automated regression runner) will feel right at home.
Cocotb does not attempt to make Python look like a hardware description language (see MyHDL [http://www.myhdl.org]). Users will describe simulator tasks as “coroutines” and events as “triggers”. By using Generators and the Yield statement, the driving and receiving of packets on buses is nicely abstracted into a Pythonic mindset.
Cocotb abstracts VPI/VHPI so it can be used with either Verilog or VHDL. Cocotb exposes the top-level module in the hardware design as a global namespace object called “dut” in Python. All interaction with the design is through the driving of packets on the pins of the DUT. There is no facility for embedding PLI calls within functional models of the Verilog code, however. These last two points differentiate it from the previously mentioned projects and further set it directly in the realm of testbench writing and further from the realm of writing of functional models in Python.
One of the most interesting things the author of Cocotb has done is shown how TCP packet definitions from a project called “Scapy” [http://www.secdev.org/projects/scapy/] can be re-used in the context of hardware simulation. Scapy is a tool for manipulating an ethernet adapter connected to a computer. Here, Cocotb re-uses these packet definitions in the context of logic simulation. This is a fantastic example of code re-use across disparate environments and domains. Scapy is field-tested and mission-hardened. Being able to reuse its packet definitions is a big win.
The regression framework of Cocotb is well thought out. Modern logging concepts are incorporated with log-levels and filtering. Each test in a regression can succeed, fail or throw an exception. Regression reports are collected as XML output for formatting. Check it out: Cocotb looks very promising.