Logic Gates

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:

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

ANDORXORNOTabababayyyy

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.

4 8 12 16 20 24 28 32 36 t (ns) y_xor y_or y_not_a y_nor y_nand y_and a b

Open this lesson in the playground →