0% found this document useful (0 votes)
24 views113 pages

Verilog Comprehensive Book

Metavlsi interview part2 questions

Uploaded by

lurecartsuppourt
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views113 pages

Verilog Comprehensive Book

Metavlsi interview part2 questions

Uploaded by

lurecartsuppourt
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 113

Table of Contents

Preface

• About This Book


• Who Should Read This Book?
• How to Use This Book?
• Verilog Development Tools and Simulators

Chapter 1: Introduction to Verilog


1.1 What is Verilog?

1.2 History and Importance of Verilog in VLSI

1.3 HDL vs. Programming Languages (Verilog vs. C)

1.4 Verilog Design Flow (Simulation, Synthesis, Implementation)

1.5 Hardware Abstraction Levels in Verilog

1.6 Setting Up Verilog Simulation Tools

1.7 Writing Your First Verilog Program

1.8 Compilation and Simulation of Verilog Code

Chapter 2: Verilog Fundamentals


2.1 Verilog Module Structure

2.2 Ports and Data Types (wire, reg, integer)

2.3 Operators in Verilog (Arithmetic, Logical, Bitwise, Shift)

2.4 Procedural Statements (if-else, case, for, while)


2.5 Continuous Assignments (assign Statement)

2.6 Blocking vs. Non-Blocking Assignments

2.7 Initial and Always Blocks

2.8 System Tasks ($display, $monitor, $finish, $stop)

2.9 Writing Simple Combinational Circuits in Verilog

Chapter 3: Fundamentals of Verilog Design


3.1 Net and Register Data Types (wire, reg, tri-state)

3.2 Modeling Styles in Verilog (Structural, Dataflow, Behavioral)

3.3 Gate-Level Modeling Using Built-in Primitives

3.4 Design Examples (Half Adder, Full Adder)

3.5 Using Loops in Verilog (for, while, repeat, forever)

3.6 Counters and State Machines Using Loops

3.7 Writing Testbenches for Basic Circuits

Chapter 4: Modeling in Verilog


4.1 Structural Modeling in Verilog

4.2 Dataflow Modeling in Verilog

4.3 Behavioral Modeling in Verilog

4.4 Mixed-Level Modeling in Verilog

4.5 Best Practices for Writing Efficient Verilog Code


Chapter 5: Modules, Ports, and Hierarchy in Verilog
5.1 Creating Modules and Ports

5.2 Connecting Multiple Modules

5.3 Parameterized Modules (parameter for Configurable Designs)

5.4 Design Hierarchy and Reusability

5.5 Writing Modular and Scalable Verilog Code

Chapter 6: Testbenches and Simulation in Verilog


6.1 Writing a Testbench (initial, always, #delay)

6.2 Using $monitor, $display, $dumpfile, $dumpvars

6.3 Generating Clock and Reset in Testbenches

6.4 Writing Self-Checking Testbenches

6.5 Debugging Verilog Code Using Waveforms

Chapter 7: Combinational Logic Design in Verilog


7.1 Logic Gates Implementation

7.2 Multiplexers and Demultiplexers

7.3 Encoders and Decoders

7.4 Arithmetic Logic Unit (ALU) Design


7.5 Designing Combinational Circuits Using Verilog

Chapter 8: Sequential Logic Design in Verilog


8.1 Flip-Flops and Latches (D, T, JK, SR)

8.2 Registers and Counters (Shift Registers, Up/Down Counters)

8.3 Finite State Machines (FSMs)

8.4 Moore vs. Mealy FSMs

8.5 State Machine Applications in Digital Design

Chapter 9: Memory Design in Verilog


9.1 Types of Memory (RAM, ROM, SRAM, DRAM)

9.2 Synchronous vs. Asynchronous Memory

9.3 Memory Design Using Verilog

9.4 FIFO and LIFO Memory Implementation

9.5 Writing Testbenches for Memory Modules

Chapter 10: Digital Communication Interfaces in Verilog


10.1 Introduction to Serial and Parallel Communication

10.2 UART (Universal Asynchronous Receiver-Transmitter)

10.3 SPI (Serial Peripheral Interface)


10.4 I2C (Inter-Integrated Circuit Protocol)

10.5 Writing Testbenches for UART, SPI, and I2C

10.6 Best Practices for Digital Communication Design

About This Book


Verilog is a powerful Hardware Description Language (HDL) that enables engineers to
design, simulate, and implement digital circuits. This book provides a comprehensive and
structured approach to learning Verilog, starting from basic concepts and progressing to
advanced digital design techniques.

Whether you are a student, FPGA/ASIC engineer, or VLSI enthusiast, this book will help you
understand and apply Verilog effectively in real-world projects.

Who Should Read This Book?


This book is designed for:
Beginners who want to learn Verilog from scratch.
Electronics and VLSI engineers working on FPGA/ASIC design.
Students and researchers exploring digital design techniques.
Professionals looking to enhance their hardware verification skills.

No prior knowledge of Verilog is required, but basic knowledge of digital logic design
(gates, flip-flops, counters, etc.) will be helpful.

How to Use This Book?


This book follows a progressive learning approach, where each chapter builds on the
previous one.
• Start with Chapters 1–4 to understand the fundamentals of Verilog.
• Chapters 5–7 focus on combinational logic design, including MUXes, ALUs, and
state machines.
• Chapters 8–10 cover sequential logic, memory design, and communication
protocols.
• Each chapter includes:
Concepts explained step-by-step
Verilog code examples for practical understanding
Testbenches for verification
Industry best practices
Review questions for self-assessment

Tip: You can follow along by coding and simulating Verilog designs using tools like
ModelSim, Xilinx Vivado, or Intel Quartus.

Verilog Development Tools and Simulators


To practice Verilog, you need an HDL simulator and synthesis tool. Below are some
commonly used tools:

Tool Use Case Download Link


ModelSim Simulation and debugging Intel ModelSim
Xilinx Vivado FPGA design and synthesis Xilinx Vivado
Intel Quartus FPGA design and synthesis Intel Quartus
Icarus Verilog Open-source Verilog Icarus Verilog
simulator
GTKWave Viewing simulation GTKWave
waveforms

Tip: If you're new to Verilog, start with Icarus Verilog + GTKWave (free and easy to use).

Why Learn Verilog?


Industry Standard for Digital Design → Used in FPGA/ASIC development.
Enables Hardware Simulation → Allows testing before fabrication.
Essential for VLSI Engineers → Forms the backbone of modern chip design.
Bridges Software and Hardware → Ideal for engineers transitioning to embedded and
hardware domains.

This book will empower you to design complex digital systems and prepare you for
FPGA/ASIC industry roles.

Chapter 1: Introduction to Verilog


1.1 What is Verilog?
Verilog is a Hardware Description Language (HDL) used for designing and modeling
digital circuits. It allows engineers to describe hardware behavior at various abstraction
levels, from simple gates to complex systems like processors. Verilog is widely used in
FPGA and ASIC design.

Key Features of Verilog

I. Supports different modeling styles (Structural, Behavioral, Dataflow)


II. Used for simulation and synthesis of digital circuits
III. Similar to programming languages like C but focuses on hardware modeling

1.2 History and Evolution of Verilog


I. Developed in 1984 by Phil Moorby and Prabhu Goel at Gateway Design Automation.
II. Acquired by Cadence Design Systems in 1990.
III. Standardized as IEEE 1364 in 1995 and later improved in 2001 and 2005.
IV. Replaced by SystemVerilog (IEEE 1800), but still widely used in industry.
1.3 Importance of Verilog in Hardware Design
Verilog enables efficient hardware design through:
I. Fast Prototyping – Quickly test designs using simulation tools.
II. RTL Design – Used in Register Transfer Level (RTL) abstraction.
III. FPGA & ASIC Implementation – Used in chip fabrication.
IV. Verification & Debugging – Helps identify design flaws before manufacturing.

1.4 Basics of Digital Design


Before diving into Verilog, it's important to understand basic digital circuit concepts:

I. Logic Gates (AND, OR, NOT, NAND, NOR, XOR)


II. Combinational Circuits (Multiplexers, Decoders, Encoders)
III. Sequential Circuits (Flip-Flops, Registers, Counters)

Example: Implementing a Basic AND Gate in Verilog

module and_gate(
input A,
input B,
output Y
)
assign Y = A & B;

endmodule

Testbench for AND Gate

module testbench;
reg A, B;
wire Y;

and_gate uut (A, B, Y);

initial begin
$monitor("A = %b, B = %b, Y = %b", A, B, Y);
A = 0; B = 0; #10;
A = 0; B = 1; #10;
A = 1; B = 0; #10;
A = 1; B = 1; #10;

$finish;
end
endmodule

Explanation

I. assign is used for continuous assignment in combinational logic.


II. #10; represents a 10-time unit delay between test cases.
III. $monitor prints values whenever they change.

Chapter 1 Review Questions


1. What is Verilog, and why is it used in digital design?
2. How does Verilog differ from traditional programming languages like C?
3. List the different abstraction levels in Verilog.
4. What are the three main modeling styles in Verilog?
5. When was Verilog standardized by IEEE?
6. What is the difference between simulation and synthesis in Verilog?
7. Define combinational and sequential circuits with examples.
8. Explain the role of assign in Verilog coding.
9. Write Verilog code for an OR gate using assign.
10. What is a testbench, and why is it used in Verilog?
11. What is the purpose of $monitor in a testbench?
12. How does an initial block differ from an always block?
13. Describe how digital circuits are used in real-world applications.
14. What are the basic logic gates used in digital design?
15. Write Verilog code for a 4-input AND gate.
16. What is RTL design, and why is it important in hardware design?
17. What is the significance of IEEE 1364?
18. Explain the role of flip-flops in digital circuits.
19. Differentiate between blocking and non-blocking assignments.
20. What is the significance of FPGA and ASIC in modern electronics?
Chapter 2: Getting Started with Verilog
2.1 Writing Your First Verilog Code
Before writing Verilog code, it is essential to understand its basic structure. Every Verilog
program consists of:

I. Module Declaration – Defines the module name and its inputs/outputs.


II. Port Definitions – Specifies input and output signals.
III. Dataflow or Behavioral Code – Implements logic inside the module.
IV. Endmodule Statement – Marks the end of a module.

Example: Simple AND Gate

module and_gate(
input A,
input B,
output Y
);
assign Y = A & B;
endmodule

• module defines the module name (and_gate).


• input and output define ports.
• assign is used for combinational logic.
• endmodule marks the end of the module.

2.2 Syntax and Structure of a Verilog Module


A Verilog module follows this syntax:

module <module_name> (input/output ports);


// Internal signals (if needed)
// Combinational or Sequential logic

endmodule

Example: 2-to-1 Multiplexer (MUX)

module mux2to1 (
input A,
input B,
input sel,
output Y
);
assign Y = sel ? B : A; // If sel=1, Y=B; else, Y=A
endmodule

Testbench for 2:1 MUX

module testbench;
reg A, B, sel;
wire Y;

mux2to1 uut (A, B, sel, Y);

initial begin
$monitor("A = %b, B = %b, sel = %b, Y = %b", A, B, sel, Y);

A = 0; B = 1; sel = 0; #10;
A = 0; B = 1; sel = 1; #10;
A = 1; B = 0; sel = 0; #10;
A = 1; B = 0; sel = 1; #10;

$finish;
end
endmodule
2.3 Simulation vs. Synthesis

I. Simulation

• Used to test and verify the behavior of the design before hardware implementation.
• Involves testbenches and waveform analysis.
• Does not consider hardware constraints.

II. Synthesis

• Converts Verilog code into a hardware circuit (gates, flip-flops, etc.).


• Used to generate netlists for FPGA and ASIC designs.
• Requires synthesizable coding techniques.

Example: Behavioral vs. Synthesizable Code

Behavioral code is valid for simulation but may not be synthesizable:

always @(A or B)
Y = A & B; // May work in simulation but is not always
synthesizable

For synthesis, we must ensure hardware representation:

assign Y = A & B; // Synthesis-friendly combinational logic

More Examples

Example 1: Full Adder in Verilog

A full adder adds two bits along with a carry-in and produces a sum and carry-out.

module full_adder (
input A,
input B,
input Cin,
output Sum,
output Cout
);
assign Sum = A ^ B ^ Cin;
assign Cout = (A & B) | (B & Cin) | (A & Cin);
endmodule

Testbench for Full Adder

module testbench;
reg A, B, Cin;
wire Sum, Cout;

full_adder uut (A, B, Cin, Sum, Cout);

initial begin
$monitor("A = %b, B = %b, Cin = %b | Sum = %b, Cout = %b", A,
B, Cin, Sum, Cout);

A = 0; B = 0; Cin = 0; #10;
A = 0; B = 0; Cin = 1; #10;
A = 0; B = 1; Cin = 0; #10;
A = 0; B = 1; Cin = 1; #10;
A = 1; B = 0; Cin = 0; #10;
A = 1; B = 0; Cin = 1; #10;
A = 1; B = 1; Cin = 0; #10;
A = 1; B = 1; Cin = 1; #10;

$finish;
end
endmodule

Example 2: D Flip-Flop (Sequential Logic Example)

module d_flip_flop (
input D,
input clk,
output reg Q
);
always @(posedge clk)
Q <= D;
endmodule

Testbench for D Flip-Flop

module testbench;
reg D, clk;
wire Q;

d_flip_flop uut (D, clk, Q);

always #5 clk = ~clk; // Clock toggles every 5 time units

initial begin
clk = 0; D = 0;
#10; D = 1;
#10; D = 0;
#10; D = 1;
#10; D = 0;
#10;
$finish;
end
endmodule

Chapter 2 Review Questions


1. What is the structure of a Verilog module?
2. What are the differences between simulation and synthesis?
3. How do assign statements work in Verilog?
4. Write Verilog code for a 2-input OR gate.
5. How does an always block differ from assign statements?
6. Explain the purpose of testbenches in Verilog.
7. What is the role of $monitor in a testbench?
8. How does a MUX (Multiplexer) function in Verilog?
9. What does the posedge keyword signify?
10. Write a testbench for a 4-input AND gate.
11. What is the difference between reg and wire in Verilog?
12. Implement a 4:1 MUX using Verilog.
13. What happens when you use assign inside an always block?
14. Explain blocking vs. non-blocking assignments.
15. What is the significance of #10 delay in testbenches?
16. How do you generate a clock signal in Verilog?
17. Write a Verilog code for a JK Flip-Flop.
18. What are the common mistakes made while writing synthesizable Verilog?
19. How does the initial block work in Verilog?
20. Implement a 2-bit binary counter using Verilog.

Chapter 3: Fundamentals of Verilog


3.1 Data Types and Operators in Verilog
Verilog has a variety of data types, which are used to describe different hardware
elements. Understanding these types is crucial for designing both combinational and
sequential circuits.

I. Data Types in Verilog

1. Nets (wire)

• Represents physical connections between components.


• Cannot hold or store a value; they must be continuously driven by some other
signal.
• Used in combinational circuits and for connecting different modules.
• Typically assigned values using assign statements.

Example: Using wire in a Combinational Circuit

module and_gate (
input A,
input B,
output wire Y
);
assign Y = A & B; // Continuous assignment using a wire
endmodule

Key takeaway: wire is used for signals that depend on other values and are constantly
updated.

2. Registers (reg)

• Represents a storage element (like a D flip-flop or latch).


• Can hold a value across clock cycles.
• Used in sequential circuits and controlled by always blocks.
• Must be updated inside a procedural block (always or initial).

Example: Using reg in Sequential Logic

module d_flip_flop (
input D,
input clk,
output reg Q
);
always @(posedge clk) // Triggered on the rising edge of the
clock
Q <= D; // Non-blocking assignment, stores the value of D
endmodule

Key takeaway: reg holds a value and updates only when an always block executes.

3. Difference Between wire and reg

Feature wire (Net) reg (Register)

Storage Does not store a value Stores a value until updated


Assignment Assigned using assign (continuous Assigned inside always block
assignment) (procedural assignment)
Usage Used in combinational logic Used in sequential logic

Example assign Y = A & B; always @(posedge clk) Q <=


D;

Synthesis Represents a physical wire Represents a flip-flop or latch

4. Integer (integer)

• Used for loop counters and calculations.


• Cannot be synthesized into hardware but is useful for testbenches.

Example: Using an Integer for Looping

integer i;
initial begin
for (i = 0; i < 10; i = i + 1)
$display("i = %d", i);
end

Key takeaway: integer is mainly for testbenches and cannot be synthesized into actual
hardware.
5. Parameter (parameter)

• A constant value that cannot change during simulation.


• Used for making code reusable and flexible.

Example: Parameterized Adder

module adder #(parameter WIDTH = 4) (


input [WIDTH-1:0] A,
input [WIDTH-1:0] B,
output [WIDTH-1:0] Sum
);
assign Sum = A + B;
endmodule

