Ruby-VPI 18.0.1 user manual

Suraj N. Kurapati

02 August 2007

About this manual

This manual is meant to be read in conjunction with the reference documentation for Ruby-VPI. In addition, if you are new to the Ruby language, you are encouraged to explore its documentation as necessary.

In this manual, you will notice that the numbers of chapters, sections, figures, admonitions, etc. are hyperlinks that take you back to the corresponding place in the table of contents. These links make it easy to navigate this manual, especially for users of text-only web browsers.

In addition, this manual is distributed as one big HTML file so that you can easily search for a particular topic using nothing more than your web browser’s built-in text search mechanism. This facilitates offline reading, where an Internet search engine is not available.

You can give feedback about this manual and, in general, any aspect of the Ruby-VPI project on the project forums. Furthermore, you can edit this manual and contribute your improvements to the project patches. Finally, you can find the newest version of this manual at the Ruby-VPI project website.


Chapter 2

Welcome

Ruby-VPI is a platform for unit testing, rapid prototyping, and systems integration of Verilog modules through the Ruby programming language. It lets you:

Ruby-VPI is open source software released under this license.


2.1   Resources

Records

  • What’s new – release notes for version 18.0.1.
    • History – a record of all release notes.
    • RSS feed for release announcements RSS feed – keep track of new releases at your leisure.
  • Plans – pending tasks for future releases.
  • Talks – materials from presentations and seminars.
  • Papers – research publications.

Documentation

  • Motivation – why was Ruby-VPI developed?
  • User manual – complete documentation for users.
    • Tutorial – learn how to use Ruby-VPI quickly.
  • Reference – API documentation for Ruby libraries and C extension.

Facilities


2.2   Features

Portable

Agile

  • Eliminates unneccesary work:

Powerful


2.3   Requirements

The following software is necessary in order to use Ruby-VPI.

Verilog simulator

Ruby-VPI is known to work with the following simulators. However, you should be able to use it with any Verilog simulator that supports VPI.
  • Synopsys VCS – any version that supports the -load option is acceptable.
  • Mentor Modelsim – any version that supports the -pli option is acceptable.
  • Cadence NC-Sim – any version that supports the +loadvpi option should be acceptable. However, version 05.83-s003 is mostly acceptable because you will not be able to force values onto wires.
  • GPL Cver – version 2.11a or newer is acceptable.

Compilers

  • make – any flavor should be acceptable.
  • SWIG – version 1.3.29 or newer is necessary.
  • Ruby – version 1.8 or newer, including header and linkable object files for building extensions, is necessary. You can install Ruby by following these instructions.

Libraries

  • POSIX threads – header and linkable object files, and operating system support for this library are necessary.


2.4   Applications

Examples of tasks that can be performed with Ruby-VPI are:
  • From the second edition of The Verilog PLI Handbook:
    • C language bus-functional models
    • Reading test vector files
    • Delay calculation
    • Custom output displays
    • Co-simulation
    • Design debug utilities
    • Simulation analysis
  • Adapted from Pin Hong’s observations:
    • Writing hardware models in Ruby
    • Dumping or processing netlist data from Verilog database
    • Dumping or processing simulation data
    • Feeding dynamic simulation stimuli
    • Back-annotating delay information
    • Interactive logic simulation
    • Building a distributed simulation


2.5   Appetizers

Here is a tiny sampling of code to whet your appetite. See the tutorial for more samples.

  • Assign the value 22048 to a register:

your_register.intVal = 2 ** 2048

  • Check if all nets in a module are at high impedance:

your_module.all_net? { |net| net.z? }

  • See a register’s path, width, and location (file & line number):

puts your_register

  • Access the first five elements in a memory:

your_memory.memoryWord_a[0..4]

  • Clear a memory by filling it with zeroes:

your_memory.each_memoryWord {|w| w.intVal = 0}


2.6   License

Copyright 2006 Suraj N. Kurapati <SNK at GNA dot ORG>
Copyright 1999 Kazuhiro HIWADA <HIWADA at KUEE dot KYOTO-U dot AC dot JP>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

1. All copies and substantial portions of the Software, whether modified or unmodified, (the "Derivatives") and their corresponding machine-readable source code (the "Code") must include the above copyright notice and this permission notice.

2. The Derivatives, upon distribution, must be accompanied by the Code or, if the Code is obtainable for no more than the cost of distribution plus a nominal fee, by information on how to obtain the Code.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


2.7   Related works

  • JOVE is a Java interface to VPI.
  • Teal is a C++ interface to VPI.
  • ScriptEDA is a Perl, Python, and Tcl interface to VPI.
  • RHDL is a hardware description and verification language based on Ruby.
  • MyHDL is a hardware description and verification language based on Python, which features conversion to Verilog and co-simulation.


2.7.1   Ye olde PLI

The following projects utilize the archaic tf and acc PLI interfaces, which have been officially deprecated in IEEE Std 1364-2005.


Chapter 3

Setup


3.1   Manifest

When you extract a release package, the following is what you would expect to find.
  • doc contains user documentation in various formats.
  • ref contains reference API documentation in HTML format.
  • ext contains source code, written in the C language, for the core of Ruby-VPI
  • lib contains Ruby libraries provided by Ruby-VPI.
  • bin contains various tools. See Section 5.4 for more information.
  • samp contains example tests. See Section 5.5 for more information.

3.2   Requirements

See Section 2.3 above.


