Verilog(国际标准为IEEE 1364), 是一种专用来模型化电子系统的硬件描述语言(HDL),它通常被用于在寄存器传输级(RTL)抽象层面进行数字电路的设计和验证,它也被用于模拟电路和混合信号电路的验证,以及通用电路的设计。

硬件描述语言(例如Verilog)同其它软件编程语言是不同的,因为硬件描述语言能够描述时延和信号的强度(灵敏度)。 有两种类型的赋值操作,一种为阻塞赋值(=), 还有一种为非阻塞赋值(⇐)。非阻塞赋值允许设计人员在不需要声明或使用临时存储变量的情况下去描述一个状态机的更新。由于这些概念是Verilog语言语义的一部分,设计人员可以快速地通过相对紧凑、简明的方式对大型电路进行描述。

Verilog的语法跟C很相似。跟C一样, Verilog区分大小写并且有一个基本的预处理(虽然比ANSI C/C++复杂度小很多). 它的流控关键词 (if/else, for, while, case, etc.) 是相当的,它的运算优先级与C兼容. 句法的差异表现在: required bit-widths for variable declarations, demarcation of procedural blocks (Verilog uses begin/end instead of curly braces {}), and many other minor differences. Verilog requires that variables be given a definite size. In C these sizes are assumed from the 'type' of the variable (for instance an integer type may be 8 bits).

A Verilog design consists of a hierarchy of modules. Modules encapsulate design hierarchy, and communicate with other modules through a set of declared input, output, and bidirectional ports. Internally, a module can contain any combination of the following: net/variable declarations (wire, reg, integer, etc.), concurrent and sequential statement blocks, and instances of other modules (sub-hierarchies). Sequential statements are placed inside a begin/end block and executed in sequential order within the block. However, the blocks themselves are executed concurrently, making Verilog a dataflow language.

Verilog's concept of 'wire' consists of both signal values (4-state: “1, 0, floating, undefined”) and signal strengths (strong, weak, etc.). This system allows abstract modeling of shared signal lines, where multiple sources drive a common net. When a wire has multiple drivers, the wire's (readable) value is resolved by a function of the source drivers and their strengths.

A subset of statements in the Verilog language are synthesizable. Verilog modules that conform to a synthesizable coding style, known as RTL (register-transfer level), can be physically realized by synthesis software. Synthesis software algorithmically transforms the (abstract) Verilog source into a netlist, a logically equivalent description consisting only of elementary logic primitives (AND, OR, NOT, flip-flops, etc.) that are available in a specific FPGA or VLSI technology. Further manipulations to the netlist ultimately lead to a circuit fabrication blueprint (such as a photo mask set for an ASIC or a bitstream file for an FPGA).

一个简单的例子 - 双寄存器:

module toplevel(clock,reset);
  input clock;
  input reset;
 
  reg flop1;
  reg flop2;
 
  always @ (posedge reset or posedge clock)
    if (reset)
      begin
        flop1 <= 0;
        flop2 <= 1;
      end
    else
      begin
        flop1 <= flop2;
        flop2 <= flop1;
      end
endmodule

运算符“⇐” 在Verilog中体现了与普通的程序语言不同的地方,它被称为“非阻塞赋值”.每一个赋值与其在程序中的顺序是无关的,它们将产生相同的结果:flop1和flop2每个时钟将交换其值。

另外一种赋值运算符“=“被称为“阻塞赋值”,当采用”=“赋值的时候,目标变量随机更新。在上述例子中,如果用”=” 替代“⇐”, flop1和flop2将不会相互交换,而像传统的编程一样,编译器将简单地将flop1设置等于flop2 (并且随后忽略掉设置flop2等于flop1这个冗余的逻辑).

一个计数器电路的例子:

module Div20x (rst, clk, cet, cep, count, tc);
// TITLE 'Divide-by-20 Counter with enables'
// enable CEP is a clock enable only
// enable CET is a clock enable and
// enables the TC output
// a counter using the Verilog language
 
