Verilog grammar learning notes 1 (combined with HDLBits question brushing website)

Posted by keyboard on Sat, 18 Sep 2021 04:16:56 +0200

Getting Start

In a digital circuit, 1 corresponds to a high-level signal and 0 corresponds to a low-level signal.

Verilog Language


The keyword assign is used for continuous assignment

assign left_side = right_side

NOT gate, logic negates the symbol!, Bitwise inverse sign~

AND gate, logic and symbol & &, bitwise and symbol&

OR gate, logic or symbol, bitwise or symbol|

The following figure shows the NOR gate, that is, add a non on the basis of the OR gate

XNOR gate, logical or symbol^~
XOR gate, logical homor symbol^

The following figure shows the same or gate, which can be regarded as adding a non or gate to the XOR gate


Vector declaration format: type [high: low] vector name

wire [99:0] my_vector;      // Declare a 100-element vector

An undeclared vector will generate a Bug during use, and it is fixed to a bit width

wire [2:0] a, c;   // Two vectors
assign a = 3'b101;  // a = 101
assign b = a;       // b =   1  implicitly-created wire
assign c = b;       // c = 001  <-- bug
my_module i1 (d,e); // d and e are implicitly one-bit wide if not declared.
                    // This could be a bug if the port was intended to be a vector.

Disabling implicit declaration requires adding a macro definition

`default_nettype none

When declaring the reg memory type, you can add the number of storage units after the reg vector name

reg [7:0] mem [255:0];   // There are 256 storage units, and each storage unit is an 8-bit reg
reg mem2 [28:0];         // There are 256 storage units, and each storage unit is a 1-bit reg

Vector partial selection operator [:]

assign out = my_vector[10]; // Part-select one bit out of the vector

Vector concatenation operator {,}, each element in the concatenation operator needs to be marked with bit width, otherwise it is illegal

assign {w,x,y,z} = {a,b,c,d,e,f,2'b11};

Vector copy operator format {num{Vector}}

assign out = ~{{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}} ^ {5{a,b,c,d,e}}; 


For the connection between parent and child modules, you need to instantiate child modules under the parent module

module mod_a ( input in1, input in2, output out ); //A mod is defined_ a
    // Module body

There are two ways to instantiate a module: by location and by name

Instantiation by location refers to one-to-one correspondence between module ports and external signals from left to right. Obviously, the disadvantage of this method is that once the port list changes, the port connections in all module instantiations need to be adjusted.

Example format: module name and example name (external signal 1, external signal 2, external signal 3,..., external signal n);

mod_a instance1 ( wa, wb, wc );

Instantiation by name is to directly correspond the signal outside the module instance to the port name of the module. In this way, the port order can be arbitrary.

Example format: module name, instance name (. Port name (external signal),. Port name (),. Port name (external signal));

mod_a instance2 ( .out(wc), .in1(wa), .in2(wb) );

Full adder expression

assign sum = a^b^cin;
assign cout = a&b | a&cin | b&cin;

Of course, in order to be closer to the adder, the above code can be written as follows

assign {cout,sum} = a + b + cin;

Another half adder expression

assign sum  = a^b;
assign cout = a&b;


Keyword always
The always statement is executed repeatedly. The always statement block executes the behavior statement from time 0; When the last statement is executed, the first statement in the statement block is executed again, and the cycle is repeated.

Combinational: always @(*)
Clocked: always @(posedge clk)

Due to the characteristics of loop execution, always statements are mostly used to simulate the generation of clock, the detection of signal behavior and so on.

always if statement

always @(*) begin
    if (condition) begin
        out = x;
    else begin
        out = y;

This is equivalent to the following successive assign statements

assign out = (condition) ? x : y;

always case statement

always @(*) begin     // This is a combinational circuit
    case (in)
      1'b1: begin 
               out = 1'b1;  // begin-end if >1 statement
      1'b0: out = 1'b0;
      default: out = 1'bx;

always casez statement

always @(*) begin
    casez (in[3:0])
        4'bzzz1: out = 0;   // The upper three digits of the input in can be any value
        4'bzz1z: out = 1;
        4'bz1zz: out = 2;
        4'b1zzz: out = 3;
        default: out = 0;


In the level triggered storage unit, the action of data storage depends on the level value of the input clock (or enable) signal. The output changes with the data input only when the latch is enabled.

When the level signal is invalid, the output signal changes with the input signal, just like passing through the buffer; When the level is valid, the output signal is latched. Any change of excitation signal will directly cause the change of latch output state, which is likely to produce oscillation due to the instability of transient characteristics.

Main hazards of Latch:

  1. The input state may change many times, which is easy to produce burrs, increasing the uncertainty of the next stage circuit;
  2. In most FPGA resources, more resources than triggers may be needed to implement Latch structure;
  3. The emergence of latches makes the static timing analysis more complex.

Ways to avoid Latch:

  1. If else or case statement, the structure must be complete
  2. Do not place the assignment signal in the assignment source or condition judgment
  3. For sensitive signal list, it is recommended to use always @ (*)

More Verilog Features

Ternary operator, similar to that in C language

(condition ? if_true : if_false)

Previously, I learned the bitwise operator between two values, but sometimes I need to bitwise operate on all bits of a vector
In fact, it is to add a bitwise operator in front of the vector, as follows

& a[3:0]     // AND: a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4'hf)
| b[3:0]     // OR:  b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4'h0)
^ c[2:0]     // XOR: c[2]^c[1]^c[0]

The advantage of this is that there is no need to write lengthy bitwise logic code

Loop for statement

for(initial_assignment; condition ; step_assignment)  begin

Topics: Verilog