Key takeaway: parameter allows customization of module behavior without modifying


the code.

II. Operators in Verilog

1. Arithmetic Operators

Operator Description Example


+ Addition A + B
- Subtraction A - B
* Multiplication A * B
/ Division A / B
% Modulus A % B

Example: Arithmetic Operations

assign Sum = A + B;
assign Diff = A - B;
assign Product = A * B;
assign Remainder = A % B;

Key takeaway: Arithmetic operators are used for mathematical calculations on signals.

2. Bitwise Operators

Operator Description Example


& AND A & B
| OR A| B
^ XOR A ^ B
~ NOT ~A

Example: Bitwise Operations

assign Out1 = A & B; // AND


assign Out2 = A | B; // OR
assign Out3 = A ^ B; // XOR
assign Out4 = ~A; // NOT

Key takeaway: Bitwise operators perform operations on each bit of a signal.

3. Logical Operators

Operator Description Example


&& Logical AND A && B
|| Logical OR A || B
! Logical NOT !A

Example: Logical Operations

assign Result = (A > B) && (B < C); // Logical AND

Key takeaway: Logical operators evaluate true (1) or false (0) based on conditions.
4. Shift Operators

Operator Description Example


<< Logical Left Shift A << 2
>> Right Right Shift A >> 2
<<< Arithmetic Left Shift A<<<2

>>> Arithmetic Right Shift A>>>2

Example: Using Shift Operators

assign ShiftLeft = A << 1; // Multiply by 2


assign ShiftRight = A >> 1; // Divide by 2

Key takeaway: Shift operators move bits left or right, effectively multiplying or dividing by
powers of two.

5. Reduction Operators

Reduction operators perform bitwise operations on all bits of a


vector.
Operator Description Example
& AND reduction &A
| | |A
^ XOR reduction ^A

Example: Using Reduction Operators

assign and_reduce = &A; // Logical AND of all bits in A


assign or_reduce = |A; // Logical OR of all bits in A
assign xor_reduce = ^A; // Logical XOR of all bits in A

Key takeaway: Reduction operators compute a single value from all bits of a signal.
3.2 Constants and Parameters in Verilog

I. Constants in Verilog

Verilog allows the use of constants, which are values that do not change during simulation.
These are mainly used for defining fixed values like clock periods, bus widths, and
predefined logic values.

1. Defining Constants Using parameter

• A parameter is a constant that helps in writing reusable and configurable code.


• Parameters are assigned values at the time of module instantiation.

Example: Defining a Constant Width for a 4-bit Adder

module adder #(parameter WIDTH = 4) (


input [WIDTH-1:0] A,
input [WIDTH-1:0] B,
output [WIDTH-1:0] Sum
);
assign Sum = A + B;
endmodule

Key takeaway: The WIDTH parameter allows this module to be modified for different bit
sizes without changing the internal logic.

2. Overriding a Parameter at Instantiation

adder #(.WIDTH(8)) adder_inst (


.A(A),
.B(B),
.Sum(Sum)
);

• Here, we override the default WIDTH = 4 with WIDTH = 8 at instantiation.


II. Local Parameters (localparam)

• localparam is similar to parameter, but it cannot be overridden at instantiation.

Example: Using localparam

module example;
localparam SIZE = 8; // Fixed constant value
reg [SIZE-1:0] data;
endmodule

Key takeaway: Use localparam when you want constants that cannot be modified
externally.

III. Defining Constants Using define

• The define directive allows macro-style constant definitions.


• Used for global constants across multiple files.

Example: Using define

`define DATA_WIDTH 8

module memory;
reg [`DATA_WIDTH-1:0] mem_data;
endmodule

Key takeaway: Use define for global constants, but parameter is preferred for better
module flexibility.

3.3 Procedural vs. Continuous Assignments


Verilog has two types of assignment methods:
Type Description Example
Continuous Used with wire, represents assign Y = A & B;
Assignment combinational logic
Procedural Used with reg, updates inside always always @(posedge
Assignment block clk) Q <= D;

I. Continuous Assignment (assign)

• Works outside of always blocks.


• Used for combinational logic.
• Only works with wire data type.

Example: Using assign in a Combinational Circuit

module or_gate (
input A,
input B,
output wire Y
);
assign Y = A | B; // Continuous assignment
endmodule

Key takeaway: Continuous assignments are for simple logic connections that change
dynamically.

II. Procedural Assignment (always block)

• Used in sequential circuits and complex logic.


• Works inside always blocks.
• Requires reg data type.

Example: Using always in a Sequential Circuit

module d_flip_flop (
input D,
input clk,
output reg Q
);
always @(posedge clk) // Triggered on clock edge
Q <= D; // Non-blocking assignment
endmodule

Key takeaway: Procedural assignments only update when the triggering condition
occurs.

III. Blocking vs. Non-Blocking Assignments

Assignment Sym Used in Execution


Type bol
Blocking = Combinational & Sequential Executes immediately, line by line
Logic
Non- <= Sequential Logic Executes in parallel (suitable for
Blocking flip-flops)

Example: Difference Between = and <=

always @(posedge clk) begin


A = B; // Blocking assignment
C <= D; // Non-blocking assignment
end

Key takeaway:

• Use = for combinational logic (inside always @(*)).


• Use <= for sequential logic (inside always @(posedge clk)).

3.4 Conditional Statements in Verilog


Conditional statements are used to implement decision-making logic.
I. if-else Statement

• Used for making decisions inside an always block.


• Similar to C programming language.

Example: Implementing a 2:1 MUX Using if-else

module mux2to1 (
input A,
input B,
input sel,
output reg Y
);
always @(*) begin
if (sel)
Y = B;
else
Y = A;
end
endmodule

Key takeaway: Use if-else when selecting between multiple conditions.

II. case Statement

• Used for multiplexer-like structures.


• More efficient than multiple if-else statements.

Example: 4:1 Multiplexer Using case

module mux4to1 (
input [1:0] sel,
input A, B, C, D,
output reg Y
);
always @(*) begin
case (sel)
2'b00: Y = A;
2'b01: Y = B;
2'b10: Y = C;
2'b11: Y = D;
default: Y = 0;
endcase
end
endmodule

Key takeaway: Use case for multi-way branching instead of multiple if-else
statements.

III. casez and casex Statements

• casez treats z (high impedance) as a wildcard.


• casex treats x (unknown) as a wildcard.

Example: casez for a Priority Encoder

module priority_encoder (
input [3:0] in,
output reg [1:0] out
);
always @(*) begin
casez (in)
4'b1???: out = 2'b11;
4'b01??: out = 2'b10;
4'b001?: out = 2'b01;
4'b0001: out = 2'b00;
default: out = 2'bxx;
endcase
end
endmodule

Key takeaway: casez allows don't-care values, making priority encoding easier.
3.5 Loops in Verilog
Loops in Verilog are used primarily in testbenches and behavioral modeling to execute
repetitive tasks efficiently. However, loops in Verilog behave differently compared to
software programming languages like C because they are typically unrolled at compile
time for synthesis.

Types of Loops in Verilog

Loop Type Description Usage


for Executes a block of code for a defined Used for counters and iterative
number of iterations logic
while Executes a block of code while a Used in testbenches for waiting
condition is true conditions
repeat Repeats a block of code a fixed Used for delays and waveform
number of times generation
forever Runs indefinitely Used for clock generation

I. for Loop

• A for loop is the most commonly used loop in Verilog.


• It requires an initialization, condition, and increment statement.
• Typically used in testbenches and for generating repetitive signals in simulation.

