Verilog Full Adder Tutorial
A comprehensive tutorial covering basic adder design and testbench concepts in Verilog.
Files
π¦ Download all files
| File | Description |
|---|---|
full_adder.v | Design file containing 1-bit full adder and 4-bit ripple carry adder |
full_adder_tb.v | Testbench with exhaustive testing and detailed comments |
Makefile | Build automation for compilation and simulation |
Quick Start
# Compile and run
make
# Or manually:
iverilog -o full_adder_sim full_adder.v full_adder_tb.v
vvp full_adder_sim
# View waveforms (if GTKWave is installed)
make wave
Key Verilog Concepts Explained
1. Module Structure
module module_name (
input wire signal_in,
output wire signal_out
);
// Internal logic here
endmodule
- Module: Basic building block (like a function/class)
- Ports: Interface to outside world (inputs/outputs)
- wire: Default type for ports, represents physical connections
2. Data Types
| Type | Use Case |
|---|---|
wire | Connections, driven by assign or module outputs |
reg | Variables assigned in procedural blocks (always, initial) |
integer | 32-bit signed integers for loops/counters |
parameter | Constants, can be overridden at instantiation |
3. Operators
| Category | Operators | Example |
|---|---|---|
| Bitwise | & ` | ^ ~` |
| Logical | && ` | |
| Comparison | == != === !== | a == b |
| Arithmetic | + - * / % | a + b |
| Shift | << >> | a << 2 |
| Concatenation | { } | {a, b} |
| Replication | {n{ }} | {4{1'b0}} |
Important: == vs ===
==returns X if either operand has X/Z===does exact comparison including X/Z values
4. Number Representation
4'b1010 // 4-bit binary = 10
8'hFF // 8-bit hex = 255
8'd255 // 8-bit decimal = 255
4'bxxxx // 4-bit unknown
4'bzzzz // 4-bit high-impedance
Format: <size>'<base><value>
5. Continuous vs Procedural Assignment
Continuous (Combinational Logic):
assign sum = a ^ b; // Always reflects current inputs
Procedural (Sequential/Complex Logic):
always @(posedge clk) begin
q <= d; // Updates on clock edge
end
always @(*) begin // Combinational in always block
if (sel)
y = a;
else
y = b;
end
6. Blocking vs Non-Blocking Assignment
| Type | Operator | Use Case |
|---|---|---|
| Blocking | = | Combinational logic, testbenches |
| Non-blocking | <= | Sequential logic (flip-flops) |
// WRONG - race condition
always @(posedge clk) begin
a = b;
b = a; // Both get same value!
end
// CORRECT
always @(posedge clk) begin
a <= b;
b <= a; // Proper swap
end
7. Module Instantiation
// Named port connection (PREFERRED)
full_adder fa0 (
.a(input_a),
.b(input_b),
.cin(carry_in),
.sum(sum_out),
.cout(carry_out)
);
// Positional (error-prone, avoid)
full_adder fa0 (input_a, input_b, carry_in, sum_out, carry_out);
Testbench Concepts
1. Testbench Structure
`timescale 1ns / 1ps
module design_tb; // No ports!
reg inputs; // Driven by testbench
wire outputs; // Driven by DUT
design dut (...); // Instantiate DUT
initial begin // Test stimulus
// Generate patterns
// Check results
$finish;
end
endmodule
2. System Tasks (Non-Synthesizable)
| Task | Purpose |
|---|---|
$display() | Print with newline |
$write() | Print without newline |
$monitor() | Print on any signal change |
$time | Current simulation time |
$random | Generate random number |
$finish | End simulation |
$stop | Pause simulation |
$dumpfile() | Specify VCD filename |
$dumpvars() | Record signals to VCD |
3. Timing Control
#10; // Delay 10 time units
#10 a = 1; // Delay then assign
@(posedge clk); // Wait for clock edge
wait(signal); // Wait until signal is true
4. Test Strategy
- Boundary Cases: Min/max values, zero, overflow
- Typical Cases: Normal operation scenarios
- Edge Cases: Carry propagation, bit transitions
- Random Testing: Cover unexpected combinations
Design Best Practices
Doβs β
- Use meaningful signal names
- Comment your code
- Initialize all signals in testbenches
- Use named port connections
- Separate combinational and sequential logic
- Use non-blocking (
<=) for sequential logic
Donβts β
- Mix blocking and non-blocking in same always block
- Use
initialblocks in synthesizable code - Create combinational loops
- Leave signals undriven (floating)
- Use delays in synthesizable code
Common Errors
1. Undriven Signal
wire result; // Never assigned!
assign y = result; // y will be 'x'
2. Multiple Drivers
assign a = b;
assign a = c; // ERROR: Two drivers!
3. Sensitivity List Issues
// WRONG - incomplete sensitivity list
always @(a)
y = a & b; // Won't update when b changes
// CORRECT
always @(*)
y = a & b;
4. Latch Inference
// WRONG - creates unintended latch
always @(*) begin
if (sel)
y = a;
// Missing else!
end
// CORRECT
always @(*) begin
if (sel)
y = a;
else
y = b; // Or: y = 0;
end
Running the Simulation
# Full build and run
make
# Step by step
make compile # Compile
make run # Simulate
make wave # View waveforms
make clean # Cleanup
Expected output shows:
- 1-bit full adder truth table verification
- 4-bit adder boundary tests
- Random pattern testing
- Pass/fail summary