parameter size = 5;
parameter length = 20;
 
input rst; // These inputs/outputs represent
input clk; // connections to the module.
input cet;
input cep;
 
output [size-1:0] count;
output tc;
 
reg [size-1:0] count; // Signals assigned
                      // within an always
                      // (or initial)block
                      // must be of type reg
 
wire tc; // Other signals are of type wire
 
// The always statement below is a parallel
// execution statement that
// executes any time the signals
// rst or clk transition from low to high
 
always @ (posedge clk or posedge rst)
  if (rst) // This causes reset of the cntr
    count <= {size{1'b0}};
  else
  if (cet && cep) // Enables both  true
    begin
      if (count == length-1)
        count <= {size{1'b0}};
      else
        count <= count + 1'b1;
    end
 
// the value of tc is continuously assigned
// the value of the expression
assign tc = (cet && (count == length-1));
 
endmodule

一个延时的例子:

...
reg a, b, c, d;
wire e;
...
always @(b or e)
  begin
    a = b & e;
    b = a | b;
    #5 c = b;
    d = #6 c ^ e;
  end
 

上述的always语句展示了另外一种类型的使用,也就是一旦列表中的任何一个变量(b或e)发生变化,它将执行得到新的值。当其一发生变化,a立即被赋予一个新的值,且因为采用了阻塞赋值,b随后被赋予新的值(a的值已经变成了新的),等延迟5个时间单位以后,c被赋予b的值,并且c ^ e的值被隐蔽起来,等再过6个时间单位以后,d被赋予被隐藏的值。

一个被(initial或always块)程序中驱动的信号必须是reg类型,在此程序块外面被驱动的信号必须是wire类型,关键词reg并不一定必须是硬件的寄存器。

Verilog中常数值的定义支持一定宽度的。基本语法如下:

<Width in bits>'<base letter><number>

例子:

  • 12'h123 - 16进制的123 (用了12bits)
  • 20'd44 - 十进制的44 (用了20 bits - 0 extension is automatic)
  • 4'b1010 - 二进制的1010 (用了4 bits)
  • 6'o77 - 八进制的77 (用了6 bits)

There are several statements in Verilog that have no analog in real hardware, e.g. $display. Consequently, much of the language can not be used to describe hardware. The examples presented here are the classic subset of the language that has a direct mapping to real gates.

// Mux examples - Three ways to do the same thing.
 
// The first example uses continuous assignment
wire out;
assign out = sel ? a : b;
 
// the second example uses a procedure
// to accomplish the same thing.
 
reg out;
always @(a or b or sel)
  begin
    case(sel)
      1'b0: out = b;
      1'b1: out = a;
    endcase
  end
 
// Finally - you can use if/else in a
// procedural structure.
reg out;
always @(a or b or sel)
  if (sel)
    out = a;
  else
    out = b;
 

The next interesting structure is a transparent latch; it will pass the input to the output when the gate signal is set for “pass-through”, and captures the input and stores it upon transition of the gate signal to “hold”. The output will remain stable regardless of the input signal while the gate is set to “hold”. In the example below the “pass-through” level of the gate would be when the value of the if clause is true, i.e. gate = 1. This is read “if gate is true, the din is fed to latchout continuously.” Once the if clause is false, the last value at latchout will remain and is independent of the value of din.

// Transparent latch example
 
reg latch_out;
always @(gate or din)
 if(gate)
   latch_out = din; // Pass through state
   // Note that the else isn't required here. The variable
   // latch_out will follow the value of din while gate is
   // high. When gate goes low, latch_out will remain constant.

The flip-flop is the next significant template; in Verilog, the D-flop is the simplest, and it can be modeled as:

reg q;
always @(posedge clk)
  q <= d;

The significant thing to notice in the example is the use of the non-blocking assignment. A basic rule of thumb is to use ⇐ when there is a posedge or negedge statement within the always clause.

A variant of the D-flop is one with an asynchronous reset; there is a convention that the reset state will be the first if clause within the statement.

reg q;
always @(posedge clk or posedge reset)
  if(reset)
    q <= 0;
  else
    q <= d;

The next variant is including both an asynchronous reset and asynchronous set condition; again the convention comes into play, i.e. the reset term is followed by the set term.

reg q;
always @(posedge clk or posedge reset or posedge set)
 if(reset)
   q <= 0;
 else
 if(set)
   q <= 1;
 else
   q <= d;

Note: If this model is used to model a Set/Reset flip flop then simulation errors can result. Consider the following test sequence of events. 1) reset goes high 2) clk goes high 3) set goes high 4) clk goes high again 5) reset goes low followed by 6) set going low. Assume no setup and hold violations.

