Skip to content
Volume V · The TensorCore.si Library

Verilog

Describe the hardware that runs AI.

Verilog is a hardware description language (HDL). Instead of instructions for a processor, you describe digital circuits: registers, adders, state machines and whole chips. GPUs, TPUs and every AI accelerator start life as HDL code like Verilog or SystemVerilog before they are turned into silicon or loaded onto an FPGA.

Prologue

Why learn Verilog?

If you want to understand how a tensor core or AI chip really works, you need to think in hardware, and Verilog is the most common way to do that.

  • Real hardware: your code becomes logic gates and wires.
  • Parallel by nature: every block runs at the same time, every clock cycle.
  • AI chips: accelerators, systolic arrays and FPGA inference engines are designed in HDLs.
Prologue

Set up and run your first program

Install Icarus Verilog to simulate and GTKWave to view waveforms. Verilator is a fast alternative for larger designs.

terminal
sudo apt install iverilog gtkwave
iverilog -o hello hello.v
vvp hello
hello.v
module hello;
  initial begin
    $display("Hello, AI! Welcome to Verilog.");
    $finish;
  end
endmodule
Tipinitial blocks and $display only work in simulation; real hardware uses modules, wires and registers.
Chapter 1

Modules and wires

A module is a hardware block with input and output ports. assign creates continuous logic: the output always reflects the inputs, like a wire connected to gates.

half_adder.v
module half_adder (
  input  wire a,
  input  wire b,
  output wire sum,
  output wire carry
);
  assign sum   = a ^ b;   // XOR gate
  assign carry = a & b;   // AND gate
endmodule
Chapter 2

Vectors and arithmetic

Signals can be multi-bit buses like [7:0] for 8 bits. Arithmetic operators synthesise into adders and multipliers.

mac8.v
module mac8 (
  input  wire [7:0]  a, b,      // two 8-bit inputs (INT8)
  input  wire [15:0] acc_in,
  output wire [15:0] acc_out
);
  // multiply-accumulate: the core operation of neural networks
  assign acc_out = acc_in + a * b;
endmodule
Chapter 3

Clocks and registers

always @(posedge clk) describes flip-flops that update on each rising clock edge. Use non-blocking assignment <= inside clocked blocks.

counter.v
module counter (
  input  wire       clk,
  input  wire       rst,
  output reg  [7:0] count
);
  always @(posedge clk) begin
    if (rst) count <= 8'd0;
    else     count <= count + 8'd1;
  end
endmodule
Chapter 4

State machines

Finite state machines control hardware step by step. A register holds the current state; a case statement picks the next one.

loader.v
module loader (input wire clk, rst, start, done, output reg busy);
  localparam IDLE = 2'd0, LOAD = 2'd1, COMPUTE = 2'd2;
  reg [1:0] state;
  always @(posedge clk) begin
    if (rst) state <= IDLE;
    else case (state)
      IDLE:    if (start) state <= LOAD;
      LOAD:    state <= COMPUTE;
      COMPUTE: if (done) state <= IDLE;
      default: state <= IDLE;
    endcase
  end
  always @(*) busy = (state != IDLE);
endmodule
Chapter 5

Testbenches

A testbench is a simulation-only module that drives inputs, generates a clock and checks outputs.

tb.v
module tb;
  reg  clk = 0, rst = 1;
  wire [7:0] count;
  counter dut (.clk(clk), .rst(rst), .count(count));
  always #5 clk = ~clk;                 // 10 ns clock
  initial begin
    $dumpfile("wave.vcd"); $dumpvars(0, tb);
    #12 rst = 0;
    #100 $display("count = %0d", count);
    $finish;
  end
endmodule
// iverilog -o sim tb.v counter.v && vvp sim && gtkwave wave.vcd
Part II · AI with Verilog

Verilog and AI hardware

AI accelerators are built from huge arrays of multiply-accumulate (MAC) units. Google's TPU uses a systolic array, where data flows between neighbouring MACs each clock cycle, and NVIDIA's tensor cores are dedicated matrix-multiply units. FPGA engineers write Verilog (or use high-level synthesis) to build custom inference engines, and LLMs are now being used to help write and verify HDL.

The essential libraries

VerilatorFast open-source simulator.
YosysOpen-source synthesis.
cocotbWrite testbenches in Python.
hls4mlTurn neural networks into FPGA firmware.

A processing element for a systolic array

Each PE multiplies its inputs, adds to its accumulator and passes data to its neighbours on the next cycle.

pe.v
module pe (
  input  wire              clk, rst,
  input  wire signed [7:0] a_in, b_in,
  output reg  signed [7:0] a_out, b_out,
  output reg  signed [31:0] acc
);
  always @(posedge clk) begin
    if (rst) begin
      acc <= 0; a_out <= 0; b_out <= 0;
    end else begin
      acc   <= acc + a_in * b_in;   // MAC
      a_out <= a_in;                // pass right
      b_out <= b_in;                // pass down
    end
  end
endmodule
Part III · Build an AI agent

An agent that runs hardware simulations

An AI agent can help design hardware if it can run the simulator. This Python agent lets a local model compile and simulate Verilog files with Icarus Verilog and read the results.

Your goal→Model plans→Tool call→Your code runs the tool→Result back to model→Final answer
agent.py
import json, subprocess
from openai import OpenAI

def simulate(files):
    b = subprocess.run(["iverilog", "-o", "sim", *files], capture_output=True, text=True)
    if b.returncode: return "Compile error:\n" + b.stderr
    return subprocess.run(["vvp", "sim"], capture_output=True, text=True, timeout=30).stdout

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
tools = [{"type": "function", "function": {"name": "simulate", "description": "Compile and simulate Verilog files",
    "parameters": {"type": "object", "properties": {"files": {"type": "array", "items": {"type": "string"}}}, "required": ["files"]}}}]
messages = [{"role": "user", "content": "Simulate tb.v with counter.v and tell me the final count."}]
for _ in range(5):
    msg = client.chat.completions.create(model="llama3.1", messages=messages, tools=tools).choices[0].message
    messages.append(msg)
    if not msg.tool_calls:
        print(msg.content); break
    for c in msg.tool_calls:
        messages.append({"role": "tool", "tool_call_id": c.id, "content": simulate(**json.loads(c.function.arguments))})

Run the simulator in a sandbox; the agent should only see the project folder.

Run it locallyInstall Ollama, run ollama pull llama3.1, and keep it running. The code talks to its OpenAI-compatible API at http://localhost:11434/v1, so the same program also works with vLLM, llama.cpp server or a cloud API by changing the URL, model name and adding an API key.
Part IV · Your path

Verilog learning roadmap

Tick items off as you go. Your progress is saved in this browser.

Beginner

Intermediate

Advanced

Part IV · Your path

Projects to build

Traffic lightA state machine with timers.
UARTSend text from an FPGA to a PC.
MAC arrayA 4x4 systolic array that multiplies matrices.
Reference

Verilog cheat sheet

TaskSyntax
Modulemodule m(input a, output y); endmodule
Wire logicassign y = a & b;
Registeralways @(posedge clk) q <= d;
Buswire [7:0] data;
Casecase (s) 0: ...; endcase
Print (sim)$display("%d", x);
Reference

Common mistakes

  • Use <= in clocked blocks and = in combinational blocks; mixing them causes simulation/hardware mismatches.
  • Forgetting a default in combinational logic creates unwanted latches.
  • Delays like #5 are ignored by synthesis; they only work in simulation.
Reference

Where to go next

On TensorCore.si: VHDL volume, What is a tensor core?.

Keyboard: [ previous language · ] next language · / search the library