tip

Tip 1.   Add support for your Verilog simulator

Write a support request for your simulator, while providing a sample transcript of the commands you use to run a test with your simulator, and I will add support for your simulator in the next release!


3.3   Recommendations

The following software may make your interactions with Ruby-VPI more pleasant.


3.3.1   Text merging tool

An interactive text merging tool can greatly simplify the process of transferring wanted changes from one file to another. In particular, such tools are especially beneficial when using the automated test generator. A handful of the currently available open-source text merging tools are listed below.
  • kdiff3 is a graphical, three-way merging tool for KDE.
  • meld is a graphical, three-way merging tool for GNOME.
  • tkdiff is a graphical, two-way merging tool that uses the cross-platform Tk windowing toolkit.
  • xxdiff is a graphical, three-way merging tool.
  • imediff2 is a textual, fullscreen two-way merging tool. It is very useful when you are working remotely via SSH.


3.4   Installation

Once you have satisfied the necessary requirements, you can install Ruby-VPI by running the

gem install -y ruby-vpi
command. RubyGems will install Ruby-VPI into the system gem directory, whose path can be determined by running the
gem env gemdir
command. Within this directory, there is a gems/ subdirectory which contains the Ruby-VPI installation, as illustrated below.

$ gem env gemdir
/usr/lib/ruby/gems/1.8

$ ls -d `gem env gemdir`/gems/ruby-vpi*
/usr/lib/ruby/gems/1.8/gems/ruby-vpi-7.0.0/


tip

Tip 2.   Tuning for maximum performance

You can tune your installation of Ruby-VPI for maximum performance by adding your C compiler’s optimization flag to the CFLAGS environment variable before you run the
gem install -y ruby-vpi
command. For example, if your C compiler is GCC, then you can set CFLAGS to -O9 for maximum optimization.

3.4.1   Installing on Windows

After Ruby-VPI is compiled, it is linked to symbols whose names begin with _vpi. In GNU/Linux and similar operating systems, these symbols are allowed to be undefined. However, one cannot compile a shared object file with references to undefined symbols in Windows.

One solution to this problem is to supply the Verilog simulator’s VPI object file, which contains definitions of all VPI symbols, to the linker. The following steps illustrate this process.

  • Install Cygwin, the Linux-like environment for Windows.
  • Search for object files whose names end with .so, .o, or .dll in your Verilog simulator’s installation directory.
  • Determine which object files, among those found in the previous step, contain symbols whose names begin with “_vpi” by running the
    for x in *.{o,so,dll}; do nm $x | grep -q '[Tt] _vpi' && echo $x; done
    command in Cygwin.
    • If you are using Mentor Modelsim, the desired object file can be found at a path similar to C:\Modeltech\win32\libvsim.dll.
    • If you are using GPL Cver, the desired object file can be found at a path similar to C:\gplcver\objs\v_vpi.o.
  • Assign the path of the object file (determined in the previous step) to the LDFLAGS environment variable. For example, if the object file’s path is /foo/bar/vpi.so, then you would run the
    export LDFLAGS=/foo/bar/vpi.so
    command in Cygwin.
  • You may now install Ruby-VPI by running the
    gem install ruby-vpi
    command in Cygwin.


3.5   Maintenance

  • You can upgrade to the latest release of Ruby-VPI by running the
    gem update ruby-vpi
    command.
  • You can uninstall Ruby-VPI by running the
    gem uninstall ruby-vpi
    command.

Learn more about using and manipulating RubyGems in the RubyGems user manual.


Chapter 4

Organization

Ruby-VPI is a bridge between IEEE 1364-2005 Verilog VPI and the Ruby language. It enables Ruby programs to use VPI either (1) in the same, verbose way that C programs do, or (2) in a simpler, higher level way. In addition, it serves as a vehicle for the application of agile software development practices, such as TDD and BDD to the realm of hardware development with Verilog.

Ruby-VPI can be used with any Verilog simulator that supports VPI. In particular, it is known to operate with (1) Synopsys VCS and Mentor Modelsim, the two most prominent Verilog simulators in the Electronic Design Automation (EDA) industry; as well as (2) GPL Cver and Icarus Verilog, the two most prevalent open source Verilog simulators today.


Figure 1.   Where does Ruby-VPI fit in?

As Figure 1 shows, Ruby-VPI is composed of two complementary parts: one interacts with VPI through the C language, while the other interacts with an executable specification written in the Ruby language. The former is complied during installation to produce dynamically loadable C libraries—-each tailored to accommodate the quirks of its respective Verilog simulator. The latter is not compiled because Ruby programs are interpreted dynamically.


4.1   Ruby/Verilog interaction

In a typical VPI application written in C, the Verilog simulator is in charge. Verilog code temporarily transfers control to C by invoking C functions, which return control to Verilog when they finish.

In contrast, Ruby-VPI puts the specification in charge. The specification temporarily transfers control to the Verilog simulator by invoking the advance_time method, which returns control to the specification when it finishes. This process is illustrated in Figure 2.

Ruby-VPI’s approach is the same as any software testing framework, where the specification drives the design under test. Whereas, the typical VPI & C approach is literally backwards because the design under test drives the specification.


