Abstract Verilog Part I

advertisement
Introduction to Abstract Verilog
Abstract
Writing Verilog programs to describe most circuits is harder and more confusing that it
needs to be. Abstract Verilog extends the Verilog language with straightforward
constructs that replace many confusing idioms that are used in Verilog for describing
synthesizable designs.
AbstractVerilog defines a clean way to declare and use registered and combinational
signals that makes it easy to write and understand programmed descriptions of hardware.
AbstractVerilog allows designers to write hardware modules without knowing about
many of the more complicated features of Verilog, and keeps them from writing the
programs that have lots of non-obvious semantic errors.
AbstractVerilog assumes a straightforward, synchronous design style. For example, in
the typical case, every module with a register has a clock and reset input, and all registers
are positive edge-triggered. (This is easily extended to multiple clocks and negative-edge
triggered registers.)
We assume that the reader already knows how to design hardware and is familiar with the
rudiments of Verilog such as port declarations and Verilog syntax for expressions and
statements like if-then-else, case and for.
Introduction
A module in Verilog, such as the one shown in Figure 1, represents a component in a
larger hardware design. Modules are instantiated and connected to other modules in a
hierarchical manner using schematics or structural Verilog. Primitive modules do not
instantiate other modules, but instead describe the complete functionality of that module
in terms of combinational and sequential logic. Modules are connected via ports, which
are input, output or bi-directional signals. The module port list contains a list of these
signals, which are then declared as inputs, outputs or inouts (bi-directional) in the body of
the module.
Abstract Verilog is used to describe the functionality of components in terms of
combinational and registers. For example, state machines are made using registers to
remember the current state and combinational logic to compute the next state and the
state machine outputs. All signals are classified as combinational or registered.
Combinational signals are computed by combinational logic and change (potentially)
whenever an input changes, whereas registered signals are the outputs of registers and
change only on a clock edge.
A
S
FA
B
Cout
Cin
Figure 1: Full-adder component
// Simple fulladder
module fulladder(S, Cout, A, B, Cin);
input A, B, Cin;
output S, Cout;
COMB S, Cout;
ALWAYS begin
{ Cout, S } = A + B + Cin;
end
endmodule
Signals in Abstract Verilog are thus declared to be combinational (COMB) or registered
(REGISTER). The syntax otherwise conforms exactly to Verilog, with some extensions.
Here are some examples of COMB signal declarations:
COMB [3:0] y;
COMB enable;
COMB dataValid = (rp != wp);
Y is a 4-bit bus while enable is a simple wire. The dataValid signal is defined to always
be the result of the expression (rp != wp). If a COMB signal is defined in the declaration,
then it cannot be assigned a value elsewhere in the module.
Here are some examples of REGISTER signal declarations:
REGISTER [2:0] state = IDLE;
REGISTER(clk,reset) foo;
REGISTER(clk2) bar;
REGISTER_NEG
face;
Note that registers can be declared with an initial value, which is assigned to the register
at reset. If no initial value is given, then no initial value is assigned to the register. If any
REGISTER signals are declared, then the module must define signals called “clk” and
“reset” by default. The REGISTER declaration can specify the name of the clock and
reset signal. If no reset is given, as in the case of bar above, then the register is cannot be
assigned an initial value in the declaration. REGISTER_NEG declares a register that
operates on the negative edge of the clock.1
Note that input ports of a module are not declared as COMB or REGISTER – they are
declared elsewhere. However, all output and inout signals must be declared along with
all internal signals.
Combinational and registered signals are assigned values in an ALWAYS block, which is
continuously executed. The ALWAYS block is evaluated whenever a signal changes that
is an input of the ALWAYS block, that is, a signal that is used to determine the value of
some signal. Statements in an ALWAYS block are executed sequentially just as in the
Verilog always block. Statements execute in zero time: This means that even though the
statements execute in order, they take no time to execute. COMB signals change value
when they are assigned, and in Verilog. REGISTER signal assignments have no effect
until the next clock edge for that register.
COMB signals are assigned using the = operator. REGISTER signals are assigned values
using the <-- operator. Register assignments are remembered but do not take effect until
the next clock “tick” (rising edge). This means that all sequential assignments take place
simultaneously at the clock edge. For example, the following code:
a <-- b;
b <-- a;
swaps the values of a and b when the clock next ticks. Note, moreover, that in the
following code, the count register value is incremented by 1, not 2, since the first
assignment is not executed until the next clock tick, and thus the second assignment uses
the current, unchanged, value of count and overwrites the first assignment.
count <-- count + 1;
. . .
count <-- count + 1;
Combinational signals take newly assigned values immediately, while register signals do
not change until the next clock tick. If a combinational output of an ALWAYS block is
also an input to the same ALWAYS block, then the ALWAYS block may be infinitely
re-executed in the same simulation step. Combinational feedback is of course forbidden.
(Multiple ALWAYS blocks are allowed in a module for clarity, but it is illegal for more
than one ALWAYS block to assign values to the same signal.) As in ordinary
synthesizable Verilog, all combinational signals that are assigned in the ALWAYS block
must be assigned a value for any execution of the block. A compiler flag can be used to
force Abstract Verilog to assign all COMB signals to ‘X’ at the beginning of the
ALWAYS block. If a signal is not assigned, then it will show up as an ‘X’ in simulation.
However, it is safer to use a good Verilog compiler to do this check. For example,
Synplify synthesis will warn of inserted latches, although Aldec will not.
Having two different assignment operators is redundant since all signals are declared as
either COMB or REGISTER, but their use makes the code much clearer.
1
Negative edge-triggered registers are not yet implemented.
Example Combinational Circuit Program
You have already seen one program, for a full-adder, in Figure 1. The following program
implements a simple decoder.
// Implement a 3:8 decoder
module decoder (in, out);
input [2:0] in;
output [7:0] out;
COMB
[7:0] out;
ALWAYS begin
case (in)
0: out
1: out
2: out
3: out
4: out
5: out
6: out
7: out
endcase
end
=
=
=
=
=
=
=
=
8’b00000001;
8’b00000010;
8’b00000100;
8’b00001000;
8’b00010000;
8’b00100000;
8’b01000000;
8’b10000000;
endmodule
Here is a more complicated program for the game of life that uses other interesting
Verilog constructs. Note that integer declares a compile-time variable used to compute
the value of “real” signals.
module life (neighbors, self, out);
input
self;
input [7:0]
neighbors;
output
out;
COMB
out;
integer
count;
integer
i;
ALWAYS begin
count = 0;
for (i = 0; i<8; i = i+1) count = count + neighbors[i];
out = 0;
out = out | (count == 3);
out = out | ((self == 1) & (count == 2));
end
endmodule
Example Sequential Circuit Program
Here is a simple Abstract-Verilog program that implements a counter. Note that the
assignment to tc could be done at the end of the ALWAYS block since any assignment
to vcount does not occur until the next clock edge. That is, even after the assignment
statement
vcount <-- vcount + 1;
is executed, the value of vcount remains unchanged until the next clock edge. In fact, the
ALWAYS block could be executed several times without changing the value of vcount.
The value of vcount is changed implicitly when the next clock edge occurs.
module countv (clk, reset, cen, vcount, tc);
parameter limit = 666;
input
clk;
input
reset;
input
cen;
output [9:0] vcount;
output
tc; // Terminal count
REGISTER [9:0] vcount = 0;
COMB tc = (vcount == limit);
// Assert terminal count when vcount has reached the limit
ALWAYS begin
if (cen) begin
if (vcount < limit) begin
vcount <-- vcount + 1;
end else begin
vcount <-- 0;
end
end
end
endmodule
Running Abstract Verilog
The Abstract Verilog translator is a Java program that is run on Windows machines using
the rv.bat command file. It assumes that the O: drive has the ntdfs\\cs
filesystem mounted, and runs programs in the courses\cse467\AstractVerilog
folder. rv.bat runs a preprocessor, which processes `defines, then calls the Java
translator. We use the .rv suffix for AbstractVerilog programs. Compiling the
AbstractVerilog program foo.rv generates the Verilog file foo.v. This output
Verilog file can then be simulated and synthesized using the usual Verilog tools.
The following statement will compile the two AbstractVerilog programs and generate
two corresponding Verilog programs.
rv foo.rv bar.rv
The rv.bat file can be edited easily (see next page) to use a different version of Java, to
not delete the foo.pp.rv preprocessed output file, and to force assignment of ‘X to
COMB signals.
I find that using emacs to edit files is convenient, and that using a shell window in emacs
makes it easy to run the rv command. If someone figures out how to insert this into the
Aldec workflow, let me know! But it’s probably more trouble than it’s worth.
rv.bat file
@echo off
rem Translate one or more RVerilog files
rem Usage rv foo.rv bar.rv ...
rem Files are in the standard network directory
set rlogdir=O:\cse\courses\cse467\AbstractVerilog\
set javaVersion=5
:loop
rem Check whether file exists
if not exist %1 (
echo File %1 missing
goto skip
)
rem Run the preprocessor
perl %rlogdir%\verilog-define-pp.pl %1 > %~n1.pp.rv
rem Run the translator
rem java -classpath %rlogdir%;%rlogdir%\antlr.jar Main %~n1.pp.rv
rem -coff Turns off the assignment of X to combinational signals
java -jar %rlogdir%RV-v%javaVersion%.jar -coff %~n1.pp.rv
rem Rename output file, leave the preprocessed file for debugging
copy/Y %~n1.pp.v %~n1.v
del %~n1.pp.v
rem Delete the .pp.rv preprocessed file
del %~n1.pp.rv
:skip
shift
if "%1"=="" goto done
goto loop
:done
Download