2001-03-14 20:26:15 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2001 Stephen Williams (steve@icarus.com)
|
|
|
|
|
*
|
2001-03-23 03:40:22 +01:00
|
|
|
* $Id: README.txt,v 1.7 2001/03/23 02:40:22 steve Exp $
|
2001-03-14 20:26:15 +01:00
|
|
|
*/
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
VVP SIMULATION ENGINE
|
|
|
|
|
|
|
|
|
|
The VVP simulator takes as input source code not unlike assembly
|
|
|
|
|
language for a conventional processor. It is intended to be machine
|
|
|
|
|
generated code emitted by other tools, including the Icarus Verilog
|
|
|
|
|
compiler, so the syntax, though readable, is not necessarily
|
|
|
|
|
convenient for humans.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GENERAL FORMAT
|
|
|
|
|
|
|
|
|
|
The source file is a collection of statements. Each statement may have
|
|
|
|
|
a label, an opcode, and operands that depend on the opcode. For some
|
|
|
|
|
opcodes, the label is optional (or meaningless) and for others it is
|
|
|
|
|
required.
|
|
|
|
|
|
|
|
|
|
Every statement is terminated by a semicolon. The semicolon is also
|
|
|
|
|
the start of a comment line, so you can put comment text after the
|
|
|
|
|
semicolon that terminates a statement. Like so:
|
|
|
|
|
|
2001-03-11 23:42:11 +01:00
|
|
|
Label .functor and, 0x5a, x, y ; This is a comment.
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
The semicolon is required, whether the comment is there or not.
|
|
|
|
|
|
|
|
|
|
Statements may span multiple lines, as long as there is no text (other
|
|
|
|
|
then the first character of a label) in the first column of hte
|
|
|
|
|
continuation line.
|
|
|
|
|
|
2001-03-23 03:40:22 +01:00
|
|
|
HEADER SYNTAX
|
|
|
|
|
|
|
|
|
|
Before any other non-commentary code starts, the source may contain
|
|
|
|
|
some header statements. These are used for passing parameters or
|
|
|
|
|
global details from the compiler to the vvp run-time. In all cases,
|
|
|
|
|
the header statement starts with a left-justified keyword, and ends
|
|
|
|
|
with a string.
|
|
|
|
|
|
|
|
|
|
* :module "name" ;
|
|
|
|
|
|
|
|
|
|
This header statement names a vpi module that vvp should load before
|
|
|
|
|
the rest of the program is compiled. The compiler looks in the
|
|
|
|
|
standard VPI_MODULE_PATH for files named "name.vpi", and tries to
|
|
|
|
|
dynamic load them.
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
LABELS AND SYMBOLS
|
|
|
|
|
|
|
|
|
|
Labels and symbols consist of the characters:
|
|
|
|
|
|
|
|
|
|
a-z
|
|
|
|
|
A-Z
|
|
|
|
|
0-9
|
|
|
|
|
.$_
|
|
|
|
|
|
|
|
|
|
Labels and symbols may not start with a digit or a '.', so that they
|
|
|
|
|
are easily distinguished from keywords and numbers. A Label is a
|
|
|
|
|
symbol that starts a statement. If a label is present in a statement,
|
|
|
|
|
it must start in the first text column. This is how the lexical
|
|
|
|
|
analyzer distinguishes a label from a symbol. If a symbol is present
|
|
|
|
|
in a statement, it is in the operand. Opcodes of statements must be a
|
|
|
|
|
keyword.
|
|
|
|
|
|
|
|
|
|
Symbols are references to labels. It is not necessary for a label to
|
|
|
|
|
be declared before its use in a symbol, but it must be declared
|
2001-03-20 07:16:23 +01:00
|
|
|
eventually. When symbols refer to functors, the symbol represents the
|
|
|
|
|
vvp_ipoint_t pointer to the output. (Inputs cannot, and need not, be
|
|
|
|
|
references symbolically.)
|
|
|
|
|
|
|
|
|
|
If the functor is part of a vector, then the symbol is the
|
|
|
|
|
vvp_ipoint_t for the first functor. The [] operator can then be used
|
|
|
|
|
to reference a functor other then the first in the vector.
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
FUNCTOR STATEMENTS:
|
|
|
|
|
|
|
|
|
|
A functor statement is a statement that uses the ``.functor''
|
|
|
|
|
opcode. Functors are the basic structural units of a simulation, and
|
|
|
|
|
include a type (in the form of a truth table) and up to four inputs. A
|
|
|
|
|
label is required for functors.
|
|
|
|
|
|
|
|
|
|
The general syntax of a functor is:
|
|
|
|
|
|
2001-03-11 23:42:11 +01:00
|
|
|
<label> .functor <type>, <init> [, symbol_list]
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
The symbol list is 0-4 names of labels of other functors. These
|
|
|
|
|
connect inputs of the functor of the statement to the output of other
|
|
|
|
|
functors. The type is the label of a .ftype statement elsewhere in the
|
2001-03-11 23:42:11 +01:00
|
|
|
program. The references .ftype describes the behavoir of the
|
|
|
|
|
functor.
|
|
|
|
|
|
|
|
|
|
The <init> value is the 8-bit initial value of the 4 input ports. The
|
|
|
|
|
LSB is port 0, and the MSB port 3.
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
Almost all of the structural aspects of a simulation can be
|
2001-03-11 23:42:11 +01:00
|
|
|
represented by functors, which perform the very basic task of
|
|
|
|
|
combining up to four inputs down to one output.
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
VARIABLE STATEMENTS:
|
|
|
|
|
|
2001-03-20 07:16:23 +01:00
|
|
|
A variable is a bit vector that can be written by behavioral code (so
|
|
|
|
|
has no structural input) and propagates its output to a functor. The
|
|
|
|
|
general syntax of a variable is:
|
|
|
|
|
|
|
|
|
|
<label> .var "name", <msb>, <lsb>;
|
2001-03-11 01:29:38 +01:00
|
|
|
|
2001-03-20 07:16:23 +01:00
|
|
|
The "name" is the declared base name of the original variable, for the
|
|
|
|
|
sake of VPI code that might access it. The variable is placed in the
|
|
|
|
|
current scope. The variable also has a width, defined by the indices
|
|
|
|
|
for the mst significant and lest significant bits. If the indices are
|
|
|
|
|
equal (normally 0) the vector has width of one. If the width is greater
|
|
|
|
|
then one, a contiguous array of functors is created and the value of
|
|
|
|
|
the label is the address of the least significant bit.
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
A variable does not take inputs, since its value is set behaviorally
|
2001-03-20 07:16:23 +01:00
|
|
|
by assignment events. It does have output, though, and its output is
|
|
|
|
|
propagated into the net of functors in the usual way.
|
2001-03-11 01:29:38 +01:00
|
|
|
|
2001-03-20 07:16:23 +01:00
|
|
|
Therefore, the .var statement implicitly also creates .functors
|
|
|
|
|
addressed by the label of the variable. It is in fact the functors
|
|
|
|
|
that behavioral code reads when the value of the variable (or net) is
|
|
|
|
|
read by behavioral code. If the .var represents a vector of .functors,
|
|
|
|
|
the index of the LSB is always, from the perspective of vvp, ZERO. The
|
|
|
|
|
<msb>,<lsb> details are there only for the benefit of VPI support.
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
The variable .functor implicitly has three inputs. The first is the
|
|
|
|
|
value that gets set by assignments or procedural continuous
|
|
|
|
|
assignments. The second is a forced value that can be connected to a
|
|
|
|
|
force expression (as a functor) when a value is being forced. And the
|
|
|
|
|
third input selects the source to use. The default is to select the
|
|
|
|
|
assignment input.
|
|
|
|
|
|
2001-03-20 07:16:23 +01:00
|
|
|
The variable statement also creates a VPI object of the appropriate
|
|
|
|
|
type. See the vpi.txt file for details about that object.
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
Note that nets in a design do not necessarily have a specific functor
|
|
|
|
|
or object allocated to them. Nets are just something that behavioral
|
|
|
|
|
code can read, so it is enough to give to the behavioral code the
|
|
|
|
|
vvp_ipoint_t object of the .functor that drives the net.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
THREAD STATEMENTS:
|
|
|
|
|
|
|
|
|
|
Thread statements create the initial threads for a simulation. These
|
|
|
|
|
represent the initial and always blocks, and possibly other causes to
|
|
|
|
|
create threads at startup.
|
|
|
|
|
|
|
|
|
|
.thread <symbol>
|
|
|
|
|
|
|
|
|
|
This statement creates a thread with a starting address at the
|
|
|
|
|
instruction given by <symbol>.
|
|
|
|
|
|
|
|
|
|
|
2001-03-11 23:42:11 +01:00
|
|
|
TRUTH TABLES
|
|
|
|
|
|
|
|
|
|
The logic that a functor represents is expressed as a truth table. The
|
|
|
|
|
functor has four inputs and one output. Each input and output has one
|
|
|
|
|
of four possible values (0, 1, x and z) so two bits are needed to
|
|
|
|
|
represent them. So the input of the functor is 8 bits, and the output
|
|
|
|
|
2 bits. A complete lookup table for generating the 2-bit output from
|
|
|
|
|
an 8-bit input is 512 bits. That can be packed into 64 bytes. This is
|
|
|
|
|
small enough that the table should take less space then the code to
|
|
|
|
|
implement the logic.
|
|
|
|
|
|
|
|
|
|
To implement the truth table, we need to assign 2-bit encodings for
|
|
|
|
|
the 4-value signals. I choose, pseudo-randomly, the following
|
|
|
|
|
encoding:
|
|
|
|
|
|
|
|
|
|
1'b0 : 00
|
|
|
|
|
1'b1 : 01
|
|
|
|
|
1'bx : 10
|
|
|
|
|
1'bz : 11
|
|
|
|
|
|
|
|
|
|
The table is an array of 64 bytes, each byte holding 4 2-bit
|
|
|
|
|
outputs. Construct a 6-bit byte address with inputs 1, 2 and 3 like
|
|
|
|
|
so:
|
|
|
|
|
332211
|
|
|
|
|
|
|
|
|
|
The input 0 2-bits can then be used to select which of the 4 2-bit
|
|
|
|
|
pairs in the 8-bit byte are the output:
|
|
|
|
|
|
|
|
|
|
MSB -> zzxx1100 <- LSB
|
|
|
|
|
|
|
|
|
|
A complete truth table, then is described as 64 8-bit bytes.
|
|
|
|
|
|
|
|
|
|
The vvp engine includes truth tables for the primitive gate types, so
|
|
|
|
|
none needs to be given by the programmer. It is sufficient to name the
|
|
|
|
|
type to get that truth table.
|
|
|
|
|
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
EXECUTABLE INSTRUCTIONS
|
|
|
|
|
|
|
|
|
|
Threads run executable code, much like a processor executes machine
|
|
|
|
|
code. VVP has a variety of opcodes for executable instructions. All of
|
|
|
|
|
those instructions start with '%' and go into a single address
|
|
|
|
|
space. Labels attached to executable instructions get assigned the
|
|
|
|
|
address of the instruction, and can be the target of %jmp instructions
|
|
|
|
|
and starting points for threads.
|
|
|
|
|
|
2001-03-14 20:26:15 +01:00
|
|
|
The opcodes.txt file has a more detailed description of all the
|
|
|
|
|
various instructions.
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
|
2001-03-13 01:49:50 +01:00
|
|
|
THE RELATIONSHIP BETWEEN FUNCTORS, THREADS AND EVENTS
|
|
|
|
|
|
|
|
|
|
Given the above summary of the major components of vvp, some
|
|
|
|
|
description of their relationship is warrented. Functors provide a
|
|
|
|
|
structural description of the design (so far as it can be described
|
|
|
|
|
structurally) and these functors run independently of the threads. In
|
|
|
|
|
particular, when an input to a functor is set, it calculates a new
|
|
|
|
|
output value; and if that output is different from the existing
|
|
|
|
|
output, a propagation event is created. Functor output is calculated
|
|
|
|
|
by truth table lookup, without the aid of threads.
|
|
|
|
|
|
|
|
|
|
Propagation events are one of three kinds of events in vvp. They are
|
|
|
|
|
scheduled to execute at some time, and they simply point to the functor
|
|
|
|
|
that is to have its output propagated. When the event expires, the
|
|
|
|
|
output of the referenced functor is propagated to all the inputs that
|
|
|
|
|
it is connected to, and those functors in turn create new events if
|
|
|
|
|
needed.
|
|
|
|
|
|
|
|
|
|
Assignment events (the second of three types of events) are created
|
|
|
|
|
by non-blocking assignments in behavioral code. When the ``<='' is
|
|
|
|
|
executed (a %assign in vvp) an assign event is created, which includes
|
|
|
|
|
the vvp_ipoint_t pointer to the functor input to receive the value,
|
|
|
|
|
as well as the value. These are distinct from propagation events because:
|
|
|
|
|
|
|
|
|
|
a) There is no functor that has as its output the value to be
|
|
|
|
|
assigned (this is how values get into the functor net in
|
|
|
|
|
the first place), and
|
|
|
|
|
|
|
|
|
|
b) This allows for behavioral code to create waveforms of
|
|
|
|
|
arbitrary length that feed into a variable. Verilog allows
|
|
|
|
|
this of non-blocking assignments, but not of gate outputs.
|
|
|
|
|
|
|
|
|
|
The last type of event is the thread schedule event. This event simply
|
|
|
|
|
points to a thread to be executed. Threads are made up of a virtual
|
|
|
|
|
processor with a program counter and some private storage. Threads
|
|
|
|
|
can execute %assign instructions to create assignment events, and can
|
|
|
|
|
execute %set instructions to do blocking assignments. Threads can also
|
|
|
|
|
use %load to read the output of functors.
|
|
|
|
|
|
|
|
|
|
The core event scheduler takes these three kinds of events and calls
|
|
|
|
|
the right kind of code to cause things to happen in the design. If the
|
|
|
|
|
event is a propagate or assignment event, the network of functors is
|
2001-03-14 20:26:15 +01:00
|
|
|
tickled; if the event is a thread schedule, then a thread is run. The
|
2001-03-13 01:49:50 +01:00
|
|
|
implementation of the event queue is not important, but currently is
|
|
|
|
|
implemented as a ``skip list''. That is, it is a sorted singly linked
|
|
|
|
|
list with skip pointers that skip over delta-time events.
|
|
|
|
|
|
|
|
|
|
The functor net and the threads are distinct. They communicate through
|
|
|
|
|
thread instructions %set, %assign, %waitfor and %load. So far as a thread
|
|
|
|
|
is concerned, the functor net is a blob of structure that it pokes and
|
|
|
|
|
prods via certain functor access instructions.
|
|
|
|
|
|
|
|
|
|
|
2001-03-21 06:12:15 +01:00
|
|
|
VVP COMPILATION AND EXECUTION
|
|
|
|
|
|
|
|
|
|
The vvp program operates in a few steps:
|
|
|
|
|
|
|
|
|
|
1) Initialization
|
|
|
|
|
Data structures are cleared to empty, and tables are
|
|
|
|
|
readied for compilation.
|
|
|
|
|
|
|
|
|
|
2) Compilation
|
|
|
|
|
The input file is read and compiled. Symbol tables are
|
|
|
|
|
build up as needed, objects are allocated and linked
|
|
|
|
|
together.
|
|
|
|
|
|
|
|
|
|
3) Cleanup
|
|
|
|
|
Symbol tables and other resources used only for
|
|
|
|
|
compilation are released to reduce the memory
|
|
|
|
|
footprint.
|
|
|
|
|
|
|
|
|
|
4) Simulation
|
|
|
|
|
Event simulation is run.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The initialization step is performed by the compile_init() function in
|
|
|
|
|
compile.cc. This function in turn calls all the *_init() functions in
|
|
|
|
|
other parts of the source that need initialization for compile. All
|
|
|
|
|
the various sub-init functions are called <foo>_init().
|
|
|
|
|
|
|
|
|
|
Compilation is controlled by the parser, it parse.y. As the parser
|
|
|
|
|
reads and parses input, the compilation proceeds in the rules by
|
|
|
|
|
calling various compile_* functions. All these functions live in the
|
|
|
|
|
compile.cc file. Compilation calls other sections of the code as
|
|
|
|
|
needed.
|
|
|
|
|
|
|
|
|
|
When the parser completes compilation, compile_cleanup() is called to
|
|
|
|
|
finish the compilation process. Unresolved references are completed,
|
|
|
|
|
then all the symbol tables and other compile-time-only resources are
|
|
|
|
|
released. Once compile_cleanup() returns, there is no more use for the
|
|
|
|
|
parser for the function in compile.cc.
|
|
|
|
|
|
|
|
|
|
After cleanup, the simulation is started. This is done by executing
|
|
|
|
|
the schedule_simulate() function. This does any final setup and starts
|
|
|
|
|
the simulation running and the event queue running.
|
|
|
|
|
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
HOW TO GET FROM THERE TO HERE
|
|
|
|
|
|
|
|
|
|
The vvp simulation engine is designed to be able to take as input a
|
|
|
|
|
compiled form of Verilog. That implies that there is a compiler that
|
|
|
|
|
compiles Verilog into a form that the vvp engine can read.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* Boolean logic gates
|
|
|
|
|
|
|
|
|
|
Gates like AND, OR and NAND are implemented simply and obviously by
|
|
|
|
|
functor statements. Any logic up to 4 inputs can be implemented with a
|
|
|
|
|
single functor. For example:
|
|
|
|
|
|
|
|
|
|
and gate (out, i1, i2, i3);
|
|
|
|
|
|
|
|
|
|
becomes:
|
|
|
|
|
|
|
|
|
|
gate .functor and, i1, i2, i3;
|
|
|
|
|
|
|
|
|
|
Notice the first parameter of the .functor is the type. The type
|
|
|
|
|
includes a truth table that describes the output with a given
|
|
|
|
|
input. If the gate is wider then four inputs, then cascade
|
|
|
|
|
functors. For example:
|
|
|
|
|
|
|
|
|
|
and gate (out, i1, i2, i3, i4, i5, i6, i7, i8);
|
|
|
|
|
|
|
|
|
|
becomes:
|
|
|
|
|
|
|
|
|
|
gate.0 .functor and, i1, i2, i3, i4;
|
|
|
|
|
gate.1 .functor and, i5, i6, i7, i8;
|
|
|
|
|
gate .functor and, gate.0, gate.1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* reg and other variables
|
|
|
|
|
|
|
|
|
|
Reg and integer are cases of what Verilog calls ``variables.''
|
|
|
|
|
Variables are, simply put, things that behavioral code can assign
|
|
|
|
|
to. These are not the same as ``nets,'' which include wires and the
|
|
|
|
|
like.
|
|
|
|
|
|
|
|
|
|
Each bit of a variable is created by a ``.var'' statement. For example:
|
|
|
|
|
|
|
|
|
|
reg a;
|
|
|
|
|
|
|
|
|
|
becomes:
|
|
|
|
|
|
2001-03-21 06:12:15 +01:00
|
|
|
a .var "a", 0, 0;
|
|
|
|
|
|
|
|
|
|
|
2001-03-14 20:26:15 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2001 Stephen Williams (steve@icarus.com)
|
|
|
|
|
*
|
|
|
|
|
* This source code is free software; you can redistribute it
|
|
|
|
|
* and/or modify it in source code form 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.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
|
|
|
*/
|