Verilog Comprehensive Book
Verilog Comprehensive Book
Preface
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.
No prior knowledge of Verilog is required, but basic knowledge of digital logic design
(gates, flip-flops, counters, etc.) will be helpful.
Tip: You can follow along by coding and simulating Verilog designs using tools like
ModelSim, Xilinx Vivado, or Intel Quartus.
Tip: If you're new to Verilog, start with Icarus Verilog + GTKWave (free and easy to use).
This book will empower you to design complex digital systems and prepare you for
FPGA/ASIC industry roles.
module and_gate(
input A,
input B,
output Y
)
assign Y = A & B;
endmodule
module testbench;
reg A, B;
wire 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
module and_gate(
input A,
input B,
output Y
);
assign Y = A & B;
endmodule
endmodule
module mux2to1 (
input A,
input B,
input sel,
output Y
);
assign Y = sel ? B : A; // If sel=1, Y=B; else, Y=A
endmodule
module testbench;
reg A, B, sel;
wire 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
always @(A or B)
Y = A & B; // May work in simulation but is not always
synthesizable
More Examples
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
module testbench;
reg A, B, Cin;
wire 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
module d_flip_flop (
input D,
input clk,
output reg Q
);
always @(posedge clk)
Q <= D;
endmodule
module testbench;
reg D, clk;
wire Q;
initial begin
clk = 0; D = 0;
#10; D = 1;
#10; D = 0;
#10; D = 1;
#10; D = 0;
#10;
$finish;
end
endmodule
1. Nets (wire)
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)
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.
4. Integer (integer)
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)
1. Arithmetic Operators
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
3. Logical Operators
Key takeaway: Logical operators evaluate true (1) or false (0) based on conditions.
4. Shift Operators
Key takeaway: Shift operators move bits left or right, effectively multiplying or dividing by
powers of two.
5. Reduction Operators
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.
Key takeaway: The WIDTH parameter allows this module to be modified for different bit
sizes without changing the internal logic.
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.
`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.
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.
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.
Key takeaway:
module mux2to1 (
input A,
input B,
input sel,
output reg Y
);
always @(*) begin
if (sel)
Y = B;
else
Y = A;
end
endmodule
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.
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.
I. for Loop
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.
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.
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.
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.
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.
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.
I. Counters in Verilog
Counters are sequential circuits that increment or decrement values based on clock
pulses.
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.
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
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
Instead of writing a long sequence of if-else conditions, we can use a for loop.
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.
1. Types of FSMs
State Encoding
module moore_fsm (
input clk,
input reset,
output reg [1:0] state
);
parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10;
Key Takeaways:
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;
Key Takeaways:
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.
A Half Adder performs binary addition of two inputs (A and B) and produces:
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
• Sum = A ⊕ B ⊕ Cin
• Carry-out = (A & B) | (B & Cin) | (A & Cin)
module full_adder (
input A,
input B,
input Cin,
output Sum,
output Cout
);
wire S1, C1, C2;
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.
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
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.
A half adder performs binary addition and produces a Sum and a Carry output.
Logic Equations
• Sum = A ⊕ B
• Carry = A & B
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
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)
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.
A ripple carry adder (RCA) connects multiple full adders in sequence to perform multi-bit
addition.
module ripple_carry_adder (
input [3:0] A,
input [3:0] B,
input Cin,
output [3:0] Sum,
output Cout
);
wire C1, C2, C3;
Key Takeaway: The {carry, sum} syntax is used for carry propagation between bits.
A 2:1 MUX selects one of two inputs (A or B) based on a select signal (sel).
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.
A 4:1 MUX selects one of four inputs based on a 2-bit select signal (sel).
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.
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
• 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.
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.
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.
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:
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:
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:
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;
Key Takeaway:
This approach allows greater flexibility and modularity, making it suitable for designing
large-scale circuits such as ALUs, processors, and memory controllers.
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;
Key Takeaway:
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
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;
Key Takeaway:
// Circuit functionality
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.
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:
module top_module (
input A,
input B,
output Y
);
and_gate U1 (.A(A), .B(B), .Y(Y)); // Instantiating and_gate
endmodule
Key Takeaway:
module half_adder (
input A,
input B,
output Sum,
output Carry
);
assign Sum = A ^ B;
assign Carry = A & B;
endmodule
module full_adder (
input A,
input B,
input Cin,
output Sum,
output Cout
);
wire S1, C1, C2;
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.
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
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.
Key Takeaway:
Key Takeaway:
Instead of creating multiple 4-bit or 8-bit adders, we design a generic N-bit adder.
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.
A N-bit multiplexer selects one of two N-bit inputs based on a select signal (sel).
Key Takeaway:
A 4-bit ALU (Arithmetic Logic Unit) performs operations like addition, subtraction, AND,
OR, XOR.
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
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));
Key Takeaway:
I. What is a Testbench?
module testbench;
// 1. Declare test signals (reg for inputs, wire for outputs)
reg A, B;
wire Y;
Key Takeaway:
I. initial Block
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.
reg clk;
initial clk = 0; // Start clock at 0
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
I. $display Statement
Key Takeaway: More useful than $display for tracking real-time changes.
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
module testbench;
reg A, B, Cin;
wire 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;
// 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.
Logic gates perform fundamental Boolean operations such as AND, OR, NOT, NAND, NOR,
XOR, and XNOR.
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:
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.
A multiplexer (MUX) selects one of multiple inputs and passes it to the output based on a
select signal.
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:
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:
I. What is a Decoder?
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
An encoder does the opposite of a decoder. It converts 2^N inputs into an N-bit output.
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
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
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).
A D flip-flop stores the input (D) on the rising edge of the clock.
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.
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:
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.
I. What is a 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.
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
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).
I. Types of FSMs
Key Takeaway:
• Moore FSMs have simpler state transitions, while Mealy FSMs respond faster to
inputs.
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;
Key Takeaways:
A Mealy FSM produces outputs based on both current state and inputs.
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;
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.
Key Takeaway:
• Moore FSMs are simpler but slower, whereas Mealy FSMs are more efficient.
Key Takeaway:
I. Classification of Memory
Key Takeaway:
Key Takeaway:
where DATA_WIDTH is the word size and DEPTH is the number of words.
A ROM stores predefined values and does not allow writing operations.
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:
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
A FIFO buffer stores data in the order it was received (queue behavior).
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;
Key Takeaway:
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
Key Takeaway:
Key Takeaway:
Key Takeaway:
I. What is UART?
Frame Format Start Data Bits Parity Bit (Optional) Stop Bit
Bit
Example (8N1) 0 8 bits No Parity 1
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;
Key Takeaway:
• Data is sent LSB first with start (0) and stop (1) bits.
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.
I. What is SPI?
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;
Key Takeaway:
I. What is I2C?
Key Takeaway: I2C is ideal for communication between sensors and microcontrollers.
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;
Key Takeaway:
This testbench sends a byte of data (8'b10101010) through a UART transmitter and
monitors the serial output.
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);
#100;
$finish;
end
initial begin
$monitor("Time=%0t, TX=%b, Busy=%b", $time, tx, busy);
end
endmodule
Key Takeaways:
module spi_master_tb;
reg clk, reset, start;
reg [7:0] data_in;
wire mosi, sclk, ss;
// Generate clock
always #5 clk = ~clk;
initial begin
$dumpfile("spi_master.vcd");
$dumpvars(0, spi_master_tb);
// 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.
module i2c_master_tb;
reg clk, reset, start;
reg [7:0] data_in;
wire sda, scl;
// Generate clock
always #5 clk = ~clk;
initial begin
$dumpfile("i2c_master.vcd");
$dumpvars(0, i2c_master_tb);
// 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:
I. Timing Considerations