Verilog Basics

George16886 (良)
13 min readAug 30, 2018

--

Very basic knowledge about Verilog

HDL

Hardware Description Language

Describe a digital system, for example, a network switch, a microprocessor, a memory or a simple flip-flop.

Top-down design methodology

From behavioral abstraction to detailed realization and complexity:

Algorithm -> Architecture -> Register Transfer Level (RTL) -> Gate Level -> Transistor Level (Switch Level)

The traditional method of electronic design is bottom-up. However, with the increasing complexity of new designs, it has to give way to new structural, hierarchical design methods.

A real top-down design allows early testing, easy change of different technologies, a structured system design and offers many other advantages.

Algorithm -> Functional specification -> High level design (block diagram)
-> Low level design (macro design/architecture design)
-> RTL coding -> Function verification (-> debug and RTL modification)
-> Logic synthesis -> Gate level simulation (-> RTL modification if needed)
-> Place and route -> Fabrication -> Post silicon validation

Verilog Abstraction Levels

Verilog supports to design at many different levels of abstraction. Three of them are very important:

  • Behavior Level: top level in VHDL, describing a system by concurrent algorithms which each of themselves is sequential.
    Functions, tasks and always blocks are the main elements.
    There is no regard to the structural realization of the design.
module and2
(in1, in2, out);
input in1,in2;
output out;
reg out;
always @(in1 or in2)
out = in1 & in2;
endmodule
  • Register Transfer Level (RTL): data flow, specifying the characteristics of a circuit by operations and the transfer of data between the registers.
    An explicit clock is used which RTL design contains exact timing bounds and operations are scheduled to occur at certain times.
module and2
(in1, in2, out);
input in1,in2;
output out;
assign out = in1 & in2;
endmodule
  • Gate Level: structure modeling, generated by synthesis tool, describing the characteristics of a system by logical links and their timing properties.
    All signals are discrete signals which can only have definite logical values 0, 1, X, or Z and the usable operations are predefined logic primitives.
module and2
(in1, in2, out);
input in1, in2;
output out;
and a1(out, in1, in2);
endmodule

Operators

Including arithmetic operators, relational operators, equality operators,
bit-wise operators and logical operator.

X: unknown, don’t care, can be 0 or 1
Z: high impedance, neither 0 nor 1

<<: left shift, multiply by 2
>>: right shift, divided by 2

<<<: signed left shift
>>>: signed right shift, do sign extension for MSB

  • Bit-wise operator (logic gate)

~: NOT
&: AND
|: OR, inclusive or
^: XOR, exclusive or
~^: XNOR, exclusive nor (equivalence)

  • Logical operator

!: NOT
&&: AND
||: OR

& can be used either in combinational AND bit-wise operation or in logical AND judgement.
&& can only used in logical AND judgement, the result id 1 bit, TRUE or FALSE).

  • Equality operators

a === b: a equal to b, including x and z (Case equality)
a !== b: a not equal to b, including x and z (Case inequality)
a == b: a equal to b, result may be unknown (logical equality)
a != b: a not equal to b, result may be unknown (logical equality)

Module

In Verilog 2001 we can define ports and port directions at the same time

module module_name 
(port_name);
port declaration
data type declaration
module functionality or structure
endmodule

Port

Three types of ports:

  • input
  • output
  • inout: bi-directional

Vector Signal

  • [7:0], little-endian convention
  • [0:7], big-endian convention

Data Type

  • Nets: physical connection between devices or structural elements.
    Must be continuously driven by continuous assignment, module or gate instantiation.
    Default initial value for a wire is Z.
wire [5:0] data;  // 6-bit wire
wire [0:31] w1, w2; // Two 32-bit wires with MSB being the 0 bit
  • Registers: abstract storage devices or data storage elements.
    Extensively used in behavioral modeling.
    Default initial value for a register is X.
reg [5:0] data;  // 6-bit vector register from MSB to LSB
  • Parameters: run-time constants

Identifier

Names of modules, ports and instances are all identifiers. First character must use a letter, other character can to use letter, number or bottom line.

Timescale

Declares the time unit and its precision.
‘timescale <time_unit>/<time_precision>
ex:‘timescale 1ns/100ps

When timescale is different in design and test-bench, the one in test-bench dominates. A way to avoid the situation is to compose .v file and include it.

Instance Port Mapping

  • In Order
module_name 
instance_name
(clock, reset, in, out);
  • By name
module_name 
#(.parameter(parameter), .param(param))
instance_name
(.clock(clock), .clock(reset), .in(in), .out(out));

Numbers

Can be different bases:

  • Binary (b or B)
  • Octal (o or O)
  • Decimal (d or D)
  • Hex (h or H)

Sized: 2'b01
Unsized: ‘d17

Structure Modeling

Build from modules with I/O interfaces, a natural representation of a digital logic circuit.