Figure 2.   Interaction between Ruby and Verilog

  1. The current simulation time is X.
  2. The specification invokes the Vpi::advance_time method with parameter Y, which specifies the number of simulation time steps to be simulated.
  3. The Verilog simulator is now in control (temporarily).
  4. The current simulation time has not changed; it is still X.
  5. The Verilog simulator simulates Y simulation time steps.
  6. The current simulation time is now X + Y.
  7. The Verilog simulator returns control back to the specification.
Another means of transferring control from the specification to the Verilog simulator is the VPI callback.


4.2   Tests

In Ruby-VPI, the process of functional verification is neatly packaged into self-contained, executable tests. As Figure 3 illustrates, a test is composed of a bench, a design, and a specification.


Figure 3.   Organization of a test in Ruby-VPI

The bench is Ruby-VPI. It defines the environment in which functional verification takes place. This is analogous to a workbench in an electronics laboratory that is furnished with tools of measurement and manipulation such as oscilloscopes, voltmeters, soldering irons, and so on which enable engineers to verify electronic components and locate the source of defects within those components.

The design is an instantiated Verilog module. To extend the analogy of the electronics laboratory, it corresponds to the electronic component that is verified by an engineer.

The specification is a Ruby program. In the electronics laboratory analogy, it corresponds to the engineer who inspects, manipulates, and verifies the electronic component. In terms of specification-driven functional verification, it corresponds to the executable specification.


4.3   VPI in Ruby


4.3.1   Deviations from the VPI standard

Ruby-VPI makes the entire IEEE Std 1364-2005 VPI interface available to Ruby, but with the following minor differences.


4.3.1.1   Names are capitalized

The names of all VPI types, structures, and constants become capitalized because Ruby requires that the names of constants begin with a capital letter. However, note that Ruby’s capitalization rule does not apply to VPI functions.

For example, the s_vpi_value structure becomes the S_vpi_value class in Ruby. Likewise, the vpiIntVal constant becomes the VpiIntVal constant in Ruby. However, the vpi_handle function remains as vpi_handle in Ruby.


4.3.1.2   vprintf is printf

The vpi_vprintf and vpi_mcd_vprintf VPI functions are aliased to vpi_printf and vpi_mcd_printf respectively because:
  • Some C compilers have trouble with pointers to the va_list type. For these compilers, the third line of source code shown below causes a “type mismatch” error.
#include <stdarg.h>
void foo(va_list ap) {
  va_list *p = &ap;
}


4.3.2   Handles

A handle is a reference to an object (such as a module, register, wire, and so on) inside the Verilog simulation. Handles allows you to inspect and manipulate the design under test and its internal components. They are instances of the Vpi::Handle class (see reference documentation for details) in Ruby-VPI.

Handles have various properties, listed in the second column of Table 1, which provide different kinds of information about the underlying Verilog objects they represent. These properties are accessed through the VPI functions listed in the last column of Table 1.

Handles are typically obtained through the vpi_handle_by_name and vpi_handle functions. These functions are hierarchical in nature, as they allow you to obtain new handles that are related to existing ones. For example, to obtain a handle to a register contained within a module, one would typically write: your_reg = vpi_handle( VpiReg, your_handle )

Shortcuts for productivity

Given a handle, Ruby-VPI allows you to access (1) its relatives and (2) its properties simply by invoking methods on the handle. If a handle’s relative happens to have the same name as one its properties, then the relative is given priority because a handle’s properties can always be accessed explicitly through the handle.get_value and handle.put_value methods.


4.3.2.2   Accessing a handle’s relatives

Imagine that the design under test, say foo, instantiated a Verilog module named bar, which in turn contained a register named baz. To access baz from Ruby, one could employ VPI idioms by writing:

foo = vpi_handle_by_name( "foo", nil )
bar = vpi_handle_by_name( "bar", foo )
baz = vpi_handle_by_name( "baz", bar )

or by writing:

baz = vpi_handle_by_name( "foo.bar.bar", nil )

These idioms seem excessively verbose in a higher level language such as Ruby, so Ruby-VPI allows you to access a handle’s relative by simply invoking the relative’s name as a method on the handle:

foo.bar.baz


4.3.2.3   Accessing a handle’s properties

Imagine that the design under test, say foo, contained a register named bar. To access the integer value of bar in Ruby-VPI, one could employ VPI idioms by writing:

wrapper = S_vpi_value.new
wrapper.format = VpiIntVal
vpi_get_value( foo.bar, wrapper )
result = wrapper.value.integer

or, if bar is capable of storing more than 32 bits, one would convert a string representation of bar’s integer value into a limitless Ruby integer by writing:

wrapper = S_vpi_value.new
wrapper.format = VpiHexStrVal
vpi_get_value( foo.bar, wrapper )
result = wrapper.value.str.to_i( 16 )

These idioms seem excessively verbose in a higher level language such as Ruby, so Ruby-VPI allows you to access a handle’s properties by simply invoking property names, using the special naming format shown in Figure 4, as methods on the handle:

result = foo.bar.intVal

Figure 4.   Method naming format for accessing a handle’s properties

Operation _ Property _ Accessor Addendum
optional required optional
  • Operation suggests a method that should be invoked in the context of the Property parameter. All methods in Ruby’s Enumerable module are valid operations.
  • Property suggests a VPI property that should be accessed. The “vpi” prefix, which is common to all VPI properties, can be omitted if you wish. For example, the VPI property “vpiFullName” is considered equivalent to “fullName” and “FullName”, but not equivalent to “full_name”.
  • Accessor suggests a VPI function that should be used in order to access the VPI property. When this parameter is not specified, Ruby-VPI will attempt to guess the value of this parameter.

    Table 1 shows a list of valid accessors and how they influence the means by which a property is accessed.

  • When Addendum is an equal sign (=), it suggests that the specified VPI property should be written to.

    When it is a question mark (?), it suggests that the specified VPI property should be accessed as a boolean value. This suggestion is the same as specifying “b” as the Accessor.


