Everything an FPGA does reduces to lookup tables implementing boolean
functions, so we start where the hardware starts. In Verilog, stateless
("combinational") logic is written with assign: the left-hand side
continuously follows the right-hand side, like wiring gates together —
there is no notion of "executing" an assign, it just is.
The design below implements the six classic gates on two inputs. Points to
notice:
&, |, ^, ~ are bitwise operators — applied to vectors they
operate lane by lane. On 1-bit signals they behave like the gate.
There is no "order of execution". All five assigns exist simultaneously,
exactly like five pieces of copper.
In VHDL the same thing is a concurrent signal assignment; the keywords
are friendlier (and, or, xor, not).
In the waveform, the testbench sweeps all four input combinations of
a/b. Check each output against the truth table you know — this habit
(predict first, then look) is the core skill of reading waveforms.
Experiment in the playground: make a 3-input majority gate
(assign y = (a & b) | (a & c) | (b & c);) and verify it with an
exhaustive 8-case testbench.
Schematic
The design
Verilog — design.v
// The six classic gates, described structurally with assign.
module gates (
input wire a,
input wire b,
output wire y_and,
output wire y_or,
output wire y_xor,
output wire y_nand,
output wire y_nor,
output wire y_not_a
);
assign y_and = a & b;
assign y_or = a | b;
assign y_xor = a ^ b;
assign y_nand = ~(a & b);
assign y_nor = ~(a | b);
assign y_not_a = ~a;
endmodule
Show the VHDL version
VHDL — design.vhd
-- The six classic gates as concurrent signal assignments.
library ieee;
use ieee.std_logic_1164.all;
entity gates is
port (
a, b : in std_logic;
y_and : out std_logic;
y_or : out std_logic;
y_xor : out std_logic;
y_nand : out std_logic;
y_nor : out std_logic;
y_not_a : out std_logic
);
end entity;
architecture rtl of gates is
begin
y_and <= a and b;
y_or <= a or b;
y_xor <= a xor b;
y_nand <= a nand b;
y_nor <= a nor b;
y_not_a <= not a;
end architecture;
The testbench
Verilog — tb.v
`timescale 1ns/1ns
module tb;
reg a = 0, b = 0;
wire y_and, y_or, y_xor, y_nand, y_nor, y_not_a;
gates dut (.a(a), .b(b), .y_and(y_and), .y_or(y_or), .y_xor(y_xor),
.y_nand(y_nand), .y_nor(y_nor), .y_not_a(y_not_a));
initial begin
$dumpfile("wave.vcd"); $dumpvars(0, tb);
// sweep the whole truth table, 10 ns per row
#10 {a, b} = 2'b01;
#10 {a, b} = 2'b10;
#10 {a, b} = 2'b11;
#10 $finish;
end
endmodule
Simulated waveform
This trace was produced by actually simulating the code
above with Icarus Verilog.