Merge pull request #679 from steveicarus/steveicarus/more-documentation
Documentation: Transferring more content from the wiki
This commit is contained in:
commit
27b09f5d72
|
|
@ -13,4 +13,6 @@ Icarus Verilog. The code generator is selected by the "-t" command line flag.
|
|||
vvp
|
||||
stub
|
||||
null
|
||||
|
||||
vhdl
|
||||
verilog95
|
||||
pcb
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
Using the PCB code generator
|
||||
============================
|
||||
|
||||
The PCB target code generator is designed to allow a user to enter a netlist
|
||||
in Verilog format, then generate input files for the GNU PCB layout program.
|
||||
|
||||
Invocation
|
||||
----------
|
||||
|
||||
The PCB target code generation is invoked with the -tpcb flag to the iverilog
|
||||
command. The default output file, "a.out", contains the generated .PCB
|
||||
file. Use the "-o" flag to set the output file name explicitly. The default
|
||||
output file contains only the elements. To generate a "netlist" file, add the
|
||||
flag "-pnetlist=<path>" command line flag.
|
||||
|
||||
Altogether, this example generates the foo.net and foo.pcb files from the
|
||||
foo.v source file::
|
||||
|
||||
% iverilog -tpcb -ofoo.pcb -pnetlist=foo.net foo.v
|
||||
|
||||
Flags
|
||||
-----
|
||||
|
||||
* -o <path>
|
||||
|
||||
Set the output (pcb) file path
|
||||
|
||||
* -pnetlist=path
|
||||
|
||||
Write a netlist file to the given path.
|
||||
|
||||
Attributes Summary
|
||||
------------------
|
||||
|
||||
Attributes are attached to various constructs using the Verilog "(* *)"
|
||||
attribute syntax.
|
||||
|
||||
* ivl_black_box
|
||||
|
||||
Attached to a module declaration or module instantiation, this indicates
|
||||
that the module is a black box. The code generator will create an element
|
||||
for black box instances.
|
||||
|
||||
Parameters Summary
|
||||
------------------
|
||||
|
||||
Within modules, The PCB code generator uses certain parameters to control
|
||||
details. Parameters may have defaults, and can be overridden using the usual
|
||||
Verilog parameter override syntax. Parameters have preferred types.
|
||||
|
||||
* description (string, default="")
|
||||
|
||||
The "description" is a text string that describes the black box. This string
|
||||
is written into the description field of the PCB Element.
|
||||
|
||||
* value (string, default="")
|
||||
|
||||
The "value" is a text tring that describes some value for the black
|
||||
box. Like the description, the code generator does not interpret this value,
|
||||
other then to write it to the appropriate field in the PCB Element."
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
|
||||
Using The Verilog '95 Code Generator
|
||||
====================================
|
||||
|
||||
Icarus Verilog contains a code generator to emit 1995 compliant Verilog from
|
||||
the input Verilog netlist. This allows Icarus Verilog to function as a Verilog
|
||||
> 1995 to Verilog 1995 translator. The main goal of the project was to convert
|
||||
@*, ANSI style arguments and other constructs to something allowed in 1995
|
||||
Verilog.
|
||||
|
||||
Invocation
|
||||
----------
|
||||
|
||||
To translate a Verilog program to 1995 compliant Verilog, invoke "iverilog"
|
||||
with the -tvlog95 flag::
|
||||
|
||||
% iverilog -tvlog95 -o my_design_95.v my_design.v
|
||||
|
||||
The generated Verilog will be placed in a single file (a.out by default), even
|
||||
if the input Verilog is spread over multiple files.
|
||||
|
||||
Generator Flags
|
||||
---------------
|
||||
|
||||
* -pspacing=N
|
||||
|
||||
Set the indent spacing (the default is 2).
|
||||
|
||||
* -pallowsigned=1
|
||||
|
||||
Allow emitting the various signed constructs as an extension to 1995 Verilog
|
||||
(off by default).
|
||||
|
||||
* -pfileline=1
|
||||
|
||||
Emit the original file and line information as a comment for each generated
|
||||
line (off by default).
|
||||
|
||||
Structures that cannot be converted to 1995 compatible Verilog
|
||||
--------------------------------------------------------------
|
||||
|
||||
The following Verilog constructs are not translatable to 1995 compatible Verilog:
|
||||
|
||||
* Automatic tasks or functions.
|
||||
|
||||
* The power operator (**). Expressions of the form (2**N)**<variable> (where N
|
||||
is a constant) can be converter to a shift.
|
||||
|
||||
* Some System Verilog constructs (e.g. final blocks, ++/-- operators,
|
||||
etc.). 2-state variables are converted to 4-state variables.
|
||||
|
||||
Icarus extensions that cannot be translated:
|
||||
|
||||
* Integer constants greater than 32 bits.
|
||||
|
||||
* Real valued nets.
|
||||
|
||||
* Real modulus.
|
||||
|
||||
* Most Verilog-A constructs.
|
||||
|
||||
|
||||
Known Issues and Limitations
|
||||
----------------------------
|
||||
|
||||
Some things are just not finished and should generate an appropriate
|
||||
warning. Here is a list of the major things that still need to be looked at.
|
||||
|
||||
* There are still a few module instantiation port issues (pr1723367 and
|
||||
partselsynth).
|
||||
|
||||
* inout ports are not converted (tran-VP).
|
||||
|
||||
* Variable selects of a non-zero based vector in a continuous assignment are
|
||||
not converted.
|
||||
|
||||
* There is no support for translating a zero repeat in a continuous
|
||||
assignment. It is currently just dropped.
|
||||
|
||||
* A pull device connected to a signal select is not translated correctly (this
|
||||
may be fixed).
|
||||
|
||||
* L-value indexed part selects with a constant undefined base in a continuous
|
||||
assignment are not translated.
|
||||
|
||||
* Logic gates are not arrayed exactly the same as the input and the instance
|
||||
name is not always the same.
|
||||
|
||||
* The signed support does not generate $signed() or $unsigned() function calls
|
||||
in a continuous assignment expression.
|
||||
|
||||
* The special power operator cases are not converted in a continuous
|
||||
assignment.
|
||||
|
||||
* Currently a signed constant that sets the MSB in an unsigned context will be
|
||||
displayed as a negative value (e.g. bit = 1 translates to bit = -1).
|
||||
|
||||
* Can net arrays, etc. be unrolled?
|
||||
|
||||
* Can generate blocks be converted?
|
||||
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
The VHDL Code Generator (-tvhdl)
|
||||
================================
|
||||
|
||||
Icarus Verilog contains a code generator to emit VHDL from the Verilog
|
||||
netlist. This allows Icarus Verilog to function as a Verilog to VHDL
|
||||
translator.
|
||||
|
||||
Invocation
|
||||
----------
|
||||
|
||||
To translate a Verilog program to VHDL, invoke "iverilog" with the -tvhdl
|
||||
flag::
|
||||
|
||||
% iverilog -t vhdl -o my_design.vhd my_design.v
|
||||
|
||||
The generated VHDL will be placed in a single file (a.out by default), even if
|
||||
the Verilog is spread over multiple files.
|
||||
|
||||
Flags
|
||||
-----
|
||||
|
||||
* -pdebug=1
|
||||
|
||||
Print progress messages as the code generator visits each part of the
|
||||
design.
|
||||
|
||||
* -pdepth=N
|
||||
|
||||
Only output VHDL entities for modules found at depth < N in the
|
||||
hierarchy. N=0, the default, outputs all entities. For example, -pdepth=1
|
||||
outputs only the top-level entity.
|
||||
|
||||
Supported Constructs
|
||||
--------------------
|
||||
|
||||
TODO
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
Signal Values and Resolution
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are several cases where the behaviour of the translated VHDL deviates
|
||||
from the source Verilog:
|
||||
|
||||
* The result of division by zero is x in Verilog but raises an exception in
|
||||
VHDL.
|
||||
|
||||
* Similarly, the result of reading past the end of an array in Verilog is x,
|
||||
whereas VHDL raises an exception.
|
||||
|
||||
* Any signal that is driven by two or more processes will have the value
|
||||
'U'. This is the result of the signal resolution function in the
|
||||
std_logic_1164 package.
|
||||
|
||||
Constructs Not Supported
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following Verilog constructs cannot be translated to VHDL:
|
||||
|
||||
* fork and join
|
||||
|
||||
* force and release
|
||||
|
||||
* disable
|
||||
|
||||
* real-valued variables
|
||||
|
||||
* switches
|
||||
|
||||
* hierarchical dereferencing
|
||||
|
||||
Other Limitations
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
* The test expressions in case statements must be constant.
|
||||
|
||||
* Translation of a parameter to a corresponding VHDL generic
|
||||
declaration. Instead the default parameter value is used.
|
||||
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
|
||||
Command File Format
|
||||
===================
|
||||
|
||||
The basic format of a command file is one source file or compiler argument per
|
||||
line. Command files may also have comments of various form, and options for
|
||||
controlling the compiler.
|
||||
|
||||
Comments
|
||||
--------
|
||||
|
||||
Lines that start with a "#" character are comments. All text after the "#"
|
||||
character, is ignored.
|
||||
|
||||
The "//" character sequence also starts a comment that continues to the end of
|
||||
the line.
|
||||
|
||||
The "/\*" and "\*/" character sequences surround multi-line comments. All the
|
||||
text between the comment start and comment end sequences is ignored, even when
|
||||
that text spans multiple lines. This style of comment does not nest, so a "/\*"
|
||||
sequence within a multi-line comment is probably an error.
|
||||
|
||||
Plus-args
|
||||
---------
|
||||
|
||||
Outside of comments, lines that start with a "+" character are compiler
|
||||
arguments. These are called plusargs but they are not the same as extended
|
||||
arguments passed to the "vvp" command. The supported plusargs are definitively
|
||||
listed in the iverilog manual page.
|
||||
|
||||
The plusargs lines are generally "+<name>+..." where the name is the name of
|
||||
an switch, and the arguments are separated by "+" characters, as in::
|
||||
|
||||
+libext+.v+.V+.ver
|
||||
|
||||
With plusargs lines, the "+" character separates tokens, and not white space,
|
||||
so arguments, which may include file paths, may include spaces. A plusarg line
|
||||
is terminated by the line end.
|
||||
|
||||
The line in the command file may also be a "-y" argument. This works exactly
|
||||
the same as the::
|
||||
|
||||
-y <path>
|
||||
|
||||
argument to the compiler; it declares a library directory. The "-y" syntax is
|
||||
also a shorthand for the "+libdir" plusarg, which is a more general form::
|
||||
|
||||
+libdir+<path>...
|
||||
|
||||
File Names
|
||||
----------
|
||||
|
||||
Any lines that are not comments, compiler arguments or plusargs are taken by
|
||||
the compiler to be a source file. The path can contain any characters (other
|
||||
then comment sequences) including blanks, although leading and trailing white
|
||||
space characters are stripped. The restriction of one file name per line is in
|
||||
support of operating systems that can name files any which way. It is not
|
||||
appropriate to expect white spaces to separate file names.
|
||||
|
||||
Variable Substitution
|
||||
---------------------
|
||||
|
||||
The syntax "$(name)" is a variable reference, and may be used anywhere within
|
||||
filenames or directory names. The contents of the variable are read from the
|
||||
environment and substituted in place of the variable reference. In Windows,
|
||||
these environment variables are the very same variables that are set through
|
||||
the Control Panel->System dialog box, and in UNIX these variables are
|
||||
environment variables as exported by your shell.
|
||||
|
||||
Variables are useful for giving command files some installation
|
||||
independence. For example, one can import a vendor library with the line::
|
||||
|
||||
-y $(VENDOR)/verilog/library
|
||||
|
||||
in the command file, and the next programmer will be able to use this command
|
||||
file without editing it to point to the location of VENDOR on his
|
||||
machine. Note the use of forward slashes as a directory separator. This works
|
||||
even under Windows, so always use forward slashes in file paths and Windows
|
||||
and UNIX users will be able to share command files.
|
||||
|
||||
An Example
|
||||
----------
|
||||
|
||||
This sample::
|
||||
|
||||
# This is a comment in a command file.
|
||||
# The -y statement declares a library
|
||||
# search directory
|
||||
-y $(PROJ_LIBRARY)/prims
|
||||
#
|
||||
# This plusarg tells the compiler that
|
||||
# files in libraries may have .v or .vl
|
||||
# extensions.
|
||||
+libext+.v+.vl
|
||||
#
|
||||
main.v // This is a source file
|
||||
#
|
||||
# This is a file name with blanks.
|
||||
C:/Project Directory/file name.vl
|
||||
|
||||
is a command file that demonstrates the major syntactic elements of command
|
||||
files. It demonstrates the use of comments, variables, plusargs and file
|
||||
names. It contains a lot of information about the hypothetical project, and
|
||||
suggests that command files can be used to describe the project as a whole
|
||||
fairly concisely.
|
||||
|
||||
The syntax of command files is rich enough that they can be used to document
|
||||
and control the assembly and compilation of large Verilog programs. It is not
|
||||
unusual to have command files that are hundreds of lines long, although
|
||||
judicious use of libraries can lead to very short command files even for large
|
||||
designs. It is also practical to have different command files that pull
|
||||
together combinations of sources and compiler arguments to make different
|
||||
designs from the same Verilog source files.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
Given the above description of the command file format, the following is a
|
||||
list of the special records with their meaning.
|
||||
|
||||
* +libdir+*dir-path*
|
||||
|
||||
Specify directories to be searched for library modules. The *dir-path* can
|
||||
have multiple directories, separated by "+" characters.
|
||||
|
||||
* +libdir-nocase+dir-path
|
||||
|
||||
This is the same as "+libdir+", but when searching "nocase" libraries for
|
||||
module files, case will not be taken as significant. This is useful when the
|
||||
library is on a case insensitive file system.
|
||||
|
||||
* +libext+*suffix-string*
|
||||
|
||||
Declare the suffix strings to use when searching library directories for
|
||||
Verilog files. The compiler may test a list of suffix strings to support a
|
||||
variety of naming conventions.
|
||||
|
||||
* -y dir-path
|
||||
|
||||
This is like "+libdir+" but each line takes only one path. Like "+libdir+"
|
||||
there can be multiple "-y" records to declare multiple library
|
||||
directories. This is similar to the "-y" flag on the iverilog command line.
|
||||
|
||||
* -v *file-name* or -l *file-name*
|
||||
|
||||
This declares a library file. A library file is just like any other Verilog
|
||||
source file, except that modules declared within it are not implicitly
|
||||
possible root modules.
|
||||
|
||||
NOTE: The "-l" alias is new as of 2 October 2016. It will become available
|
||||
in releases and snapshots made after that date.
|
||||
|
||||
* +incdir+*include-dir-path*
|
||||
|
||||
Declare a directory or list of directories to search for files included by
|
||||
the "include" compiler directive. The directories are searched in
|
||||
order. This is similar to the "-I" flag on the iverilog command line.
|
||||
|
||||
* +define+*name=value*
|
||||
|
||||
Define the preprocessor symbol "name" to have the string value "value". If
|
||||
the value (and the "=") are omitted, then it is assumed to be the string
|
||||
"1". This is similar to the "-D" on the iverilog command line.
|
||||
|
||||
* +timescale+*units/precision*
|
||||
|
||||
Define the default timescale. This is the timescale that is used if there is
|
||||
no other timescale directive in the Verilog source. The compiler default
|
||||
default is "+timescale+1s/1s", which this command file setting can
|
||||
change. The format of the units/precision is the same as that for the
|
||||
timescale directive in the verilog source.
|
||||
|
||||
* +toupper-filename
|
||||
|
||||
This token causes file names after this in the command file to be translated
|
||||
to uppercase. this helps with situations where a directory has passed
|
||||
through a DOS machine (or a FAT file system) and in the process the file
|
||||
names become munged. This is not meant to be used in general, but only in
|
||||
emergencies.
|
||||
|
||||
* +tolower-filename
|
||||
|
||||
The is the lowercase version of "+toupper-filename".
|
||||
|
||||
* +parameter+*name=value*
|
||||
|
||||
This token causes the compiler to override a parameter value for a top-level
|
||||
module. For example, if the module main has the parameter WIDTH, set the
|
||||
width like this "+parameter+main.WIDTH=5". Note the use of the complete
|
||||
hierarchical name. This currently only works for parameters defined in root
|
||||
(top level) modules and a defparam may override the command file value.
|
||||
|
||||
* +vhdl-work+*path*
|
||||
|
||||
When compiling VHDL, this token allows control over the directory to use for
|
||||
holding working package declarations. For example, "+vhdl-work+workdir" will
|
||||
cause the directory "workdir" to be used as a directory for holding working
|
||||
working copies of package headers.
|
||||
|
|
@ -8,7 +8,12 @@ Icarus Verilog.
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
installation
|
||||
getting_started
|
||||
simulation
|
||||
command_line_flags
|
||||
command_files
|
||||
verilog_attributes
|
||||
vvp_flags
|
||||
vpi
|
||||
ivl_target
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
|
||||
Installation Guide
|
||||
==================
|
||||
|
||||
Icarus Verilog may be installed from source code, or from pre-packaged binary
|
||||
distributions. If you don't have need for the very latest, and prepackaged
|
||||
binaries are available, that would be the best place to start.
|
||||
|
||||
Installation From Source
|
||||
------------------------
|
||||
|
||||
Icarus is developed for Unix-like environments but can also be compiled on
|
||||
Windows systems using the Cygwin environment or MinGW compilers. The following
|
||||
instructions are the common steps for obtaining the Icarus Verilog source,
|
||||
compiling and installing. Note that there are precompiled and/or prepackaged
|
||||
versions for a variety of systems, so if you find an appropriate packaged
|
||||
version, then that is the easiest way to install.
|
||||
|
||||
The source code for Icarus is stored under the git source code control
|
||||
system. You can use git to get the latest development head or the latest of a
|
||||
specific branch. Stable releases are placed on branches, and in particular v11
|
||||
stable releases are on the branch "v11-branch" To get the development version
|
||||
of the code follow these steps::
|
||||
|
||||
% git config --global user.name "Your Name Goes Here"
|
||||
% git config --global user.email you@yourpublicemail.example.com
|
||||
% git clone https://github.com/steveicarus/iverilog.git
|
||||
|
||||
The first two lines are optional and are used to tell git who you are. This
|
||||
information is important if/when you submit a patch. We suggest that you add
|
||||
this information now so you don't forget to do it later. The clone will create
|
||||
a directory, named iverilog, containing the source tree, and will populate
|
||||
that directory with the most current source from the HEAD of the repository.
|
||||
|
||||
Change into this directory using::
|
||||
|
||||
% cd iverilog
|
||||
|
||||
Normally, this is enough as you are now pointing at the most current
|
||||
development code, and you have implicitly created a branch "master" that
|
||||
tracks the development head. However, If you want to actually be working on
|
||||
the v11-branch (the branch where the latest v11 patches are) then you checkout
|
||||
that branch with the command::
|
||||
|
||||
% git checkout --track -b v11-branch origin/v11-branch
|
||||
|
||||
This creates a local branch that tracks the v11-branch in the repository, and
|
||||
switches you over to your new v11-branch. The tracking is important as it
|
||||
causes pulls from the repository to re-merge your local branch with the remote
|
||||
v11-branch. You always work on a local branch, then merge only when you
|
||||
push/pull from the remote repository.
|
||||
|
||||
Now that you've cloned the repository and optionally selected the branch you
|
||||
want to work on, your local source tree may later be synced up with the
|
||||
development source by using the git command::
|
||||
|
||||
% git pull
|
||||
|
||||
The git system remembers the repository that it was cloned from, so you don't
|
||||
need to re-enter it when you pull.
|
||||
|
||||
Finally, configuration files are built by the extra step::
|
||||
|
||||
% sh autoconf.sh
|
||||
|
||||
The source is then compiled as appropriate for your system. See the specific
|
||||
build instructions below for your operation system for what to do next.
|
||||
|
||||
You will need autoconf and gperf installed in order for the script to work.
|
||||
If you get errors such as::
|
||||
|
||||
Autoconf in root...
|
||||
autoconf.sh: 10: autoconf: not found
|
||||
Precompiling lexor_keyword.gperf
|
||||
autoconf.sh: 13: gperf: not found.
|
||||
|
||||
You will need to install download and install the autoconf and gperf tools.
|
||||
|
||||
Icarus Specific Configuration Options
|
||||
-------------------------------------
|
||||
|
||||
Icarus takes many of the standard configuration options and those will not be
|
||||
described here. The following are specific to Icarus::
|
||||
|
||||
--enable-suffix[=suffix]
|
||||
|
||||
This option allows the user to build Icarus with a default suffix or when
|
||||
provided a user defined suffix. Older stable releases have this flag on by
|
||||
default e.g.(V0.8 by default will build with a "-0.8" suffix). All versions
|
||||
have an appropriate default suffix ("-<base_version>").
|
||||
|
||||
All programs or directories are tagged with this suffix. e.g.(iverilog-0.8,
|
||||
vvp-0.8, etc.). The output of iverilog will reference the correct run time
|
||||
files and directories. The run time will check that it is running a file with
|
||||
a compatible version e.g.(you can not run a V0.9 file with the V0.8 run
|
||||
time). ::
|
||||
|
||||
--with-valgrind
|
||||
|
||||
This option adds extra memory cleanup code and pool management code to allow
|
||||
better memory leak checking when valgrind is available. This option is not
|
||||
need when checking for basic errors with valgrind.
|
||||
|
||||
Compiling on Linux/Unix
|
||||
-----------------------
|
||||
|
||||
(Note: You will need to install bison, flex, g++ and gcc) This is probably the
|
||||
easiest case. Given that you have the source tree from the above instructions,
|
||||
the compile and install is generally as simple as::
|
||||
|
||||
% ./configure
|
||||
% make
|
||||
(su to root)
|
||||
# make install
|
||||
|
||||
The "make install" typically needs to be done as root so that it can install
|
||||
in directories such as "/usr/local/bin" etc. You can change where you want to
|
||||
install by passing a prefix to the "configure" command::
|
||||
|
||||
% ./configure --prefix=/my/special/directory
|
||||
|
||||
This will configure the source for eventual installation in the directory that
|
||||
you specify. Note that "rpm" packages of binaries for Linux are typically
|
||||
configured with "--prefix=/usr" per the Linux File System Standard.
|
||||
|
||||
Make sure you have the latest version of flex otherwise you will get an error
|
||||
when parsing lexor.lex.
|
||||
|
||||
Compiling on Macintosh OS X
|
||||
---------------------------
|
||||
|
||||
Since Mac OS X is a BSD flavor of Unix, you can install Icarus Verilog from
|
||||
source using the procedure described above. You need to install the Xcode
|
||||
software, which includes the C and C++ compilers for Mac OS X. The package is
|
||||
available for free download from Apple's developer site. Once Xcode is
|
||||
installed, you can build Icarus Verilog in a terminal window just like any
|
||||
other Unix install.
|
||||
|
||||
For versions newer than 10.3 the GNU Bison tool (packaged with Xcode) needs to
|
||||
be updated to version 3. ::
|
||||
|
||||
brew install bison
|
||||
echo 'export PATH="/usr/local/opt/bison/bin:$PATH"' >> ~/.bash_profile
|
||||
|
||||
Icarus Verilog is also available through the Homebrew package manager: "brew
|
||||
install icarus-verilog".
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
|
||||
Loadable Target API (ivl_target.h)
|
||||
==================================
|
||||
|
||||
In addition to the standard VPI API, Icarus Verilog supports a non-standard
|
||||
loadable target module API. This API helps C programmers write modules that
|
||||
Icarus Verilog can use to generate code. These modules are used at compile
|
||||
time to write the elaborated design to the simulation or netlist files. For
|
||||
example, the vvp code generator is a loadable target module that writes vvp
|
||||
code into the specified file.
|
||||
|
||||
Loadable target modules gain access to the 'elaborated' design. That means,
|
||||
the source files have been checked for syntax and correctness, any synthesis
|
||||
and general optimization steps have been performed, and what is left is a
|
||||
design that reflects but is not exactly the same as the input Verilog source
|
||||
code. This relieves the modules of the burden of supporting all the odd
|
||||
corners and complexities of the Verilog language.
|
||||
|
||||
The Target Module API
|
||||
---------------------
|
||||
|
||||
The API is defined in the header file "ivl_target.h" which is installed with
|
||||
Icarus Verilog. The header defines the functions that the module writer can
|
||||
use to get at the elaborated design during the course of writing the output
|
||||
format.
|
||||
|
||||
The target module API function "target_design" is special in that the API does
|
||||
not provide this function: The target module itself provides it. When the
|
||||
compiler loads the target module, it invokes the "target_design" function with
|
||||
a handle to the design. This is the point where the target module takes over
|
||||
to process the design.
|
||||
|
||||
Compiling Target Modules
|
||||
------------------------
|
||||
|
||||
Compiling loadable target modules is similar to compiling VPI modules, in that
|
||||
the module must be compiled with the "-fPIC" flag to gcc, and linked with the
|
||||
"-shared" flag. The module that you compile is then installed in a place where
|
||||
the "iverilog" command can find it, and configuration files are adjusted to
|
||||
account for the new module.
|
||||
|
||||
This code::
|
||||
|
||||
# include <ivl_target.h>
|
||||
|
||||
int target_design(ivl_design_t des)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
is an example module that we can write into the file "empty.c"; and let us
|
||||
compile it into the module file "empty.tgt" like so::
|
||||
|
||||
% gcc -o empty.tgt -fpic -shared empty.c
|
||||
|
||||
This makes the "empty.tgt" file an a dynamically loaded shared object.
|
||||
|
||||
Creating the Target Config File
|
||||
-------------------------------
|
||||
|
||||
The target config file tells the Icarus Verilog core how to process your new
|
||||
code generator. The ivl core expects two configuration files: the name.conf
|
||||
and the name-s.config files. The "-s" version is what is used if the user
|
||||
gives the "-S" (synthesis) flag on the command line.
|
||||
|
||||
The stub target, included in most distributions, demonstrates the config
|
||||
files. The "stub.conf" file is::
|
||||
|
||||
functor:cprop
|
||||
functor:nodangle
|
||||
-t:dll
|
||||
flag:DLL=stub.tgt
|
||||
|
||||
and the "stub-s.conf" file is::
|
||||
|
||||
functor:synth2
|
||||
functor:synth
|
||||
functor:syn-rules
|
||||
functor:cprop
|
||||
functor:nodangle
|
||||
-t:dll
|
||||
flag:DLL=stub.tgt
|
||||
|
||||
Note that the "stub-s.conf" file contains more lines to invoke internal
|
||||
synthesis functions, whereas the "stub.conf" invokes only the basic
|
||||
optimization steps.
|
||||
|
||||
In general, only the last line (The "flag:DLL=<name>.tgt" record) varies for
|
||||
each target. For your target, replace the <name> with the name of your target
|
||||
and you have a configuration file ready to install. Note that this is the name
|
||||
of your target module. This is in fact how the config file tells the compiler
|
||||
the name of your module.
|
||||
|
||||
The rest of the config file is best taken as boiler plate and installed as is,
|
||||
with one difference. If your target is a synthesis target (for example a mosis
|
||||
code generator or a pld code generator) that expects synthesis to happen, then
|
||||
it makes the most sense to create both your config file like the "stub-s.conf"
|
||||
config file. This causes the compiler to do synthesis for your target whether
|
||||
the user gives the "-S" flag or not.
|
||||
|
||||
Installing the Target Module
|
||||
----------------------------
|
||||
|
||||
Finally, the "empty.conf", the "empty-s.conf" and the "empty.tgt" files need
|
||||
to be installed. Where they go depends on your system, but in Linux they are
|
||||
normally installed in "/usr/lib/ivl".
|
||||
|
|
@ -0,0 +1,487 @@
|
|||
|
||||
Simulation Using Icarus Verilog
|
||||
===============================
|
||||
|
||||
Simulation is the process of creating models that mimic the behavior of the
|
||||
device you are designing (simulation models) and creating models to exercise
|
||||
the device (test benches). The simulation model need not reflect any
|
||||
understanding of the underlying technology, and the simulator need not know
|
||||
that the design is intended for any specific technology.
|
||||
|
||||
The Verilog simulator, in fact, is usually a different program than the
|
||||
synthesizer. It may even come from a different vendor. The simulator need not
|
||||
know of or generate netlists for the target technology, so it is possible to
|
||||
write one simulator that can be used to model designs intended for a wide
|
||||
variety of technologies. A synthesizer, on the other hand, does need to know a
|
||||
great deal about the target technology in order to generate efficient
|
||||
netlists. Synthesizers are often technology specific and come from vendors
|
||||
with specialized knowledge, whereas simulators are more general purpose.
|
||||
|
||||
Simulation models and test benches, therefore, can use the full range of
|
||||
Verilog features to model the intended design as clearly as possible. This is
|
||||
the time to test the algorithms of the design using language that is
|
||||
relatively easy for humans to read. The simulator, along with the test bench,
|
||||
can test that the clearly written model really does behave as intended, and
|
||||
that the intended behavior really does meet expectations.
|
||||
|
||||
The test benches model the world outside the design, so they are rarely
|
||||
destined for real hardware. They are written in Verilog simply as a matter of
|
||||
convenience, and sometimes they are not written in Verilog at all. The test
|
||||
benches are not throw-away code either, as they are used to retest the device
|
||||
under test as it is transformed from a simulation model to a synthesizeable
|
||||
description.
|
||||
|
||||
Compilation and Elaboration
|
||||
---------------------------
|
||||
|
||||
Simulation of a design amounts to compiling and executing a program. The
|
||||
Verilog source that represents the simulation model and the test bench is
|
||||
compiled into an executable form and executed by a simulation
|
||||
engine. Internally, Icarus Verilog divides the compilation of program source
|
||||
to an executable form into several steps, and basic understanding of these
|
||||
steps helps understand the nature of failures and errors. The first step is
|
||||
macro preprocessing, then compilation, elaboration, optional optimizations and
|
||||
finally code generation. The boundary between these steps is often blurred,
|
||||
but this progression remains a useful model of the compilation process.
|
||||
|
||||
The macro preprocessing step performs textual substitutions of macros defined
|
||||
with "\`define" statements, textual inclusion with "\`include" statements, and
|
||||
conditional compilation by "\`ifdef" and "\`ifndef" statements. The
|
||||
macropreprocessor for Icarus Verilog is internally a separate program that can
|
||||
be accessed independently by using the "-E" flag to the "iverilog" command,
|
||||
like so::
|
||||
|
||||
% iverilog -E -o out.v example.v
|
||||
|
||||
This command causes the input Verilog file "example.v" to be preprocessed, and
|
||||
the output, a Verilog file without preprocessor statements, written into
|
||||
"out.v". The "\`include" and "\`ifdef" directives in the input file are interpreted,
|
||||
and defined macros substituted, so that the output, a single file, is the same
|
||||
Verilog but with the preprocessor directives gone. All the explicitly
|
||||
specified source files are also combined by the preprocessor, so that the
|
||||
preprocessed result is a single Verilog stream.
|
||||
|
||||
Normally, however, the "-E" flag is not used and the preprocessed Verilog is
|
||||
instead sent directly to the next step, the compiler. The compiler core takes
|
||||
as input preprocessed Verilog and generates an internal parsed form. The
|
||||
parsed form is an internal representation of the Verilog source, in a format
|
||||
convenient for further processing, and is not accessible to the user.
|
||||
|
||||
The next step, elaboration, takes the parsed form, chooses the root modules,
|
||||
and instantiates (makes *instances* of) those roots. The root instances may
|
||||
contain instances of other modules, which may in turn contain instances of yet
|
||||
other modules. The elaboration process creates a hierarchy of module instances
|
||||
that ends with primitive gates and statements.
|
||||
|
||||
Note that there is a difference between a module and a module instance. A
|
||||
module is a type. It is a description of the contents of module instances that
|
||||
have its type. When a module is instantiated within another module, the module
|
||||
name identifies the type of the instance, and the instance name identifies the
|
||||
specific instance of the module. There can be many instances of any given
|
||||
module.
|
||||
|
||||
Root modules are a special case, in that the programmer does not give them
|
||||
instance names. Instead, the instance names of root modules are the same as
|
||||
the name of the module. This is valid because, due to the nature of the
|
||||
Verilog syntax, a module can be a root module only once, so the module name
|
||||
itself is a safe instance name.
|
||||
|
||||
Elaboration creates a hierarchy of scopes. Each module instance creates a new
|
||||
scope within its parent module, with each root module starting a
|
||||
hierarchy. Every module instance in the elaborated program has a unique scope
|
||||
path, a hierarchical name, that starts with its root scope and ends with its
|
||||
own instance name. Every named object, including variables, parameters, nets
|
||||
and gates, also has a hierarchical name that starts with a root scope and ends
|
||||
with its own base name. The compiler uses hierarchical names in error messages
|
||||
generated during or after elaboration, so that erroneous items can be
|
||||
completely identified. These hierarchical names are also used by waveform
|
||||
viewers that display waveform output from simulations.
|
||||
|
||||
The elaboration process creates from the parsed form the scope hierarchy
|
||||
including the primitive objects within each scope. The elaborated design then
|
||||
is optimized to reduce it to a more optimal, but equivalent design. The
|
||||
optimization step takes the fully elaborated design and transforms it to an
|
||||
equivalent design that is smaller or more efficient. These optimizations are,
|
||||
for example, forms of constant propagation and dead code elimination. Useless
|
||||
logic is eliminated, and constant expressions are pre-calculated. The
|
||||
resulting design behaves as if the optimizations were not performed, but is
|
||||
smaller and more efficient. The elimination (and spontaneous creation) of
|
||||
gates and statements only affects the programmer when writing VPI modules,
|
||||
which through the API have limited access to the structures of the design.
|
||||
|
||||
Finally, the optimized design, which is still in an internal form not
|
||||
accessible to users, is passed to a code generator that writes the design into
|
||||
an executable form. For simulation, the code generator is selected to generate
|
||||
the vvp format--a text format that can be executed by the simulation
|
||||
engine. Other code generators may be selected by the Icarus Verilog user, even
|
||||
third party code generators, but the vvp code generator is the default for
|
||||
simulation purposes.
|
||||
|
||||
Making and Using Libraries
|
||||
--------------------------
|
||||
|
||||
Although simple programs may be written into a single source file, this gets
|
||||
inconvenient as the designs get larger. Also, writing the entire program into
|
||||
a single file makes it difficult for different programs to share common
|
||||
code. It therefore makes sense to divide large programs into several source
|
||||
files, and to put generally useful source code files somewhere accessible to
|
||||
multiple designs.
|
||||
|
||||
Once the program is divided into many files, the compiler needs to be told how
|
||||
to find the files of the program. The simplest way to do that is to list the
|
||||
source files on the command line or in a command file. This is for example the
|
||||
best way to divide up and integrate test bench code with the simulation model
|
||||
of the device under test.
|
||||
|
||||
The Macro Preprocessor
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Another technique is to use the macro preprocessor to include library files
|
||||
into a main file. The `include` directive takes the name of a source file to
|
||||
include. The preprocessor inserts the entire contents of the included file in
|
||||
place of the `include` directive. The preprocessor normally looks in the
|
||||
current working directory (the current working directory of the running
|
||||
compiler, and not the directory where the source file is located) for the
|
||||
included file, but the "-I" switch to "iverilog" can add directories to the
|
||||
search locations list. ::
|
||||
|
||||
% iverilog -I/directory/to/search example.v
|
||||
|
||||
It is common to create include directories shared by a set of programs. The
|
||||
preprocessor `include` directive can be used by the individual programs to
|
||||
include the source files that it needs.
|
||||
|
||||
The preprocessor method of placing source code into libraries is general
|
||||
(arbitrary source code can be placed in the included files) but is static, in
|
||||
the sense that the programmer must explicitly include the desired library
|
||||
files. The automatic module library is a bit more constrained, but is
|
||||
automatic.
|
||||
|
||||
Automatic Module Libraries
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A common use for libraries is to store module definitions that may be of use
|
||||
to a variety of programs. If modules are divided into a single module per
|
||||
file, and the files are named appropriately, and the compiler is told where to
|
||||
look, then the compiler can automatically locate library files when it finds
|
||||
that a module definition is missing.
|
||||
|
||||
For this to work properly, the library files must be Verilog source, they
|
||||
should contain a single module definition, and the files must be named after
|
||||
the module they contain. For example, if the module "AND2" is a module in the
|
||||
library, then it belongs in a file called "AND2.v" and that file contains only
|
||||
the "AND2" module. A library, then, is a directory that contains properly
|
||||
named and formatted source files. ::
|
||||
|
||||
% iverilog -y/library/to/search example.v
|
||||
|
||||
The "-y" flag to "iverilog" tells the compiler to look in the specified
|
||||
directory for library modules whenever the program instantiates a module that
|
||||
is not otherwise defined. The programmer may include several "-y" flags on the
|
||||
command line (or in a command file) and the compiler will search each
|
||||
directory in order until an appropriate library file is found to resolve the
|
||||
module.
|
||||
|
||||
Once a module is defined, either in the program or by reading a library
|
||||
module, the loaded definition is used from then on within the program. If the
|
||||
module is defined within a program file or within an included file, then the
|
||||
included definition is used instead of any library definition. If a module is
|
||||
defined in multiple libraries, then the first definition that the compiler
|
||||
finds is used, and later definitions are never read.
|
||||
|
||||
Icarus Verilog accesses automatic libraries during elaboration, after it has
|
||||
already preprocessed and parsed the non-library source files. Modules in
|
||||
libraries are not candidates for root modules, and are not even parsed unless
|
||||
they are instantiated in other source files. However, a library module may
|
||||
reference other library modules, and reading in a library module causes it to
|
||||
be parsed and elaborated, and further library references resolved, just like a
|
||||
non-library module. The library lookup and resolution process iterates until
|
||||
all referenced modules are resolved, or known to be missing from the
|
||||
libraries.
|
||||
|
||||
The automatic module library technique is useful for including vendor or
|
||||
technology libraries into a program. Many EDA vendors offer module libraries
|
||||
that are formatted appropriately; and with this technique, Icarus Verilog can
|
||||
use them for simulation.
|
||||
|
||||
Advanced Command Files
|
||||
----------------------
|
||||
|
||||
Command files were mentioned in the "Getting Started" chapter, but only
|
||||
briefly. In practice, Verilog programs quickly grow far beyond the usefulness
|
||||
of simple command line options, and even the macro preprocessor lacks the
|
||||
flexibility to combine source and library modules according to the advancing
|
||||
development process.
|
||||
|
||||
The main contents of a command file is a list of Verilog source files. You can
|
||||
name in a command file all the source files that make up your design. This is
|
||||
a convenient way to collect together all the files that make up your
|
||||
design. Compiling the design can then be reduced to a simple command line like
|
||||
the following::
|
||||
|
||||
% iverilog -c example.cf
|
||||
|
||||
The command file describes a configuration. That is, it lists the specific
|
||||
files that make up your design. It is reasonable, during the course of
|
||||
development, to have a set of different but similar variations of your
|
||||
design. These variations may have different source files but also many common
|
||||
source files. A command file can be written for each variation, and each
|
||||
command file lists the source file names used by each variation.
|
||||
|
||||
A configuration may also specify the use of libraries. For example, different
|
||||
configurations may be implementations for different technologies so may use
|
||||
different parts libraries. To make this work, command files may include "-y"
|
||||
statements. These work in command files exactly how they work on "iverilog"
|
||||
command line. Each "-y" flag is followed by a directory name, and the
|
||||
directories are searched for library modules in the order that they are listed
|
||||
in the command file.
|
||||
|
||||
The include search path can also be specified in configuration files with
|
||||
"+incdir+" tokens. These tokens start with the "+incdir+" string, then
|
||||
continue with directory paths, separated from each other with "+" characters
|
||||
(not spaces) for the length of the line.
|
||||
|
||||
Other information can be included in the command file. See the section Command
|
||||
File Format for complete details on what can go in a command file.
|
||||
|
||||
Input Data at Runtime
|
||||
---------------------
|
||||
|
||||
Often, it is useful to compile a program into an executable simulation, then
|
||||
run the simulation with various inputs. This requires some means to pass data
|
||||
and arguments to the compiled program each time it is executed. For example,
|
||||
if the design models a micro-controller, one would like to run the compiled
|
||||
simulation against a variety of different ROM images.
|
||||
|
||||
There are a variety of ways for a Verilog program to get data from the outside
|
||||
world into the program at run time. Arguments can be entered on the command
|
||||
line, and larger amounts of data can be read from files. The simplest method
|
||||
is to take arguments from the command line.
|
||||
|
||||
Consider this running example of a square root calculator::
|
||||
|
||||
module sqrt32(clk, rdy, reset, x, .y(acc));
|
||||
input clk;
|
||||
output rdy;
|
||||
input reset;
|
||||
|
||||
input [31:0] x;
|
||||
output [15:0] acc;
|
||||
|
||||
// acc holds the accumulated result, and acc2 is
|
||||
// the accumulated square of the accumulated result.
|
||||
reg [15:0] acc;
|
||||
reg [31:0] acc2;
|
||||
|
||||
// Keep track of which bit I'm working on.
|
||||
reg [4:0] bitl;
|
||||
wire [15:0] bit = 1 << bitl;
|
||||
wire [31:0] bit2 = 1 << (bitl << 1);
|
||||
|
||||
// The output is ready when the bitl counter underflows.
|
||||
wire rdy = bitl[4];
|
||||
|
||||
// guess holds the potential next values for acc,
|
||||
// and guess2 holds the square of that guess.
|
||||
wire [15:0] guess = acc | bit;
|
||||
wire [31:0] guess2 = acc2 + bit2 + ((acc << bitl) << 1);
|
||||
|
||||
task clear;
|
||||
begin
|
||||
acc = 0;
|
||||
acc2 = 0;
|
||||
bitl = 15;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial clear;
|
||||
|
||||
always @(reset or posedge clk)
|
||||
if (reset)
|
||||
clear;
|
||||
else begin
|
||||
if (guess2 <= x) begin
|
||||
acc <= guess;
|
||||
acc2 <= guess2;
|
||||
end
|
||||
bitl <= bitl - 1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
One could write the test bench as a program that passes a representative set
|
||||
of input values into the device and checks the output result. However, we can
|
||||
also write a program that takes on the command line an integer value to be
|
||||
used as input to the device. We can write and compile this program, then pass
|
||||
different input values on the run time command line without recompiling the
|
||||
simulation.
|
||||
|
||||
This example demonstrates the use of the "$value$plusargs" to access command
|
||||
line arguments of a simulation::
|
||||
|
||||
module main;
|
||||
|
||||
reg clk, reset;
|
||||
reg [31:0] x;
|
||||
wire [15:0] y;
|
||||
wire rdy;
|
||||
|
||||
sqrt32 dut (clk, rdy, reset, x, y);
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial begin
|
||||
clk = 0;
|
||||
reset = 1;
|
||||
|
||||
if (! $value$plusargs("x=%d", x)) begin
|
||||
$display("ERROR: please specify +x=<value> to start.");
|
||||
$finish;
|
||||
end
|
||||
|
||||
#35 reset = 0;
|
||||
|
||||
wait (rdy) $display("y=%d", y);
|
||||
$finish;
|
||||
end // initial begin
|
||||
|
||||
endmodule // main
|
||||
|
||||
The "$value$plusargs" system function takes a string pattern that describes
|
||||
the format of the command line argument, and a reference to a variable that
|
||||
receives the value. The "sqrt_plusargs" program can be compiled and executed
|
||||
like this::
|
||||
|
||||
% iverilog -osqrt_plusargs.vvp sqrt_plusargs.v sqrt.v
|
||||
% vvp sqrt_plusargs.vvp +x=81
|
||||
y= 9
|
||||
|
||||
Notice that the "x=%d" string of the "$value$plusargs" function describes the
|
||||
format of the argument. The "%d" matches a decimal value, which in the sample
|
||||
run is "81". This gets assigned to "x" by the "$value$plusargs" function,
|
||||
which returns TRUE, and the simulation continues from there.
|
||||
|
||||
If two arguments have to be passed to the testbench then the main module would
|
||||
be modified as follows::
|
||||
|
||||
module main;
|
||||
|
||||
reg clk, reset;
|
||||
reg [31:0] x;
|
||||
reg [31:0] z;
|
||||
wire [15:0] y1,y2;
|
||||
wire rdy1,rdy2;
|
||||
|
||||
sqrt32 dut1 (clk, rdy1, reset, x, y1);
|
||||
sqrt32 dut2 (clk, rdy2, reset, z, y2);
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial begin
|
||||
clk = 0;
|
||||
reset = 1;
|
||||
|
||||
if (! $value$plusargs("x=%d", x)) begin
|
||||
$display("ERROR: please specify +x=<value> to start.");
|
||||
$finish;
|
||||
end
|
||||
|
||||
if (! $value$plusargs("z=%d", z)) begin
|
||||
$display("ERROR: please specify +z=<value> to start.");
|
||||
$finish;
|
||||
end
|
||||
|
||||
|
||||
#35 reset = 0;
|
||||
|
||||
wait (rdy1) $display("y1=%d", y1);
|
||||
wait (rdy2) $display("y2=%d", y2);
|
||||
$finish;
|
||||
end // initial begin
|
||||
|
||||
endmodule // main
|
||||
|
||||
and the "sqrt_plusargs" program would be compiled and executed as follows::
|
||||
|
||||
% iverilog -osqrt_plusargs.vvp sqrt_plusargs.v sqrt.v
|
||||
% vvp sqrt_plusargs.vvp +x=81 +z=64
|
||||
y1= 9
|
||||
y2= 8
|
||||
|
||||
In general, the "vvp" command that executes the compiled simulation takes a
|
||||
few predefined argument flags, then the file name of the simulation. All the
|
||||
arguments after the simulation file name are extended arguments to "vvp" and
|
||||
are passed to the executed design. Extended arguments that start with a "+"
|
||||
character are accessible through the "$test$plusargs" and "$value$plusargs"
|
||||
system functions. Extended arguments that do not start with a "+" character
|
||||
are only accessible to system tasks and functions written in C using the VPI.
|
||||
|
||||
In the previous example, the program pulls the argument from the command line,
|
||||
assigns it to the variable "x", and runs the sqrt device under test with that
|
||||
value. This program can take the integer square root of any single value. Of
|
||||
course, if you wish to test with a large number of input values, executing the
|
||||
program many times may become tedious.
|
||||
|
||||
Another technique would be to put a set of input values into a data file, and
|
||||
write the test bench to read the file. We can then edit the file to add new
|
||||
input values, then rerun the simulation without compiling it again. The
|
||||
advantage of this technique is that we can accumulate a large set of test
|
||||
input values, and run the lot as a batch.
|
||||
|
||||
This example::
|
||||
|
||||
module main;
|
||||
|
||||
reg clk, reset;
|
||||
reg [31:0] data[4:0];
|
||||
reg [31:0] x;
|
||||
wire [15:0] y;
|
||||
wire rdy;
|
||||
|
||||
sqrt32 dut (clk, rdy, reset, x, y);
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
/* Load the data set from the hex file. */
|
||||
$readmemh("sqrt.hex", data);
|
||||
for (i = 0 ; i <= 4 ; i = i + 1) begin
|
||||
clk = 0;
|
||||
reset = 1;
|
||||
|
||||
x = data[i];
|
||||
|
||||
#35 reset = 0;
|
||||
|
||||
wait (rdy) $display("y=%d", y);
|
||||
end
|
||||
$finish;
|
||||
end // initial begin
|
||||
|
||||
endmodule // main
|
||||
|
||||
demonstrates the use of "$readmemh" to read data samples from a file into a
|
||||
Verilog array. Start by putting into the file "sqrt.hex" the numbers::
|
||||
|
||||
51
|
||||
19
|
||||
1a
|
||||
18
|
||||
1
|
||||
|
||||
Then run the simulation with the command sequence::
|
||||
|
||||
% iverilog -osqrt_readmem.vvp sqrt_readmem.vl sqrt.vl
|
||||
% vvp sqrt_readmem.vvp
|
||||
y= 9
|
||||
y= 5
|
||||
y= 5
|
||||
y= 4
|
||||
y= 1
|
||||
|
||||
It is easy enough to change this program to work with larger data sets, or to
|
||||
change the "data.hex" file to contain different data. This technique is also
|
||||
common for simulating algorithms that take in larger data sets. One can extend
|
||||
this idea slightly by using a "$value$plusargs" statement to select the file
|
||||
to read.
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
|
||||
Using VPI
|
||||
=========
|
||||
|
||||
Icarus Verilog implements a portion of the PLI 2.0 API to Verilog. This allows
|
||||
programmers to write C code that interfaces with Verilog simulations to
|
||||
perform tasks otherwise impractical with straight Verilog. Many Verilog
|
||||
designers, especially those who only use Verilog as a synthesis tool, can
|
||||
safely ignore the entire matter of the PLI (and this chapter) but the designer
|
||||
who wishes to interface a simulation with the outside world cannot escape VPI.
|
||||
|
||||
The rest of this article assumes some knowledge of C programming, Verilog PLI,
|
||||
and of the compiler on your system. In most cases, Icarus Verilog assumes the
|
||||
GNU Compilation System is the compiler you are using, so tips and instructions
|
||||
that follow reflect that. If you are not a C programmer, or are not planning
|
||||
any VPI modules, you can skip this entire article. There are references at the
|
||||
bottom for information about more general topics.
|
||||
|
||||
How It Works
|
||||
------------
|
||||
|
||||
The VPI modules are compiled loadable object code that the runtime loads at
|
||||
the user's request. The user commands vvp to locate and load modules with the
|
||||
"-m" switch. For example, to load the "sample.vpi" module::
|
||||
|
||||
% vvp -msample foo.vvp
|
||||
|
||||
The vvp run-time loads the modules first, before executing any of the
|
||||
simulation, or even before compiling the vvp code. Part of the loading
|
||||
includes invoking initialization routines. These routines register with the
|
||||
run-time all the system tasks and functions that the module implements. Once
|
||||
this is done, the run time loader can match names of the called system tasks
|
||||
of the design with the implementations in the VPI modules.
|
||||
|
||||
(There is a special module, the system.vpi module, that is always loaded to
|
||||
provide the core system tasks.)
|
||||
|
||||
The simulator run time (The "vvp" program) gets a handle on a freshly loaded
|
||||
module by looking for the symbol "vlog_startup_routines" in the loaded
|
||||
module. This table, provided by the module author and compiled into the
|
||||
module, is a null terminated table of function pointers. The simulator calls
|
||||
each of the functions in the table in order. The following simple C definition
|
||||
defines a sample table::
|
||||
|
||||
void (*vlog_startup_routines[])() = {
|
||||
hello_register,
|
||||
0
|
||||
};
|
||||
|
||||
Note that the "vlog_startup_routines" table is an array of function pointers,
|
||||
with the last pointer a 0 to mark the end. The programmer can organize the
|
||||
module to include many startup functions in this table, if desired.
|
||||
|
||||
The job of the startup functions that are collected in the startup table is to
|
||||
declare the system tasks and functions that the module provides. A module may
|
||||
implement as many tasks/functions as desired, so a module can legitimately be
|
||||
called a library of system tasks and functions.
|
||||
|
||||
Compiling VPI Modules
|
||||
---------------------
|
||||
|
||||
To compile and link a VPI module for use with Icarus Verilog, you must compile
|
||||
all the source files of a module as if you were compiling for a DLL or shared
|
||||
object. With gcc under Linux, this means compiling with the "-fpic" flag. The
|
||||
module is then linked together with the vpi library like so::
|
||||
|
||||
% gcc -c -fpic hello.c
|
||||
% gcc -shared -o hello.vpi hello.o -lvpi
|
||||
|
||||
This assumes that the "vpi_user.h header file and the libvpi.a library file
|
||||
are installed on your system so that gcc may find them. This is normally the
|
||||
case under Linux and UNIX systems. An easier, the preferred method that works
|
||||
on all supported systems is to use the single command::
|
||||
|
||||
% iverilog-vpi hello.c
|
||||
|
||||
The "iverilog-vpi" command takes as command arguments the source files for
|
||||
your VPI module, compiles them with proper compiler flags, and links them into
|
||||
a vpi module with any system specific libraries and linker flags that are
|
||||
required. This simple command makes the "hello.vpi" module with minimum fuss.
|
||||
|
||||
A Worked Example
|
||||
----------------
|
||||
|
||||
Let us try a complete, working example. Place the C code that follows into the
|
||||
file hello.c::
|
||||
|
||||
# include <vpi_user.h>
|
||||
|
||||
static int hello_compiletf(char*user_data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hello_calltf(char*user_data)
|
||||
{
|
||||
vpi_printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hello_register()
|
||||
{
|
||||
s_vpi_systf_data tf_data;
|
||||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$hello";
|
||||
tf_data.calltf = hello_calltf;
|
||||
tf_data.compiletf = hello_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = 0;
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
|
||||
void (*vlog_startup_routines[])() = {
|
||||
hello_register,
|
||||
0
|
||||
};
|
||||
|
||||
and place the Verilog code that follows into hello.v::
|
||||
|
||||
module main;
|
||||
initial $hello;
|
||||
endmodule
|
||||
|
||||
Next, compile and execute the code with these steps::
|
||||
|
||||
% iverilog-vpi hello.c
|
||||
% iverilog -ohello.vvp hello.v
|
||||
% vvp -M. -mhello hello.vvp
|
||||
Hello, World!
|
||||
|
||||
The compile and link in this example are conveniently combined into the
|
||||
"iverilog-vpi" command. The "iverilog" command then compiles the "hello.v"
|
||||
Verilog source file to the "hello.vvp" program. Next, the "vvp" command
|
||||
demonstrates the use of the "-M" and "-m" flags to specify a vpi module search
|
||||
directory and vpi module name. Specifically, they tell the "vvp" command where
|
||||
to find the module we just compiled.
|
||||
|
||||
The "vvp" command, when executed as above, loads the "hello.vpi" module that
|
||||
it finds in the current working directory. When the module is loaded, the
|
||||
vlog_startup_routines table is scanned, and the "hello_register" function is
|
||||
executed. The "hello_register" function in turn tells "vvp" about the system
|
||||
tasks that are included in this module.
|
||||
|
||||
After the modules are all loaded, the "hello.vvp" design file is loaded and
|
||||
its call to the "$hello" system task is matched up to the version declared by
|
||||
the module. While "vvp" compiles the "hello.vvp" source, any calls to "$hello"
|
||||
are referred to the "compiletf" function. This function is called at compile
|
||||
time and can be used to check parameters to system tasks or function. It can
|
||||
be left empty like this, or left out completely. The "compiletf" function can
|
||||
help performance by collecting parameter checks in compile time, so they do
|
||||
not need to be done each time the system task is run, thus potentially saving
|
||||
execution time overall.
|
||||
|
||||
When the run-time executes the call to the hello system task, the
|
||||
"hello_calltf" function is invoked in the loaded module, and thus the output
|
||||
is generated. The "calltf" function is called at run time when the Verilog
|
||||
code actually executes the system task. This is where the active code of the
|
||||
task belongs.
|
||||
|
||||
System Function Return Types
|
||||
----------------------------
|
||||
|
||||
Icarus Verilog supports system functions as well as system tasks, but there is
|
||||
a complication. Notice how the module that you compile is only loaded by the
|
||||
"vvp" program. This is mostly not an issue, but elaboration of expressions
|
||||
needs to keep track of types, so the main compiler needs to know the return
|
||||
type of functions.
|
||||
|
||||
Starting with Icarus Verilog v11, the solution is quite simple. The names and
|
||||
locations of the user's VPI modules can be passed to the compiler via the
|
||||
"iverilog" -m and -L flags and the IVERILOG_VPI_MODULE_PATH environment
|
||||
variable. The compiler will load and analyse the specified modules to
|
||||
automatically determine any function return types. The compiler will also
|
||||
automatically pass the names and locations of the specified modules to the
|
||||
"vvp" program, so that they don't need to be specified again on the "vvp"
|
||||
command line.
|
||||
|
||||
For Icarus Verilog versions prior to v11, the solution requires that the
|
||||
developer of a module include the table in a form that the compiler can
|
||||
read. The System Function Table file carries this information. A simple
|
||||
example looks like this::
|
||||
|
||||
# Example sft declarations of some common functions
|
||||
$random vpiSysFuncInt
|
||||
$bitstoreal vpiSysFuncReal
|
||||
$realtobits vpiSysFuncSized 64 unsigned
|
||||
|
||||
This demonstrates the format of the file and support types. Each line contains
|
||||
a comment (starts with "#") or a type declaration for a single function. The
|
||||
declaration starts with the name of the system function (including the leading
|
||||
"$") and ends with the type. The supported types are:
|
||||
|
||||
* vpiSysFuncInt
|
||||
* vpiSysFuncReal
|
||||
* vpiSysFuncSized <wid> <signed|unsigned>
|
||||
|
||||
Any functions that do not have an explicit type declaration in an SFT file are
|
||||
implicitly taken to be "vpiSysFuncSized 32 unsigned".
|
||||
|
||||
The module author provides, along with the ".vpi" file that is the module, a
|
||||
".sft" that declares all the function return types. For example, if the file
|
||||
is named "example.sft", pass it to the "iverilog" command line or in the
|
||||
command file exactly as if it were an ordinary source file.
|
||||
|
||||
Cadence PLI Modules
|
||||
-------------------
|
||||
|
||||
With the cadpli module, Icarus Verilog is able to load PLI1 applications that
|
||||
were compiled and linked to be dynamic loaded by Verilog-XL or
|
||||
NC-Verilog. This allows Icarus Verilog users to run third-party modules that
|
||||
were compiled to interface with XL or NC. Obviously, this only works on the
|
||||
operating system that the PLI application was compiled to run on. For example,
|
||||
a Linux module can only be loaded and run under Linux. In addition, a 64-bit
|
||||
version of vvp can only load 64-bit PLI1 applications, etc.
|
||||
|
||||
Icarus Verilog uses an interface module, the "cadpli" module, to connect the
|
||||
worlds. This module is installed with Icarus Verilog, and is invoked by the
|
||||
usual -m flag to iverilog or vvp. This module in turn scans the extended
|
||||
arguments, looking for -cadpli= arguments. The latter specify the share object
|
||||
and bootstrap function for running the module. For example, to run the module
|
||||
product.so, that has the bootstrap function "my_boot"::
|
||||
|
||||
% vvp -mcadpli a.out -cadpli=./product.so:my_boot
|
||||
|
||||
The "-mcadpli" argument causes vvp to load the cadpli.vpl library module. This
|
||||
activates the -cadpli= argument interpreter. The -cadpli=<module>:<boot_func>
|
||||
argument, then, causes vvp, through the cadpli module, to load the loadable
|
||||
PLI application, invoke the my_boot function to get a veriusertfs table, and
|
||||
scan that table to register the system tasks and functions exported by that
|
||||
object. The format of the -cadpli= extended argument is essentially the same
|
||||
as the +loadpli1= argument to Verilog-XL.
|
||||
|
||||
The integration from this point is seamless. The PLI application hardly knows
|
||||
that it is being invoked by Icarus Verilog instead of Verilog-XL, so operates
|
||||
as it would otherwise.
|
||||
|
||||
Other References
|
||||
----------------
|
||||
|
||||
Since the above only explains how to get PLI/VPI working with Icarus Verilog,
|
||||
here are some references to material to help with the common aspects of
|
||||
PLI/VPI.
|
||||
|
||||
* Principles of Verilog PLI by Swapnajit Mittra. ISBN 0-7923-8477-6
|
||||
* The Verilog PLI Handbook by Stuart Sutherland. ISBN 0-7923-8489-X
|
||||
Loading…
Reference in New Issue