What is Verilog?
Introduction
Verilog is a hardware description language (HDL). It is generally used in two closely coupled ways:
- to describe the structure or behaviour of (often complex) digital circuits, e.g. CPUs, digital watches, ASICs
- to write “testbench” code to emulate the hardware described above being exposed to preprogrammed signal conditions so you can verify the hardware’s behaviour/output
As used in the sense of (1), Verilog is not a procedural programming language. (Well… it’s simpler to think about it that way for now. Suffice to say, you will have a bad time if you try to apply the same mode of thinking to writing Verilog as you do to writing a Python script.) Writing “code” to describe a hardware module mostly ends up being like a combination of describing logical connections between nodes in a diagram (à la Mermaid or LaTeX diagram packages) and registering many different mini event handlers (à la Javascript or React, albeit with significant limitations to what code you can write in the handler).
As used in the sense of (2), Verilog is kind of a procedural programming language. I know this is confusing, sorry.
It has somewhat C-like syntax; it uses shell script-like keyword pairings (module/endmodule, begin/end) instead of brackets for control blocks; it has macros with backticks and special “system tasks” with dollar signs to do mostly testbench things like print to standard output.
Example
An example should help. This is Verilog code that describes a “dynamic logical operation” module. It performs a logical operation on two bytes and outputs the resulting byte. The logical operation is set by the op0 and op1 logical inputs as follows:
- op0 = 0, op1 = 0: AND
- op0 = 0, op1 = 1: OR
- op0 = 1, op1 = 0: XOR
- op0 = 1, op1 = 1: NAND
That is, if op0 = 0 and op1 = 1, the output will be the result of an OR operation being performed on the two input bytes.
/* dynamic_logic.v */
module dynamic_logic(
input [7:0] byte1,
input [7:0] byte2,
input op0,
input op1,
output [7:0] result
);
wire [7:0] and_result = byte1 & byte2;
wire [7:0] or_result = byte1 | byte2;
wire [7:0] xor_result = byte1 ^ byte2;
wire [7:0] nand_result = ~and_result;
wire [7:0] and_or = op1 ? or_result : and_result;
wire [7:0] xor_nand = op1 ? nand_result : xor_result;
assign result = op0 ? xor_nand : and_or;
endmodule
Here is an example of some very primitive testbench code for the module described above.
/* dynamic_logic_tb.v */
`timescale 1ns / 1ps
module dynamic_logic_tb ();
reg [7:0] byte1;
reg [7:0] byte2;
reg [0:1] op;
wire [7:0] out;
/* instantiate device under test */
dynamic_logic dut (
.byte1(byte1),
.byte2(byte2),
.op0(op[0]),
.op1(op[1]),
.result(out)
);
initial begin
/* set initial state */
byte1 = 8'ha5;
byte2 = 8'hf0;
/* AND operation */
op = 0;
#2 /* #2 waits two time units before evaluating the expression */
$display("expected 0xa0, got 0x%x", out);
/* OR operation */
op = 2'b01;
#2
$display("expected 0xf5, got 0x%x", out);
/* XOR */
op = 2'b10;
#2
$display("expected 0x55, got 0x%x", out);
/* NAND */
op = 2'b11;
#2
$display("expected 0x5f, got 0x%x", out);
$finish;
end
endmodule
Who uses Verilog?
Verilog and other HDLs are used at companies that design hardware (integrated circuits (ICs)). HDLs is used to describe and validate designs for digital circuits before they go off to manufacturing; since you can simulate a hardware module described in Verilog on a computer, you can run a bunch of functional tests at scale without burning actual silicon. IP cores (i.e. detailed design descriptions of a digital circuit that a company can buy the rights to use in their own designs and fabricate – selling these is a large part of ARM’s business model) can be described and distributed as Verilog.
Since the advent of FPGAs, it is now possible to “run” a Verilog description of hardware on an actual chip. How this works is you program the logic gates in your FPGA (it’s a programmable array of gates) to connect in such a way that they match a concrete logic-gate (RTL) synthesis of the hardware description. Organisations sometimes want high-performance chips for doing very specific tasks (e.g. high-frequency trading), but some problems with getting such custom ICs (application-specific ICs (ASICs)) fabricated is that it’s (a) expensive, especially if you don’t need a lot of them, and (b) those expensive chips can become useless if your requirements change or you figure out a different, better computational approach to your task. FPGAs are more power-hungry than ASICs and slightly less efficient but they can be cheaper and you can reprogram them. They’re also used for educational purposes in some modern university electrical engineering and computer science courses.
How can I run Verilog?
- You can simulate a hardware module written in Verilog (most likely simulating it within the context of a testbench) using open-source software such as Verilator or Icarus Verilog, which can convert hardware behavioural models (and testbenches) to executable binaries.
- You can synthesise a Verilog module into a netlist (or some kind of scary binary blob) that you can upload to an FPGA using mostly arcane proprietary methods because the FPGA ecosystem skews heavily closed-source. Look into Yosys.
For the former point, simulating the Verilog module from earlier with Icarus Verilog looks like:
$ iverilog -o tb dynamic_logic_tb.v dynamic_logic.v
$ ./tb
expected 0xa0, got 0xa0
expected 0xf5, got 0xf5
expected 0x55, got 0x55
expected 0x5f, got 0x5f
What are some resources I can use to learn Verilog?
Happy hacking!