Back to EECS 31L Index

1. EECS 31L / 01 Verilog Basics

EECS 31L • Study Notes • Verilog Basics
Mahmoud Elfar • Spring 2026 • v0.2

0.1: Initial version
0.2: Added concatenation and replication operators


Table of Contents


1.1. How to Learn Verilog as a Language

Like any other language, you need to mainly focus on three things:

  1. Syntax: what can you write without the simulator cursing back at you (again)
    Ex: can I write a = b + c?
  2. Semantics: understanding the meaning behind what you write
    Ex: what does a = b + c mean? Does it mean addition or logical OR?
  3. Constructs: learning the common ways the language is used to do its job: modeling digital systems
    Ex: Depending on what you are modeling, you might want to use assign keyword before a = b + c

This study note will not stop and explain everything, so it is crucial to have some level of awareness and self reflection while reading it: what are you reading now? What are you struggling to understand? Is it the syntax, semantics, or constructs?

↑ Back to top


1.2. Verilog Basic Constructs

↑ Back to top


1.3. Modules

See: IEEE 1800-2017, 3.3 Modules, pg. 47

module <module_name> (port_list);
  // module body
endmodule

Module consists of:

1.3.1. Syntax for Module Definition

Example: Let’s write a module that implements a 3-input AND gate.

// Explicitly declaring port directions in the header
module and3 (input a, input b, input c, output y);
  y = a & b & c; endmodule
  
// Grouping ports that share the same direction and bit width
module and3_2 (input a, b, c, output y);
  y = a & b & c; endmodule
  
// Only declare port names in the header, and their directions in the body
module and3_3 (a, b, c, y);
  input a; input b; input c; output y;
  y = a & b & c; endmodule
  
// Same story
module and3_4 (a, b, c, y);
  input a, b, c; output y;
  y = a & b & c; endmodule
  

1.3.2. Syntax for Module Instantiation

REF: IEEE 1800-2017, 23.3 Module instances, pg. 708

Suppose you want to use 10 of those and3 gates in your design. You instantiate 10 instances (copies) of and3.

Syntax:

// Instantiation via named port connections
<module_name> <instance_name> ( .port_name(signal_name), ... );
// Instantiation via ordered port connections
<module_name> <instance_name> (signal_name1, signal_name2, ...);

So, you either connect ports by explicitly naming them, or by providing the signals in the same order as the ports are declared in the module definition.

Example:

// Instantiate a 3-input AND, call it minterm_7, and connect its ports to signals d0, d1, d2, and m7
and3 minterm_7 ( .a(d0), .b(d1), .c(d2), .y(m7) );
// Instantiate another for minterm_6
and3 minterm_6 ( .a(d0not), .b(d1), .c(d2), .y(m6) );

1.3.3. Module Semantics

A module is the basic building block of the digital design — just like the mitochondria is the powerhouse of the cell.
When you design a digital system, you must specify its peripheral interfaces first (i.e. its ports).
A module in Verilog models this concept of a self-contained design unit with a well-defined interface.
Once a module is defined, it can be used and reused. As an analogy, a module definition is like a blueprint for a car, and each time you instantiate the module, it’s like building a new car based on that blueprint.
If you are familiar with object-oriented programming, think of classes and objects.

1.3.4. Entry Points and Top-Level Modules


1.4. Data Types

See: IEEE 1800-2017, 6 Data types, pg. 83

Data types in Verilog refers to the different types of data that can be used in a Verilog design. A data type is a classification that is associated with:

There are many data types in Verilog, and covering them now won’t make much sense. This will be one of the topics that will randomly resurface when studying other topics. So, whenever you see a new data type, it is time to learn it.

1.4.1. Object vs Data Type

1.4.2. Nets vs Variables

There are two main categories of data types (or objects if you will): nets and variables.
There is no analogy for this in software, so pay attention.

Aspect Nets Variables
Concept Connection / signal Storage / value holder
Assignment type Continuous (assign) Procedural (always, initial)
Stores value? No (reflects drivers) Yes (holds last assigned value)
Drivers Can have multiple drivers Typically single driver
Update behavior Updates continuously with inputs Updates when assigned
Used in Dataflow / structural Behavioral
Default value z (undriven) x (uninitialized)
Example wire y; assign y = a & b; reg y; always @(*) y = a & b;

1.4.3. Wire/Tri and Their Values

wire/tri 0 1 x z
0 0 x x 0
1 x 1 x 1
x x x x x
z 0 1 x z

1.4.4. Vectors (Multi-bit Objects)

wire [3:0] a; // 4-bit wire vector (bits 3 down to 0)
wire [0:3] b; // 4-bit wire vector (bits 0 up to 3), same as a, no difference in functionality
wire [-1:4] c; // 6-bit wire vector (bits -1 up to 4), don't do this though, it is mildly infuriating