module and_or
(in1, in2, in3, in4, out);
input in1, in2, in3, in4;
output out;
wire tmp1, tmp2;
and m1(tmp1, in1, in2), m2(tmp2, in3, in4);
or (out, tmp1, tmp2);
endmodule
  • Modules contain instances of other modules or local signals, etc.
  • Module configuration is static and all run concurrently.
  • Not a convenient way to express test benches.
  • The following gates are built-in types in the simulator:

and, nand, nor, or, xor, xnor: first terminal is output, followed by inputs
buf, not: one or more outputs first, followed by one input
bufif0, bufif1, notif0, notif1: three-state drivers, output terminal first, then input, then control
pullup, pulldown: put 1 or 0 on all terminals

  • Instance names are optional, here are some examples:
and a1 (out1, in1, in2);
nand (out2, in21, in22, in23, in24);
not N1 (OUT1, OUT2, OUT3, OUT4, INA);
buf B1 (BO1, BIN);
bufif1 BF1 (OUTA,INA,CTRLA);
pullup PUP (PWRA, PWRB, PWRC);

Behavioral Modeling

Perform standard data manipulation tasks (assignment, if-then, case) in a procedural block, processes run until they delay for a period of time or wait for a triggering event.

module and_or
(in1, in2, in3, in4, out);
input in1, in2, in3, in4;
output out;
reg out;
always @(in1 or in2 or in3 or in4) begin
if(in1 & in2) out = 1;
else out = in3 & in4;
end
endmodule
  • Describe the system at a high level of abstraction.
  • A much easier way to write test benches.
  • Also good for more abstract models of circuits.
  • Easier to write, simulates faster and more flexible.
  • Verilog allowed both the model and the test-bench to be described together.
  • Specify a set of concurrently active procedural blocks.

Procedural blocks

Can drive only reg or integer data types, wire data type is not allowed.

  • always block: executes in a loop
    All blocks marked with always would run simultaneously and the statements inside would execute in parallel.
    Good for modeling/specifying hardware.
    Should have a sensitive list with level sensitive for combinational circuits or edge sensitive for flip-flops.
    Delay in always block is used in test-benches, can’t be used in real design.
// level sensitive for combinational circuits
reg [3:0] out;
always @(a or b or sel) begin
out = 4'd0; // we should set default value or connection
if (!sel) begin // for any wire in combinational circuits,
out = a; // especially when there is no "else" statement
end // for default case, which may induce an error
end
// edge sensitive for sequential circuits
reg [3:0] out;
always @(posedge clk or negdege rst) begin
if (~rst) begin // asynchronous reset
out <= 4'd0;
end else if (!sel) begin
out <= a;
end
end
// always block without a sensitive list should have a delay
reg clk;
always begin
#5 clk = ~clk;
end
  • initial block: executes only once, used in test-benches
    All blocks marked with initial run when simulation starts and terminate when control reaches the end or encounters a delay.
    Good for providing stimulus.
    Delay can be used in initial blocks.
reg clk, rst; 
integer a, b;
initial begin
clk = 0;
rst = 1;
#5 rst = 0;
#10 a = 1;
b = 0;
#10 a = 0;
b = 1;
end

Condition Statement

One thing that is common to if-else and case statement is that, if you don’t cover all the cases, that is, no “else” in if-else statement or “default” in case statement, and you are trying to write a combinational statement, the synthesis tool will infer a latch. In addition, if the Verilog machine enters into a non-covered statement, the machine hangs.

  • If-else statement: else if conditions are evaluated in order from top to bottom. Can be replaced with the “?” operator.
// priority encoder
reg out;
always @(a or b or c) begin
if (a) out = result1;
else if (b) out = result2;
else if (c) out = result3;
else out = result0;
end
// above block is the same as
always @(a or b or c)
out = (a)? result1 : (b)? result2 : (c)? result3 : result0;
// or we can declare out as a wire
wire out;
assign out = (a)? result1 : (b)? result2 : (c)? result3 : result0;
// or declare an assign at the same time
wire out = (a)? result1 : (b)? result2 : (c)? result3 : result0;
  • Case statement: conditions are evaluated at once, all possible conditions must be considered (should be full case or adding default case)
// 2 to 1 multiplexor
reg [4:0] out;
always @(a or b or sel) begin
case (sel)
1'b0: out = a;
1'b1: out = b;
endcase
end
// above block is the same as
always @(a or b or sel) begin
out = 5'd0; // blocking assignment
if (sel == 0) begin
out = a;
end else begin
out = b;
end
end
// use flip-flop as output
always @(posedge clk) begin
if (reset == 0) begin // synchronous reset
out <= 5'd0; // non-blocking assignment
end else if (sel == 0) begin
out <= a;
end else begin
out <= b;
end
end

Loops Statement