In this example the always @ statement would first execute when the rising edge of reset occurs which would place q to a value of 0. The next time the always block executes would be the rising edge of clk which again would keep q at a value of 0. The always block then executes when set goes high which because reset is high forces q to remain at 0. This condition may or may not be correct depending on the actual flip flop. However, this is not the main problem with this model. Notice that when reset goes low, that set is still high. In a real flip flop this will cause the output to go to a 1. However, in this model it will not occur because the always block is triggered by rising edges of set and reset - not levels. A different approach may be necessary for set/reset flip flops.

The final basic variant is one that implements a D-flop with a mux feeding its input. The mux has a d-input and feedback from the flop itself. This allows a gated load function.

// Basic structure with an EXPLICIT feedback path
always @(posedge clk)
  if(gate)
    q <= d;
  else
    q <= q; // explicit feedback path
 
// The more common structure ASSUMES the feedback is present
// This is a safe assumption since this is how the
// hardware compiler will interpret it. This structure
// looks much like a latch. The differences are the
// '''@(posedge clk)''' and the non-blocking '''<='''
//
always @(posedge clk)
  if(gate)
    q <= d; // the "else" mux is "implied"

Note that there are no “initial” blocks mentioned in this description. There is a split between FPGA and ASIC synthesis tools on this structure. FPGA tools allow initial blocks where reg values are established instead of using a “reset” signal. ASIC synthesis tools don't support such a statement. The reason is that an FPGA's initial state is something that is downloaded into the memory tables of the FPGA. An ASIC is an actual hardware implementation.

There are two separate ways of declaring a Verilog process. These are the always and the initial keywords. The always keyword indicates a free-running process. The initial keyword indicates a process executes exactly once. Both constructs begin execution at simulator time 0, and both execute until the end of the block. Once an always block has reached its end, it is rescheduled (again). It is a common misconception to believe that an initial block will execute before an always block. In fact, it is better to think of the initial-block as a special-case of the always-block, one which terminates after it completes for the first time.

//Examples:
initial
  begin
    a = 1; // Assign a value to reg a at time 0
    #1; // Wait 1 time unit
    b = a; // Assign the value of reg a to reg b
  end
 
always @(a or b) // Any time a or b CHANGE, run the process
begin
  if (a)
    c = b;
  else
    d = ~b;
end // Done with this block, now return to the top (i.e. the @ event-control)
 
always @(posedge a)// Run whenever reg a has a low to high change
  a <= b;

These are the classic uses for these two keywords, but there are two significant additional uses. The most common of these is an always keyword without the @(…) sensitivity list. It is possible to use always as shown below:

always
 begin // Always begins executing at time 0 and NEVER stops
   clk = 0; // Set clk to 0
   #1; // Wait for 1 time unit
   clk = 1; // Set clk to 1
   #1; // Wait 1 time unit
 end // Keeps executing - so continue back at the top of the begin

The always keyword acts similar to the “C” construct while(1) {..} in the sense that it will execute forever.

The other interesting exception is the use of the initial keyword with the addition of the forever keyword.

The example below is functionally identical to the always example above.

initial forever // Start at time 0 and repeat the begin/end forever
 begin
   clk = 0; // Set clk to 0
   #1; // Wait for 1 time unit
   clk = 1; // Set clk to 1
   #1; // Wait 1 time unit
 end