Table 1.   Possible accessors and their implications

Accessor Kind of value accessed VPI functions used to access the value
d delay vpi_get_delays, vpi_put_delays
l logic vpi_get_value, vpi_put_value
i integer vpi_get
b boolean vpi_get
s string vpi_get_str
h handle vpi_handle
a array vpi_iterate

Table 2.   Examples of accessing a handle’s properties

Ruby expression Method naming format Description
Operation _ Property _ Accessor Addendum
handle.vpiIntVal     vpiIntVal       Obtain the logic value of the handle’s VpiIntVal property.
handle.vpiIntVal_l     vpiIntVal _ l  
handle.intVal     intVal      
handle.intVal_l     intVal _ l  
handle.vpiIntVal = 15     vpiIntVal     = Assign the integer 15 to the logic value of the handle’s VpiIntVal property.
handle.vpiIntVal_l = 15     vpiIntVal _ l =
handle.intVal = 15     intVal     =
handle.intVal_l = 15     intVal _ l =
handle.vpiType     vpiType       Obtain the integer value of the handle’s VpiType property.
handle.vpiType_i     vpiType _ i  
handle.type     type      
handle.type_i     type _ i  
handle.vpiProtected     vpiProtected       Obtain the boolean value of the handle’s VpiProtected property.
handle.vpiProtected_b     vpiProtected _ b  
handle.vpiProtected?     vpiProtected     ?
handle.protected     protected      
handle.protected_b     protected _ b  
handle.protected?     protected     ?
handle.vpiFullName     vpiFullName       Obtain the string value of the handle’s VpiFullName property.
handle.vpiFullName_s     vpiFullName _ s  
handle.fullName     fullName      
handle.fullName_s     fullName _ s  
handle.vpiParent     vpiParent       Obtain the handle value of the handle’s VpiParent property.
handle.vpiParent_h     vpiParent _ h  
handle.parent     parent      
handle.parent_h     parent _ h  
handle.each_vpiNet {|net| puts net.fullName} each _ vpiNet       Use the each operation to print the full name of each VpiNet object associated with the handle.
handle.each_net {|net| puts net.fullName} each _ net      
handle.all_vpiReg? {|reg| reg.size == 1} all? _ vpiReg       Use the all? operation to check whether all VpiReg objects associated with the handle are capable of storing only one bit of information.
handle.all_reg? {|reg| reg.size == 1} all? _ reg      
handle.select_vpiNet {|net| net.x?} select _ VpiNet       Use the select operation to obtain a list of VpiNet objects whose logic value is unknown (x).
handle.select_net {|net| net.x?} select _ net      


4.3.3   Callbacks

A callback is a mechanism that makes the Verilog simuluator execute a block of code, which is known as a “callback handler”, when some prescribed event occurs in the simulation.

Callbacks are added using the vpi_register_cb function and removed using the vpi_remove_cb function. However, instead of storing the address of a C function in the cb_rtn field of the s_cb_data structure (as you would do in C) you pass a block of code to the vpi_register_cb method in Ruby. This block will be executed whenever the callback occurs.


Example 1.   Using a callback for value change notification

This example shows how to use a callback for notification of changes in a handle’s VpiIntVal property. When you no longer need this callback, you can tear it down using vpi_remove_cb(cb_handle).

In this example, the handle being monitored is the Counter.count signal from Example 3.

cbTime         = S_vpi_time.new
cbTime.type    = VpiSimTime
cbTime.low     = 0
cbTime.high    = 0

cbValue        = S_vpi_value.new
cbValue.format = VpiIntVal

cbData         = S_cb_data.new
cbData.reason  = CbValueChange
cbData.obj     = Counter.count
cbData.time    = cbTime
cbData.value   = cbValue
cbData.index   = 0

cbHandle = vpi_register_cb(cbData) do |data|
  time   = (data.time.high << 32) | data.time.low
  count  = data.value.value.integer
  puts "hello from callback! time=#{time} count=#{count}"
end

Append this code to the RSpec/counter_spec.rb file (provided in Section 5.5 and discussed in Section 5.6.3) and run the counter_RSpec test


Chapter 5

Usage


5.1   Prototyping

Ruby-VPI enables you to rapidly prototype your designs in Ruby without having to do full-scale implementations in Verilog. This lets you explore and evaluate different design choices quickly.

The prototyping process is completely transparent: there is absolutely no difference, in the eyes of your executable specification, between a real Verilog design or its Ruby prototype.

In addition, the prototyping process is completely standard-based: Ruby prototypes emulate the behavior of real Verilog designs using nothing more than the VPI itself.

For example, compare the Verilog design shown in Example 11 with its Ruby prototype shown in figure Example 8. The prototype uses only VPI to (1) detect changes in its inputs and (2) manipulate its outputs accordingly. In addition, notice how well the prototype’s syntax reflects the intended behavior of the Verilog design. This similarity facilitates rapid translation of a prototype from Ruby into Verilog later in the design process.

Getting started

