Dr Ryan Robucci
Objective
- Introduction to Verilog
- Verilog basics
- Testbenches
- Coding Style
- Extended Introduction
Dr Ryan Robucci
Objective
Verilog (IEEE 1364) is a hardware description language (HDL) that can be used to model electric systems for simulation and hardware synthesis and verification.
Structural models
Dataflow models
Behavioral models :
Register-Transfer Level (RTL)
Built-in primatives provide a starting set of building blocks for describing digital logic.
not
and
, or
: e.g. and myand1 (out,a,b);
nmos
, pmos
, supply1
, supply0
, etc...module mux_structural(d0,d1,sel,y); input d0,d1; input sel; output y; //defaults to type wire, which is suitable for // making connections between parts in netlists wire sel_n,w0,w1; not i0 (sel_n,sel); and i1 (w0,d0,sel_n); and i2 (w1,d1,sel); or i3 (y,w0,w1); endmodule
Line 1
Line 3
Line 7
Line 8
Understand the concurrent description: at the base Verilog constructs are concurrent by default. In the example, the order of the gates provided in lines 8-11 do not matter
A testbench:
module tb(); //a testbench is typically self-containtd and thus has no ports //Internal Signals reg d0,d1,sel; //reg: signals that will be assigned within the same // hierarchy level using procedural code wire y; //wire: signals for connections //In SystemVerilog, use: logic d0,d1,sel,y; //Instantiation of module, referred to as Device Under Test mux_structural dut(.*); //SystemVerilog default connection syntax // for standare Verilog use explicit port mapping // mux_structural dut(.d0(d0),.d1(d1),.sel(sel),.y(y)); integer count; //working integer variable initial begin //produral code is typically used to generate input stimulous count = 0; // unless a test-jig module forever #1 count++; // is instatiated to interact with the DUT end assign {d0,d1,sel}=count; initial #0 $display("d0 d1 sel y"); // zero-delay #0 ensures that printing // after circuit initialization is completed initial $monitor("%2b ",d0,"%2b ",d1,"%2b ",sel," ","%2b ",y); initial #7 $finish; //terminates simulation endmodule
The highlighted code lines with the DUT instantiation, the assign
statement, the run-once initial
lines, and the block of code in lines 14-17 may be provded in any order since the base level of code is concurrent.
The lines within begin end statements, lines 15-16, are sequntial code and only there does order matter.
The $monitor
is a printing task supporting multiple inputs, as well as formatting strings followed by the arguments satisfying inputs to the formatting string. The monitor task automatically retriggers and prints when any mutable input changes.
Results: (iverilog '-Wall' '-g2012' design.sv testbench.sv && unbuffer vvp a.out)
d0 d1 sel y
0 0 0 0
0 0 1 0
0 1 0 0
0 1 1 1
1 0 0 1
1 0 1 0
1 1 0 1
1 1 1 1
Another Example Using Multi-Bit Vectors:
module mux_structural_2bit(d0,d1,sel,y); input [1:0] d0,d1; //multi-bit input input sel; output [1:0] y; //multi-bit output // making connections between parts in netlists wire sel_n; wire [1:0] w0,w1; //multi-bit internal buses not i0 (sel_n,sel); and i1 (w0[0],d0[0],sel_n); and i2 (w1[0],d1[0],sel); or i3 ( y[0],w0[0],w1[0]); and i4 (w0[1],d0[1],sel_n); and i5 (w1[1],d1[1],sel); or i6 ( y[1],w0[1],w1[1]); endmodule
module tb(); reg [1:0] d0,d1; reg sel; wire [1:0] y; mux_structural_2bit dut(.*); integer count; initial begin count = 0; forever #1 count++; end initial $display("%3s ","sel","%3s ","d1","%3s ","d0"," ","%3s ","y"); initial $monitor("%3b ", sel ,"%3b ", d1 ,"%3b ", d0 ," ","%3b ", y ); assign {sel,d1,d0}=count; initial #31 $finish; endmodule
sel d1 d0 y
0 00 00 00
0 00 01 01
0 00 10 10
0 00 11 11
0 01 00 00
0 01 01 01
0 01 10 10
0 01 11 11
0 10 00 00
0 10 01 01
0 10 10 10
0 10 11 11
0 11 00 00
0 11 01 01
0 11 10 10
0 11 11 11
1 00 00 00
1 00 01 00
1 00 10 00
1 00 11 00
1 01 00 01
1 01 01 01
1 01 10 01
1 01 11 01
1 10 00 10
1 10 01 10
1 10 10 10
1 10 11 10
1 11 00 11
1 11 01 11
1 11 10 11
1 11 11 11
Dataflow models : assignment expressions
=
with either wire delcaration or later with assign
+
, -
, *
, /
, %
, **
<
, >
,<=
, >=
==
, !=
, also ===
and !==
for metalogic support
==?
, !=?
&&
, ||
, !
->
, and logical equivalence <->
(see Verilog Manual)&
, |
, ^
, ~^
(xnor), ^~
(xnor)~
&
, ~&
, |
, ~|
, ^
, ~^
>>
, <<
>>
, <<
?
:
{
comma-separated list }
{
count{
comma-separated list }
}
One might consider Structural Verilog as inclusive of dataflow, since many of the dataflow operations that map directly to built-in logic primitives and specifications of net connections.
The following are the same in many contexts
assign x = a & b & c;
and n0(x,a,b,c);
The exact implementation and structure implied in the following is less certain unless we explicitly know the exact module that addition would map to with our synthesizer and library
assign x = a+b;
Dataflow modeling example
module mux_dataflow_2bit_enables(d0,d1,sel,enCh0,enCh1,enGlobal,y); input [1:0] d0,d1; input sel,enCh0,enCh1,enGlobal; output [1:0] y; wire sel_n; wire w0,w1; wire y0,y1; assign w0 = (d0[0] & ~sel) | (d1[0] & sel); //NOT, ANDs, OR assign w1 = sel ? d1[1] : d0[1]; // C-like conditional ternary operator assign y0 = w0 & enCh0 & enGlobal; // string of binary (2-inputs) AND gates forms // a three-input AND gate assign y1 = & {w1,enCh1,enGlobal}; // reduction and compresses 3 bits to // one resulting bit result using the AND operator assign y = {y1,y0}; // concatentation operator endmodule
module tb(); reg [1:0] d0,d1; reg enCh0,enCh1,enGlobal; reg sel; wire [1:0] y; mux_dataflow_2bit_enables dut(.*); integer count; initial begin count = 0; forever #1 count++; end initial $display("%8s ","enGlobal","%6s ","enCh1","%6s ","enCh0", "%3s " ,"sel","%3s ","d1","%3s ","d0"," ","| %3s","y"); initial $monitor("%8b ", enGlobal ,"%6b ", enCh1 ,"%6b ", enCh0 , "%3b " , sel ,"%3b ", d1 ,"%3b ", d0 ," ","| %3b", y ); assign {enGlobal,enCh1,enCh0,sel,d1,d0}=count; initial #255 $finish; endmodule
enGlobal enCh1 enCh0 sel d1 d0 | y
0 0 0 0 00 00 | 00
0 0 0 0 00 01 | 00
0 0 0 0 00 10 | 00
0 0 0 0 00 11 | 00
0 0 0 0 01 00 | 00
...
0 0 1 1 00 11 | 00
0 0 1 1 01 00 | 00
...
0 1 0 0 10 00 | 00
0 1 0 0 10 01 | 00
0 1 0 0 10 10 | 00
0 1 0 0 10 11 | 00
0 1 0 0 11 00 | 00
...
1 0 1 1 01 11 | 01
1 0 1 1 10 00 | 00
1 0 1 1 10 01 | 00
...
1 1 0 0 01 11 | 10
1 1 0 0 10 00 | 00
1 1 0 0 10 01 | 00
1 1 0 0 10 10 | 10
1 1 0 0 10 11 | 10
....
1 1 1 1 10 01 | 10
1 1 1 1 10 10 | 10
1 1 1 1 10 11 | 10
1 1 1 1 11 00 | 11
1 1 1 1 11 01 | 11
1 1 1 1 11 10 | 11
1 1 1 1 11 11 | 11
Behavioral code is implemented in procedural blocks that include one or several statements that describe an algorithm to define the behavior of a block of logic in a simulation or in hardware
A procedural block may include sequential statements from which the algorithm may be understood by beginning interpretation of statements one at a time (similar to traditional software coding languages) or
parallel statements intended to be interpreted in parallel.
The creation of behavioral code is sometimes characterized by a lack of regard for hardware realization
Synthesizable Behavioral Code is code that a given synthesizer can map to a hardware implementation
The definition of synthesizable is synthesizer dependant- some simple prodecural code constructs are universally
synthesizable by every synthesizer, while more complex code blocks and certain operators are not considered
synthesizable by many
Example :
x = myUINT8 >> 2; // This is a shift by a constant implemented by a simple routing of bits. // It is generally regarded as synthesizable x = myUINT8 >> varShift; // This is a variable shift with many possible implementations. // It will simulate just fine, but at the synthesis step many // synthesizers will throw an error saying that this is not synthesizable
Behavioral code may indeed describe behavior in such a way that is not directly synthesizable by almost any synthesizer (such as reading waveforms from a .txt file) – though what is “synthesizable” is always defined by the synthesizer tool being used
Initial and Always blocks will be the first two types of blocks we will discuss (tasks, functions)
Initial blocks are triggered once at the start of a simulation or in the case of some synthesis tools may be used to describe the power-up state of registers or may be used to describe the initial default value of an intermediate variable
Always blocks continually repeats its execution during a simulation, but optionally can be gated (paused) so as to describe evaluation triggered with change in one or more signals as provided in a sensitivity list.
Assuming no delay statements are included in the procedural code: the keywords begin
and end
may be used to encapsulate a description of an algorithm using a block of sequential code.The code is just a description of a desired behavior and does not necessarily mimic the implementation itself – the entire description is evaluated in one instant in time (takes 0 time to complete)
begin
and end
like { and } in CWhen describing clocked, register hardware that should updates its observable outputs only when the clock and/or aync control signals changes, provide only the change-invoking control signals with the appropriate edge selection in the sensitivity list
always @ (posedge clk, posedge reset)
When describing only combinational hardware, which should update its observable output when data inputs change regardless of the direction of change, list all of the function dependencies in the sensitivity list.
always @ (a, b, c, d)
When providing a mix, the combinational results may not be used outside the block, and edge-selection must be used
RTL Verilog example:
module mux_rtl_2bit_enables(d0,d1,sel,enCh0,enCh1,enGlobal,y); input [1:0] d0,d1; input sel,enCh0,enCh1,enGlobal; output reg [1:0] y; wire sel_n; wire w0, w1; always @ (d0,d1,sel,enCh0,enCh1,enGlobal) begin: blk0 reg y1,y0; y1 = 1'bx; y0 = 1'bx; if (enGlobal) begin case (sel) 1'b0 : begin {y1,y0} = { d0[1]&enCh1 , d0[0]&enCh0 }; y = {y1,y0}; end 1'b1 : y = d1 & {enCh1,enCh0}; //bit-wise AND, mult-bit assignment endcase end else begin y = 2'b0; end end endmodule
Testbench:
module tb(); reg [1:0] d0,d1; reg enCh0,enCh1,enGlobal; reg sel; wire [1:0] y_rtl; wire [1:0] y_df; mux_dataflow_2bit_enables dut_df (.y(y_df) ,.*); mux_rtl_2bit_enables dut_rtl(.y(y_rtl),.*); integer count; initial begin count = 0; forever #1 count++; end wire [1:0] match = y_df~^y_rtl; initial $display("%8s ","enGlobal","%6s ","enCh1","%6s ","enCh0", "%3s " ,"sel","%3s ","d1","%3s ","d0"," ","| %6s","y_df"," %6s","y_rtl", "%6s" ,"match"); initial $monitor("%8b ", enGlobal ,"%6b ", enCh1 ,"%6b ", enCh0 , "%3b " , sel ,"%3b ", d1 ,"%3b ", d0 ," ","| %6b", y_df ," %6b", y_rtl , "%6b" , match ); assign {enGlobal,enCh1,enCh0,sel,d1,d0}=count; initial #255 $finish; endmodule
Output:
enGlobal enCh1 enCh0 sel d1 d0 | y_df y_rtl match
0 0 0 0 00 00 | 00 00 11
0 0 0 0 00 01 | 00 00 11
0 0 0 0 00 10 | 00 00 11
...
1 1 1 1 11 10 | 11 11 11
1 1 1 1 11 11 | 11 11 11
...
Prev versions of Verilog used or keyword in sensitivity list, which referred to a sensitivity to a change in any of a or b or c.
always @ (a or b or c)
This is not the same as being sensitive to a change in the result (a|b|c).
always @ (a|b|c); //not the same
With Verilog 2001:
Use a comma-separated sensitivity list
always @ (a, b, c, d) always @ (posedge clk, posedge reset)
Shortcut for including all dependencies (inputs) in a combinatorial block:
always @ (*)
There are 2 kinds of assignment statements:
=
operator, and<=
operator.
When discussing a variable y declared using reg y
; refer to y as "reg" "y", not as "register" "y". You can also refer to it as "variable" "y".
reg
keyword does not mean Register : Here is something to be cleared up right away when learning Verilog reg
is just a variable. In fact the type of the signal that is declared by using ref
is called a variable
in later versions of Verilog because the the name is so confusing. So, don't let yourself be confused by the name, reg
does not necessarily register manifest as a register in hardware.Origin of the confusion: "Although the Verilog HDL is used for more than just simulation, the semantics of the language are defined for simulation, and everything else is abstracted from this base definition." -- page 64 of Verilog Standard
From a simulation perspective, a reg
is assigned through procedural code and in the execution of that code the simulator might require a memory in order to compute the resulting value of a reg
.
The code below describes a signal y that is updated with the value a|b|c any time a,b,or c changes. Clearly this is a 3-input OR gate, which is combinatorial. However, the algorithm/code used to describe the function requires a memory of y to compute the resulting value of y. To a simulator, a type reg
is distinctly differnt then a wire
.
reg y; always @(a,b,c) begin y = a; y = y|b; y = y|c; end
This code computes the result of the expression assigning a|b|c and after computing the reult, independant of y, and then schedules an assignment of that result to y.
wire y; assign y = a|b|c;
A reg
is used in procedural-code as variables that may end up being implemented using with if sequential logic is generated and are just represent the output net of combinatorial logic otherwise. Wires on the other hand are for structural connections (nets/wires) between modules or outputs of combinatorial expressions. There is a simple rule to decide if you should use a reg
or a wire
: if the signal is assigned from a procedural block of code or not. It is so straight forward that in SystemVerilog you can just use the datatype logic
and the type of reg or wire is effectively inferred from its use.
Verilog Supports more than the two logic values 0/Low and 1/High. The following Metalogic Values are worth being aware of an may be encountered unintentionally.
z
: high-impedance (not driven), supports modeling of bus drivers where multiple potential drivers are connected to one output
x
: undetermined, can represent the following
Register data type is now called a variable, as the previous name of register created a lot of confusion for beginners. Also it is possible to specify an initial value for the register/variable data type.
reg a = 0; // v2k allows to init variables reg b, c, d = 0; //just init d
New signed reg:
// reg data type can be signed in v2k // We can assign with signed constants reg signed [7:0] data = 8'shF0;
Setting Default Net Type
using `default net_type none at the top of your Verlog file is a recommended beginning practice, otherwise typing errors can generate additional nets rather than make connections. These errors are difficult to add to the list of debugging challenges for a beginner
In Verilog 1995, default data type is net and its width is always 1 bit.
This can be dangerous for two
reasons…
a simple typing mistake can declare
a new variable instead of an intended connection to an existing net causing a confusing error message or lead to a coding mistake
forgetting a declaration can lead to 1-bit wires which loose information
wire [7:0] a; wire [7:0] b; wire [7:0] d; wire [7:0] e; c=a+b; //one bit!!!! e=c+d;
In Verilog 2001 the width is adjusted automatically
In Verilog 2001, we can disable default data type by using a
special directive at the top of the code:
`default net_type none
wire a,b,c,d,y; mylib_and2(w1,a,b); mylib_and2(w2,c,d); mylib_and2(y,w1,w2);
Take care in specifying the sensitivity list : the contents of sensitivity list
needs careful attention. A mistake here is a common cause for diferences between Verilog simulation and synthesized hardware.
always @ (a,b,c) begin x = (c & a) | (~c & b); end
always @ (a,c) begin x = (c & a) | (~c & x); end
always @ (a,b,c) begin if (c) x = a; else x = b; end
always @ (c) begin if (c) x = a; else x = b; end
always @ (a,b,c) begin if c x = a; end
Code interpretation for combinational logic
If results from any execution of the block directly rely on signals/results generated from a previous execution of the block then sequential logic is describe. This does not include the case when results are saved using external sequential logic.
Draw the truth table for each block with and without considering the sensitivity list, it should include a row for every possible input combination and the output variables should should never occur in the output columns
q <= a+b;
to assign the output of a register (here the input to the register is a+b)y=a+b;
y=a+b;q<=y;q_prev<q;
Coding Requirements
A,B,C,D, and F are also enforced requirements for this course
A) Use non-blocking statements to assign outputs of sequential gates
B) Use blocking statements to assign outputs of combinatorial gates
C) Avoid unnecessary latches when coding for FPGAs, but when necessary use non-blocking statements
D) Do not make assignments to the same variable from more than one always block
E) Consider what each output bit is assigned for any possible evaluation under any input and sequential state condition
F) Do not attempt to use any delay operator for the purpose of affecting hardware synthesis
Related guidelines:
http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf page 22
Poor, uses blocking Assignment
module dffb (q, d, clk, rst); output q; input d, clk, rst; reg q; always @(posedge clk) if (rst) q = 1'b0; else q = d; endmodule
Good, uses Non-blocking Assignment
module dffx (q, d, clk, rst); output q; input d, clk, rst; reg q; always @(posedge clk) if (rst) q <= 1'b0; //coding all sequential always blocks, even simple single-block modules, using nonblocking assignments. else q <= d; endmodule
Course Guideline/Requirement: Use blocking assignment for any output of combinational logic, regardless of the trigger type
Combinatorial:
reg y; always @(a,b) y = a & b;
In this isolated block you might have have alternatively tried and used
y <= a & b;
but we will follow a convention explained later whereby we use blocking for all combinatorial logic
Sequential (registered-output combinatorial logic):
reg q; always @(posedge clk) q <= a & b;
// Combinational Logic reg y; always @(a,b) y = a & b;
// Sequential Logic mixed with Implied Combinational Logic reg q; always @(posedge clk) q <= a & b;
// Sequential mixed with Explicit Combinational Logic reg q; always @(posedge clk): someBlockName reg _q; _q = a & b; q <= _q;
// Poor, uses blocking Assignment module dffb (q, d, clk, rst); output q; input d, clk, rst; reg q; always @(posedge clk) if (rst) q = 1'b0; else q = d; endmodule
// Coding all sequential always blocks,even simple single-block modules,using non-blocking assignments. // Good, uses Non-blocking Assignment module dffx (q, d, clk, rst); output q; input d, clk, rst; reg q; always @(posedge clk) begin if (rst) q <= 1'b0; else q <= d; end endmodule
Determinitisic, Cycle-Accurate Descriptions are used obtain matching of synthesized hardware function to pre-synthesis simulated function
In VHDL, Variables
update immediately upon assignment and do not facilitate communication between processes. Signals
are used to communicate between processes and are updated at the end of a simulation cycle.
In Verilog, we can adopt a coding conventions that mimics this by using named blocks
Sequential Circuit with Embedded Combinatorial:
always @(posedge clk) begin {a,b,c} <= {l|m,m|n,(n|l)&e}; end
Separated Combinatorial and Sequential with explicit naming and modeling of combinatorial outputs:
reg a_prereg,b_prereg,c_prereg,c_or; always @(l,m,n) begin c_or = n|l; {a_prereg,b_prereg,c_prereg} = {(l|m),(m|n),c_or&e}; end always @(posedge clk) begin {a,b,c} <= {a_prereg,b_prereg,c_prereg}; end
Merged Style with local internal variables supporting "cycle-acurate" hardware reprentation and simuation
local variables require the use of a named block, but the advantage is that this prevents accidential use elsewhere
always @(posedge clk) begin: someBlockName reg _a,_b,_c,_c_or; //variables indended for use in this block only // though this is not enforced _c_or = n|l; {_a,_b,_c} = {l|m,m|n,_c_or&e}; {a,b,c} <= {a_prereg,b_prereg,c_prereg}; end
What is the value of a signal when nothing is observing it?
Xilinx tools, at the time of this writing, will generate extra edge-sensitive hardware for _a,_b,_c,_c_or, and then since they are not used elsewhere Xilinx will report that hardware is trimmed leaving only our inteded output. This makes many distracting warning that I hope are removed in newer software adopting the newer SystemVerilog constructs like always_ff.
Enforced Guileline on Using Block Scope
This guideline is enforced in this Course: Avoid declaring block-local variables needlessly outside the block. Name blocks as needed to allow new scope for variables.
The following is poor style. It creates potential confusion and potential mistakes of use elsewhere in the circuit by delaring _c_or,_a,_b,_c and making them available outside the block. Using _c_or outside the block would actually genrate a new register outputing a delayed version of _c_or. Using _a,_b,_c would result in a new register that would be recognized to be the same as a,b,c respectively
reg _a,_b,_c; always @(posedge clk) begin {_a,_b,_c} = {l|m,m|n,n|l}; {a,b,c} <= {a_prereg,b_prereg,c_prereg}; end
Good: uses named block and local scope
always @(posedge clk) begin: blockC reg _a,_b,_c; {_a,_b,_c} = {l|m,m|n,n|l}; {a,b,c} <= {_a,_b,_c}; end
reg y; always @(posedge clk, negedge clr_n) begin: blockY /************************************************//**/ /* Depends on a,b, current value of y, clr_n //**/ /************************************************//**/ /**/ reg y_prereg,partial; /**/ /**/ y_prereg=1'bx;partial=1'bx; /**/ /**/ if (!clr_n) y <= 1'b0; /**/ /**/ else begin /**/ /**/ partial = a & b; /**/ /**/ y_prereg = ~a | partial; /**/ /**/ y <= y_prereg; /**/ /**/ end /**/ /****************************************************/ /* Output: block updates value of y /**/ /* Side effect: updates to y_prereg and partial /**/ /************************************************//**/ end
Danger
Do not attempt to use intermediate combinational logic generated within an edge-triggered block outside the block.
It will generate unintential logic, or reduce your grade.
If you need the combinational version of the signal elsewhere, code the generating logic in a separate combinational block
Some examples quoted from https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8299595 IEEE Std 1800-2017
Unsized, unsigned homogenious literals. Assignment using any of the following sets all bits to specified value:
'0
, '1
, 'X
, 'x
, 'Z
, 'z
Plain unsized, unsided decimal is provided without any extra decaration:
456
In general
Start with optional bit size specifier
Provide tick '
specify literal reprensentation type with b
,h
,d
or alternatives B
,H
,D
provide the value using the syntax of the literal
_
for reablity may be freed used except for the first characterleft padding rules are non-trivial and beginners are recommended to avoid, but simple use of unsized literals is reasonable '0
,'1
,'z
,'x
Examples:
4'b1001 // 4-bit binary number 8'b1001_1011 // 8-bit binary number 'h2AF // 12-bit hex 5 'D 3 // 5-bit decimal number 12'hx // 12-bit unknown number 'x // all x
?
is an alternative for z
in literals. ?
and z
have a special meaning in certain contexts, which will be explained later.
Signed Literals will be covered later.
wire
(or alernatively logic
in the case of SystemVerilog)not inv1 (out,in)
nand
,or
,and
,or
,xor
,xnor
, though other primatives exist... wire a,b,c,d,e,x,y,z; ... or orX (x,a,b); or orY (y,c,d); and i0 (z,x,y,e);
Continous assignment may be decribed implicity with the wire declartion or provided explicilty later along with an assign
keyword
... wire a,b,c,d,e; ... wire x = a | b; //implicit continous assignment wire z; ... assign z = x & (c&d) & e ;
Keyword: Implicit Continous Assignment
The stimulus (input)
$display
$monitor
or $strobe
statements to print result to screen or filePossible Blueprint:
Wires and registers with specified precision and sign
Arithmetic and bitwise operations
Comparison Operations
Bitvector Operations (selecting multiple bits from a vector, concatenation)
Logical Operators
Selection (muxes)
Indexed Storage (arrays)
Organizational syntax elements and Precedence
Modules (Hardware) Definition and Instantiation
Verilog 2000 has New Port Declaration Options
Verilog 95 code:
module memory( read, write, data_in, addr, data_out); input read; input write; input [7:0] data_in; input [3:0] addr; output [7:0] data_out; reg [7:0] data_out;
After the port list, port direction must be declared to be input, output, or inout as well as the width if more than one bit
Type declaration: type is by default a wire unless another type is provided
Verilog 2k with direction and data type listed:
module memory( input wire read, input wire write, input wire [7:0] data_in, input wire [3:0] addr, output reg [7:0] data_out );
Verilog 2k with no type in port list
module memory( input read, input write, input [7:0] data_in, input [3:0] addr, output reg [7:0] data_out );
ports are declared as wire by default, override with keyword reg
keyword
However, there is a stylistic disadvantage to exposing type in port declaration. In the following two examples, the function is the same but the type for y must change.
Verilog 2000 – port y as reg
module dff2y(output reg qA, output reg qB, output reg y, input dA, input dB, input en_n, input clk) always@(posedge clk) if (~en_n) begin qA <= dA; qB <= dB; end always @(qA,aB) begin y = 1’b0; if (a&b) begin y = 1; end end end
Verilog 2000 – port y as wire
module dff2y(output reg qA, output reg qB, output wire y, input dA, input dB, input en_n, input clk) always@(posedge clk) if (~en_n) begin qA <= dA; qB <= dB; end assign y = qA&qB; end
y is not the output of a register though declared as reg along with qA and qB in the left module declaration It is arguable that such an internal coding implementation detail does not belong in the presentation of an “external” interface
Verilog 2000 -- hiding reg while exposing port direction using an intermediate signal
module dff2y( output qA, output qB, output y, input dA, input dB, input en_n, input clk) reg qA_int,qB_int,y_int; assign qA = qB_int; assign qB= qA_int; assign y= y_int; always@(posedge clk) if (~en_n) begin qA_int <= dA; qB_int <= dB; end always @(qA,aB) begin y_int = 1’b0; if (a&b) begin y_int = a&b; end end end
Implicit port mapping uses the order of the ports in the definition to imply connections to local nets
Instantiation with Implicit Port Mapping
module dff2y_en( output qA, qB, output y, // at the instiation, y is a wire regardless of the internal code implementation input dA, dB, input en, input clk) wire en_n = ~en; // wire en_n; // assign en_n = ~en; dff2y dff2yInstance( qA,qB, //dff out A B y, //and of qA&qB dA,dB, //dff inputs en_n, //input clk en clk); //clk endmodule
Explicit port mapping uses the port names prefixed with . and allows reordering, no-connect, and omission of ports
Instantiation with Explicit Port Mapping
module dff2y_en( output y, input dA, dB, input en, input clk) dff2y dff2yInstance( .dA(dA), .dB(dB), .qA(), //qA not used (no-connect) //qB omitted .y(y), .en_n(~en), //** .clk(clk)); //clk endmodule
Implicit net declaration of a net holding the result ~en. This is NOT allowed if default net declaration is disabled. ie using `default net_type none
Combinatorial
reg y; always @(a,b) y = a & b;
In this isolated block you might have have also used y<= a & b;
but we will follow a convention explained later whereby we use blocking for all combinatorial logic
Sequential (registered-output combinatorial logic):
reg q; always @(posedge clk) q <= a & b;
Separated Combinatorial and Sequential Logic:
reg y,y_prereg,partial; always @(a,b,c) begin partial = a & b; y_prereg = c | partial; end always @(posedge clk) begin y <= y_prereg; end
Mentally, associate the variable name (e.g. y) with the output of the register, not the register itself.
Implicit Mix of Combinatorial and Sequential Logic:
reg y,partial; always @(posedge clk) begin partial = a & b; y <= c | partial; end
Explicit Mix of Combinatorial and Sequential Logic:
reg y,y_prereg,partial; always @(posedge clk) begin partial = a & b; y_prereg = a | partial; y =<= y_prereg; end
Separated Combinatorial and Sequential Logic:
reg y,y_prereg,partial; always @(a,b,c) begin partial = a & b; y_prereg = a | partial; end always @(posedge clk, negedge clr_n) begin // Asynchronous control signals must appear in the sensitivity list if (!clr_n) y <= 1'b0; else y <= y_prereg; end
Tip
Mentally learn to associate the variable name to the output of the register, not the register itself. This rule later helps distinguish the input to the register from the output. Unfortunally the generated diagram here DOES NOT follow this convention.
Typically Templates can be found in a synthesizer manual or through the
development GUI
You need to follow template statles that the synthesizer recognizes. Check the documentation of a synthesizer for additional coding style requirements.
Both of the following examples generate an error in Xilinx ISE:
ERROR:Xst:899 - "top.v" line 28: The logic for <partial> does not match
a known FF or Latch template. The description style you are using to describe
a register or latch is not supported in the current software release.
Problem Code 1:
reg y,partial; always @(posedge clk, negedge clr_n) begin partial = a & b; if (!clr_n) y <= 1'b0; else y <= ~a | partial; end
Problem Code 2:
reg partial,y_prereg; always @(posedge clk, negedge clr_n) begin if (!clr_n) begin partial = a & b; y_prereg = ~c | partial; y <= 1'b0; end else begin partial = a & b; y_prereg = ~c | partial; y <= y_prereg; end end endmodule
The following code is more compact than the initial separated version, but leads to warnings
reg y,y_prereg,partial; always @(posedge clk, negedge clr_n) //------ begin if (!clr_n) y <= 1'b0; else begin partial = a & b; y_prereg = ~a | partial; y <= y_prereg; end end //-------
implied registers and latches are trimmed since they are only used inside this procedural code block and feed into another signal
If the are used outside, additional sequential logic would be generate to provide the saved values externally
WARNING:Xst:646 - Signal <y_prereg> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
WARNING:Xst:646 - Signal <partial> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
The warning tells us that these are not used externally to the procedural block and so they are trimmed-- we’ll call this a trimmable implementation and I allow it even though beginners probably should avoid this style.
The variable scope used for y_prereg and partial is unessisary.
If you follow our coding guidelines, you must use blocking assignment for outputs of combinatorial logic even if within edge-triggered procedural blocks.
Any signal that is both
(1) assigned by a BLOCKING assignment AND
(2) assigned withing an edge triggered block
I) should be considered as an internal net and you
should not (AND MAY NOT FOR THIS COURSE) use
that net outside the block in which it is assigned
II) must be declared as a local variable, requiring a
named block (see prev. slide), and thus enforcing I)
placing a colon and a name after the keyword begin creates a named block with a named variable scope
Local variables are preferred to disallow the mistake of using the combinatorial outputs from an edge-triggered block by keeping them declaring them locally.
(This is part of a Verilog coding style convention to softly enfore what VHDL handles more explicitly. ) This along with guidelines that will be refined in the slideset “Suggested Coding and Design Practices allow use to represent both sequntial and combinatorial hardware in the same procedural block. The alternative is to always separate combinatorial and seqential hardware, which I don’t find practical and is limiting compared to standing coding styles in VHDL.
Use of named block and local variables to explicitly constrain use of internal variables:
reg y; always @(posedge clk, negedge clr_n) begin: blockY /*************************************************//**/ /* Depends on a,b, pre-existing value of y, clr_n //**/ /*************************************************//**/ /**/ reg y_prereg,partial; /**/ /**/ y_prereg=1'bx;partial=1'bx; /**/ /**/ if (!clr_n) y <= 1'b0; /**/ /**/ else begin /**/ /**/ partial = a & b; /**/ /**/ y_prereg = ~a | partial; /**/ /**/ y <= y_prereg; /**/ /**/ end /**/ /*****************************************************/ /* Output: block updates value of y /**/ /* Side effect: updates to y_prereg and partial /**/ /*************************************************//**/ end
module mydevice_tb(); reg clk, rst; // many signals will be reg since they are driven by procedural code in the testbench reg x1, x2; wire y1, y2; // Outputs from the module under test are simply structural connections at this level so wires are used mydevice DUT(clk,rst, y1,y2, x1,x2); //An instance of the device under test initial clk = 0; // An initial statement or block can set initial values of signals always begin //A always block with delays can be used to drive cyclic signals #50; //delay clk = ~clk; end initial begin //Stops simulation at T=1000 #1000 $finish; end // Initial value and a change at T=10 initial begin rst = 1; #10; //delay rst = 0; end /* Intialize signals immediately if not otherwise initialized, then add delays and assignments We'll see other examples later, but at first avoid changing signals input to clocked blocks at the same time as the clock edge it is sensitive to */ initial begin y1=0; y2=0; #50; //delay y1=1; #50; //delay y1=0; y2=1; #50; //delay y1=1; y2=0; end // This testbench includes no print or output statements, so it is assumed that a results waveform viewer (GUI) is used endmodule //end testbench module
For more discussion, see http://www.sunburst-design.com/papers/CummingsHDLCON2002_Parameters_rev1_2.pdf
Avoid magic numbers and use local parameters
localparam is new with Verilog 2001 For older code, you might also see the keyword parameter
, but that defines a value that can be overridden when a module is instantiated
localparam a=31; //int
localparam a=32,b=31; //int (multiple on one line)
localparam byte_size=8, byte_max=bytesize-1; //int
localparam a =6.22; //real
localparam delay = (min_delay + max_delay) /2;//real
localparam initial_state = 8'b1001_0110; //reg
In C, standard variable types are constrained by the processor architecture, but for custom hardware it makes sense to allow declaring a variables of various bit lengths. We do this by appending the type with and bit address range.
“A net or reg declaration without a range specification shall be considered 1 bit wide and is known as a scalar. Multibit net and reg data types shall be declared by specifying a range, which is known as a vector.” IEEE Verilog Specification
Standard bit vectors defined by a high to low index are created with square brackets and colon preceding the identifier
<type> [<HighBit>:<LowBit>] id;
They are accessed using square brackets for single bits and additionally using a colon if accessing a slice of bits. Examples from IEEE Specification:
reg[3:0] v; // a 4-bit vector reg made up of (from most to // least significant) v[3], v[2], v[1], and v[0] reg [-1:4] b; // a 6-bit vector reg reg [4:0] x, y, z; // declares three 5-bit regs
Expanding and slicing vectors is supported for evaluation and assignment
reg[7:0] v; wire msb,lsb; wire [3:0] highNibble, lowNibble; wire [7:0] s; … assign lsb = v[0]; assign msb = v[7]; assign highNibble = v[7:4]; assign lowNibble = v[3:0]; s[3:0] = 4’b0000; s[7:0] = lowNibble;
Whereas vectors essentially define a new variable type with a specified bit range and length, array allow creating an addressable group of elements of the same type
Standard arrays defined by a high to low index are created with square brackets and colon following the identifier
They are accessed using square brackets
reg x[11:0]; //scalar reg array with length of 12 … //within procedural code… x[3] = 1’0;
Standard memories vectors defined using both bit vector and object array notation
<type> [<HighBit>:<LowBit>] id [<HighAddress>:<LowAddress>];
The objects (e.g. words) can be accessed like this:
id[address]
Individual bit slices can be accessed like this:
id[address][highbit:lowbit]
reg [7:0] ram[0:4095]; // 4096 memory cells that are 8 bits wide
//code excerpt from Chapter 2 SRAM model input [11:0] ABUS; // 12-bit address bus to access all 4096 memory cells inout [7:0] DATABUS;// 8-bit data bus to write into and out of a memory cell reg [7:0] DATABUS_driver; wire [7:0] DATABUS = DATABUS_driver; //inout must be driven by a wire .... for (i=0; i <= 4095; i = i + 1) // Setting individual memory cells to 0 ram[i] = 0; end .... ram[ABUS] = DATABUS; //writing to a memory cell .... DATABUS_driver = ram[ABUS]; //reading from a memory cell
I STRONGLY recommend sticking with high to low indexing for all array and vectors definitions and access Create a mix leads to errors.
One reasonable exception for read access is
bit order reversing for a bus:
wire [31:0] a;
wire [31:0] b;
assign b = a[0:31]
Integar types
Signedness:
Default Signed: byte, shortint, int, integer, and longint default to signed
Default Unsigned: time, bit, reg, and logic
Signedness can be provdided using keywords signed and unsigned
int unsigned ui; int signed si;
Real:
Conversions:
$realtobits
(see Verilog Manual)What follows is a quick overview of a subset of operators in Verilog. This will familiarize you sufficiently with various operators and syntax so that you can start working with Verilog, though more advanced details will be discussed in later lectures.
Some additional examples may be found here:
http://www.asic-world.com/verilog/operators1.html
http://www.asic-world.com/verilog/operators2.html
Logical Operators force the evaluation of input arguments to TRUE or FALSE and result in a single TRUE or FALSE.
Operator | Name | Examples |
---|---|---|
! |
logical negation | if (!(a==b)) , if (!a) |
&& |
logical and | if ((c==a)&&(c==b)) |
|| |
logical or | if ((c==a)||(c==b)) |
produce a multi-bit result
The bitwise negation operator ~ takes one argument (it is a “unary” operator) and acts in parallel on the individual bits
Example:
wire [3:0] a,y; assign a = 4’b0101; assign y = ~a;
Other bitwise operators take two arguments and act in parallel on pairs of bits. Logical and && or ||
Example:
wire [3:0] a = 4’b0101,b= 4’b0011,y; assign y = a&b; //produces 0001
take a single input argument of one or multiple bits
output a single bit (it reduces a multi-bit input to a single-bit output)
Example:
wire a = 4’b0001; wire y1,y2; assign y1 = |a; assign y2 = |a;
Operator | Name | Examples |
---|---|---|
| & |
bitwise or bitwise and | y=a|b; |
| & |
reduction or reduction and | y = |b; |
~| ~& |
reduction nor reduction nand | y = ~|b; |
a^b |
bitwise xor | y = a^b; |
^b |
reduction xor | y = ^b; |
~^ ^~ |
bitwise xnor | y = a~^b; |
~^ ^~ |
reduction xnor | y = ~^b; |
~ |
bitwise negation | y = ~a; |
Operator | Name, Description | Examples |
---|---|---|
== |
logical equality | if (a==b) |
!= |
logical inequality | if (a!=b) |
a>b |
relational greater than | if (a>b) |
>= |
relational greater than or equal | if (a>=b) |
< |
relational greater than | if (a<b) |
<= |
relational greater than or equal | if (a<=b) |
Operator | Name, Description | Examples |
---|---|---|
>> ,<< |
shift right or left by a number of positions | a = shiftvalue >> 2; |
<<< ,>>> |
Signed shifts, shift right or left by a number of positions with a signed left argument. Signed variables may be declared with the keyword signed, integers are signed by default | |
+ ,- ,* ,/ ,% |
Arithmetic Operators. Note for division: synthesizers may only support divide or modulo by constant power of two | y=a+b; y=b/4; //right shift by 2 |
** |
Power. Note: synthesizers may only support a right argument that is a constant | c=a**3; |
There are two types of assignments that you will see in procedural code
Operator | Name, Description | Examples |
---|---|---|
<= |
non blocking assignment statement, schedules assignment but allows next statement to execute | y <= a & b; |
= |
blocking assignment statement, blocks execution of next statement until assignment to left-hand argument (e.g. y) is completed | y = a & b; |
Operator | Name, Description | Examples |
---|---|---|
{ , } |
Concatenation: concatenation of one, two, or more operands | {4'b1111,4'b0000} {2'b11,2'b11,2'b00,2'b00} Both produce 8'b11110000 |
{n{x}} |
Replication: Allows fixed number of replications (n must be a constant) | assume a=16'hFFFF; then 2{a} produces 32'hFFFFFFFF 2{a} produces 32'hFFFFFFFF {16{1'b0},a} produces 32'h0000FFFF 8{2'b10} roduces 16'b1010101010101010 |
Operator | Name, Description | Examples |
---|---|---|
== | logical quality | if (bus == 4’b0000) |
=== | case equality | if (bus === 4’bzzzz) |
~= | logical inequality | |
!== | case inequality | if (bus !== 4’bzzzz) |
In this code, I assigned combinatorial variables x (don’t care) at the top of the code to see if Xilinx ISE not complain, but it still does. Following it are two versions along with the Xilinx ISE synthesis report.
reg y; always @(posedge clk, negedge clr_n) begin: blockY reg y_prereg,partial; y_prereg=1'bx;partial=1'bx; if (!clr_n) y <= 1'b0; else begin partial = a & b; y_prereg = ~a | partial; y <= y_prereg; end end
Trimmable Without Combinatorial Assignment to X:
module trimmable(output reg y, input a, input b, input clk,input clr_n); always @(posedge clk, negedge clr_n) begin: blockY reg y_prereg,partial; if (!clr_n) y <= 1'b0; else begin partial = a & b; y_prereg = ~a | partial; y <= y_prereg; end end endmodule
Started : "Synthesize - XST".
Running xst...
Command Line: xst -intstyle ise -ifn "/home/robucci/Nextcloud/covail/Courses/CMPE415/XilinxProjects/test_synth/trimmable.xst" -ofn "/home/robucci/Nextcloud/covail/Courses/CMPE415/XilinxProjects/test_synth/trimmable.syr"
Reading design: trimmable.prj
=========================================================================
* HDL Compilation *
=========================================================================
Compiling verilog file "trimmable.v" in library work
Module <trimmable> compiled
No errors in compilation
Analysis of file <"trimmable.prj"> succeeded.
=========================================================================
* Design Hierarchy Analysis *
=========================================================================
Analyzing hierarchy for module <trimmable> in library <work>.
=========================================================================
* HDL Analysis *
=========================================================================
Analyzing top module <trimmable>.
Module <trimmable> is correct for synthesis.
=========================================================================
* HDL Synthesis *
=========================================================================
Performing bidirectional port resolution...
Synthesizing Unit <trimmable>.
Related source file is "trimmable.v".
WARNING:Xst:646 - Signal <blockY/y_prereg> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
WARNING:Xst:646 - Signal <blockY/partial> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
Found 1-bit register for signal <y>.
Summary:
inferred 1 D-type flip-flop(s).
Unit <trimmable> synthesized.
=========================================================================
HDL Synthesis Report
Macro Statistics
# Registers : 1
1-bit register : 1
=========================================================================
Trimmable With Combinatorial Assignment to X:
module trimmable(output reg y, input a, input b, input clk,input clr_n); always @(posedge clk, negedge clr_n) begin: blockY reg y_prereg,partial; y_prereg=1'bx; partial=1'bx; if (!clr_n) y <= 1'b0; else begin partial = a & b; y_prereg = ~a | partial; y <= y_prereg; end end endmodule
Started : "Synthesize - XST".
Running xst...
Command Line: xst -intstyle ise -ifn "/home/robucci/Nextcloud/covail/Courses/CMPE415/XilinxProjects/test_synth/trimmable.xst" -ofn "/home/robucci/Nextcloud/covail/Courses/CMPE415/XilinxProjects/test_synth/trimmable.syr"
Reading design: trimmable.prj
=========================================================================
* HDL Compilation *
=========================================================================
Compiling verilog file "trimmable.v" in library work
Module <trimmable> compiled
No errors in compilation
Analysis of file <"trimmable.prj"> succeeded.
=========================================================================
* Design Hierarchy Analysis *
=========================================================================
Analyzing hierarchy for module <trimmable> in library <work>.
=========================================================================
* HDL Analysis *
=========================================================================
Analyzing top module <trimmable>.
Module <trimmable> is correct for synthesis.
=========================================================================
* HDL Synthesis *
=========================================================================
Performing bidirectional port resolution...
Synthesizing Unit <trimmable>.
Related source file is "trimmable.v".
WARNING:Xst:646 - Signal <blockY/y_prereg> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
WARNING:Xst:646 - Signal <blockY/partial> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
Found 1-bit register for signal <y>.
Summary:
inferred 1 D-type flip-flop(s).
Unit <trimmable> synthesized.
=========================================================================
HDL Synthesis Report
Macro Statistics
# Registers : 1
1-bit register : 1
=========================================================================