Hardware Description
Languages : Verilog
David Nguyen, Duy-Ky Nguyen, PhD
1. Introduction
VHDL was first created by DOD contractors IBM, Texas Intruments and Intermetrics to document a large discrete digital project in 1980. Verilog was created by an engineer named Phil Moorby at a CAE company called Gateway Design Automation to verify a large discrete digital projects in 1981.
VHDL is a Pascal/Ada-like language (case insensitive) while Verilog is C-like language (case sensitive).
VHDL is more programming language than Verilog as it has many data types for signal and variable. Verilog is better to describe hardware and 2 basic data types, 1 for combinatory digital (memory-less like gate, decoder, mux, ...) and 1 for sequential digital (memory like flip-flop). It has only signal, not variable (signal is also variable, anyway).
A Verilog Quick Reference CHM file
Remark
- In this note, the term sequential has the meaning of sequential digital circuitry, like flip-flop; where sequential assignment is executed at clock edge. It`s totally different from the regular meaning used in SW for sequential statements where one statement is executed after another.
- HDL has another term concurrent may cause some misunderstanding. In HDL, concurrent assignment is associated with combinatory digital circuitry, like gate, and is executed immediately, ie not waiting for clock.
There have been a serious mistake published in HDL books saying the order of sequential statements are important. The reason could be the authors may be confused between regular meaning of sequential where one after another and its digital meaning where clock action.
The Verilog is below where keywords are in bold.
module Ent_Name ( I, O, IO ); input I; output [3:0] O; // vector inout [7:0] IO; -- module body endmodule
2. Combinatory Digital
2.1. Gates
INV | O = ~ I; |
AND2 | O = I0 & I1; |
AND2B1 | O = (~ I0) & I1; |
OR2 | O = I0 | I1; |
NAND2 | O = ~ (I0 & I1); |
XOR | O = I0 ^ I1; |
BUFE | O = E ? I : 1`bZ; // 1`b: 1-bit binary, conditional statement |
In Verilog, combinatory digital uses `=` while sequential uses <=.
2.2. Decoder
Below is an implementation for D2_4E (2-bit select, 4-bit output with Enable).
If we have vector interfaces, say S_2x and O_4x, then
O_4x = (~E) ? 0 : (S_2x==0) ? 1 : (S_2x==1) ? 2 : (S_2x==2) ? 4 : 8;
It`s far more convenient to deal with bits in Verilog than VHDL by using concatenation operator {}.
{D3, D2, D1, D0} = (~E) ? 0 : ({S1, S0}==0) ? 1 : ({S1, S0}==1) ? 2 : ({S1, S0}==2) ? 4 : 8;
2.3. Mux
Below is an implementation for M4_1E (2-bit select, 4-bit input with Enable)
O <= (~E) ? `Z` // Tri-state : (S_2x==0) ? D0 : (S_2x==1) ? D1 : (S_2x==2) ? D2 : D3;
If we have bitwise select, we use the same method for decoder above.
2.4. Adder
For 4-bit adder, we have
O_4x <= A_4x + B_4x;
3. Sequential Digital
3.1. Flip-Flop
For the simplest D-FF, FD in Xilinx, we have
always @ (posedge C ) // equiv process in VHDL Q <= D;
Or for falling edge trigger, FD_1, we have
always @ (negedge C ) Q <= D;
If async clr required, FDC, we have
always @ ( posedge C or posedge CLR ) if (CLR) Q <= 0; else Q <= D;
If clk enable required, FDCE, we have
always @ ( posedge C or posedge CLR ) if (CLR) Q <= 0; else if (E) Q <= D;
Note that always has a sensitive list that trigger its action, like @ ( posedge C ) or @ ( posedge C or posedge CLR) above;
3.2. Shifter
For a 8-bit left shifter S with 1-bit input I clocked by C, we have
always @(posedge C) O <= {S[7:1], I}; // uses concat op { }
3.3. Counter
If we need a 10-counter, then
always @ ( posedge C or posedge RST ) begin if (RST) Q <= 0; else Q <= (Q==9) ? 0 : (Q + 1);
4. Language
Verilog has only one 2 data types of 1-bit size wire for combinatory digital and reg for sequential digital. It best represents the digital nature. It follows closely C style, like type before signal, index with [ : ]
wire sig_bit; // 1-bit combinatory signal reg [7:0] sig_vec; // 8-bit sequential signal
Signal uses `=` for combinatory and `<=` for sequential assignments, like
Sig_a = sig_b; -- comb` signals Sig_c <= sig_d; -- seq` signals
Signal has value 0, 1 and Z for tri-state. 3-bitvector has value 3`b0 (binary) or 3`h0 (hex) or 3`d0 (decimal). The bit size can even be dropped like `b0, `h0 or `d0.
Verilog follows C style, like signal after type, operators, original function convention, like
Int add (x, y); Int x; Int y; { return x+y; }
4.1. Structure
Verilog object has 2 parts
- Declaration: module header with IO list;
- Implementation: architecture with signal and variable declarations, with concurrent and sequential assignments (within process);
Verilog block statement is several single statements (concurrent or sequential) embedded between begin - end.
module Name ( I, O, IO ); input I; output O; inout IO; // signal declarations // concurrent assignments always @ (sensitive_list) -- sequential assignments (begin - end) endmodule
4.2. Relational Operators: ==(equal) !=(unequal) < (less than) > (greater than)
Relational operators result in 1-bit value where TRUE for non-zero and FALSE for zero value.
For example,
X <= (a==b); // 1 if a equal to b, else 0
4.3. Logical Operators: ~ ! & `&&` | `||` ^
The 3 operators ! (logical not), &&(logical and) and || (logical or) are for 1-bit boolean logical (with relational operators), while ~(bit-wise inv), & (bit-wize and) and | (bit-wise or) and bit-wise logical (digital circuitry)
X_bool <= (a = b) && !(c = d); // boolean X_4x <= A_4x & ~B_8x[3:0]; // bit-wise
4.4. Concatenation Operator { }
Concatenation combines many values into a composite value, regardless sizes. It`s equivalent to both concatenation and aggregate in VHDL.
X_4x <= {x, x_2, x_1, x_0}; // combine 4 bits into 4-bit vector
Or even
X_4x <= {x_3, x_vec[2:0]}; // OK to combine 1 bit and 1 vector !!!
4.5. Replication Operator { { } }
X_16x <= {4{x_4x}}; // Make 16-bit vector from 4-bit ones
4.6. Conditional Statements ? :
X <= condition_1 ? value_1 : condition_2 ? value_2 : value_3;
It can be used in any statement (concurrent or sequential).
4.7. IF Statements
if (condition_1) Statement_1; else if (condition_2) Statement_2; else Statement_3;
4.8. CASE Statements
case (expression) is value_1 : statement_1; value_2, value3 : Statement_2; default : Statement_3; endcase
4.9. ALWAYS Statement
always @(sensitive_list) // or may be used -- sequential assignment
Remark
or must be used in sensitive list, and this is the only case it is used.
Any concurrent statement can be put into always statement whose sensitive list includes all inputs;
The simple concurrent assignment below
a = b;
can be written as
always @ (b) a = b;
Or a mux
O = (~E) ? `Z` : (S_2x==0) ? D0 : (S_2==1) ? D1 : (S_2x==2) ? D2 : D3;
can be written as
always ({D3, D2, D1, D0} or S_2x or E) O = (~E) ? `Z` : (S_2x==0) ? D0 : (S_2==1) ? D1 : (S_2x==2) ? D2 : D3;
Remark
We will not use always statement for concurrent assignments for clarity and sake of debugging.
4.10. Function
Function must execute in zero time and return only one signal for computational purpose. It therefore does not have timing process statement.
function func_name; -- input list as arguments begin -- function body end endfunction
For example,
function [3:0] Decoder; input E; input [1:0] S; begin Decoder = (~E) ? 0 : (S_2x==0) ? 1 : (S_2x==1) ? 2 : (S_2x==2) ? 4 : 8; End endfunction
4.11. Task
In contrast to function, task can return zero or many signals using IO list for modularisation purpose. It may include timing always statements.
task task_name; -- IO list begin -- procedure body end endtask
For example,
task ALU; input [7:0] A, B; input [1:0] OP; output [7:0] Z; out EQ, LT begin case OP 0 : Z = A + B; // add 1 : Z = A - B; // sub 2 : EQ = (A == B); 3 : LT = (A < B); end case; endtask;
4.12. Instantiation
To instantiate a module module_name, we have
module_name module_name_i ( .module_if_1(sig_1), // interface signal 1 .module _if_2(sig_2), .module _if_3(sig_3) );
4.13. Simulation Memory Model
module spram_5x8 ( addr, din, we, clk, en, dout); input [4:0] addr; input [7:0] din; input we, input clk; input en; output [7:0] dout; reg [7:0] MW, RAMa [0:31; always @ (posedge clk) begin if (we) begin RAMa[addr] <= din; MW <= din; end; dout <= RAMa[addr]; end if; end endmodule
4.14. Test Bench
Below is a complete Verilog code for a counter
module cntr (reset_n, clk, q); input reset_n, clk; output [3:0] q; reg [3:0] q; always @(negedge reset_n or posedge clk) if (~reset_n) q <= 0; else q <= (q==9) ? 0 : (q + 1); endmodule
Remark
Unlike VHDL, Verilog Output q can be read too.
Below is test bench for counter above
`timescale 1ns/1ps module cntr_t; //(); reg clk, reset_n; wire[3:0] q; cntr cntr_0 ( .clk(clk), .reset_n(reset_n), .q(q)); always #50 clk <= ~clk; // generate clk initial begin clk = 0; reset_n = 0; #200; reset_n = 1; // generate reset end endmodule