Including for, while, repeat and forever, can only be used in always blocks.

  • For loop
    In Verilog, operators ++ and -- are not supported.
reg [3:0] x;
reg [3:0] y;
always @(a, b, cin) begin
y[0] = cin;
for (i = 0 ; i < 4; i = i + 1) begin
x[i] = a[i] & b[i];
y[i+1] = x[i] & y[i];
end
cout = y[4];
end
// above block is the same as
always @(a, b, cin) begin
y[0] = cin;
x[0] = a[0] & b[0]; y[1] = x[0] & y[0]; // i = 0
x[1] = a[1] & b[1]; y[2] = x[1] & y[1]; // i = 1
x[2] = a[2] & b[2]; y[3] = x[2] & y[2]; // i = 2
x[3] = a[3] & b[3]; y[4] = x[3] & y[3]; // i = 3
cout = y[4];
end
  • While
    Not normally used in hardware implement, but used in test-benches.
module counter 
(clk, rst, enable, count);
input clk, rst, enable;
output [3:0] count;
reg [3:0] count;
always @ (posedge clk or posedge rst)
if (rst) begin
count <= 0;
end else begin : COUNT
while (enable) begin
count <= count + 1;
disable COUNT; // disable a block of code
end
end
endmodule
  • Repeat
    Specify how many times to run the block, usually used in test-benches
repeat (16) begin
$display ("Current value of i is %d", i);
i = i + 1;
end

Procedural Assignment (always block)

  • Blocking: combinational circuit, executes code sequentially inside a block
  • Non-Blocking: sequential circuit, executes code in parallel inside a block

Continuous Assignment

Describe combinational function, which is convenient for logical or data-path specifications. Combinational elements can be modeled using assign or always statements.

wire [3:0] ans = 4'hf;reg  [3:0] a;
always @ (*)
a = ans; // blocking assignment
wire [3:0] b;
assign b = ans;
// Assign statement can only be used to model combinational logic
wire [3:0] c = ans; // can define and assign wire at the same time

Sequential Assignment

Sequential elements can be modeled using only always statement.

reg  [3:0] a;
always @ (posedge clk or negedge rst) begin
if (~rst)
a <= 4'd0; // non-blocking assignment
else
a <= a_nxt;
end

FSM

Finite State Machine

  • Moore
  • Meely

Two coding style of FSM

  • Define the next-state logic combinationally and define the state-holding latches explicitly.
  • Define the behavior in a single always @(posedge clk) block.

User-defined Primitives

Define gates and sequential elements using a truth table.

  • Often simulate faster than using expressions, collections of primitive gates.
  • Gives more control over behavior with X inputs.
  • Most often used for specifying custom gate libraries.

Combination logic example

primitive drt_and
(out, a, b);
input a, b;
output out; // always have exactly one output
table // truth table
0 0 :0;
0 1 :0;
1 0 :0;
1 1 :1;
endtable
endprimitive

Sequential logic exaple

primitive dff
(q, clk, data);
output q;
input clk, data;
reg q;
table // clk data q new-q
(01) 0 : ? : 0; // Latch a 0
(01) 1 : ? : 1; // Latch a 1
(0x) 1 : 1 : 1; // Hold when d and q both 1
(0x) 0 : 0 : 0; // Hold when d and q both 0
(?0) ? : ? : -; // Hold when clk falls
? (??): ? : -; // Hold when clk stable
endtable
endprimitive

Test-bench

A top level module without inputs and outputs which is used to model the input behavior and run simulation, we declare all the inputs of DUT (Design Under Test) as reg and outputs as wire, and then drive the inputs and monitor the outputs. $finish is used to terminate the simulation.

Module test_bench
data type declaration
module instantiation
applying stimulus
display results
endmodule

Timing Control

  • Simple Delay
reg enable, a; 
#5 enable = 1;
#10 a = b;
reg clk;
parameter CYCLE = 10;
#(CYCLE/2) clk = ~clk;
  • Edge-Triggered Timing Control
reg [2:0] a;
@(in_a or in_b) a = b; // controlled by "in_a” or “in_b”
@(posedge clk) a = 3'd0; // triggered when postive edge of clock
@(negedge clk) a = 3'd2; // triggered when negative edge of clock
  • Level-Triggered Timing Control
reg [2:0] a;
wait (!enable) a = b; // wait until enable = 0

There are some system functions or tasks in Verilog:

  • Simulation time: $time, $stime, $realtime
    $time returns time as a 64-bit integer
    $stime returns time as a 32-bit integer
    $realtime returns time as a real number
  • Textual output: $display, $write, $strobe, $monitor
    $display automatically prints a new line
    $write does not print a new line character
    $strobe delays the argument evaluation just prior to advance of the simulation time, prints at the end of simulation
    $monitor prints messages once signal changes
  • Graphic output: $gr_waves, $gr_regs, $cWaves
    $gr_waves displays the argument list in a graphic window
