This manual was last updated on Sat Jan 13 16:42:19 PST 2007.
It 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 alongside this manual.
You can give feedback about this manual and, in general, any aspect of the Ruby-VPI project on the project forums.
Happy reading!
Copyright© 2006 Suraj N. Kurapati.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the license is included in the the file named LICENSE.
The admonition and navigation graphics used in this manual are Copyright© 2005, 2006 Tango Desktop Project and are licensed under these terms.
Ruby-VPI is a Ruby interface to IEEE 1364-2005 Verilog VPI. It lets you create complex Verilog test benches easily and wholly in Ruby.
Here is a modest sampling of tasks that Ruby-VPI can be used to perform.
Here is a modest sampling of code to whet your appetite.
some_register.intVal = 2 ** 2048
some_module.all_net? { |net| net.z? }
puts some_register
15.times { simulate }
Ruby-VPI is free software ; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation ; either version 2 of the License, or (at your option) any later version.
The following projects utilize the archaic tf and acc PLI interfaces, which have been officially deprecated in IEEE Std 1364-2005.
Ruby-VPI presents an open-ended interface to VPI. Thus, you can use any methodology you wish when writing tests. However, being an agile language, Ruby makes it very easy to use agile development practies such as TDD and BDD.
Note:
Have a look at the glossary for definitions of terms used in this manual.
As a newcomer into the world of Verilog, I often heard the term test bench: “I ran the test bench, but it didn’t work!” or “Are you crazy?!! You still haven’t written the test bench?”, for example. I poured through my textbook for a definition of the term, but it was to no avail. Instead, it nonchalantly employed the term throughout its being, as if mocking my ignorance of what seems to be universal knowledge.
Defeated, I turned to my inner faculties to determine the answer. Let’s see, the term test bench has the word test—so it has something to do with testing—and it has the word bench—so maybe it’s referring to a table where the testing should occur. This reasoning grew increasingly familiar as my mind rummaged through towering stores of obsolescence and ultimately revealed dreaded memories of sleepless anguish: debugging electronics in the robotics laboratory.
Aha! I exclaimed, hesitantly, rushing to dismiss the past. The term has its roots in the testing of electronic devices, where an engineer would sit at a bench in an electronics laboratory and verify that an electronic component satisfies some criteria. The bench would be furnished with tools of measurement and manipulation—such as oscilloscopes, voltmeters, soldering irons, and so on—which help the engineer to verify the electronic component or locate the sources of defects in the component.
Alright, now I remember what a laboratory bench is, but how does that compare with the term test bench? Surely they cannot have the same meaning, because it doesn’t make sense to run a laboratory bench or to write one. Thus, to avoid propagating such confusion into this manual, I have attempted to clarify the terminology by simplifying and reintroducing it in a new light.
Figure 1. Overall organization of a test
As the figure named “Overall organization of a test” shows, a test is composed of a bench, a design, and a specification.
To extend the analogy of an electronics laboratory, the bench acts as the laboratory bench which provides measurement and manipulation tools. The design acts as the electronic component being verified by the engineer. And the specification acts as the engineer who measures, manipulates, and verifies the electronic component.
Figure 2. Detailed organization of a test
Now, let us take a more detailed look at this organization, as illustrated in the figure named “Detailed organization of a test”.
Notice that Ruby-VPI encapsulates all communication between the Ruby interpreter and VPI. This allows the specification, or any Ruby program in general, to access VPI using nothing more than the Ruby language! Thus, Ruby-VPI removes the burden of having to write C programs in order to use VPI.
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 Vpi::advance_time
method, which returns control to the specification when it finishes. This process is illustrated in the figure named “Interaction between Ruby and Verilog”.
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 3. Interaction between Ruby and Verilog
Vpi::advance_time
method with parameter Y, which specifies the number of simulation time steps to be simulated.When you extract a release package, the following is what you would expect to find.
The following software is necessary in order to use Ruby-VPI.
Tip: 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 we will add support for your simulator in the next release!
The following software may make your interactions with Ruby-VPI more pleasant.
Once you have satisfied the necessary requirements, you can install Ruby-VPI by running the
gem install -y ruby-vpicommand. RubyGems will install Ruby-VPI into the system gem directory, whose path can be determined by running the
gem env gemdircommand. 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 /usr/lib/ruby/gems/1.8/gems/ruby-vpi* /usr/lib/ruby/gems/1.8/gems/ruby-vpi-7.0.0/
Note: Undefined symbols in 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 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.
for x in *.{o,so,dll}; do nm $x | grep -q '[Tt] _vpi' && echo $x; donecommand in Cygwin.
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.socommand in Cygwin.
gem install ruby-vpicommand in Cygwin.
gem uninstall ruby-vpicommand.
gem update ruby-vpicommand.
Note:
Learn more about using and manipulating RubyGems in the RubyGems user manual.
The entire IEEE Std 1364-2005 VPI interface is available in Ruby, but with one minor difference: the names of all VPI types, structures, and constants become capitalized because Ruby requires that the names of constants begin with a capital letter.
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.
Note that this capitalization rule does not apply to VPI functions; their names remain unchanged in Ruby.
A handle is a reference to an object, such as a module, register, wire, and so on, inside the Verilog simulation. In short, handles allow you to inspect and manipulate the design under test and its components.
Note:
Handles are instances of the Vpi::Handle
class (see reference documentation for details) in Ruby-VPI.
Handles have various properties, which provide different kinds of information (see the “Kind of value accessed” column in the table named “Possible accessors and their implications”) about the underlying Verilog object represented by a handle. These properties are accessed through various functions, which are listed in the “VPI functions used to access the value” column in the table named “Possible accessors and their implications”.
Handles are typically obtained through the vpi_handle_by_name
and vpi_handle
functions. These functions are hierarchical in nature, because they allow you to obtain new handles that are related to existing handles. For example, to obtain a handle to a register inside a module, you would typically write: some_reg = vpi_handle(VpiReg, some_handle)
.
Shortcuts for productivity
Given a handle, Ruby-VPI allows you to access (1) its relatives and (2) its properties by simply invoking its methods.
If a handle’s relative happens to have the same name as one of the handle’s properties, then the relative is given preference. However, if you really need to access a handle’s property in such a situation, then you can use the Vpi::Handle.get_value
and Vpi::Handle.put_value
methods.
To access a handle’s relative (a handle related to it), simply invoke the relative’s name as a method on the handle.
For example, to access the reset
signal of the counter
module shown in the example named “Declaration of a simple up-counter with synchronous reset”, you would write the following:
counter_module = vpi_handle_by_name("counter", nil) reset_signal = counter_module.reset # <== shortcut!
In this code, the shortcut is that you simply wrote counter_module.reset
instead of having to write vpi_handle_by_name("reset", counter_module)
.
To access a handle’s properties, invoke the property name, using the following format, as a method on the handle. the example named “Examples of accessing a handle’s properties” shows how this naming format is used.
Figure 4. Method naming format for accessing a handle’s properties
Operation | _ | Property | _ | Accessor | Addendum |
---|---|---|---|---|---|
optional | required | optional |
the table named “Possible accessors and their implications” shows a list of valid accessors and how they affect the access to a property.
?
. This suggestion is the same as specifying b
for the Accessor parameter.
Also, when this parameter is an equal sign =
, it suggests that the specified VPI property should be written to.
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 |
Example 1. Examples of accessing a handle’s properties
Ruby expression | Parts of speech | Description | |||||
---|---|---|---|---|---|---|---|
Operation | _ | Property | _ | Accessor | Addendum | ||
handle.vpiIntVal |
vpiIntVal | These expressions access 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 | = | These expressions assign the number 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 | These expressions access 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 | These expressions access 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 | These expressions access 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 | These expressions access 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 | These expressions 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 | These expressions check if all registers associated with the handle are capable of storing only one bit. | |||
handle.all_reg? {|reg| reg.size == 1} |
all? | _ | reg | ||||
handle.select_vpiNet {|net| net.x?} |
select | _ | VpiNet | These expressions return a list of nets whose logic value is unknown or “don’t care” (x). | |||
handle.select_net {|net| net.x?} |
select | _ | net |
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. They are set up using the vpi_register_cb
function and torn down using the vpi_remove_cb
function.
Example 2. 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 the example named “Declaration of a simple up-counter with synchronous reset”.
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
To see this code in action, append it to the counter_rspec_spec.rb and counter_xunit_spec.rb files, which are provided in the section named “Sample tests” and discussed in the section named “Specify your expectations”.
Figure 5. Output from the example named “Using a callback for value change notification”
Shown below is the output from running the counter_rspec test after appending the code shown in the example named “Using a callback for value change notification” to the counter_rspec_spec.rb file.
$ rake -f counter_rspec_runner.rake cver (in /home/sun/src/ruby-vpi/samp/counter) cver +loadvpi=/home/sun/src/ruby-vpi/lib/ruby-vpi/../../obj/ruby-vpi.cver.so:vlog_startup_routines_bootstrap counter.v counter_rspec_bench.v GPLCVER_2.11a of 07/05/05 (Linux-elf). Copyright (c) 1991-2005 Pragmatic C Software Corp. All Rights reserved. Licensed under the GNU General Public License (GPL). See the 'COPYING' file for details. NO WARRANTY provided. Today is Sat Dec 30 09:24:09 2006. Compiling source file "counter.v" Compiling source file "counter_rspec_bench.v" Highest level modules: counter_rspec_bench A resetted counter's value hello from callback! time=1 count=0 - should be zero hello from callback! time=5 count=1 hello from callback! time=7 count=2 hello from callback! time=9 count=3 hello from callback! time=11 count=4 hello from callback! time=13 count=5 hello from callback! time=15 count=6 hello from callback! time=17 count=7 hello from callback! time=19 count=8 hello from callback! time=21 count=9 hello from callback! time=23 count=10 hello from callback! time=25 count=11 hello from callback! time=27 count=12 hello from callback! time=29 count=13 hello from callback! time=31 count=14 hello from callback! time=33 count=15 hello from callback! time=35 count=16 hello from callback! time=37 count=17 hello from callback! time=39 count=18 hello from callback! time=41 count=19 hello from callback! time=43 count=20 hello from callback! time=45 count=21 hello from callback! time=47 count=22 hello from callback! time=49 count=23 hello from callback! time=51 count=24 hello from callback! time=53 count=25 hello from callback! time=55 count=26 hello from callback! time=57 count=27 hello from callback! time=59 count=28 hello from callback! time=61 count=29 hello from callback! time=63 count=30 hello from callback! time=65 count=31 hello from callback! time=67 count=0 - should increment by one count upon each rising clock edge A counter with the maximum value hello from callback! time=71 count=1 hello from callback! time=73 count=2 hello from callback! time=75 count=3 hello from callback! time=77 count=4 hello from callback! time=79 count=5 hello from callback! time=81 count=6 hello from callback! time=83 count=7 hello from callback! time=85 count=8 hello from callback! time=87 count=9 hello from callback! time=89 count=10 hello from callback! time=91 count=11 hello from callback! time=93 count=12 hello from callback! time=95 count=13 hello from callback! time=97 count=14 hello from callback! time=99 count=15 hello from callback! time=101 count=16 hello from callback! time=103 count=17 hello from callback! time=105 count=18 hello from callback! time=107 count=19 hello from callback! time=109 count=20 hello from callback! time=111 count=21 hello from callback! time=113 count=22 hello from callback! time=115 count=23 hello from callback! time=117 count=24 hello from callback! time=119 count=25 hello from callback! time=121 count=26 hello from callback! time=123 count=27 hello from callback! time=125 count=28 hello from callback! time=127 count=29 hello from callback! time=129 count=30 hello from callback! time=131 count=31 hello from callback! time=133 count=0 - should overflow upon increment Finished in 0.042328 seconds 3 specifications, 0 failures
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.
In order to create a prototype,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.
The ruby-debug project serves as the interactive debugger for Ruby-VPI.
DEBUG
environment variable (see the section named “Test runner” for details).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.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:
DEBUG
environment variable.RubyVpi.init_bench
line in your generated spec.rb file.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.
Example 3. Seeing what a test runner can do
When you invoke a test runner without any arguments, it will show you a list of available tasks:$ rake -f some_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 vcs # Simulate with Synopsys VCS. rake vsim # Simulate with Mentor Modelsim.
Tip: Running multiple tests at once.
Create a file named Rakefile containing the following line.
require 'ruby-vpi/runner_proxy'
Now you can invoke all test runners in the current directory simply by executing
rake cver(where cver denotes the GPL Cver simulator).
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.DEBUG
enables the interactive debugger in its post-mortem debugging mode.PROTOTYPE
enables the Ruby prototype for the design under test.To activate these variables, simply assign a non-empty value to them. For example, DEBUG=1
and DEBUG=yes
and DEBUG=foo_bar_baz
all activate the DEBUG
variable.
To deactivate these variables, simply assign an empty value to them, unset them in your shell, or do not specify them as command-line arguments to rake. For example, both DEBUG=
dectivates the DEBUG
variable.
In addition, you can specify variable assignments as arguments to the rake command. For example,
rake DEBUG=1is equivalent to
$ DEBUG=1 $ export DEBUG $ rakein Bourne shell or
% setenv DEBUG 1 % rakein C shell.
Example 4. Running a test with environment variables
Here we enable the prototype and code coverage analysis:rake -f some_test_runner.rake PROTOTYPE=1 COVERAGE=1Here we disable the prototype and enable the code coverage analysis. Note that both of these invocations are equivalent:
rake -f some_test_runner.rake PROTOTYPE= COVERAGE=1
rake -f some_test_runner.rake COVERAGE=1
The samp directory 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
rakecommand to get started.
The bin directory contains various utilities which ease the process of writing tests. Each tool provides help and usage information invoked with the --help option.
The automated test generator (generate_test.rb) generates tests from Verilog 2001 module declarations, as demonstrated in the tutorial. A generated test is composed of the following parts:
The reason for dividing a single test into these parts is mainly to decouple the design from the specification. This allows you to focus on writing the specification while the remainder is automatically generated by the tool. For example, when the interface of a Verilog module changes, you would simply re-run this tool and incorporate those changes (using a text merging tool) into the test without diverting your focus from the specification.
Tip: Using kdiff3 with the automated test generator.
#!/bin/sh # args: old file, new file kdiff3 --auto --output "$2" "$@"
chmod +x merge2command.
PATH
environment variable.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.
The header_to_ruby.rb tool can be used to convert Verilog header files into Ruby. You can try it by running the
header_to_ruby.rb --helpcommand.
By converting Verilog header files into Ruby, your test can utilize the same `define
constants that are used in the Verilog design.
First, we need a design to verify. In this tutorial, the example named “Declaration of a simple up-counter with synchronous reset” will serve as our design. 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 5. 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
Important: Before we continue…
Save the source code shown in the example named “Declaration of a simple up-counter with synchronous reset” into a file named counter.v.
Now that we have a design to verify, let us generate a test for it using the automated test generator. This tool allows us to implement our specification in either rSpec, xUnit, or our very own format.
Each format represents a different software development methodology:Note:
Both rSpec and xUnit formats are presented in this tutorial.
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 the example named “Generating a test with specification in rSpec format” and the example named “Generating a test with specification in xUnit format”.
Example 6. Generating a test with specification in rSpec format
$ generate_test.rb counter.v --rspec --name rspec module counter create counter_rspec_runner.rake create counter_rspec_bench.v create counter_rspec_bench.rb create counter_rspec_design.rb create counter_rspec_proto.rb create counter_rspec_spec.rb
Example 7. Generating a test with specification in xUnit format
$ generate_test.rb counter.v --xunit --name xunit module counter create counter_xunit_runner.rake create counter_xunit_bench.v create counter_xunit_bench.rb create counter_xunit_design.rb create counter_xunit_proto.rb create counter_xunit_spec.rb
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. That is, how do we expect the design to behave under certain conditions?
Here are some reasonable expectations for our simple counter: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 the example named “Specification implemented in rSpec format” and the example named “Specification implemented in xUnit format”.
Example 8. Specification implemented in rSpec format
# This file is a behavioral specification for the design under test. # lowest upper bound of counter's value LIMIT = 2 ** Counter.Size.intVal # maximum allowed value for a counter MAX = LIMIT - 1 context "A resetted counter's value" do setup do Counter.reset! end specify "should be zero" do Counter.count.intVal.should == 0 end specify "should increment by one count upon each rising clock edge" do LIMIT.times do |i| Counter.count.intVal.should == i simulate # increment the counter end end end context "A counter with the maximum value" do setup do Counter.reset! # increment the counter to maximum value MAX.times {simulate} Counter.count.intVal.should == MAX end specify "should overflow upon increment" do simulate # increment the counter Counter.count.intVal.should == 0 end end
Example 9. Specification implemented in xUnit format
# This file is a behavioral specification for the design under test. # lowest upper bound of counter's value LIMIT = 2 ** Counter.Size.intVal # 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 simulate # increment the counter end end end class MaximumCounterValue < Test::Unit::TestCase def setup Counter.reset! # increment the counter to maximum value MAX.times {simulate} assert_equal MAX, Counter.count.intVal end def test_overflow simulate # increment the counter assert_equal 0, Counter.count.intVal end end
Important: Before we continue…
# This is a Ruby interface to the design under test. # This method resets the design under test. def Counter.reset! reset.intVal = 1 simulate reset.intVal = 0 end
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 the example named “Ruby prototype of our Verilog design”.
Example 10. Ruby prototype of our Verilog design
# This is a prototype of the design under test. # When prototyping is enabled, Vpi::advance_time invokes this # method instead of transferring control to the Verilog simulator. def Counter.simulate! if clock.posedge? if reset.intVal == 1 count.intVal = 0 else count.intVal += 1 end end end
Important: Before we continue…
Replace the contents of the files named counter_rspec_proto.rb and counter_xunit_proto.rb with the source code shown in the example named “Ruby prototype of our Verilog design”
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 the example named “Running a test with specification in rSpec format” and the example named “Running a test with specification in xUnit format”.
In these examples, the PROTOTYPE
environment variable is assigned a non-empty value while running the test so that, instead of our design, our prototype is verified against our specification. You can also assign a value to PROTOTYPE
before running the test, by using your shell’s export or setenv command. Finally, the GPL Cver simulator, denoted by cver, is used to run the simulation.
Example 11. Running a test with specification in rSpec format
$ rake -f counter_rspec_runner.rake cver PROTOTYPE=1 Ruby-VPI: prototype has been enabled for test "counter_rspec" A resetted counter's value - should be zero - should increment by one count upon each rising clock edge A counter with the maximum value - should overflow upon increment Finished in 0.018199 seconds 3 specifications, 0 failures
Example 12. Running a test with specification in xUnit format
$ rake -f counter_xunit_runner.rake cver PROTOTYPE=1 Ruby-VPI: prototype has been enabled for test "counter_xunit" Loaded suite counter_xunit_bench Started ... Finished in 0.040668 seconds. 3 tests, 35 assertions, 0 failures, 0 errors
Tip: 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.
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 the example named “Implementation of a simple up-counter with synchronous reset”.
Example 13. 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
Important: Before we continue…
Replace the contents of the file named counter.v with the source code shown in the example named “Implementation of a simple up-counter with synchronous reset”
Now that we have implemented our design, we are ready to verify it against our specification by running the test. the example named “Running a test with specification in rSpec format” and the example named “Running a test with specification in xUnit format” 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 14. Running a test with specification in rSpec format
$ rake -f counter_rspec_runner.rake cver A resetted counter's value - should be zero - should increment by one count upon each rising clock edge A counter with the maximum value - should overflow upon increment Finished in 0.005628 seconds 3 specifications, 0 failures
Example 15. Running a test with specification in xUnit format
$ rake -f counter_xunit_runner.rake cver Loaded suite counter_xunit_bench Started ... Finished in 0.006766 seconds. 3 tests, 35 assertions, 0 failures, 0 errors
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 releaseto build the release packages. Also, see the output of
rake -Tfor more build options.
This chapter presents known problems and possible solutions. In addition, previously solved problems have been retained for historical reference.
Note: Fixed in 2.0.0.
This problem was fixed in release 2.0.0 (2006-04-17).
If a “stack level too deep (SystemStackError)” error occurs during the simulation, then increase the system-resource limit for stack-size by running the
ulimit -s unlimitedcommand before starting the simulation.
Note: Fixed in 2.0.0.
This problem was fixed in release 2.0.0 (2006-04-17).
If your specification employs Ruby’s unit testing framework, then you will encounter an error saying “[BUG] cross-thread violation on rb_gc()”.
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 the example named “Part of a bench which instantiates a Verilog design”. 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 16. Part of a bench which instantiates a Verilog design
module TestFoo;
reg clk_reg;
Foo my_foo(.clk(clk_reg));
endmodule
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 the example named “Bad design with unconnected registers” 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 the example named “Fixed design with wired registers” where the register is connected to a wire, or the example named “Part of a bench which instantiates a Verilog design” where the register is connected to a module instantiation.
Example 17. Bad design with unconnected registers
module TestFoo;
reg clk_reg;
endmodule
Here the clk_reg
register is not connected to anything.
Example 18. Fixed design with wired registers
module TestFoo;
reg clk_reg;
wire clk_wire;
assign clk_wire = clk_reg;
endmodule
Here the clk_reg
register is connected to the clk_wire
wire.
Caution:
The vpi_control
method was removed in release 3.0.0 (2006-04-23). Please use Vpi::vpi_control(VpiReset)
instead.
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.
Note: Fixed in 2.0.0.
This problem was fixed in release 2.0.0 (2006-04-17).
Version 6.1b of Mentor Modelsim doesn’t play nicely with either an embedded Ruby interpreter or POSIX threads in a PLI application. When Ruby-VPI invokes the ruby_run function (which starts the Ruby interpreter), the simulator terminates immediately with an exit status of 0.
An environment in which a design is verified against a specification. Often, it is used to emulate conditions in which the design will be eventually deployed.
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.
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?
The desired response to some stimulus.
A reference to an object inside the Verilog simulation that was obtained through the vpi_handle_by_name
function.
Rake is a build tool, written in Ruby, using Ruby as a build language. Rake is similar to make in scope and purpose.
The BDD framework for Ruby.
See the rSpec website and tutorial for more information.
A set of expectations which define the desired behavior of a design when it is subjected to certain stimulus.
An agile software development methodology which emphasizes (1) testing functionality before implementing it and (2) refactoring.
See this introductory article for more information.
Something that checks if a design satisfies a specification.
An allusion to a bench in an electronics laboratory, or so it seems.