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;
Show the MyHDL (Python) version
MyHDL, design.py
from myhdl import block, Signal, always_comb, instance, delay, StopSimulation

@block
def gates(a, b, y_and, y_or, y_xor, y_nand, y_nor, y_not_a):
    @always_comb
    def logic():
        y_and.next   = a and b
        y_or.next    = a or b
        y_xor.next   = a ^ b
        y_nand.next  = not (a and b)
        y_nor.next   = not (a or b)
        y_not_a.next = not a
    return logic

@block
def tb():
    a, b = Signal(bool(0)), Signal(bool(0))
    y_and, y_or, y_xor, y_nand, y_nor, y_not_a = [Signal(bool(0)) for _ in range(6)]
    dut = gates(a, b, y_and, y_or, y_xor, y_nand, y_nor, y_not_a)

    @instance
    def stim():
        yield delay(10); a.next, b.next = 0, 1
        yield delay(10); a.next, b.next = 1, 0
        yield delay(10); a.next, b.next = 1, 1
        yield delay(10)
        raise StopSimulation
    return dut, stim

inst = tb()
inst.config_sim(trace=True)
inst.run_sim()

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
Show the VHDL testbench
VHDL, tb.vhd
library ieee;
use ieee.std_logic_1164.all;
entity tb is end entity;
architecture sim of tb is
  signal a, b : std_logic := '0';
  signal y_and, y_or, y_xor, y_nand, y_nor, y_not_a : std_logic;
begin
  dut : entity work.gates port map (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);
  process begin
    wait for 10 ns; a <= '0'; b <= '1';   -- sweep the truth table
    wait for 10 ns; a <= '1'; b <= '0';
    wait for 10 ns; a <= '1'; b <= '1';
    wait for 10 ns; std.env.stop;
  end process;
end architecture;

The MyHDL version keeps the design and its testbench in one design.py.

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

Try it live

Open this lesson in a playground, edit the code, and re-run it.

Verilog → VHDL → MyHDL →