Exercise: Build Combinational Logic and a Testbench
This exercise makes the first FPGA habit concrete: do not trust a design just because it looks simple. Simulate it.
Learning Objectives
You will:
- implement a 3-input majority function;
- write an exhaustive testbench;
- run every input combination;
- understand expected behavior, failure symptoms, debugging steps, and an extension challenge.
Prerequisites
- Boolean truth tables.
- Basic Verilog syntax.
- A simulator such as Icarus Verilog, Verilator, Vivado Simulator, ModelSim/Questa, or another equivalent tool.
Task
Build a module named majority3. Output y must be 1 when at least two of the three inputs are 1.
Truth table:
| a | b | c | y |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 1 |
| 1 | 1 | 1 | 1 |
Implementation
Create majority3.v:
module majority3 (
input wire a,
input wire b,
input wire c,
output wire y
);
assign y = (a & b) | (a & c) | (b & c);
endmodule
Create tb_majority3.v:
`timescale 1ns/1ps
module tb_majority3;
reg a;
reg b;
reg c;
wire y;
integer i;
reg expected;
majority3 dut (
.a(a),
.b(b),
.c(c),
.y(y)
);
initial begin
for (i = 0; i < 8; i = i + 1) begin
{a, b, c} = i[2:0];
#1;
expected = ((a + b + c) >= 2);
if (y !== expected) begin
$display("FAIL a=%0b b=%0b c=%0b y=%0b expected=%0b",
a, b, c, y, expected);
$finish;
end
end
$display("PASS majority3");
$finish;
end
endmodule
Expected Behavior
The simulation should print:
PASS majority3
No waveform is required for such a small exhaustive check, but viewing one is helpful.
title "Illustrative exhaustive combinational check"
time start=0 end=8 unit=ns divisions=8
A: square label="a" low=0 high=1 duty=50 cycles=1 unit=logic color=#2563eb
B: square label="b" low=0 high=1 duty=50 cycles=2 unit=logic color=#7c3aed
C: square label="c" low=0 high=1 duty=50 cycles=4 unit=logic color=#dc2626
Y: square label="expected y" low=0 high=1 duty=50 cycles=2 phase=180 unit=logic color=#16a34a
marker DONE at=8 label="all cases checked"
The waveform is conceptual; the pass/fail assertion in the testbench is the real verification.
Verification Steps
- Compile
majority3.vandtb_majority3.v. - Run the simulator.
- Confirm
PASS majority3. - Temporarily break the equation, for example remove
(a & c). - Re-run and confirm the testbench catches the bug.
Common Failure Symptoms
| Symptom | Likely cause |
|---|---|
Output is always 0 |
wrong expression or unconnected output |
Fails only for 101 |
missing (a & c) term |
Fails only for 011 |
missing (b & c) term |
Simulator prints x |
uninitialized signal or wrong port connection |
Debugging Guidance
- Print the input combination and expected value.
- Check port names in the DUT instantiation.
- Confirm the testbench waits at least one delta/time step after changing inputs.
- Use
!==instead of!=when checking possiblexorzvalues.
Extension Challenge
After finishing the VHDL track, write the same majority function in VHDL and test it with a VHDL testbench. Then change the design to a 5-input majority function and test all 32 input combinations.
Explained Solution
The expression (a & b) | (a & c) | (b & c) is high whenever any pair of inputs is high. If all three are high, all three terms are high. If fewer than two are high, every pair term is false. The exhaustive testbench checks all eight combinations, so there is no untested input case.
Summary
Small combinational circuits are perfect for learning disciplined verification. The design is tiny, but the habit is serious: define expected behavior, simulate every relevant case, and make the testbench fail when the design is wrong.
Next: Sequential Logic in Verilog.
Further Reading
- Icarus Verilog usage examples
- Verilator simulation guide
- GHDL documentation for VHDL simulation
- Vendor simulator user guides