To create a prototype,
  1. Start with a Verilog module declaration for your design.
  2. Generate a test using that module declaration.
  3. Implement the prototype in the generated proto.rb file.
  4. Verify the prototype against its specification.

Once you are satisfied with your prototype, you can proceed to implement your design in Verilog. This process is often a simple translation your Ruby prototype into your Verilog. At the very least, your prototype serves as a reference while you are implementing your Verilog design.

Once your design has been implemented in Verilog, you can use the same specification, which was originally used to verify your prototype, to verify your Verilog design (see Section 5.3 for details).


5.1.2   How does prototyping work?

The advance_time method normally transfers control from the executable specification to the Verilog simulator. However, when prototyping is enabled, Ruby-VPI redefines it so that the feign! method (which is defined in a test’s proto.rb file) is invoked on the design under test. The feign! method artificially simulates the behavior of the real Verilog design.

In this manner, control is kept within the Ruby interpreter when prototyping is enabled. An advantage of this approach is that it reduces the total execution time of a Ruby-VPI test by allowing Ruby’s POSIX thread to commandeer the Verilog simulator’s process. A disadvantage of this approach is that callbacks, which require the transfer of control to the Verilog simulator, must be ignored.


5.2   Debugging

The ruby-debug project serves as the interactive debugger for Ruby-VPI.

  1. Enable the debugger by activating the DEBUGGER environment variable (see Section 5.3 for details).
  2. Put the debugger command in your code—anywhere you wish to activate an interactive debugging session. These commands are automatically ignored when the debugger is disabled; so you can safely leave them in your code, if you wish.


5.2.1   Advanced initialization

By default, Ruby-VPI enables the debugger by invoking the Debugger.start method. If you wish to perform more advanced initialization, such as having the debugger accept remote network connections for interfacing with a remote debugging session or perhaps with an IDE (see the ruby-debug documentation for details), then:
  1. Deactivate the DEBUG environment variable.
  2. Put your own code, which initializes the debugger, at the top of your test’s spec.rb file.


5.3   Test runner

A test runner is a file, generated by the automated test generator whose name ends with .rake. It helps you run generated tests—you can think of it as a makefile if you are familiar with C programming in a UNIX environment.

When you invoke a test runner without any arguments, it will show you a list of available tasks:

$ rake -f your_test_runner.rake

(in /home/sun/src/ruby-vpi/doc)
rake clean    # Remove any temporary products.
rake clobber  # Remove any generated file.
rake cver     # Simulate with GPL Cver.
rake default  # Show a list of available tasks.
rake ivl      # Simulate with Icarus Verilog.
rake ncsim    # Simulate with Cadence NC-Sim.
rake vcs      # Simulate with Synopsys VCS.
rake vsim     # Simulate with Mentor Modelsim.


5.3.1   Environment variables

Test runners support the following environment variables, which allow you to easily change the behavior of the test runner.

  • COVERAGE enables code coverage analysis and generation of code coverage reports.
  • DEBUGGER enables the interactive debugger in its post-mortem debugging mode.
  • PROTOTYPE enables the Ruby prototype for the design under test so that the prototype, rather than the real Verilog design, is verified by the specification.

To activate these variables, simply assign the number 1 to them. For example, DEBUG=1 activates the DEBUG variable.

To deactivate these variables, simply assign a different value to them or unset them in your shell. For example, both DEBUG=0 and DEBUG= dectivate the DEBUG variable.

Variables as command-line arguments

You can specify variable assignments as arguments to the rake command. For example,
rake DEBUG=1
is equivalent to
DEBUG=1
export DEBUG
rake
in Bourne shell or
setenv DEBUG 1
rake
in C shell.


Example 2.   Running a test with environment variables

Below, we enable the prototype and code coverage analysis:

rake -f your_test_runner.rake PROTOTYPE=1 COVERAGE=1

Below, we disable the prototype and enable the code coverage analysis. These invocations are equivalent if the PROTOTYPE environment variable is unset.

rake -f your_test_runner.rake PROTOTYPE=0 COVERAGE=1
rake -f your_test_runner.rake PROTOTYPE=  COVERAGE=1
rake -f your_test_runner.rake COVERAGE=1


5.4   Tools

The ruby-vpi command serves as a front-end to the tools provided by Ruby-VPI. You can see its help information (reproduced below) by simply running the command without any arguments.

This is a front-end for tools provided by Ruby-VPI.

Usage:

  ruby-vpi                                    Show this help message
  ruby-vpi TOOL --help                        Show help message for TOOL
  ruby-vpi TOOL arguments...                  Run TOOL with some arguments

Tools:
  convert    Converts Verilog source code into Ruby.
  generate   Generates Ruby-VPI tests from Verilog 2001 and Verilog 95 module declarations.

Simulators:
  cver       GPL Cver
  ivl        Icarus Verilog
  vcs        Synopsys VCS
  vsim       Mentor Modelsim
  ncsim      Cadence NC-Sim


5.4.1   Automated test generation

The generate tool generates scaffolding for Ruby-VPI tests from Verilog module declarations (written in either Verilog 2001 or Verilog 95 style).

A Ruby-VPI test is composed of the following files:
  • runner.rake runs the test by starting a Verilog simulator and loading Ruby-VPI into it.
  • spec.rb is the executable specification for the design under test.
  • design.rb is an optional file that provides convenience methods for controlling the design under test.
  • proto.rb is an optional file that defines a Ruby prototype of the design under test.
  • Rakefile is an optional file that recursively executes all runner.rake files found immediately within or beneath the current directory. It lets you simply run
    rake ...
    instead of having to write
    rake -f runner.rake ...
    every time.