Example: Using a for Loop in a Testbench

module testbench;
integer i;
initial begin
for (i = 0; i < 8; i = i + 1) begin
$display("Iteration: %d", i);
end
end
endmodule
Key takeaway: for loops cannot be synthesized for hardware unless used for unrolling
during synthesis.

Example: for Loop for Register Initialization

module register_array;
reg [7:0] registers [0:15]; // 16 registers of 8-bit width
integer i;

initial begin
for (i = 0; i < 16; i = i + 1)
registers[i] = 8'hFF; // Initialize all registers to 0xFF
end
endmodule

Key takeaway: for loops are useful for initializing large arrays in testbenches.

II. while Loop

• Runs while the condition is true.


• Unlike for, it does not require an increment statement.
• Used in testbenches for waiting on conditions.

Example: Using while Loop in a Testbench

module testbench;
integer count;
initial begin
count = 0;
while (count < 5) begin
$display("Count = %d", count);
count = count + 1;
end
end
endmodule
Key takeaway: Be cautious when using while loops; they may never terminate if the
condition is always true.

III. repeat Loop

• Executes a block of code a fixed number of times.


• Used when the number of iterations is predetermined.

Example: Using repeat for a Pulse Signal

module pulse_generator;
reg clk;

initial begin
repeat (10) begin
#5 clk = ~clk; // Toggle clock every 5 time units
end
end
endmodule

Key takeaway: repeat is useful when you know the exact number of iterations required.

IV. forever Loop

• Runs indefinitely until stopped by an external condition.


• Used in clock generation for testbenches.

Example: Clock Generation Using forever

module clock_gen;
reg clk;

initial begin
clk = 0;
forever #5 clk = ~clk; // Toggle clock every 5 time units
end
endmodule

Key takeaway: forever loops must have an external stopping condition; otherwise, they
will run infinitely.

Practical Example: Counter Using a for Loop

A 4-bit up-counter using a for loop inside a sequential always block.