$display ($time, “%b \t %h \t %d \t %o”, a, b, c, d); 
// supports different bases, default is %h
$gr_waves (“data %b”, data, “clk”,clk, “load”, load, “cnt”,cnt);

Function & Task

Usually used for behavior modeling and be called inside a always block, while neither of them can use a always or initial statement as well as wire data type.

  • Function is often be used for modeling combinatinal logic.
    No delay can be used, that is, [#, @, wait] can’t be used.
    Can return a value.
    Can call another function but task (since task can have a delay).
    Need at least one input and there is only one output.
    Can be in a procedural block or assign statement, but must be at RHS.
module parity_using_function 
(data_in, parity_out);
input [7:0] data_in;
output wire parity_out;
function parity;
input [31:0] data;
integer i;
begin
parity = 0;
for (i= 0; i < 32; i = i + 1) begin
parity = parity ^ data[i];
end
end
endfunction
assign parity_out = parity(data_in);
endmodule
  • Task is similar to a process.
    Delay can be used.
    Can’t return a value.
    Can call another function or task.
    Can have 0 or more input, output, inout.
    Must be in a procedural block.
    Input, output or inout to call a task must be reg data type.
task load_count;
input [3:0] load_value;
begin
@(negedge clk_50);
$display($time, "< Loading the counter with %h >", load_value);
load_l = 1’b0;
count_in = load_value;
@(negedge clk_50);
load_l = 1’b1;
end
endtask
initial begin
load_count(4’hA);
end

Simulation

Verify the functional characteristics of design at any level of abstraction. Use simulators to simulate the hardware models and test if the RTL code meets the functional requirements of the specification. We can check results from waveform or comparing output with golden patterns.

Another kind of simulation, called timing simulation, is done after synthesis or P&R (Place and Route) with applying the gate delays and wire delays to see if DUT works at rated clock speed. This is also called as SDF simulation or gate level simulation.

Synthesis

Map RTL to gates, also do the minimal amount of timing analysis to see if the mapped design is meeting the timing requirements. However, synthesis tools are not aware of wire delays (transport delay/intra assignment delay), they only know of gate delays(inertial delay/intra assignment delay).

Synthesis is constraint driven and technology independent, which mainly consists of two parts, translation and optimization:

  • Translation of Verilog (or VHDL) source to a netlist.
  • Optimization of the resulting netlist to improve speed and area.

What in Verilog can be translate into gates:

  • Structural definitions
  • User-defined primitives
  • Continuous assignment
  • Behavioral blocks
    Only when they have reasonable interpretation as combinational logic, edge, or level-sensitive latches, including blocks sensitive to both edges of the clock, changes on unrelated signals, changing sensitivity lists.

What can’t be translated:

  • Initial blocks
    Used to set up initial state or describe finite test-bench, which don’t have obvious hardware component .
  • Delays, just be ignored

Place & Route

Place all the gates and flip-flops and clock tree synthesis and reset is routed. The P&R tool output is a GDS file, used by foundry for fabricating the ASIC. Back-end team normally dumps out SPEF, RSPF, or DSPF from layout tools like ASTRO to the front-end team, who then use the read_parasitic command in tools like Prime Time to write out SDF file for gate level simulation.

  • SPEF: Standard Parasitic Exchange Format
  • RSPF: Reduced Parasitic Exchange Format
  • DSPF: Detailed Parasitic Exchange Format
  • SDF: Standard Delay Format

Minimum Clock Period

= propagation delay + setup time

SRAM

Static Random-Access Memory

  • Types:

One-port: (data_in, data_out, addr), supports 1R1W in a clock cycle.
Two-port: (data_in, data_out, addr0, addr1), supports 1R0W, 0R1W, 1R1W in a clock cycle.
Dual-port: (data0_in, data1_in, data0_out, data1_out, addr0, addr1), supports 2R0W, 0R2W, 1R1W in a clock cycle.

  • Regular ports:

CE: Chip Enable
CS: Chip Select
WE: Write Enable
OE: Output Enable

Inter Delay & Intra Delay

  • Inter assignment delay: #10 a = b;
    The value of ‘b’ should be constant for the minimum delay period to get reflected at the ’a’, that is, the pulse period of ‘b’ will be ignored if it is less than the delay period.
    Generally, inter assignment delay will act as a pulse filter.
    The time taken by a gate to change its output, also called as gate delay or inertial delay.
  • Intra assignment delay: a <= #10 b;
    The value of ‘b’ will get update to ‘a’ after delay period.
    The delay caused by the wires connecting the gates, that is, propagation delay on a wire or transport delay.
    Wires do delay the signal they carry, this is due to the wire resistance, capacitance, and inductance.

--

--