As Example 4 shows, the name of each generated file is prefixed with the name of the Verilog module for which the test was generated. This convention helps organize tests within the file system, so that they are readily distinguishable from one another.


caution

Caution 1.   Do not rename generated files

Ruby-VPI uses the convention described above to dynamically create a direct Ruby interface to the design under test, so do not rename the generated files arbitrarily.
By producing multiple files, the automated test generator physically decouples the various parts of a test. As a result, when the interface of a Verilog module changes, you can simply regenerate the test to incorporate those changes without diverting your focus from the task at hand. Furthermore, the incorporation of changes can be catalyzed by interactive text merging tools, which allow you to selectively accept or reject the merging of changes into your source code. Fully automated text merging tools may also be used for this purpose.

You can try this tool by running the

ruby-vpi generate --help
command.


tip

Tip 3.   Using kdiff3 with the automated test generator.

  1. Create a file named merge2 with the following content:
    #!/bin/sh
    # args: old file, new file
    kdiff3 --auto --output "$2" "$@"
    
  2. Make the file executable by running the
    chmod +x merge2
    command.
  3. Place the file somewhere accessible by your PATH environment variable.
  4. Assign the value “merge2” to the MERGER environment variable using your shell’s export or setenv command.

From now on, kdiff3 will be invoked to help you transfer your changes between generated files. When you are finished transferring changes, simply issue the “save the file” command and quit kdiff3. Or, if you do not want to transfer any changes, simply quit kdiff3 without saving the file.


5.4.2   Verilog to Ruby conversion

The convert tool can be used to convert Verilog header files into Ruby. You can try it by running the

ruby-vpi convert --help
command.