module up_counter (
input clk,
input reset,
output reg [3:0] count
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b0000;
else
count <= count + 1;
end
endmodule

Key takeaway: A for loop is not needed in hardware description here because hardware
updates naturally on clock edges.

Comparison of Loops in Verilog

Loop Type Synthesizable? Common Use Case


for Partially (if used for unrolling) Testbenches, array initialization
while No Testbenches, waiting conditions
repeat No Testbenches, waveform
generation
forever No Clock generation in testbenches

Common Mistakes When Using Loops in Verilog

Mistake Issue Solution


Infinite while loop Causes simulation to hang Ensure an exit condition
Using for loops in Hardware cannot iterate Use loop unrolling for
synthesis dynamically synthesis
Forgetting delays inside Can lead to simulation Always add #time in
forever hanging loops

3.6 Counters and State Machines Using Loops


Counters and state machines are widely used in digital circuits for timing operations,
control sequencing, and implementing complex digital logic. This section covers counter
designs and finite state machines (FSMs) using loops and conditional statements.

I. Counters in Verilog
Counters are sequential circuits that increment or decrement values based on clock
pulses.

1. Simple 4-bit Up Counter

A 4-bit up counter increments its value on every positive clock edge.

Code: 4-bit Up Counter

module up_counter (
input clk,
input reset,
output reg [3:0] count
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b0000; // Reset the counter to 0
else
count <= count + 1; // Increment count on each clock cycle
end
endmodule

Key Takeaways:

• The always @(posedge clk or posedge reset) block triggers on the rising
edge of the clock.
• If reset is high, the counter resets to 0000.
• Otherwise, it increments by 1 on every clock cycle.

2. 4-bit Down Counter

A down counter decrements its value on every clock cycle.

Code: 4-bit Down Counter

module down_counter (
input clk,
input reset,
output reg [3:0] count
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b1111; // Reset to max value (15)
else
count <= count - 1; // Decrement count
end
endmodule

Key Takeaway: Instead of adding 1, we subtract 1 on each clock cycle.


3. Modulo-N Counter (Using a Loop)

A Modulo-N counter counts from 0 to N-1 and then resets.

Code: Mod-10 Counter (Counts 0 to 9)

module mod10_counter (
input clk,
input reset,
output reg [3:0] count
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b0000;
else if (count == 9)
count <= 4'b0000; // Reset when count reaches 9
else
count <= count + 1;
end
endmodule

Key Takeaway: This counter rolls over when it reaches 9.

4. Counter Using a for Loop

Instead of writing a long sequence of if-else conditions, we can use a for loop.

Code: Counter with Loop for Initialization

module counter_with_loop;
reg [3:0] count;
integer i;

initial begin
for (i = 0; i < 10; i = i + 1) begin
count = i;
#10;
$display("Count: %d", count);
end
end
endmodule

Key Takeaway: This for loop simulates a counter, but for synthesis, we should use an
always block.

II. Finite State Machines (FSMs) in Verilog


State machines are used for controlling sequential logic in digital design. They transition
between different states based on inputs.

1. Types of FSMs

FSMs can be classified into two types:

FSM Type Description


Moore FSM Output depends only on the current state
Mealy FSM Output depends on both current state and
inputs

2. Example: 3-State Moore FSM

This FSM has three states: S0 → S1 → S2 → S0.

State Encoding

State Binary Encoding


S0 2'b00
S1 2'b01
S2 2'b10
Code: 3-State Moore FSM

module moore_fsm (
input clk,
input reset,
output reg [1:0] state
);
parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10;

always @(posedge clk or posedge reset) begin


if (reset)
state <= S0; // Reset to initial state
else begin
case (state)
S0: state <= S1;
S1: state <= S2;
S2: state <= S0;
default: state <= S0;
endcase
end
end
endmodule

Key Takeaways:

• The FSM cycles through three states: S0 → S1 → S2 → S0.


• The case statement is used to define state transitions.
• The FSM resets to S0 when reset = 1.

3. Example: Mealy FSM for Sequence Detector

A Mealy FSM generates an output based on both state and input.

Code: Detects "101" Sequence

module mealy_fsm (
input clk,
input reset,
input in,
output reg out
);
typedef enum reg [1:0] {S0, S1, S2} state_t;
state_t state;

always @(posedge clk or posedge reset) begin


if (reset)
state <= S0;
else begin
case (state)
S0: state <= (in) ? S1 : S0;
S1: state <= (in) ? S1 : S2;
S2: begin
if (in) begin
state <= S1;
out <= 1; // Sequence detected
end
else begin
state <= S0;
out <= 0;
end
end
default: state <= S0;
endcase
end
end
endmodule

Key Takeaways:

• Detects the input sequence 101 and sets out = 1.


• Unlike a Moore FSM, the output is dependent on input.
III. Comparing Moore and Mealy FSMs

Feature Moore FSM Mealy FSM


Output Depends Current State Current State + Inputs
on
Timing More stable (delayed Faster response
response)
Design Complexity Easier More complex

III. Best Practices for Writing Efficient Verilog Code


• Use reg for storage elements and wire for combinational logic.
• Use non-blocking (<=) assignments inside sequential blocks.
• Always include a default case in case statements.
• Use parameterized modules to improve reusability.
• Avoid latches by initializing all if-else conditions properly.

Chapter 3 Review Questions


1. What is the difference between an up counter and a down counter?
2. Write Verilog code for an 8-bit Mod-16 counter.
3. How does a for loop differ from a while loop in Verilog?
4. Write a Moore FSM for a traffic light controller.
5. What is the purpose of parameter in Verilog?
6. Differentiate between Moore FSM and Mealy FSM.
7. Write Verilog code for a 3-bit binary counter using an always block.
8. What is the significance of using <= in sequential circuits?
9. How does an FSM transition between states?
10. Write a testbench for a 4-bit up/down counter.
Chapter 4: Modeling in Verilog
Verilog allows different levels of abstraction for designing digital circuits. The three primary
modeling styles in Verilog are:

I. Structural Modeling – Describes a circuit as an interconnection of logic gates and


components.
II. Dataflow Modeling – Uses Boolean expressions and assign statements to describe the
circuit.
III. Behavioral Modeling – Uses procedural constructs like always blocks, if-else, and
case statements to describe the behavior of a circuit.
IV. Mixed-Level Modeling – A combination of all three modeling styles in a single design.

4.1 Structural Modeling in Verilog

I. What is Structural Modeling?

• Represents a circuit as a collection of interconnected components (gates,


multiplexers, adders, etc.).
• Similar to designing a circuit using a schematic diagram in hardware.
• Uses gate-level primitives (and, or, xor, etc.) and module instantiation for
hierarchy.
II. Basic Logic Gate Example (AND, OR, XOR)

1. Implementing an AND Gate Using Structural Modeling

module and_gate (
input A,
input B,
output Y
);
and U1 (Y, A, B); // Instantiating an AND gate
endmodule

Key Takeaway: The and keyword represents a built-in Verilog AND gate primitive.

III. Implementing a Half Adder Using Structural Modeling

A Half Adder performs binary addition of two inputs (A and B) and produces:

• Sum = A ⊕ B (XOR operation)


• Carry = A & B (AND operation)

Code: Half Adder Using Structural Modeling

module half_adder (
input A,
input B,
output Sum,
output Carry
);
xor G1 (Sum, A, B); // XOR gate for Sum
and G2 (Carry, A, B); // AND gate for Carry
endmodule

Key Takeaway: Structural modeling directly connects gates, mimicking real-world


circuits.
IV. Implementing a Full Adder Using Structural Modeling

A Full Adder extends a Half Adder by adding a Carry-in (Cin).

Logic Equations for a Full Adder

• Sum = A ⊕ B ⊕ Cin
• Carry-out = (A & B) | (B & Cin) | (A & Cin)

Code: Full Adder Using Half Adders

module full_adder (
input A,
input B,
input Cin,
output Sum,
output Cout
);
wire S1, C1, C2;

half_adder HA1 (.A(A), .B(B), .Sum(S1), .Carry(C1));


half_adder HA2 (.A(S1), .B(Cin), .Sum(Sum), .Carry(C2));

or G1 (Cout, C1, C2); // OR gate for Carry-out


endmodule

Key Takeaway: Module instantiation allows reusing smaller building blocks in


structural design.

V. Structural Modeling of a 4-bit Ripple Carry Adder

A Ripple Carry Adder (RCA) connects multiple full adders in sequence to add multi-bit
numbers.
Code: 4-bit Ripple Carry Adder

module ripple_carry_adder (
input [3:0] A,
input [3:0] B,
input Cin,
output [3:0] Sum,
output Cout
);
wire C1, C2, C3;

full_adder FA1
(.A(A[0]), .B(B[0]), .Cin(Cin), .Sum(Sum[0]), .Cout(C1));
full_adder FA2
(.A(A[1]), .B(B[1]), .Cin(C1), .Sum(Sum[1]), .Cout(C2));
full_adder FA3
(.A(A[2]), .B(B[2]), .Cin(C2), .Sum(Sum[2]), .Cout(C3));
full_adder FA4
(.A(A[3]), .B(B[3]), .Cin(C3), .Sum(Sum[3]), .Cout(Cout));
endmodule

Key Takeaway: Hierarchy in structural modeling helps build complex circuits using small
reusable modules.

VI. Advantages and Disadvantages of Structural Modeling

Advantages Disadvantages
Closely resembles hardware design Difficult to write for large designs
Reusable components simplify testing Debugging is complex compared to
behavioral modeling
Accurate timing since it's gate-level Slower simulation than behavioral
modeling

VII. When to Use Structural Modeling?

• When designing low-level gate-based circuits.


• When working on FPGA and ASIC synthesis.
• When implementing custom logic architectures.

4.2 Dataflow Modeling in Verilog

I. What is Dataflow Modeling?

Dataflow modeling describes circuits using Boolean equations and continuous


assignments (assign). This abstraction level focuses on how data flows between inputs
and outputs rather than individual gates.

• Uses assign statements instead of gate-level components.


• Provides a more compact and readable representation than structural modeling.
• Typically used for combinational logic circuits.

II. Basic Syntax of Dataflow Modeling

assign <output> = <expression>;

• assign is a continuous assignment (executes whenever inputs change).


• <expression> can contain bitwise, logical, arithmetic, and shift operators.

III. Implementing Logic Gates Using Dataflow Modeling

1. AND, OR, XOR, and NOT Gate

module logic_gates (
input A,
input B,
output AND_out,
output OR_out,
output XOR_out,
output NOT_A
);
assign AND_out = A & B; // AND operation
assign OR_out = A | B; // OR operation
assign XOR_out = A ^ B; // XOR operation
assign NOT_A = ~A; // NOT operation
endmodule

Key Takeaway: Dataflow modeling eliminates the need for explicit gate instantiations.

IV. Half Adder Using Dataflow Modeling

A half adder performs binary addition and produces a Sum and a Carry output.

Logic Equations

• Sum = A ⊕ B
• Carry = A & B

Code: Half Adder Using Dataflow

module half_adder (
input A,
input B,
output Sum,
output Carry
);
assign Sum = A ^ B; // XOR for Sum
assign Carry = A & B; // AND for Carry
endmodule

Key Takeaway: Dataflow modeling simplifies arithmetic circuits by using Boolean


expressions.

V. Full Adder Using Dataflow Modeling

A full adder adds two bits and a carry-in (Cin), producing a Sum and Carry-out (Cout).
Logic Equations

• Sum = A ⊕ B ⊕ Cin
• Cout = (A & B) | (B & Cin) | (A & Cin)

Code: Full Adder Using Dataflow

module full_adder (
input A,
input B,
input Cin,
output Sum,
output Cout
);
assign Sum = A ^ B ^ Cin;
assign Cout = (A & B) | (B & Cin) | (A & Cin);
endmodule

Key Takeaway: The assign statement allows direct computation using Boolean algebra.

VI. 4-bit Ripple Carry Adder Using Dataflow Modeling

A ripple carry adder (RCA) connects multiple full adders in sequence to perform multi-bit
addition.

Code: 4-bit Ripple Carry Adder (Dataflow)

module ripple_carry_adder (
input [3:0] A,
input [3:0] B,
input Cin,
output [3:0] Sum,
output Cout
);
wire C1, C2, C3;

assign {C1, Sum[0]} = A[0] + B[0] + Cin;


assign {C2, Sum[1]} = A[1] + B[1] + C1;
assign {C3, Sum[2]} = A[2] + B[2] + C2;
assign {Cout, Sum[3]} = A[3] + B[3] + C3;
endmodule

Key Takeaway: The {carry, sum} syntax is used for carry propagation between bits.

VII. Multiplexer (MUX) Using Dataflow Modeling

1. 2-to-1 Multiplexer (MUX)

A 2:1 MUX selects one of two inputs (A or B) based on a select signal (sel).

Code: 2-to-1 MUX

module mux2to1 (
input A,
input B,
input sel,
output Y
);
assign Y = (sel) ? B : A; // Using conditional operator
endmodule

Key Takeaway: The ternary operator (? :) makes MUX implementation simple and
readable.

2. 4-to-1 Multiplexer Using Dataflow

A 4:1 MUX selects one of four inputs based on a 2-bit select signal (sel).

Code: 4-to-1 MUX

module mux4to1 (
input [1:0] sel,
input A, B, C, D,
output Y
);
assign Y = (sel == 2'b00) ? A :
(sel == 2'b01) ? B :
(sel == 2'b10) ? C :
D;
endmodule

Key Takeaway: Using ? : makes the code more compact than using multiple if-else
conditions.

VIII. Advantages and Disadvantages of Dataflow Modeling

Advantages Disadvantages
More compact than structural modeling Not suitable for complex sequential
circuits
Easier to read and modify Cannot represent internal circuit delays
accurately
Works well for combinational circuits Requires behavioral modeling for memory
elements

IX. When to Use Dataflow Modeling?

• When designing combinational circuits (e.g., adders, multiplexers).


• When a concise and readable description is needed.
• When focusing on functionality rather than structure.
4.3 Behavioral Modeling in Verilog

I. What is Behavioral Modeling?

Behavioral modeling is the highest level of abstraction in Verilog. It describes how a


circuit behaves using procedural statements inside always or initial blocks, rather
than specifying individual logic gates or Boolean expressions.

• Uses always blocks for describing sequential and complex combinational logic.
• Includes control flow statements (if-else, case, for, while).
• Enables the description of complex circuits like state machines, counters, and
memory elements.

II. Basics of the always Block


The always block is fundamental to behavioral modeling. It executes whenever the
specified signals change.

Syntax of always Block

always @(sensitivity_list) begin


// Procedural statements
end

• The sensitivity list determines when the block executes.


• It can be triggered by combinational logic (@(*)) or sequential logic (@(posedge
clk)).

III. Combinational Circuits Using Behavioral Modeling

1. 2-to-1 Multiplexer (MUX) Using always Block

module mux2to1 (
input A,
input B,
input sel,
output reg Y
);
always @(*) begin
if (sel)
Y = B;
else
Y = A;
end
endmodule

Key Takeaway:

• always @(*) ensures the block runs whenever any input changes.
• The if-else statement selects the output based on sel.

2. 4-to-1 Multiplexer (MUX) Using case Statement

module mux4to1 (
input [1:0] sel,
input A, B, C, D,
output reg Y
);
always @(*) begin
case (sel)
2'b00: Y = A;
2'b01: Y = B;
2'b10: Y = C;
2'b11: Y = D;
default: Y = 0;
endcase
end
endmodule

Key Takeaway:
• The case statement is more efficient than multiple if-else statements for
multiplexer design.
• Always include a default case to avoid undefined outputs.

IV. Sequential Circuits Using Behavioral Modeling


Sequential circuits depend on clock edges (posedge or negedge). The always block runs
only when the clock or reset signal changes.

1. D Flip-Flop Using Behavioral Modeling

module d_flip_flop (
input D,
input clk,
input reset,
output reg Q
);
always @(posedge clk or posedge reset) begin
if (reset)
Q <= 0;
else
Q <= D;
end
endmodule

Key Takeaway:

• The always @(posedge clk or posedge reset) ensures updates on clock


edges.
• Q stores data from D only on the rising edge of clk.

2. 4-bit Counter Using Behavioral Modeling

module up_counter (
input clk,
input reset,
output reg [3:0] count
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b0000; // Reset counter
else
count <= count + 1; // Increment counter
end
endmodule

Key Takeaway:

• count increments only on clock edges, making it a sequential circuit.


• The reset condition ensures safe initialization.

3. 4-bit Up/Down Counter Using if-else

module up_down_counter (
input clk,
input reset,
input up_down, // 1 for up, 0 for down
output reg [3:0] count
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b0000;
else if (up_down)
count <= count + 1; // Up Counter
else
count <= count - 1; // Down Counter
end
endmodule

Key Takeaway:

• Uses an if-else condition to toggle between up and down counting based on


up_down.
4. Finite State Machine (FSM) Using Behavioral Modeling

FSMs control systems where the output depends on previous states.

Example: Traffic Light Controller (FSM)

This FSM cycles between Green → Yellow → Red → Green.

module traffic_light (
input clk,
input reset,
output reg [1:0] light
);
typedef enum reg [1:0] {GREEN = 2'b00, YELLOW = 2'b01, RED =
2'b10} state_t;
state_t state;

always @(posedge clk or posedge reset) begin


if (reset)
state <= GREEN; // Start with GREEN light
else begin
case (state)
GREEN: state <= YELLOW;
YELLOW: state <= RED;
RED: state <= GREEN;
default: state <= GREEN;
endcase
end
end

always @(*) begin


case (state)
GREEN: light = 2'b00;
YELLOW: light = 2'b01;
RED: light = 2'b10;
default: light = 2'b00;
endcase
end
endmodule

Key Takeaway:

• State transitions are defined in the first always block.


• Outputs (light) depend on the current state in the second always block.

V. Differences Between Combinational and Sequential


Circuits in Behavioral Modeling
Feature Combinational (always @(*)) Sequential (always @(posedge
clk))
Memory Does not store values Stores values across clock cycles
Timing Executes immediately Executes on clock edge
Triggering Changes with inputs Changes on clock edges
Examples Multiplexers, Adders Flip-Flops, Counters, FSMs

VI. Advantages and Disadvantages of Behavioral


Modeling
Advantages Disadvantages
High readability and abstraction Less control over hardware structure
Ideal for complex logic like FSMs May lead to inefficient synthesis
Works well for testbenches and verification Timing behavior needs careful handling

VII. When to Use Behavioral Modeling?


• When designing complex sequential circuits (e.g., counters, FSMs).
• When simulating and verifying testbenches.
• When describing circuits at a high level before optimizing for hardware.
4.4 Mixed-Level Modeling in Verilog

I. What is Mixed-Level Modeling?

Mixed-level modeling is the combination of Structural, Dataflow, and Behavioral


modeling in a single Verilog design.

• It leverages structural modeling for interconnections,


• Dataflow modeling for logic operations, and
• Behavioral modeling for sequential logic like counters and FSMs.

This approach allows greater flexibility and modularity, making it suitable for designing
large-scale circuits such as ALUs, processors, and memory controllers.

II. Example: 4-bit Ripple Carry Adder Using Mixed-Level


Modeling
A 4-bit Ripple Carry Adder (RCA) is a good example of mixed-level modeling.

• The Full Adder is implemented using dataflow modeling.


• The Ripple Carry Adder is implemented using structural modeling by instantiating
multiple full adders.

Step 1: Full Adder Using Dataflow Modeling

module full_adder (
input A,
input B,
input Cin,
output Sum,
output Cout
);
assign Sum = A ^ B ^ Cin;
assign Cout = (A & B) | (B & Cin) | (A & Cin);
endmodule
Step 2: 4-bit Ripple Carry Adder Using Structural Modeling

module ripple_carry_adder (
input [3:0] A,
input [3:0] B,
input Cin,
output [3:0] Sum,
output Cout
);
wire C1, C2, C3;

// Instantiate 4 full adders using structural modeling


full_adder FA1
(.A(A[0]), .B(B[0]), .Cin(Cin), .Sum(Sum[0]), .Cout(C1));
full_adder FA2
(.A(A[1]), .B(B[1]), .Cin(C1), .Sum(Sum[1]), .Cout(C2));
full_adder FA3
(.A(A[2]), .B(B[2]), .Cin(C2), .Sum(Sum[2]), .Cout(C3));
full_adder FA4
(.A(A[3]), .B(B[3]), .Cin(C3), .Sum(Sum[3]), .Cout(Cout));
endmodule

Key Takeaway:

• The Full Adder uses dataflow modeling (assign statements).


• The Ripple Carry Adder uses structural modeling by instantiating full_adder
modules.

III. Example: 4-bit Up/Down Counter Using Mixed-Level


Modeling
A 4-bit Up/Down Counter that counts up or down based on a mode signal.

• Structural Modeling is used to connect multiple registers.


• Behavioral Modeling is used inside always blocks for counting logic.
Step 1: D Flip-Flop Using Behavioral Modeling

module d_flip_flop (
input D,
input clk,
input reset,
output reg Q
);
always @(posedge clk or posedge reset) begin
if (reset)
Q <= 0;
else
Q <= D;
end
endmodule

Step 2: 4-bit Up/Down Counter Using Structural and Behavioral Modeling

module up_down_counter (
input clk,
input reset,
input mode, // 1 for up, 0 for down
output [3:0] count
);
reg [3:0] next_count;

always @(posedge clk or posedge reset) begin


if (reset)
next_count <= 4'b0000;
else if (mode)
next_count <= next_count + 1; // Up Counter
else
next_count <= next_count - 1; // Down Counter
end

// Instantiate D Flip-Flops to store the count value


d_flip_flop DFF0
(.D(next_count[0]), .clk(clk), .reset(reset), .Q(count[0]));
d_flip_flop DFF1
(.D(next_count[1]), .clk(clk), .reset(reset), .Q(count[1]));
d_flip_flop DFF2
(.D(next_count[2]), .clk(clk), .reset(reset), .Q(count[2]));
d_flip_flop DFF3
(.D(next_count[3]), .clk(clk), .reset(reset), .Q(count[3]));
endmodule

Key Takeaway:

• The D Flip-Flop is implemented using behavioral modeling.


• The Counter logic (up/down counting) is implemented using an always block.
• The registers are implemented using structural modeling by instantiating
d_flip_flop modules.

IV. Advantages and Disadvantages of Mixed-Level


Modeling
Advantages Disadvantages
Provides modularity by combining different Can increase design complexity
modeling styles
Allows reusability of structural components Debugging may be harder than using a
single modeling style
Ideal for large-scale designs like ALUs, May introduce simulation-synthesis
processors, and memory mismatches

V. When to Use Mixed-Level Modeling?


• When designing hierarchical systems (e.g., CPUs, ALUs).
• When you need both structural and behavioral representations.
• When working with complex sequential circuits.
VI. Best Practices for Efficient Verilog Modeling
1. Use Structural Modeling for Small Modules
a. Example: Use gates and module instantiations for low-level circuits (e.g.,
adders).
2. Use Dataflow Modeling for Combinational Circuits
a. Example: Use assign statements for Boolean expressions instead of gate
instantiation.
3. Use Behavioral Modeling for Complex Control Logic
a. Example: Use always @(posedge clk) for sequential circuits like FSMs
and counters.
4. Keep Modules Reusable
a. Use parameters instead of hardcoded values.
5. Minimize reg Usage in Combinational Circuits
a. Registers should only be used in sequential circuits.
6. Always Include a default Case in case Statements
a. Helps avoid unintended latches.
7. Use localparam Instead of define for Constants
a. localparam is better for synthesis since it avoids global dependencies.

Chapter 4 Review Questions


1. What are the three main modeling styles in Verilog?
2. What is mixed-level modeling, and when should it be used?
3. How is dataflow modeling different from behavioral modeling?
4. Write a Verilog code for a 2-to-4 decoder using structural modeling.
5. What is the advantage of using assign in dataflow modeling?
6. Implement a 4-bit subtractor using dataflow modeling.
7. What is the difference between always @(*) and always @(posedge clk)?
8. Write a testbench for a 4-bit ripple carry adder.
9. How does a state machine differ from a simple sequential circuit?
10. Implement a 3-bit counter with enable and reset using behavioral modeling.
Chapter 5: Modules, Ports, and Hierarchy
in Verilog
In Verilog, modules are the fundamental building blocks used to design digital circuits. A
module defines the circuit's inputs, outputs, and internal functionality. This chapter will
cover:

• 5.1 Creating Modules and Ports


• 5.2 Connecting Multiple Modules
• 5.3 Parameterized Modules
• 5.4 Design Hierarchy and Reusability

5.1 Creating Modules and Ports

I. What is a Module in Verilog?

A module in Verilog is similar to a function in programming languages like C. It


encapsulates logic that can be reused and instantiated multiple times in a design.

A module consists of:

• Module Name – The identifier for the module.


• Port List – Defines inputs and outputs.
• Internal Logic – Defines the behavior using structural, dataflow, or behavioral
modeling.

II. Basic Syntax of a Module

module <module_name> (port_list);


// Internal signals (if needed)

// Circuit functionality
endmodule

III. Example: Basic AND Gate Module


module and_gate (
input A,
input B,
output Y
);
assign Y = A & B; // AND operation
endmodule

Key Takeaway:
• The module keyword defines the module name (and_gate).
• The ports (A, B, Y) define inputs and outputs.
• The assign statement implements the AND logic.

IV. Module Ports: Inputs, Outputs, and Inouts

Port Type Keyword Usage


Input input Takes values from external signals
Output output Sends values to other modules
Inout inout Can act as both input and output
(bidirectional)

Example: Using Input, Output, and Inout Ports

module port_example (
input wire A,
input wire B,
output reg Y,
inout wire Z
);
always @(*) begin
Y = A & B; // Combinational logic
end
endmodule
Key Takeaway:

• input wire → Accepts data from external sources.


• output reg → Used for sequential logic inside always blocks.
• inout wire → Used for bidirectional signals (e.g., data buses).

V. Rules for Defining Ports

• Inputs must be type wire (default).


• Outputs can be wire (for combinational logic) or reg (for sequential logic).
• inout ports are used for bidirectional communication, but require tristate
buffers.

5.2 Connecting Multiple Modules

I. Instantiating a Module Inside Another Module

To use a module inside another module, we need module instantiation.

Example: Instantiating the and_gate Module

module top_module (
input A,
input B,
output Y
);
and_gate U1 (.A(A), .B(B), .Y(Y)); // Instantiating and_gate
endmodule

Key Takeaway:

• U1 is the instance name of and_gate.


• .A(A) → Connects the module’s A port to the top module’s A input.
• Multiple instances can be created from the same module.
II. Example: Full Adder Using Half Adder Instantiation

A Full Adder can be created by instantiating two Half Adders.

Step 1: Half Adder Module

module half_adder (
input A,
input B,
output Sum,
output Carry
);
assign Sum = A ^ B;
assign Carry = A & B;
endmodule

Step 2: Full Adder Module (Using Two Half Adders)

module full_adder (
input A,
input B,
input Cin,
output Sum,
output Cout
);
wire S1, C1, C2;

half_adder HA1 (.A(A), .B(B), .Sum(S1), .Carry(C1));


half_adder HA2 (.A(S1), .B(Cin), .Sum(Sum), .Carry(C2));

assign Cout = C1 | C2;


endmodule

Key Takeaway: Reusing modules simplifies large designs by breaking them into
smaller, reusable components.
III. Multi-Bit Adder Using Structural Modeling

A 4-bit Ripple Carry Adder can be built using multiple Full Adder modules.

Code: 4-bit Ripple Carry Adder

module ripple_carry_adder (
input [3:0] A,
input [3:0] B,
input Cin,
output [3:0] Sum,
output Cout
);
wire C1, C2, C3;

full_adder FA1
(.A(A[0]), .B(B[0]), .Cin(Cin), .Sum(Sum[0]), .Cout(C1));
full_adder FA2
(.A(A[1]), .B(B[1]), .Cin(C1), .Sum(Sum[1]), .Cout(C2));
full_adder FA3
(.A(A[2]), .B(B[2]), .Cin(C2), .Sum(Sum[2]), .Cout(C3));
full_adder FA4
(.A(A[3]), .B(B[3]), .Cin(C3), .Sum(Sum[3]), .Cout(Cout));
endmodule

Key Takeaway: Module instantiation allows hierarchical design and reusability.

5.3 Parameterized Modules

I. What Are Parameterized Modules?

Parameterized modules allow designers to create flexible and reusable Verilog modules
by using parameters instead of hardcoded values.

• Helps in defining generic circuits that can work for different bit-widths.
• Avoids duplicate code by modifying a single parameter.
• Useful in designs like multipliers, adders, ALUs, and memory units.

II. Defining a Parameterized Module

module example #(parameter WIDTH = 8) (


input [WIDTH-1:0] A,
input [WIDTH-1:0] B,
output [WIDTH-1:0] Y
);
assign Y = A + B; // Simple addition
endmodule

Key Takeaway:

• The parameter WIDTH = 8; allows us to change the bit-width when instantiating


the module.

III. Overriding Parameters at Instantiation

A parameterized module can be customized during instantiation by overriding its default


value.

Example: Instantiating the example Module with Different Bit Widths

example #(.WIDTH(4)) adder1 (.A(A), .B(B), .Y(Y)); // 4-bit Adder


example #(.WIDTH(16)) adder2 (.A(A), .B(B), .Y(Y)); // 16-bit Adder

Key Takeaway:

• #(.WIDTH(4)) → Changes the width from 8 (default) to 4.


• The same module is used for different bit-widths without modifying the original
code.
IV. Example: Parameterized N-bit Adder

Instead of creating multiple 4-bit or 8-bit adders, we design a generic N-bit adder.

module n_bit_adder #(parameter N = 8) (


input [N-1:0] A,
input [N-1:0] B,
output [N-1:0] Sum
);
assign Sum = A + B;
endmodule

Key Takeaway:

• The same n_bit_adder module can be used for any bit-width (4-bit, 8-bit,
16-bit, etc.).
• It avoids code duplication and improves reusability.

V. Example: Parameterized Multiplexer (MUX)

A N-bit multiplexer selects one of two N-bit inputs based on a select signal (sel).

module mux #(parameter WIDTH = 8) (


input [WIDTH-1:0] A,
input [WIDTH-1:0] B,
input sel,
output [WIDTH-1:0] Y
);
assign Y = sel ? B : A;
endmodule

Key Takeaway:

• The MUX size can be changed by modifying WIDTH during instantiation.


• Avoids manually creating different-sized multiplexers.
VI. When to Use Parameterized Modules?

Use Case Example


Configurable Bit-width Circuits Adders, Subtractors, Multiplexers
Reusable Memory Blocks Register Files, FIFOs, RAMs
Adjustable Logic Blocks ALUs, Shift Registers, Counters

5.4 Design Hierarchy and Reusability

I. What is Design Hierarchy?

• Large digital systems are designed using hierarchical modules.


• Small modules (e.g., logic gates, adders) are combined to form complex modules
(e.g., processors).
• This approach makes debugging, simulation, and synthesis easier.

II. Example: 4-bit ALU Using Hierarchical Design

A 4-bit ALU (Arithmetic Logic Unit) performs operations like addition, subtraction, AND,
OR, XOR.

Step 1: Define an Arithmetic Module

module arithmetic_unit (
input [3:0] A,
input [3:0] B,
input mode, // 0 for Add, 1 for Subtract
output [3:0] Result
);
assign Result = mode ? (A - B) : (A + B);
endmodule
Step 2: Define a Logic Unit

module logic_unit (
input [3:0] A,
input [3:0] B,
input [1:0] sel, // 00: AND, 01: OR, 10: XOR, 11: NOT A
output reg [3:0] Y
);
always @(*) begin
case (sel)
2'b00: Y = A & B;
2'b01: Y = A | B;
2'b10: Y = A ^ B;
2'b11: Y = ~A;
default: Y = 4'b0000;
endcase
end
endmodule

Step 3: Integrate into a 4-bit ALU

module alu (
input [3:0] A,
input [3:0] B,
input [2:0] op, // 000: ADD, 001: SUB, 010: AND, 011: OR, 100:
XOR, 101: NOT A
output reg [3:0] Result
);
wire [3:0] arith_result, logic_result;

arithmetic_unit AU
(.A(A), .B(B), .mode(op[0]), .Result(arith_result));
logic_unit LU (.A(A), .B(B), .sel(op[1:0]), .Y(logic_result));

always @(*) begin


case (op)
3'b000, 3'b001: Result = arith_result; // ADD or SUB
3'b010, 3'b011, 3'b100, 3'b101: Result = logic_result; //
Logic Operations
default: Result = 4'b0000;
endcase
end
endmodule

Key Takeaway:

• The arithmetic unit and logic unit are separate modules.


• The ALU integrates both using module instantiation.
• Using hierarchical design makes the ALU modular and reusable.

III. Best Practices for Hierarchical Verilog Design

Best Practice Reason


Break large designs into small modules Easier to debug and modify
Use parameterized modules Makes design scalable and reusable
Use meaningful module and signal names Improves readability
Avoid deeply nested hierarchies Simplifies synthesis and simulation
Always include a testbench Verifies functionality before
synthesis

Chapter 5 Review Questions


1. What are the three main types of module ports in Verilog?
2. Explain the concept of module instantiation.
3. Write a Verilog module for an 8-bit parameterized adder.
4. What is the advantage of using parameterized modules?
5. Implement a 4-to-1 multiplexer using parameterized modeling.
6. What is the significance of hierarchical design in Verilog?
7. Write a testbench for a 4-bit ALU.
8. How can you override a default parameter in Verilog?
9. What is the difference between hardcoded and parameterized designs?
10. Implement a generic N-bit counter using parameterized Verilog.
Chapter 6: Testbenches and Simulation in
Verilog
Simulation is a crucial part of digital design. Before synthesizing a circuit, we must verify its
functionality using testbenches. A testbench is a special Verilog module that generates
inputs, observes outputs, and verifies correctness.

In this chapter, we will cover:

• 6.1 Writing a Testbench


• 6.2 Using initial and always Blocks
• 6.3 Timing Control and Delays
• 6.4 Displaying and Monitoring Signals ($display, $monitor, $dumpfile)

6.1 Writing a Testbench

I. What is a Testbench?

A testbench is a self-contained Verilog module that:


1. Generates input stimulus for the design under test (DUT).
2. Observes outputs to check correctness.
3. Uses simulation constructs ($monitor, $display, #delay) to analyze behavior.
4. Does not require input/output ports because it is not synthesized.

II. Basic Structure of a Testbench

module testbench;
// 1. Declare test signals (reg for inputs, wire for outputs)
reg A, B;
wire Y;

// 2. Instantiate the Design Under Test (DUT)


and_gate uut (.A(A), .B(B), .Y(Y));

// 3. Generate input stimulus using an initial block


initial begin
A = 0; B = 0; #10; // Apply input and wait 10 time units
A = 0; B = 1; #10;
A = 1; B = 0; #10;
A = 1; B = 1; #10;
$finish; // End simulation
end

// 4. Display signal values


initial begin
$monitor("Time = %0t | A = %b, B = %b, Y = %b", $time, A, B,
Y);
end
endmodule

Key Takeaway:

• reg is used for inputs (since we assign values to them).


• wire is used for outputs (since they are computed by DUT).
• #10 introduces a delay before changing values.
• $monitor prints signal values when they change.
6.2 Using initial and always Blocks

I. initial Block

• Runs only once at the beginning of the simulation.


• Used to apply stimulus in testbenches.

Example: Applying Test Cases Using initial

initial begin
A = 0; B = 0; #10;
A = 1; B = 1; #10;
$finish;
end

Key Takeaway: The initial block is not synthesizable and is used only for testing.

II. always Block

• Runs continuously whenever its sensitivity list triggers.


• Used for clock generation or repeating patterns.

Example: Generating a Clock Using always

reg clk;
initial clk = 0; // Start clock at 0

always #5 clk = ~clk; // Toggle clock every 5 time units

Key Takeaway: The always block toggles clk every 5 time units, creating a 10-time unit
clock period.
6.3 Timing Control and Delays

I. #delay Statement

• Introduces a time delay before the next operation.


• Used in testbenches to space out input transitions.

Example: Delaying Input Transitions

A = 1; B = 0; #10; // Waits for 10 time units


A = 0; B = 1; #20; // Waits for 20 time units

Key Takeaway: Useful for sequencing test inputs.

II. @(posedge clk) and @(negedge clk)

• @(posedge clk) → Waits for the next rising edge of clk.


• @(negedge clk) → Waits for the next falling edge of clk.

Example: Synchronizing Inputs to Clock

always @(posedge clk) begin


A <= B;
end

Key Takeaway: Used in synthesizable sequential circuits.

6.4 Displaying and Monitoring Signals

I. $display Statement

• Prints values once when executed.


• Similar to printf in C.
Example: Using $display

$display("A = %b, B = %b, Y = %b", A, B, Y);

Key Takeaway: Prints the values only once when executed.

II. $monitor Statement

• Continuously prints values whenever any of the monitored signals change.

Example: Using $monitor

$monitor("Time = %0t | A = %b, B = %b, Y = %b", $time, A, B, Y);

Key Takeaway: More useful than $display for tracking real-time changes.

III. $dumpfile and $dumpvars for Waveform Output

To generate waveforms for GTKWave, use $dumpfile and $dumpvars.

Example: Generating Waveform Output

initial begin
$dumpfile("waveform.vcd"); // VCD (Value Change Dump) file
$dumpvars(0, testbench); // Dumps all variables
end

Key Takeaway: Used for visual debugging of simulation in waveform viewers like
GTKWave.
Putting Everything Together: Full Testbench Example

Example: Testbench for a Full Adder

module testbench;
reg A, B, Cin;
wire Sum, Cout;

// Instantiate the Full Adder module


full_adder uut (.A(A), .B(B), .Cin(Cin), .Sum(Sum), .Cout(Cout));

// Apply input stimulus


initial begin
$dumpfile("full_adder.vcd");
$dumpvars(0, testbench);

A = 0; B = 0; Cin = 0; #10;
A = 0; B = 0; Cin = 1; #10;
A = 0; B = 1; Cin = 0; #10;
A = 0; B = 1; Cin = 1; #10;
A = 1; B = 0; Cin = 0; #10;
A = 1; B = 0; Cin = 1; #10;
A = 1; B = 1; Cin = 0; #10;
A = 1; B = 1; Cin = 1; #10;

$finish; // End simulation


end

// Monitor outputs
initial begin
$monitor("Time = %0t | A = %b, B = %b, Cin = %b | Sum = %b,
Cout = %b",
$time, A, B, Cin, Sum, Cout);
end
endmodule

Key Takeaway:
• Uses test stimulus, monitoring, and waveform generation in one testbench.

Chapter 6 Review Questions


1. What is the purpose of a testbench in Verilog?
2. How does an initial block differ from an always block?
3. What is the use of $monitor in a testbench?
4. How can you generate a waveform file for GTKWave?
5. Write a testbench for a 4-bit counter.
6. What is the function of @(posedge clk) in Verilog?
7. How does #10 delay affect test execution?
8. Write a testbench for a 2:1 MUX.
9. How do $display and $monitor differ?
10. How can you create a clock generator in a testbench?
Chapter 7: Combinational Logic Design in
Verilog
Combinational logic circuits are fundamental to digital design. Unlike sequential circuits,
combinational circuits do not have memory and their output depends only on the current
inputs.

In this chapter, we will cover:

• 7.1 Logic Gates Implementation


• 7.2 Multiplexers and Demultiplexers
• 7.3 Encoders and Decoders
• 7.4 Arithmetic Logic Unit (ALU)

7.1 Logic Gates Implementation in Verilog

I. Basic Logic Gates

Logic gates perform fundamental Boolean operations such as AND, OR, NOT, NAND, NOR,
XOR, and XNOR.

Gate Symbol Verilog Operator Truth Table Example (A = 1, B = 0)


AND A&B assign Y = A & B; Y = 1 & 0 = 0
OR A|B `assign Y = A B;`
NOT ~A assign Y = ~A; Y = ~1 = 0
NAND ~(A & B) assign Y = ~(A & B); Y = ~(1 & 0) = 1
NOR ~(A | B) `assign Y = ~(A B);`
XOR A⊕B assign Y = A ^ B; Y = 1 ^ 0 = 1
XNOR ~(A ⊕ B) assign Y = ~(A ^ B); Y = ~(1 ^ 0) = 0
II. Implementing Logic Gates in Verilog

Example: 2-Input Logic Gates in Verilog

module logic_gates (
input A,
input B,
output AND_out,
output OR_out,
output XOR_out,
output NOT_A
);
assign AND_out = A & B;
assign OR_out = A | B;
assign XOR_out = A ^ B;
assign NOT_A = ~A;
endmodule

Key Takeaway:

• assign statements implement combinational logic directly.


• This module supports AND, OR, XOR, and NOT gates.

III. Testbench for Logic Gates

module testbench;
reg A, B;
wire AND_out, OR_out, XOR_out, NOT_A;

logic_gates uut
(.A(A), .B(B), .AND_out(AND_out), .OR_out(OR_out), .XOR_out(XOR_out),
.NOT_A(NOT_A));

initial begin
$monitor("A = %b, B = %b, AND = %b, OR = %b, XOR = %b, NOT A
= %b", A, B, AND_out, OR_out, XOR_out, NOT_A);
A = 0; B = 0; #10;
A = 0; B = 1; #10;
A = 1; B = 0; #10;
A = 1; B = 1; #10;
$finish;
end
endmodule

Key Takeaway:

• The testbench applies all possible inputs (00, 01, 10, 11).
• $monitor continuously prints the output changes.

7.2 Multiplexers and Demultiplexers

I. What is a Multiplexer (MUX)?

A multiplexer (MUX) selects one of multiple inputs and passes it to the output based on a
select signal.

MUX Type Inputs Select Lines Output


2:1 MUX A, B 1-bit (sel) Y = sel ? B : A
4:1 MUX A, B, C, D 2-bit Y depends on sel
(sel[1:0])

II. 2-to-1 Multiplexer in Verilog

module mux2to1 (
input A,
input B,
input sel,
output Y
);
assign Y = sel ? B : A; // If sel=1, Y=B; else, Y=A
endmodule
Key Takeaway:

• Uses the ternary operator ? : to select between A and B.

III. 4-to-1 Multiplexer Using case

module mux4to1 (
input [1:0] sel,
input A, B, C, D,
output reg Y
);
always @(*) begin
case (sel)
2'b00: Y = A;
2'b01: Y = B;
2'b10: Y = C;
2'b11: Y = D;
endcase
end
endmodule

Key Takeaway:

• The case statement efficiently selects one of four inputs.

7.3 Encoders and Decoders

I. What is a Decoder?

A decoder takes an N-bit input and activates one of 2^N outputs.

Example: 2-to-4 Decoder

Input (sel[1:0]) Output (Y[3:0])


00 0001
01 0010
10 0100
11 1000

II. 2-to-4 Decoder in Verilog

module decoder2to4 (
input [1:0] sel,
output reg [3:0] Y
);
always @(*) begin
case (sel)
2'b00: Y = 4'b0001;
2'b01: Y = 4'b0010;
2'b10: Y = 4'b0100;
2'b11: Y = 4'b1000;
endcase
end
endmodule

III. What is an Encoder?

An encoder does the opposite of a decoder. It converts 2^N inputs into an N-bit output.

Example: 4-to-2 Encoder

module encoder4to2 (
input [3:0] Y,
output reg [1:0] sel
);
always @(*) begin
case (Y)
4'b0001: sel = 2'b00;
4'b0010: sel = 2'b01;
4'b0100: sel = 2'b10;
4'b1000: sel = 2'b11;
default: sel = 2'bxx;
endcase
end
endmodule

7.4 Arithmetic Logic Unit (ALU)


An ALU (Arithmetic Logic Unit) performs arithmetic and logic operations.

Example: 4-bit ALU in Verilog

module alu (
input [3:0] A,
input [3:0] B,
input [2:0] op,
output reg [3:0] Result
);
always @(*) begin
case (op)
3'b000: Result = A + B;
3'b001: Result = A - B;
3'b010: Result = A & B;
3'b011: Result = A | B;
3'b100: Result = A ^ B;
3'b101: Result = ~A;
default: Result = 4'b0000;
endcase
end
endmodule
Combinational Logic Design in Verilog – Review Questions

7.1 Logic Gates Implementation


1. What is the difference between bitwise AND (&) and logical AND
(&&) in Verilog?
2. Write a Verilog module for a 3-input AND gate.
3. How does a NOR gate function, and how can you implement it in
Verilog?
4. Implement an XNOR gate using both dataflow (assign) and
behavioral (always) modeling.
5. How does the assign statement help in implementing combinational
logic?

7.2 Multiplexers and Demultiplexers


6. What is a multiplexer (MUX), and where is it used in digital
design?
7. Write Verilog code for a 4-to-1 multiplexer using case
statements.
8. How does a demultiplexer (DEMUX) differ from a multiplexer (MUX)?
9. Implement an 8-to-1 multiplexer using two 4-to-1 multiplexers in
Verilog.
10. What is the advantage of using the ternary (? :) operator in
Verilog for multiplexers?

7.3 Encoders and Decoders


11. What is the purpose of a decoder in digital circuits?
12. Implement a 3-to-8 decoder using behavioral modeling in
Verilog.
13. How does a binary encoder differ from a priority encoder?
14. Write Verilog code for a 4-to-2 priority encoder.
15. What happens if multiple inputs are 1 in a binary encoder?
How can a priority encoder solve this issue?
7.4 Arithmetic Logic Unit (ALU)
16. What is an ALU (Arithmetic Logic Unit), and what operations
can it perform?
17. Implement a 4-bit ALU that supports addition, subtraction,
AND, OR, and XOR operations.
18. How can the case statement be used to implement an ALU
operation selector?
19. Write a testbench for a 4-bit ALU that tests all its
operations.
20. Explain the concept of parameterized ALU design and its
advantages in Verilog.

Chapter 8: Sequential Logic Design in


Verilog
Sequential circuits differ from combinational circuits in that they store information and
operate based on clock cycles. Their outputs depend on both current inputs and previous
states.

In this chapter, we will cover:

• 8.1 Flip-Flops and Latches


• 8.2 Registers and Counters
• 8.3 State Machines (FSMs)
8.1 Flip-Flops and Latches

I. What is a Flip-Flop?

A flip-flop is a sequential storage element that stores one bit of data and updates its state
on a clock edge (posedge clk or negedge clk).

Flip-Flop Type Description


D Flip-Flop Stores the value of D on the clock edge
T Flip-Flop Toggles its state on each clock cycle
JK Flip-Flop Can toggle, set, or reset based on inputs
SR Flip-Flop Stores 1 or 0 unless both inputs are 1
(invalid state)

II. D Flip-Flop in Verilog

A D flip-flop stores the input (D) on the rising edge of the clock.

Code: D Flip-Flop with Asynchronous Reset

module d_flip_flop (
input D,
input clk,
input reset,
output reg Q
);
always @(posedge clk or posedge reset) begin
if (reset)
Q <= 0; // Reset output to 0
else
Q <= D; // Store D on clock edge
end
endmodule

Key Takeaway:

• The always @(posedge clk or posedge reset) ensures updates only on clock
edges.
• The reset signal clears the stored value.

III. T Flip-Flop in Verilog

A T flip-flop toggles its state whenever T = 1 on the clock edge.

Code: T Flip-Flop

module t_flip_flop (
input T,
input clk,
input reset,
output reg Q
);
always @(posedge clk or posedge reset) begin
if (reset)
Q <= 0;
else if (T)
Q <= ~Q; // Toggle when T=1
end
endmodule

Key Takeaway:

• If T = 1, the output toggles between 0 and 1.


• If T = 0, the output remains unchanged.

IV. JK Flip-Flop in Verilog

A JK flip-flop is a universal flip-flop that can be configured as a D, T, or SR flip-flop.

J K Next Q
0 0 No change
0 1 Reset (Q = 0)
1 0 Set (Q = 1)
1 1 Toggle
Code: JK Flip-Flop

module jk_flip_flop (
input J,
input K,
input clk,
input reset,
output reg Q
);
always @(posedge clk or posedge reset) begin
if (reset)
Q <= 0;
else if (J == 0 && K == 0)
Q <= Q; // No change
else if (J == 0 && K == 1)
Q <= 0; // Reset
else if (J == 1 && K == 0)
Q <= 1; // Set
else
Q <= ~Q; // Toggle
end
endmodule

Key Takeaway: The JK Flip-Flop combines the behavior of the D, T, and SR flip-flops.

8.2 Registers and Counters

I. What is a Register?

A register is a group of multiple flip-flops used to store multi-bit data.

Register Type Description


Shift Register Shifts data left or right on each clock cycle
Parallel Register Stores multiple bits simultaneously
Universal Register Supports both parallel and serial
operations
II. 4-bit Shift Register in Verilog

A shift register moves data left or right on each clock cycle.

Code: 4-bit Shift Register

module shift_register (
input clk,
input reset,
input serial_in,
output reg [3:0] Q
);
always @(posedge clk or posedge reset) begin
if (reset)
Q <= 4'b0000;
else
Q <= {Q[2:0], serial_in}; // Shift left and insert new
bit
end
endmodule

Key Takeaway:

• {Q[2:0], serial_in} shifts Q left and inserts a new bit from serial_in.

III. Counters in Verilog

A counter is a register that increments or decrements on clock pulses.

Counter Type Description


Up Counter Increments on every clock cycle
Down Counter Decrements on every clock cycle
Up/Down Counter Can count both up and down
Code: 4-bit Up Counter

module up_counter (
input clk,
input reset,
output reg [3:0] count
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b0000; // Reset to 0
else
count <= count + 1; // Increment
end
endmodule

Key Takeaway: The up counter increments count every clock cycle.

Code: 4-bit Up/Down Counter

module up_down_counter (
input clk,
input reset,
input mode, // 1 for up, 0 for down
output reg [3:0] count
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b0000;
else if (mode)
count <= count + 1; // Up Count
else
count <= count - 1; // Down Count
end
endmodule

Key Takeaway:
• The mode signal determines whether the counter increments (mode=1) or
decrements (mode=0).

8.3 Finite State Machines (FSMs)


Finite State Machines (FSMs) are used to model sequential logic circuits where the output
depends not only on current inputs but also on past states. They are widely used in digital
design for tasks such as traffic light control, sequence detection, and communication
protocols.

I. Types of FSMs

There are two main types of FSMs in digital design:

FSM Type Output Depends On Example Applications


Moore FSM Only the current state Traffic lights, counters
Mealy FSM Both the current state and inputs Sequence detectors, protocol
controllers

Key Takeaway:

• Moore FSMs have simpler state transitions, while Mealy FSMs respond faster to
inputs.

II. Moore FSM Example: 3-State Counter

A Moore FSM produces outputs based only on its current state.

State Transition Table

Current State Next State Output


S0 (00) S1 (01) 00
S1 (01) S2 (10) 01
S2 (10) S0 (00) 10
Code: 3-State Moore FSM

module moore_fsm (
input clk,
input reset,
output reg [1:0] state,
output reg [1:0] out
);
parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10;

always @(posedge clk or posedge reset) begin


if (reset)
state <= S0; // Start at state S0
else begin
case (state)
S0: state <= S1;
S1: state <= S2;
S2: state <= S0;
default: state <= S0;
endcase
end
end

// Output logic depends only on the state


always @(*) begin
case (state)
S0: out = 2'b00;
S1: out = 2'b01;
S2: out = 2'b10;
default: out = 2'b00;
endcase
end
endmodule

Key Takeaways:

• State transitions occur on clock edges (posedge clk).


• Output depends only on the state, not inputs.
III. Mealy FSM Example: Sequence Detector ("101")

A Mealy FSM produces outputs based on both current state and inputs.

State Transition Table for Detecting "101"

Current State Input (X) Next State Output (Y)


S0 (Start) 1 S1 0
S1 (Got "1") 0 S2 0
S2 (Got "10") 1 S1 1 (Detected "101")
S2 (Else) 0 S0 0

Code: 3-State Mealy FSM for "101" Detection

module mealy_fsm (
input clk,
input reset,
input X, // Serial input
output reg Y // Output signal
);
typedef enum reg [1:0] {S0, S1, S2} state_t;
state_t state;

always @(posedge clk or posedge reset) begin


if (reset)
state <= S0;
else begin
case (state)
S0: state <= (X) ? S1 : S0;
S1: state <= (X) ? S1 : S2;
S2: begin
if (X) begin
state <= S1;
Y <= 1; // Sequence "101" detected
end
else begin
state <= S0;
Y <= 0;
end
end
default: state <= S0;
endcase
end
end
endmodule

Key Takeaways:

• The state transitions depend on both the clock and input (X).
• The output (Y) is updated immediately when the sequence "101" is detected.

IV. Comparison: Moore FSM vs. Mealy FSM


Feature Moore FSM Mealy FSM
Output Depends On Only the current state Current state + Inputs
Response Time Slower (state-dependent) Faster (input-dependent)
Design Complexity Easier to design More complex

Key Takeaway:

• Moore FSMs are simpler but slower, whereas Mealy FSMs are more efficient.

V. Best Practices for Designing FSMs in Verilog


Best Practice Reason
Use typedef enum for state names Improves readability
Include a reset condition Ensures FSM starts in a valid state
Avoid unnecessary state transitions Reduces logic complexity
Use default cases in case statements Prevents unintended behavior
VI. State Machine Applications in Digital Design
Application FSM Type
Traffic Light Controller Moore FSM
Vending Machine Controller Mealy FSM
Sequence Detector Mealy FSM
CPU Control Unit Moore FSM

Key Takeaway:

• FSMs are essential in real-world digital systems like microprocessors,


controllers, and protocol handlers.

Chapter 8 Review Questions


1. What is the difference between a combinational circuit and a sequential circuit?
2. What are the main differences between Moore and Mealy FSMs?
3. Implement a 4-bit shift register that shifts data right on every clock cycle.
4. Write a Verilog module for an 8-bit counter with enable and reset.
5. How does an up/down counter work? Implement it in Verilog.
6. Write a Verilog FSM to control a traffic light system.
7. What is the purpose of state encoding in FSMs?
8. Modify the sequence detector FSM to detect "110" instead of "101".
9. How can you optimize an FSM to minimize power consumption?
10. Explain how FSMs are used in digital communication protocols (e.g., UART, SPI).
Chapter 9: Memory Design in Verilog
Memory is an essential component of digital systems, used for storing data and
instructions. In Verilog, memory can be designed using register arrays, RAM, ROM, FIFO,
and LIFO structures.

In this chapter, we will cover:

• 9.1 Types of Memory (RAM, ROM, SRAM, DRAM)


• 9.2 Synchronous vs. Asynchronous Memory
• 9.3 Memory Design Using Verilog
• 9.4 FIFO and LIFO Memory Implementation

9.1 Types of Memory in Digital Design

I. Classification of Memory

Memory Type Description Examples


Read-Only Memory (ROM) Stores permanent data BIOS,
Microcontroller
firmware
Random Access Memory (RAM) Volatile storage for fast CPU cache, System
access RAM
Static RAM (SRAM) Faster, expensive, and used CPU registers, L1/L2
in caches caches
Dynamic RAM (DRAM) Slower but denser, used for DDR RAM
main memory
FIFO (First In, First Out) Data is read in the same order Buffers, UART
it was written
LIFO (Last In, First Out) Data is read in reverse order Stack memory

Key Takeaway:

• ROM stores permanent data, while RAM is volatile.


• SRAM is faster and used for cache, whereas DRAM is cheaper but slower.
9.2 Synchronous vs. Asynchronous Memory
Feature Synchronous Memory Asynchronous Memory
Clock Uses a clock signal Works without a clock
Dependency
Access Time Determined by clock Based on signal
cycles propagation
Example DDR RAM, SRAM EEPROM, Flash Memory

Key Takeaway:

• Synchronous memory is clock-driven (e.g., RAM, FIFO).


• Asynchronous memory operates instantly when addressed (e.g., Flash, ROM).

9.3 Memory Design Using Verilog


Verilog allows memory modeling using register arrays. Memory elements are declared
using:

reg [DATA_WIDTH-1:0] memory [0:DEPTH-1];

where DATA_WIDTH is the word size and DEPTH is the number of words.

I. Read-Only Memory (ROM) Implementation

A ROM stores predefined values and does not allow writing operations.

Code: 8x4 ROM (8 locations, 4-bit data)

module rom (
input [2:0] address,
output reg [3:0] data
);
always @(*) begin
case (address)
3'b000: data = 4'b0001;
3'b001: data = 4'b0010;
3'b010: data = 4'b0011;
3'b011: data = 4'b0100;
3'b100: data = 4'b0101;
3'b101: data = 4'b0110;
3'b110: data = 4'b0111;
3'b111: data = 4'b1000;
default: data = 4'b0000;
endcase
end
endmodule

Key Takeaway:

• ROM values do not change after being programmed.

II. Random Access Memory (RAM) Implementation

A RAM module allows both read and write operations.

Code: 8x4 RAM (8 locations, 4-bit data)

module ram (
input clk,
input we, // Write Enable
input [2:0] address,
input [3:0] data_in,
output reg [3:0] data_out
);
reg [3:0] memory [7:0]; // 8x4 memory

always @(posedge clk) begin


if (we)
memory[address] <= data_in; // Write operation
else
data_out <= memory[address]; // Read operation
end
endmodule
Key Takeaway:

• When we = 1, data is written; when we = 0, data is read.

9.4 FIFO and LIFO Memory Implementation

I. First-In, First-Out (FIFO) Memory

A FIFO buffer stores data in the order it was received (queue behavior).

Code: FIFO Buffer (4-bit, 8-depth)

module fifo (
input clk,
input reset,
input wr_en,
input rd_en,
input [3:0] data_in,
output reg [3:0] data_out,
output reg full,
output reg empty
);
reg [3:0] memory [7:0]; // 8-depth FIFO
reg [2:0] wr_ptr, rd_ptr, count;

always @(posedge clk or posedge reset) begin


if (reset) begin
wr_ptr <= 0; rd_ptr <= 0; count <= 0;
end
else begin
if (wr_en && !full) begin
memory[wr_ptr] <= data_in;
wr_ptr <= wr_ptr + 1;
count <= count + 1;
end
if (rd_en && !empty) begin
data_out <= memory[rd_ptr];
rd_ptr <= rd_ptr + 1;
count <= count - 1;
end
end
end

always @(*) begin


full = (count == 8);
empty = (count == 0);
end
endmodule

Key Takeaway:

• FIFO uses wr_ptr and rd_ptr to manage data flow.

II. Last-In, First-Out (LIFO) Memory (Stack)

A LIFO (stack) works on a push-pop principle.

Code: LIFO Stack

module lifo (
input clk,
input reset,
input push,
input pop,
input [3:0] data_in,
output reg [3:0] data_out,
output reg full,
output reg empty
);
reg [3:0] stack [7:0]; // 8-depth stack
reg [2:0] sp; // Stack pointer

always @(posedge clk or posedge reset) begin


if (reset) begin
sp <= 0;
end
else begin
if (push && !full) begin
stack[sp] <= data_in;
sp <= sp + 1;
end
if (pop && !empty) begin
sp <= sp - 1;
data_out <= stack[sp];
end
end
end

always @(*) begin


full = (sp == 8);
empty = (sp == 0);
end
endmodule

Key Takeaway:

• The stack pointer (sp) manages push and pop operations.

Chapter 9 Review Questions


1. What is the difference between volatile and non-volatile memory?
2. How does SRAM differ from DRAM in digital design?
3. Implement an 8-bit ROM that stores ASCII characters in Verilog.
4. What is the function of a FIFO buffer in a communication system?
5. Write a Verilog module for a 16x8 RAM with synchronous read and write.
6. Explain the role of address decoding in memory design.
7. How does a stack (LIFO) memory differ from a queue (FIFO)?
8. Implement a dual-port RAM that allows simultaneous read and write operations.
9. Why is an asynchronous FIFO used in clock domain crossing?
10. Write a testbench to verify FIFO read and write operations.

Chapter 10: Digital Communication


Interfaces in Verilog
Digital communication interfaces allow data transfer between different components or
systems. Verilog can be used to design, simulate, and implement these protocols for real-
world applications like microprocessors, memory controllers, and data transmission
systems.

In this chapter, we will cover:

• 10.1 Introduction to Serial and Parallel Communication


• 10.2 UART (Universal Asynchronous Receiver-Transmitter)
• 10.3 SPI (Serial Peripheral Interface)
• 10.4 I2C (Inter-Integrated Circuit Protocol)

10.1 Introduction to Serial and Parallel Communication

I. Serial vs. Parallel Communication

Communication Type Description Example


Serial Data is sent bit by bit over a single wire UART, SPI, I2C
Parallel Multiple bits are sent simultaneously Memory buses,
over multiple wires PCIe

Key Takeaway:

• Serial communication is slower but requires fewer wires.


• Parallel communication is faster but requires more wires.

II. Synchronous vs. Asynchronous Communication

Feature Synchronous Asynchronous Communication


Communication
Clock Required? Yes (uses a clock signal) No (uses start/stop bits)
Data Rate Faster (clocked transfer) Slower (extra control bits)
Example SPI, I2C UART, RS232

Key Takeaway:

• Synchronous (SPI, I2C) uses a shared clock for timing.


• Asynchronous (UART) uses start/stop bits to determine timing.
10.2 UART (Universal Asynchronous Receiver-
Transmitter)

I. What is UART?

• UART is a full-duplex communication protocol used for low-speed serial data


transfer.
• It transmits data one bit at a time without a clock signal.
• Uses start, stop, and parity bits for synchronization.

Frame Format Start Data Bits Parity Bit (Optional) Stop Bit
Bit
Example (8N1) 0 8 bits No Parity 1

Key Takeaway: UART is commonly used in microcontrollers and serial devices.

II. UART Transmitter in Verilog

A UART Transmitter converts parallel data into a serial stream.

Code: UART Transmitter

module uart_tx (
input clk,
input reset,
input [7:0] data_in,
input start,
output reg tx,
output reg busy
);
reg [3:0] bit_count;
reg [9:0] shift_reg;

always @(posedge clk or posedge reset) begin


if (reset) begin
tx <= 1; // Idle state
busy <= 0;
end
else if (start && !busy) begin
shift_reg <= {1'b1, data_in, 1'b0}; // Start, data, stop
bits
bit_count <= 0;
busy <= 1;
end
else if (busy) begin
tx <= shift_reg[0];
shift_reg <= shift_reg >> 1;
bit_count <= bit_count + 1;
if (bit_count == 9)
busy <= 0;
end
end
endmodule

Key Takeaway:

• Data is sent LSB first with start (0) and stop (1) bits.

III. UART Receiver in Verilog

A UART Receiver converts serial data into parallel data.

Code: UART Receiver

module uart_rx (
input clk,
input reset,
input rx,
output reg [7:0] data_out,
output reg valid
);
reg [3:0] bit_count;
reg [9:0] shift_reg;
always @(posedge clk or posedge reset) begin
if (reset) begin
valid <= 0;
bit_count <= 0;
end
else begin
shift_reg <= {rx, shift_reg[9:1]};
if (bit_count == 9) begin
data_out <= shift_reg[8:1]; // Extract data
valid <= 1;
bit_count <= 0;
end
else begin
bit_count <= bit_count + 1;
end
end
end
endmodule

Key Takeaway:

• The receiver detects the start bit and captures the data bits.

10.3 SPI (Serial Peripheral Interface)

I. What is SPI?

• SPI is a synchronous serial protocol used for high-speed communication


between devices.
• Uses 4 lines:
o MOSI (Master Out, Slave In)
o MISO (Master In, Slave Out)
o SCLK (Clock)
o SS (Slave Select)
Key Takeaway: SPI is faster than UART but requires more wires.

II. SPI Master in Verilog

Code: SPI Master

module spi_master (
input clk,
input reset,
input [7:0] data_in,
input start,
output reg mosi,
output reg sclk,
output reg ss
);
reg [3:0] bit_count;

always @(posedge clk or posedge reset) begin


if (reset) begin
ss <= 1;
bit_count <= 0;
end
else if (start) begin
ss <= 0;
mosi <= data_in[7 - bit_count];
bit_count <= bit_count + 1;
if (bit_count == 7)
ss <= 1; // Stop after 8 bits
end
end
endmodule

Key Takeaway:

• Data is shifted out MSB first on the rising clock edge.


10.4 I2C (Inter-Integrated Circuit Protocol)

I. What is I2C?

• I2C is a multi-device communication protocol that uses only 2 wires:


o SDA (Serial Data Line)
o SCL (Serial Clock Line)
• It supports multiple masters and slaves on the same bus.

Key Takeaway: I2C is ideal for communication between sensors and microcontrollers.

II. I2C Master in Verilog

Code: I2C Master

module i2c_master (
input clk,
input reset,
input start,
input [7:0] data_in,
output reg sda,
output reg scl
);
reg [3:0] bit_count;

always @(posedge clk or posedge reset) begin


if (reset) begin
sda <= 1;
scl <= 1;
bit_count <= 0;
end
else if (start) begin
sda <= 0; // Start condition
scl <= 0;
sda <= data_in[7 - bit_count];
bit_count <= bit_count + 1;
if (bit_count == 7)
sda <= 1; // Stop condition
end
end
endmodule

Key Takeaway:

• I2C uses a start and stop condition to control communication.

• 10.5 Testbenches for UART, SPI, and I2C


• 10.6 Best Practices for Digital Communication Design

10.5 Testbenches for UART, SPI, and I2C


To verify the correct operation of UART, SPI, and I2C modules, we need to write
testbenches that simulate real-world behavior by providing test inputs and observing
outputs.

I. Testbench for UART Transmitter

This testbench sends a byte of data (8'b10101010) through a UART transmitter and
monitors the serial output.

Code: UART Transmitter Testbench

module uart_tx_tb;
reg clk, reset, start;
reg [7:0] data_in;
wire tx, busy;
// Instantiate UART transmitter
uart_tx uut
(.clk(clk), .reset(reset), .data_in(data_in), .start(start), .tx(tx),
.busy(busy));
// Generate clock signal
always #5 clk = ~clk;

initial begin
$dumpfile("uart_tx.vcd");
$dumpvars(0, uart_tx_tb);

clk = 0; reset = 1; start = 0;


#10 reset = 0; // Release reset

// Send first byte


data_in = 8'b10101010;
start = 1;
#10 start = 0; // Deassert start signal

// Wait for transmission to complete


wait (!busy);

#100;
$finish;
end

initial begin
$monitor("Time=%0t, TX=%b, Busy=%b", $time, tx, busy);
end
endmodule

Key Takeaways:

• The testbench initializes signals, toggles start, and observes tx output.


• $monitor prints the transmitted bits for debugging.
• $dumpfile and $dumpvars generate a waveform for GTKWave analysis.

II. Testbench for SPI Master

This testbench verifies SPI data transmission.


Code: SPI Master Testbench

module spi_master_tb;
reg clk, reset, start;
reg [7:0] data_in;
wire mosi, sclk, ss;

// Instantiate SPI master


spi_master uut
(.clk(clk), .reset(reset), .data_in(data_in), .start(start), .mosi(mos
i), .sclk(sclk), .ss(ss));

// Generate clock
always #5 clk = ~clk;

initial begin
$dumpfile("spi_master.vcd");
$dumpvars(0, spi_master_tb);

clk = 0; reset = 1; start = 0;


#10 reset = 0; // Release reset

// Send data
data_in = 8'b11001100;
start = 1;
#10 start = 0;

#100;
$finish;
end

initial begin
$monitor("Time=%0t, MOSI=%b, SCLK=%b, SS=%b", $time, mosi,
sclk, ss);
end
endmodule

Key Takeaways:
• Tests data shifting on MOSI line.
• Observes chip select (ss) and clock (sclk) behavior.

III. Testbench for I2C Master

This testbench verifies an I2C start condition and data transfer.

Code: I2C Master Testbench

module i2c_master_tb;
reg clk, reset, start;
reg [7:0] data_in;
wire sda, scl;

// Instantiate I2C master


i2c_master uut
(.clk(clk), .reset(reset), .start(start), .data_in(data_in), .sda(sda)
, .scl(scl));

// Generate clock
always #5 clk = ~clk;

initial begin
$dumpfile("i2c_master.vcd");
$dumpvars(0, i2c_master_tb);

clk = 0; reset = 1; start = 0;


#10 reset = 0;

// Send data
data_in = 8'b10110011;
start = 1;
#10 start = 0;

#100;
$finish;
end
initial begin
$monitor("Time=%0t, SDA=%b, SCL=%b", $time, sda, scl);
end
endmodule

Key Takeaways:

• Observes I2C data transitions (SDA) and clock (SCL).


• Checks the start and stop conditions.

10.6 Best Practices for Digital Communication Design

I. Timing Considerations

• Use non-blocking (<=) assignments in sequential logic to prevent race conditions.


• Use #delays or @(posedge clk) for testbench synchronization.

II. Error Handling in Serial Communication

Issue Cause Solution


Framing Error (UART) Incorrect baud rate Ensure TX and RX match
Clock Mismatch (SPI, Different clock sources Synchronize to a common
I2C) clock
Bus Contention (I2C) Two devices drive SDA Implement bus arbitration
simultaneously

III. Power Optimization in Serial Communication

• Disable unused clock domains when the module is inactive.


• Use clock gating to reduce switching power in SPI and I2C.
Chapter 10 Review Questions
1. What is the difference between UART, SPI, and I2C?
2. How does baud rate synchronization work in UART?
3. Write a testbench for a UART receiver in Verilog.
4. What is the role of the SS (Slave Select) signal in SPI?
5. Implement a bidirectional SPI slave module in Verilog.
6. How does I2C bus arbitration work?
7. Write a Verilog module for an SPI slave receiver.
8. How can you detect and handle bus contention in I2C?
9. What are the advantages of synchronous serial communication over
asynchronous?
10. Modify the UART transmitter to support parity checking.

You might also like