The fork/join pair are used by Verilog to create parallel processes. All statements (or blocks) between a fork/join pair begin execution simultaneously upon execution flow hitting the fork. Execution continues after the join upon completion of the longest running statement or block between the fork and join.

initial
 fork
   $write("A"); // Print Char A
   $write("B"); // Print Char B
   begin
     #1; // Wait 1 time unit
     $write("C");// Print Char C
   end
 join

The way the above is written, it is possible to have either the sequences “ABC” or “BAC” print out. The order of simulation between the first $write and the second $write depends on the simulator implementation, and may purposefully be randomized by the simulator. This allows the simulation to contain both accidental race conditions as well as intentional non-deterministic behavior.

Notice that VHDL cannot dynamically spawn multiple processes like Verilog.

The order of execution isn't always guaranteed within Verilog. This can best be illustrated by a classic example. Consider the code snippet below:

initial
  a = 0;
 
initial
  b = a;
 
initial
  begin
    #1;
    $display("Value a=%d Value of b=%d",a,b);
  end
 

What will be printed out for the values of a and b? Depending on the order of execution of the initial blocks, it could be zero and zero, or alternately zero and some other arbitrary uninitialized value. The $display statement will always execute after both assignment blocks have completed, due to the #1 delay.

The IEEE 1364 standard defines a four-valued logic with four states: 0, 1, Z (high impedance), and X (unknown logic value). For the competing VHDL, a dedicated standard for multi-valued logic exists as IEEE 1164 with nine levels.

System tasks are available to handle simple I/O and various design measurement functions during simulation. All system tasks are prefixed with $ to distinguish them from user tasks and functions. This section presents a short list of the most frequently used tasks. It is by no means a comprehensive list.

  • $display - Print to screen a line followed by an automatic newline.
  • $write - Write to screen a line without the newline.
  • $swrite - Print to variable a line without the newline.
  • $sscanf - Read from variable a format-specified string. (Verilog-2001) * $fopen - Open a handle to a file (read or write) * $fdisplay - Write to file a line followed by an automatic newline. * $fwrite - Write to file a line without the newline. * $fscanf - Read from file a format-specified string. (Verilog-2001)
  • $fclose - Close and release an open file handle.
  • $readmemh - Read hex file content into a memory array.
  • $readmemb - Read binary file content into a memory array.
  • $monitor - Print out all the listed variables when any change value.
  • $time - Value of current simulation time.
  • $dumpfile - Declare the VCD (Value Change Dump) format output file name.
  • $dumpvars - Turn on and dump the variables.
  • $dumpports - Turn on and dump the variables in Extended-VCD format.
  • $random - Return a random value.

The PLI provides a programmer with a mechanism to transfer control from Verilog to a program function written in C language. It is officially deprecated by IEEE Std 1364-2005 in favor of the newer Verilog Procedural Interface, which completely replaces the PLI.

The PLI (now VPI) enables Verilog to cooperate with other programs written in the C language such as test harnesses, instruction set simulators of a microcontroller, debuggers, and so on. For example, it provides the C functions tfputlongp() and tfgetlongp() which are used to write and read the argument of the current Verilog task or function, respectively.

For information on Verilog simulators, see the list of Verilog simulators.

  • IEEE Std 1364-2005 – The official standard for Verilog 2005 (not free).
  • IEEE P1364 – Working group for Verilog (inactive).
  • IEEE P1800 – Working group for SystemVerilog (replaces above).
  • Verilog syntax – A description of the syntax in Backus-Naur form. This predates the IEEE-1364 standard.
  • Verilog-AMS – Accellera mixed signal extensions to Verilog
  • Verilog 2001 syntax – A heavily linked BNF syntax for Verilog 2001 (generated by EBNF tools).
  • Free Verilog Utilities – RTL and Netlist parsers, Testbench generator, verilog2vhdl, vhdl2verilog, verilog2systemc, flattener, dependency browser and sorting .