Data Types, Operators and Primitives in Verilog
Data Types
Nets
- Purpose: represent physical connections (wires).
- Declared with keywords:
wire
,tri
,wand
,wor
,supply0
,supply1
, etc. - Driven by: continuous assignments or module/gate outputs.
- Cannot hold value unless driven.
- Default initialization =
z
(high-impedance).
wire a, b, sum;
assign sum = a ^ b; // continuous drive
Corner case: multiple drivers → resolved by net type (wired-AND, wired-OR, etc.). If conflict without wired type → x
.
Net Types
Net | What it models / resolves | Notes |
---|---|---|
wire | plain resolved wire | default for ports (unless declared otherwise) |
tri | tri-state wire | same as wire (historical name) |
tri0 | tri-state with weak pull-down when undriven | handy for default 0 on bus |
tri1 | tri-state with weak pull-up when undriven | handy for default 1 on bus |
wand | wired-AND resolution | multiple drivers ANDed bitwise |
wor | wired-OR resolution | multiple drivers ORed bitwise |
triand | tri-state wired-AND | z participants ignored; else ANDed |
trior | tri-state wired-OR | z participants ignored; else ORed |
trireg | charge-storage net | holds last driven value when all drivers go z (decay optional via strengths/delays) |
supply0 | power net tied to logic 0 (strong0) | cannot be driven by logic |
supply1 | power net tied to logic 1 (strong1) | cannot be driven by logic |
uwire | unresolved wire (Verilog-2005) | compile error if multiply driven (great to catch fan-in mistakes) |
Registers
- Represent variables/storage elements.
- Declared with
reg
. - Hold last assigned value until updated.
- Used in procedural blocks (
initial
,always
). - Default initialization =
x
(unknown).
reg q;
always @(posedge clk) q <= d; // storage
⚠️ reg
does not imply flip-flop; inference depends on procedural style.
Register Types
Type | Width / Signedness | Typical use |
---|---|---|
reg | user-defined width (default 1-bit, unsigned unless declared signed ) | general procedural variable (flops/latches/comb as coded) |
integer | 32-bit signed | loop counters, arithmetic temporaries |
time | 64-bit unsigned | simulation time storage |
real | double-precision floating-point | testbench math (unsynthesizable) |
realtime | alias of real | testbench timing calcs |
event | event handle | testbench synchronization |
Parameters
- Compile-time constants.
- Used for configurability & readability.
parameter WIDTH = 8;
reg [WIDTH-1:0] data;
- Can be overridden:
- By name:
my_module #(.WIDTH(16)) u1 (...);
- By defparam:
defparam u1.WIDTH = 16;
(discouraged).
- By name:
- localparam = constant that cannot be overridden.
Examples:
// Parameter declaration
module m #(parameter WIDTH=8, parameter SIGNED=0) (
input [WIDTH-1:0] a, b,
output [WIDTH-1:0] y
);
localparam HALF = WIDTH/2; // cannot be overridden
assign y = a + b;
endmodule
// Overriding parameters
// 1) By name (preferred)
m #(.WIDTH(16), .SIGNED(1)) u1 (...);
// 2) By position (use with care; order matters as declared)
m #(16, 1) u2 (...);
Choosing Data Types for Ports
- input/output/inout must be declared as
wire
(default) orreg
explicitly. - Rules:
- Combinational outputs →
wire
. - Sequential outputs (from
always
) →reg
. - Bidirectional buses →
inout wire
.
- Combinational outputs →
module example(input wire clk,
input [7:0] a, b,
output reg [7:0] sum,
inout wire bus);
Continuous Assignment
- Syntax:
assign <net> = <expr>;
- Drives nets only.
- Updates whenever RHS changes.
assign y = a & b; // combinational logic
assign #5 y = a ^ b; // with delay
⚠️ Cannot assign to reg
with assign
.
Procedural Constructs
Initial Block
- Executes once at time 0.
- Used in testbenches (stimulus, init values).
initial begin
clk = 0;
forever #5 clk = ~clk;
end
Always Block
- Executes repeatedly whenever trigger occurs.
Syntax:
always @(<sensitivity_list>) <statements>
Examples:
Combinational:
always @(*) y = a & b; // implied sensitivity
Sequential:
always @(posedge clk or posedge rst) if (rst) q <= 0; else q <= d;
Named Blocks
- Group statements with a name → can be disabled.
always begin : block1
a = b + c;
if (stop) disable block1;
end
Verilog Primitives or Inbuilt gates & switches
n_input gates | n_output_gates | Three-state gates | Pull gates | MOS switches | Bidirectional switches |
---|---|---|---|---|---|
and | buf | bufif0 | pulldown | cmos | rtran |
nand | not | bufif1 | pullup | nmos | rtranif0 |
nor | notif0 | pmos | rtranif1 | ||
or | notif1 | rcmos | tran | ||
xnor | rnmos | tranif0 | |||
xor | rpmos | tranif1 |
n-input Gates
Basic combinational logic gates with n
inputs (2 or more).
- and, nand, or, nor, xor, xnor
- Used to build basic Boolean functions directly.
- Synthesizable (mapped to standard logic cells).
n-output Gates
Single-input, multiple-output primitives.
- buf → buffer (copies input to output(s)).
- not → inverter (logical NOT).
- Multiple outputs can be driven by a single source.
Three-State Gates
Output can be 0
, 1
, or high-impedance (Z
).
- bufif0, bufif1 → buffer with active-low or active-high enable.
- notif0, notif1 → inverter with active-low or active-high enable.
- Commonly used for tri-state buses.
Pull Gates
Provide weak constant logic values.
- pullup → weak ‘1’.
- pulldown → weak ‘0’.
- Often used for default bus values or test benches.
- Synthesizers usually replace them with explicit tie cells.
MOS Switches
Model ideal MOS transistors (digital switch-level).
- nmos, pmos → NMOS/PMOS transistors.
- cmos → CMOS transmission gate (p- and n-MOS pair).
- rnmos, rpmos, rcmos → resistive versions (weak drive strength).
- Mostly for switch-level simulation, not synthesizable in RTL.
Bidirectional Switches
Transmission gates passing signals both ways.
- tran → bidirectional wire connection.
- rtran → resistive bidirectional.
- tranif0/tranif1 → conditional pass switch, controlled by enable (low/high).
- rtranif0/rtranif1 → resistive conditional pass switch.
- Used in switch-level modeling, not synthesizable.
👉 Rule of thumb for RTL design:
- Use n-input gates and n-output gates sparingly (structural design).
- Use three-state gates for I/O buffers (top-level ports).
- Avoid MOS and bidirectional switches in RTL (simulation-only).
2’s Complement
- Negative numbers stored in two’s complement.
- Example:
4'b1011
= −5 if treated signed.
Declare signed explicitly:
reg signed [7:0] a, b;
Operand
- Can be constant, variable, net, part-select, concatenation, function call, etc.
- Bit-select:
a[3]
- Part-select:
a[7:4]
- Concatenation:
{a, b, c}
- Replication:
{4{1’b1}} → 1111
Operators
Blocking vs. Non-Blocking Assignments
Blocking (
=
): Executes immediately, sequential.a = b; c = a; // c gets new a
Non-blocking (
<=
): Updates scheduled, parallel.a <= b; c <= a; // c gets old a
- Guideline:
- Use
=
in combinational always blocks. - Use
<=
in sequential (clocked) always blocks.
- Use
Arithmetic
+
, -
, *
, /
, %
- Operands default to unsigned unless
signed
. - Division by zero →
x
.
reg signed [3:0] x = -3, y = 2;
assign z = x / y; // result = -1
Sign Operators
- Unary
+
,-
- Applies sign extension when needed.
Relational
<
, <=
, >
, >=
- Result:
1
or0
. - If any operand has
x/z
→ result =x
.
Equality/Inequality
==
,!=
→ logical compare,x/z
→x
.===
,!==
→ case equality (checksx
/z
too).
if (a === 1'bx) $display("a is unknown");
Logical Comparison
&&
, ||
, !
- Treats any
x/z
as unknown (x
).
Bitwise Logical
&
, |
, ^
, ~^
- Operates bit-by-bit.
assign y = a ^ b; // XOR
Shift
<<
,>>
→ logical shift.<<<
,>>>
→ arithmetic shift (preserve sign).
reg signed [7:0] a = -4;
assign b = a >>> 1; // arithmetic shift right → -2
Concatenation / Replication
assign x = {a, b}; // concat
assign y = {4{1’b1}}; // replicate → 1111
Reduction
Applies operator across all bits: &a
, |a
, ^a
, ~&a
, etc.
assign parity = ^data; // XOR reduction (parity bit)
Conditional (?:
)
assign out = sel ? a : b;
- If
sel = x/z
→ result =x
(unless both branches same value).
Signed expressions
$signed(expression)
- Converts
expression
into a signed expression of the same bit-width. - Interprets MSB as the sign bit (two’s complement).
Example:
reg [3:0] a = 4'b1000; // 8 (unsigned) $display("%0d", $signed(a)); // -8
$unsigned(expression)
- Converts
expression
into an unsigned expression of the same bit-width. - MSB is treated as a normal bit, not a sign bit.
Example:
reg signed [3:0] b = 4'b1000; // -8 (signed) $display("%0d", $unsigned(b)); // 8
💡Remember
- Uninitialised
reg
=x
. - Unconnected
wire
=z
. - Multiple drivers on
wire
without resolution =x
. - Division by 0 =
x
. ==
vs===
→ don’t confuse (case equality is safer for X/Z detection).- Procedural assignments:
- Blocking (
=
) → sequential. - Non-blocking (
<=
) → parallel, preferred in sequential logic.
- Blocking (
- Declared nets/regs are unsigned by default unless explicitly marked
signed
. $signed()
/$unsigned()
do not resize — width is unchanged.
Concept understood? Let's apply and learn for real