1.4.5. Integer Variables

We are only interested in the following integer data types:

1.4.6. Number Representation

↑ Back to top


1.5. Operators

See: IEEE 1800-2017, 11.3 Operators, pg. 255

Operators are special symbols used to build expressions. Okay let’s take it one step at a time.

Operators and Data Types (source: IEEE 1800-2017, Table 11-1) Operators and Data Types (source: IEEE 1800-2017, Table 11-1)
Operator token Name Examples
= Binary assignment a = b;
+= -= /= *= Binary arithmetic assignment a += b; –> a = a + b;
%= Binary arithmetic modulus assignment a %= b; –> a = a % b;
&= |= ^= Binary bitwise assignment a &= b; –> a = a & b;
>>= <<= Binary logical shift assignment a >>= b; –> a = a >> b;
>>>= <<<= Binary arithmetic shift assignment a >>>= b; –> a = a >>> b;
?: Conditional operator Y = (A) ? B : C; –> if (A) Y = B; else Y = C;
! Unary logical negation Y = !A; –> if (A) Y = 0; else Y = 1;
~ & ~& | ~| ^ ^~ ~^ Unary logical reduction a = ~4'b0010; –> a = 4'b1101;
+ - * / ** Binary arithmetic  
% Binary arithmetic modulus operator  
& | ^ ^~ ~^ Binary bitwise  
>> << Binary logical shift  
>>> <<< Binary arithmetic shift  
&& || -> <-> Binary logical  
< <= > >= Binary relational  
=== !== Binary case equality  
== != Binary logical equality  
++ -- Unary increment/dec. (SystemVerilog)  
{ } { { } } Concatenation, replication  

We will only cover a subset of these operators in this course.

1.5.1. Unary Logical Reduction

They are operators that take a single operand, operate on its bits, and produce a single-bit result (thus the word “reduction”).

1.5.2. Binary Bitwise Operators

1.5.3. Binary Logical Operators

This is where it gets a bit confusing. Notice how these operators result in 1 single bit, and not a vector of bits like the bitwise operators above.
Do not forget: Anything that is not 0 is logically 1/true.

1.5.4. Other Operators

1.5.5. Concatenation { }

Concatenation joins multiple signals or constants into a single wider vector. The first item listed becomes the MSBs.

assign <lhs> = { expr1, expr2, ..., exprN };

Examples:

wire [3:0] a, b;
wire [7:0] y;
assign y = {a, b};              // y[7:4] = a, y[3:0] = b

wire [1:0] hi;
wire [2:0] lo;
wire [4:0] out;
assign out = {hi, lo};          // 5-bit result: hi in top 2 bits, lo in bottom 3

wire c_out, s;
wire [3:0] x, z;
assign {c_out, s} = x[0] + z[0];  // split carry and sum of a 1-bit addition

The last example shows that concatenation can appear on the LHS as well — a useful trick for capturing the carry and sum of an addition into separate wires.

1.5.6. Replication { { } }

Replication repeats a signal or constant a specified number of times.

assign <lhs> = { N { expr } };

Examples:

wire [7:0] y;
wire a;
assign y = {8{a}};              // replicate a 8 times: y = aaaaaaaa

wire [3:0] nibble;
assign y = {2{nibble}};         // y[7:4] = nibble, y[3:0] = nibble

A common application: sign-extending a signed value.

wire [3:0] signed_in;
wire [7:0] sign_extended;
assign sign_extended = { {4{signed_in[3]}}, signed_in };
// The sign bit (MSB) of signed_in is replicated 4 times to fill the upper byte

↑ Back to top


1.6. Comments

/* 
  This is a block comment.
	It can span multiple lines.
*/
	 
// This is a single-line comment.

↑ Back to top


1.7. Compiler Directives

// Compiler directive syntax
`directive_name [arguments]

A compiler directive is a special instruction that provides information to the compiler or simulator.
For now, there is only one directive that we will be using.

`timescale <time_unit> / <time_precision>

Example:

// Tell your compiler that the time unit in your design is 1 nanosecond, and the time precision is 1 picosecond.
`timescale 1ns / 1ps

↑ Back to top


1.8. System Tasks

A system task is a built-in function provided by the Verilog language that performs a specific operation during simulation. System tasks are typically used for

Example:

// Print a silly message to the console
$display("a silly message to the console");
// Print the value of a variable as a decimal, hexadecimal, and binary number
$display("The value of a is: %d (decimal), %h (hex), %b (binary)", a, a, a);
// Print the value with specific field width and padding
$display("The value of a is: %8d (decimal, right-aligned, occupies at least 8 characters)", a);
$display("The value of a is: %08b (binary, zero-padded to 8 bits)", a);
// Print special characters
$display("This is a new line\nThis is a tab\tThis is a percent sign%%");
// Stop the simulation right there
$finish;

↑ Back to top