By converting Verilog header files into Ruby, your test can utilize the same `define constants that are used in the Verilog design.


5.5   Sample tests

The samp directory (browse it online) contains several sample tests which illustrate how Ruby-VPI can be used. Each sample has an associated Rakefile which simplifies the process of running it. Therefore, simply navigate into an example directory and run the
rake
command to get started.

5.6   Tutorial

  1. Declare a design using Verilog 2001 syntax.
  2. Generate a test for the design using the automated test generator tool.
  3. Identify your expectations for the design and implement them in the specification.
  4. (Optional) Implement the prototype of the design in Ruby.
  5. (Optional) Verify the prototype against the specification.
  6. Implement the design in Verilog once the prototype has been verified.
  7. Verify the design against the specification.


5.6.1   Start with a Verilog design

First, we need a Verilog design to test. In this tutorial, Example 3 will serve as our design under test. Its interface is composed of the following parts:

  • Size defines the number of bits used to represent the counter’s value.
  • clock causes the count register to increment whenever it reaches a positive edge.
  • reset causes the count register to become zero when asserted.
  • count is a register that contains the counter’s value.


Example 3.   Declaration of a simple up-counter with synchronous reset

module counter #(parameter Size = 5) (
  input                   clock,
  input                   reset,
  output reg [Size-1 : 0] count
);
endmodule
Before we continue, save the source code shown in Example 3 into a file named counter.v.


5.6.2   Generate a test

Now that we have a Verilog design to test, we shall use the generate tool to generate some scaffolding for our test. This tool allows us to implement our specification using RSpec, xUnit, or any other format.

Each format represents a different software development methodology:
  • RSpec represents BDD
  • xUnit represents TDD
  • our own format can represent another methodology

In this tutorial, you will see how both RSpec and xUnit formats are used. So let us make separate directories for both formats to avoid generated tests from overwriting each other:

$ mkdir RSpec xUnit
$ cp counter.v RSpec
$ cp counter.v xUnit

Once we have decided how we want to implement our specification, we can proceed to generate a test for our design. This process is illustrated by Example 4 and Example 5.


Example 4.   Generating a test with specification in RSpec format

$ ruby-vpi generate counter.v --RSpec

  module  counter
  create  counter_runner.rake
  create  counter_design.rb
  create  counter_proto.rb
  create  counter_spec.rb
  create  Rakefile

Example 5.   Generating a test with specification in xUnit format

$ ruby-vpi generate counter.v --xUnit

  module  counter
  create  counter_runner.rake
  create  counter_design.rb
  create  counter_proto.rb
  create  counter_spec.rb
  create  Rakefile


5.6.3   Specify your expectations

So far, the test generation tool has created a basic foundation for our test Now we must build upon this foundation by identifying our expectation of the design under test. That is, how do we expect the design to behave under certain conditions?

Here are some reasonable expectations for our simple counter:
  • A resetted counter’s value should be zero.
  • A resetted counter’s value should increment by one count upon each rising clock edge.
  • A counter with the maximum value should overflow upon increment.

Now that we have identified a set of expectations for our design, we are ready to implement them in our specification. This process is illustrated by Example 6 and Example 7.


Example 6.   Specification implemented in RSpec format

require 'spec'

# lowest upper bound of counter's value
LIMIT = 2 ** Counter::Size

# maximum allowed value for a counter
MAX = LIMIT - 1

describe "A resetted counter's value" do
  setup do
    Counter.reset!
  end

  it "should be zero" do
    Counter.count.intVal.should == 0
  end

  it "should increment upon each rising clock edge" do
    LIMIT.times do |i|
      Counter.count.intVal.should == i
      Counter.cycle! # increment the counter
    end
  end
end

describe "A counter with the maximum value" do
  setup do
    Counter.reset!

    # increment the counter to maximum value
    MAX.times { Counter.cycle! }
    Counter.count.intVal.should == MAX
  end

  it "should overflow upon increment" do
    Counter.cycle! # increment the counter
    Counter.count.intVal.should == 0
  end
end

Example 7.   Specification implemented in xUnit format

require 'test/unit'

# lowest upper bound of counter's value
LIMIT = 2 ** Counter::Size

# maximum allowed value for a counter
MAX = LIMIT - 1

class ResettedCounterValue < Test::Unit::TestCase
  def setup
    Counter.reset!
  end

  def test_zero
    assert_equal( 0, Counter.count.intVal )
  end

  def test_increment
    LIMIT.times do |i|
      assert_equal( i, Counter.count.intVal )
      Counter.cycle! # increment the counter
    end
  end
end

class MaximumCounterValue < Test::Unit::TestCase
  def setup
    Counter.reset!

    # increment the counter to maximum value
    MAX.times { Counter.cycle! }
    assert_equal( MAX, Counter.count.intVal )
  end

  def test_overflow
    Counter.cycle! # increment the counter
    assert_equal( 0, Counter.count.intVal )
  end
end
Before we continue,
  1. Replace the contents of the file named RSpec/counter_spec.rb with the source code shown in Example 6.
  2. Replace the contents of the file named xUnit/counter_spec.rb with the source code shown in Example 7.


5.6.4   Implement the prototype

Now that we have a specification against which to verify our design let us build a prototype of our design. By doing so, we exercise our specification, experience potential problems that may arise when we later implement our design in Verilog, and gain confidence in our work. The result of this proceess is illustrated by Example 8.


Example 8.   Ruby prototype of our Verilog design

# Ruby prototype of the design under test's Verilog implementation.
def feign!
  if clock.posedge?
    if reset.high?
      count.intVal = 0
    else
      count.intVal += 1
    end
  end
end
Before we continue, replace the contents of the files named RSpec/counter_proto.rb and xUnit/counter_proto.rb with the source code shown in Example 8.


5.6.5   Verify the prototype

Now that we have implemented our prototype, we are ready to verify it against our specification by running the test This process is illustrated by Example 9 and Example 10.

In these examples, the PROTOTYPE environment variable is assigned the value 1 while running the test so that, instead of our design, our prototype is verified against our specification (see Section 5.3.1 for details). Also, the GPL Cver simulator denoted by cver, is used to run the simulation.


Example 9.   Running a test with specification in RSpec format

$ cd RSpec
$ rake cver PROTOTYPE=1

Ruby-VPI: prototype is enabled
...

Finished in 0.05106 seconds

3 examples, 0 failures
cd -

Example 10.   Running a test with specification in xUnit format

$ cd xUnit
$ rake cver PROTOTYPE=1

Ruby-VPI: prototype is enabled
Loaded suite counter
Started
...
Finished in 0.043859 seconds.

3 tests, 35 assertions, 0 failures, 0 errors

tip

Tip 4.   What can the test runner do?

If you invoke the test runner (1) without any arguments or (2) with the --tasks option, it will show you a list of tasks that it can perform for you.


5.6.6   Implement the design

Now that we have implemented and verified our prototype, we are ready to implement our design This is often quite simple because we translate existing code from Ruby (our prototype) into Verilog (our design). The result of this process is illustrated by Example 11.


Example 11.   Implementation of a simple up-counter with synchronous reset

/**
  A simple up-counter with synchronous reset.

  @param  Size   Number of bits used to represent the counter's value.
  @param  clock  Increments the counter's value upon each positive edge.
  @param  reset  Zeroes the counter's value when asserted.
  @param  count  The counter's value.
*/
module counter #(parameter Size = 5) (
  input                   clock,
  input                   reset,
  output reg [Size-1 : 0] count
);
  always @(posedge clock) begin
    if (reset)
      count <= 0;
    else
      count <= count + 1;
  end
endmodule
Before we continue, replace the contents of the files named RSpec/counter.v and xUnit/counter.v with the source code shown in Example 11


5.6.7   Verify the design

Now that we have implemented our design we are ready to verify it against our specification by running the test Example 12 and Example 13 illustrate this process.

In these examples, the PROTOTYPE environment variable is not specified while running the test, so that our design, instead of our prototype, is verified against our specification. You can also achieve this effect by assigning an empty value to PROTOTYPE, or by using your shell’s unset command. Finally, the GPL Cver simulator denoted by cver, is used to run the simulation.


Example 12.   Running a test with specification in RSpec format

$ cd RSpec
$ rake cver

...

Finished in 0.041198 seconds

3 examples, 0 failures

Example 13.   Running a test with specification in xUnit format

$ cd xUnit
$ rake cver

Loaded suite counter
Started
...
Finished in 0.040262 seconds.

3 tests, 35 assertions, 0 failures, 0 errors


Chapter 6

Hacking


6.1   Getting the source code

Check out the source code using Darcs from the project repository:
darcs get http://ruby-vpi.rubyforge.org/src/ruby-vpi


6.2   Building release packages

In addition to the normal requirements you need the following software to build release packages:

Once you have satisfied these requirements, you can run

rake release
to build the release packages. Also, see the output of
rake -T
for more build options.


6.3   Editing this manual

The “doc” files inside the doc/ directory are really plain text files that contain the source code of this manual. You can edit these files and run the
rake
command to automatically generate the HTML documentation you are currently viewing.


Chapter 7

Known problems

This chapter presents known problems and possible solutions.


7.1   Icarus Verilog

The following sections describe problems that occur when Icarus Verilog is used with Ruby-VPI.


7.1.1   Give full paths to Verilog objects

In version 0.8 and snapshot 20061009 of Icarus Verilog, the vpi_handle_by_name function requires an absolute path (including the name of the bench which instantiates the design) to a Verilog object. In addition, vpi_handle_by_name always returns nil when its second parameter is specified.

For example, consider Example 14. Here, one must write vpi_handle_by_name("TestFoo.my_foo.clk", nil) instead of vpi_handle_by_name("my_foo.clk", TestFoo) in order to access the clk input of the my_foo module instance.


Example 14.   Part of a bench which instantiates a Verilog design

module TestFoo;
  reg clk_reg;
  Foo my_foo(.clk(clk_reg));
endmodule


7.1.2   Registers must be connected

In version 0.8 of Icarus Verilog, if you want to access a register in a design, then it must be connected to something (either assigned to a wire or passed as a parameter to a module instantiation). Otherwise, you will get a nil value as the result of vpi_handle_by_name method.

For example, suppose you wanted to access the clk_reg register, from the bench shown in Example 15 If you execute the statement clk_reg = vpi_handle_by_name("TestFoo.clk_reg", nil) in a specification, then you will discover that the vpi_handle_by_name method returns nil instead of a handle to the clk_reg register.

The solution is to change the design such that it appears like the one shown in Example 16 where the register is connected to a wire, or Example 14 where the register is connected to a module instantiation.


Example 15.   Bad design with unconnected registers

Here the clk_reg register is not connected to anything.
module TestFoo;
  reg clk_reg;
endmodule

Example 16.   Fixed design with wired registers

Here the clk_reg register is connected to the clk_wire wire.
module TestFoo;
  reg clk_reg;
  wire clk_wire;
  assign clk_wire = clk_reg;
endmodule


7.1.3   Vpi::reset

In version 0.8 of Icarus Verilog, the vpi_control(vpiReset) VPI function causes an assertion to fail inside the simulator. As a result, the simulation terminates and a core dump is produced.


7.2   Cadence NC-Sim

The following sections describe problems that occur when Cadence NC-Sim (version 05.83-s003) is used with Ruby-VPI.


7.2.1   Cannot force values onto handles

When you write to a handle’s value using vpi_put_value() with the VpiForceFlag propagation parameter, it does not have any effect. As a result, the “register_file” sample test fails when you run it with NC-Sim.

This might be a bug in NC-Sim itself: even though I specified the “+access+rwc” command-line option for NC-Sim, I’m thinking that the force/release capability is not really enabled. However, it’s more likely that there’s a bug in the “register_file” sample test.

If you happen to know the solution, please tell me either on the project forums or via e-mail (see the LICENSE file for my e-mail address). Thanks.


Chapter 8

Glossary


8.1   Test

Something that checks if a design satisfies a specification

8.2   Design

A Verilog module that is verified against a specification in order to ensure correctness or soundness of its being. In other words, it is the thing being checked: does it work or not?

8.3   Specification

A set of expectations which define the desired behavior of a design when it is subjected to certain stimulus.

8.4   Expectation

The desired response to some stimulus.

8.5   Handle

A reference to an object inside the Verilog simulation. See Section 4.3.2 for usage instructions.

8.6   Rake

Rake is a build tool, written in Ruby, using Ruby as a build language. Rake is similar to make in scope and purpose.

Rake documentation


8.7   RSpec

The BDD framework for Ruby.

See the RSpec website and tutorial for more information.


8.8   Test driven development

An agile software development methodology which emphasizes (1) testing functionality before implementing it and (2) refactoring.

See this introductory article for more information.


8.9   Behavior driven development

An agile software development methodology which emphasizes thinking in terms of behavior when designing, implementing, and verifying software.

See the official wiki for more information.


Contents

Tips

  1. Add support for your Verilog simulator
  2. Tuning for maximum performance
  3. Using kdiff3 with the automated test generator.
  4. What can the test runner do?

Cautions

  1. Do not rename generated files

Figures

  1. Where does Ruby-VPI fit in?
  2. Interaction between Ruby and Verilog
  3. Organization of a test in Ruby-VPI
  4. Method naming format for accessing a handle’s properties

Tables

  1. Possible accessors and their implications
  2. Examples of accessing a handle’s properties

Examples

  1. Using a callback for value change notification
  2. Running a test with environment variables
  3. Declaration of a simple up-counter with synchronous reset
  4. Generating a test with specification in RSpec format
  5. Generating a test with specification in xUnit format
  6. Specification implemented in RSpec format
  7. Specification implemented in xUnit format
  8. Ruby prototype of our Verilog design
  9. Running a test with specification in RSpec format
  10. Running a test with specification in xUnit format
  11. Implementation of a simple up-counter with synchronous reset
  12. Running a test with specification in RSpec format
  13. Running a test with specification in xUnit format
  14. Part of a bench which instantiates a Verilog design
  15. Bad design with unconnected registers
  16. Fixed design with wired registers