HDL Coding Concepts
Module 2.1: Structure of a Verilog Program
What is Verilog?
Verilog is a Hardware Description Language (HDL) used to design and describe digital systems through text-based coding. It
enables the design of digital systems such as ALUs, timers, counters, and microprocessors.
Key characteristics:
Designs described in HDL are independent of circuit fabrication technology
Fabrication technologies include: 90nm, 45nm, 180nm, 22nm, 11nm, and others
Knowledge of digital design and HDL is required in the frontend, focusing on logical design and verification
Verilog Programs
A Verilog program typically defines a module of the digital system. The entire code must be defined between module and
endmodule keywords.
Components included between module and endmodule :
Inputs and outputs
Port declarations with direction specification
Extra signals required for the design
Implementation code
Verilog Code Template
module [design_name] ([port_list]);
// List of input ports
input ...;
// List of output ports
output ...;
// Declaration of signals
wire ..., reg ...;
// Code for the module
...
endmodule
Module Definition
What is a module?
A module is a block of Verilog code that implements certain functionality. One module can be part of another module and can
be controlled by it. Modules communicate with each other through I/O ports.
Important note: Your file name should be the same as the module name.
Naming Rules
Module names and identifiers must follow specific naming conventions:
Must start with an alphabet (a-zA-Z) or underscore (_)
Remaining characters can be alphabets (a-zA-Z), numbers (0-9), underscores (_), or dollar symbols ($)
Maximum identifier length: 1024 characters
Ports
Ports are the input and output variables of a module. In programming languages, they are called variables, but in Verilog they
are referred to as ports.
Key points about ports:
They help in communication with other modules
Ports are the pins of a fabricated chip
Port types: input, output, and inout (when a port is both input and output with feedback)
Assign Statement
The assign statement is used for continuous assignments in combinational logic.
Syntax:
assign [net_expression] = [drive_strength] [delay] <expression or constant>
Example (Full Adder):
module full_adder (input a, input b, input c_in,
output c_out, output sum);
assign {c_out, sum} = a + b + c_in;
endmodule
Module 2.2: Different Coding Styles
Verilog Coding Styles
There are three levels of abstraction in Verilog:
1. Behavioral/Algorithmic - High-level system behavior
2. Data Flow - Using operators on operands
3. Gate Level - Using logic gates (primitives)
Behavioral and Data Flow Modeling
Characteristics:
System functionality is defined using functions
Gate-level structures are not used
Uses several operators that act on operands to produce desired results
More popular and practical due to sophisticated EDA tools
Mainly used to describe combinational circuits (arithmetic circuits)
Data Flow Modeling Examples:
assign y1 = i1 ^ i2;
assign y1 = (i1 & i2) | i3;
assign #20 y[0] = ~(ABAR & BBAR & EN);
Example (Full Adder using Half Adders):
module FA_using_HA(input a, b, cin,
output sum, carry);
wire S1, S2, S3;
HA_case1 d0 (a, b, S1, S2);
HA_case1 d1 (.A(S1), .B(cin), .S(sum), .Cout(S3));
assign carry = S2 | S3;
endmodule
Gate Level Modeling
Simple circuits obtained from Boolean expressions can be designed using gate-level modeling.
Supported gates (primitives):
Multiple-input gates: and, nand, or, nor, xor, xnor
Multiple-output gates: buf, not
Additional gates: tri-state and pull gates
Gate Level Examples:
and (Y1, x1, x2); // Y1 is AND of x1 and x2
or (Y2, x1, x2); // Y2 is OR of x1 and x2
xor (Y3, x1, x2); // Y3 is XOR of x1 and x2
Example (Half Adder using Gate Level):
module HA_gate (input a, b,
output sum, carry);
xor (sum, a, b);
and (carry, a, b);
endmodule
Module 2.3: Half Adder using Data Flow Modeling
Half Adder Functionality
The half adder is the most basic arithmetic operation unit. It is an adder without input carry.
Characteristics:
Two inputs generate sum and output carry
Truth table:
o 0 + 0 = 0 (sum=0, carry=0)
o 0 + 1 = 1 (sum=1, carry=0)
o 1 + 0 = 1 (sum=1, carry=0)
o 1 + 1 = 10 (sum=0, carry=1)
Boolean expressions:
Sum = Input1 XOR Input2
Carry = Input1 AND Input2
Verilog Code (Data Flow Modeling)
module half_adder_dataflow(input a, b,
output sum, carry);
assign sum = a ^ b;
assign carry = a & b;
endmodule
Alternative representation:
module half_adder_dataflow(input a, b,
output sum, carry);
assign {carry, sum} = a + b;
endmodule
Module 2.4: Half Adder using Gate Level Modeling
Gate Level Modeling Approach
To design using gate-level modeling, we solve truth tables to obtain Boolean expressions and then identify individual gates.
Half Adder Boolean expressions:
Sum = A XOR B
Carry = A AND B
Verilog Code (Gate Level)
module HA_gate (input a, b,
output sum, carry);
xor (sum, a, b);
and (carry, a, b);
endmodule
Module 2.5: Test Bench Writing in Verilog
What is a Test Bench?
A test bench is a module that generates stimuli for testing a design. It helps verify the functionality of a design module by
providing input values and observing output responses.
Purpose of test benches:
Generating stimuli for design verification
Testing specific test cases
Creating waveforms for simulation analysis
Test Bench Structure
Test bench files are created using design tools like Vivado and follow these characteristics:
Describe a new module similar to the design module
Instantiate the design module inside the test bench module
Initialize simulation with initial conditions
Define specific delays for multiple test cases
Inputs declared as reg, outputs as wire (opposite of design module)
Module 2.6: Data Types in Verilog
Verilog Data Types
Verilog supports two main categories of data types:
1. Nets - Wires that carry signals (do not store values except trireg)
2. Registers - Storage elements to hold values
Nets
Nets represent physical connections between structural entities. Common net types:
wire (most frequently used)
tri, wor, trior, wand, triand, trireg, tri0, tri1
supply0, supply1
Registers
Registers are variables that can store values. Used in procedural assignments when the signal is on the left-hand side.
Common register types:
reg - Most frequently used for logic description
integer - For variables and arithmetic calculations
time - For storing simulation times
real - For decimal point calculations
Logic Values in Verilog
Value Description
0 Logic zero or false
1 Logic one or true
x Unknown logic value
z High impedance (tri-state gate)
Integer and Real Data Types
Type Size Sign Description
byte 8 bits Signed -
shortint 16 bits Signed -
int 32 bits Signed -
longint 64 bits Signed -
real - - Floating-point numbers
Verilog Strings
Strings are stored in reg data type and can contain multiple characters. One byte of memory is needed for each character.
Examples:
reg [8
13:1] str = "Learn Verilog";reg [820:1] str = "Learn Verilog Concepts";
Module 2.7: Module Instantiation
What is Module Instantiation?
Module instantiation is the process of calling or using a module within another module (parent module). Complex systems are
designed by combining sub-systems using top-down or bottom-up approaches.
Key concepts:
Parent modules call and instantiate sub-modules
Used to connect repeated smaller modules
Enables hierarchical design methodology
Methods of Module Instantiation
1. Port Connection by Ordered List
Use ordered lists inside the parent module to instantiate lower modules. The exact order of ports in the lower module must be
known.
Syntax:
// Module instantiation
sub1 d0 (in1, in2, out1, out2);
sub2 d1 (S1, in3, out3);
Caution: This approach is inconvenient as the order of ports might change.
2. Port Connection by Name
A better approach that uses dot notation to indicate port mapping. The parent port is written after the dot, and the lower module
port name is inside the brackets.
Syntax:
// Module instantiation by name
sub1 d0 (.a(in1), .b(in2), .c(out1), .d(out2));
sub2 d1 (.P(S1), .Q(in3), .R(out3));
Unconnected Ports
Highly recommended to connect every port
Unconnected ports are assigned high impedance (z) by the simulator
Module 2.8: Full Adder using Module Instantiation
Full Adder Functionality
The full adder completes the addition operation. It takes the input carry along with two standard inputs, effectively adding three
numbers.
Characteristics:
Generates 1 bit of sum and 1 bit of carry out
Three inputs: A, B, and Cin
Two outputs: Sum and Cout
Boolean expressions:
Cout = (A + B) & Cin | (A & B)
Sum = A XOR B XOR Cin
Full Adder Implementation using Half Adders
A full adder can be identified as comprising:
Two half adders
One OR gate
Verilog Code:
module FA_using_HA(input A, B, Cin,
output Sum, Cout);
wire S1, C1, C2;
// First Half Adder
HA_case1 d0 (.a(A), .b(B), .s(S1), .cout(C1));
// Second Half Adder
HA_case1 d1 (.a(S1), .b(Cin), .s(Sum), .cout(C2));
// OR gate for final Cout
or d2 (Cout, C1, C2);
endmodule
Module 2.9: Verilog Assignments
Assignment in Verilog
Assignment means assigning values to a variable or wire. Every assignment has two sides:
RHS (Right-Hand Side) - Expression or value
LHS (Left-Hand Side) - Variable or net receiving the value
Both are related by an equal (=) or less-than-equal symbol (<=).
Forms of Assignments
1. Procedural Assignment
Used inside Verilog procedures: initial, always, task, and functions.
Characteristic: The variable keeps its value until the assigned value is changed.
Example:
reg [15:0] val_count;
initial begin
val_count = 0;
end
always @(negedge clk)
val_count++;
2. Continuous Assignment
Alternate form of procedural assignment, mainly used to assign values to vector and scalar nets.
Characteristic: Whenever the RHS changes, the LHS value is automatically updated.
Example:
wire pir;
assign pir = temp_sensor;
// pir will be updated whenever temp_sensor changes
3. Procedural Continuous Assignment
Allows continuous assignments on nets or variables. Two types:
a) Assign and Deassign:
reg Y1;
initial begin
assign Y1 = 1024;
#50 deassign Y1;
end
b) Force and Release:
initial begin
force V1 = x1 ^ 2;
force V2 = x1 + x2;
#100
release V1;
end
Module 2.10: Blocking and Non-Blocking Assignments
Types of Assignments
1. Blocking Assignments
Used with the = operator
Compiler executes statements one after another
Statements in a parallel block are not prevented by blocking assignments
Execution order is strictly sequential
Example:
reg [7:0] x1, x2, x3, y1, y2;
initial begin
x1 = 8'h10;
#20 x2 = 8'h20;
#20 x3 = 8'h30;
end
always @(x1)
y1 = 8'h40;
2. Non-Blocking Assignments
Specified using the <= symbol
Do not confuse <= with the relational operator
Statements can be scheduled without blocking execution of next statements
All assignments can be processed in parallel
Advantage: Better for modeling sequential logic and avoiding race conditions.
Example:
always @(negedge clk)
begin
x1 <= temp1;
x2 <= temp2;
x3 <= temp3;
end
Module 2.11: Conditional Statements
Conditional Statements (if-else)
Conditional statements are used to decide actions in different situations.
Purpose: The if statement describes statements to execute under certain conditions. If the condition is true, statements within
the if block execute. If false, statements within the else block execute.
if Statement
Syntax:
if (expression)
single_statement;
// For multiple statements
if (expression) begin
multiple_statements;
end
if-else Statement
Syntax:
if (expression) begin
multiple_statements;
end else begin
multiple_statements;
end
With else-if:
if (expression1)
statement1;
else if (expression2) begin
multiple_statements;
end else
statement3;
Example (2-to-1 Multiplexer):
module MUX_2x1 (input I0, I1, S,
output reg Y);
always @(I0, I1, S) begin
if (S)
Y = I1;
else
Y = I0;
end
endmodule
Module 2.12: Case Statement
Case Statements
Conditional statements used to decide actions in different situations.
Case statements are best suited when there are multiple conditions to test.
Advantage: Better choice than multiple if-else statements when testing multiple cases.
Syntax of Case Statement
case (list_of_sensitive_ports)
case_1: statement;
case_2: statement;
...
default: statement_for_default_case;
endcase
Example (Half Adder using Case):
module HA_case (input A, B,
output reg S, Cout);
always @(A or B) begin
case ({A, B})
2'b00: begin S = 0; Cout = 0; end
2'b01: begin S = 1; Cout = 0; end
2'b10: begin S = 1; Cout = 0; end
2'b11: begin S = 0; Cout = 1; end
endcase
end
endmodule
Module 2.13: Loops in Verilog
Types of Loops
1. Forever Loop
Executes statements infinitely.
Disadvantage: Cannot be synthesized into hardware.
Syntax:
forever begin
// Statements executed infinitely
end
Example (Generating Square Pulse):
initial begin
clk = 1'b0;
forever begin
#400 clk = ~clk;
end
end
2. Repeat Loop
Executes statements repeatedly for a fixed number of times.
Advantage: Unlike forever loop, repeat can be synthesized into hardware.
Syntax:
repeat (number_of_iterations) begin
// Statements to be executed
end
Example:
reg sig;
repeat (10) begin
@(negedge clk)
sig = ~sig;
end
3. While Loop
Executes statements while a condition is true. The loop may iterate infinitely if the condition is never true.
Limitation: Cannot synthesize while loop into hardware.
Syntax:
while (condition_to_be_true) begin
// Statements executed in while loop
end
Example:
integer i = 0;
while (i < 4) begin
$display("The value of i = %d", i);
i = i + 1;
end
4. For Loop
One of the most popular loops. Used to execute a block for a number of iterations.
Advantage: Can be synthesized into hardware.
Syntax:
for (start_condition; stop_condition; movement) begin
// Statements to be executed in for loop
end
Example (Sum of First 10 Integers):
integer i, sum = 0;
for (i = 0; i < 10; i = i + 1) begin
sum = sum + i;
end
Module 2.14: Operators in Verilog
What are Operators?
Operators perform operations on one or more operands within an expression. An expression combines operands with
appropriate operators to produce the desired functional expression.
Arithmetic Operators
Operator Description Example
+ Addition Y=a+b
- Subtraction Y=a-b
* Multiplication Y=a*b
/ Division Y=a/b
% Modulo (remainder) Y=a%b
** Exponentiation Y = a ** b
Relational Operators
Operator Description Example
< Less than a<b
> Greater than a>b
<= Less than or equal to a <= b
>= Greater than or equal to a >= b
Equality Operators
Operator Description Example
== Equal to (including x and z values) a == b
!= Not equal to (including x and z values) a != b
=== Exactly equal to (result can be unknown) a === b
!== Exactly not equal to a !== b
Logical Operators
Operator Description Example
&& Both sides true (AND) if (a == 1 && b == 1)
|| Either side true (OR) if (a == 1 || b == 1)
! Invert value if (!a == 1)
Bitwise Operators
Operator Description Example
& Bitwise AND Y=a&b
| Bitwise OR Y=a|b
^ Bitwise XOR Y=a^b
~^ or ^~ Bitwise XNOR Y = ~(a ^ b)
Shift Operators
Operator Description Example
<< Logical left shift A << 2
>> Logical right shift B >> 3
<<< Arithmetic left shift C <<< 3
>>> Arithmetic right shift D >>> 4