Skip to content

SystemVerilog

Click on a tile to change the color scheme:

1. Preparation

1.1 Environment

IDE: Quartus Lite

Editor on macOS: VSCode + Verilog-HDL/SystemVerilog/Bluespec SystemVerilog + verilator

(Install verilator by Homebrew, and then use it as the linter in the VSCode plugin configuration.)

(Parameters for verilator: -sv --language 1800-2012 --lint-only.

Example: verilator -sv --language 1800-2012 --lint-only mycode.sv)

1.2 Example

A simple example of displaying the sequence "2021040907" with a 7-input digital display:

module seqdisplay (
    input logic rst,
    input logic clk,
    output logic [6:0] natural,
    output logic [3:0] even,
    output logic [3:0] odd
);

int counter;
logic[3:0] date [0:9] = '{ 4'h2, 4'h0, 4'h2, 4'h1, 4'h0, 4'h4, 4'h0, 4'h9, 4'h0, 4'h7 };
logic[3:0] index = 0;

initial begin
    even = 0;
    odd  = 0;
    //date = '{ 4'h2, 4'h0, 4'h2, 4'h1, 4'h0, 4'h4, 4'h0, 4'h9, 4'h0, 4'h7 };
    natural = digital_number(0);
end

always @ (posedge clk)
begin
    if (rst) begin
        counter <= 32'd4_000_000;
        index   = 0;
        natural <= digital_number(date[0]); // display 0
    end
    else begin
        if (counter == 32'd4_000_000) begin   // need update
            // display the next number
            natural <= digital_number(date[index]);
            index = index + 4'b1;
            if (index == 10) begin
                index = 0;
            end
            counter <= 0;
        end
        else begin
            counter <= counter + 32'd1;
        end
    end
end

function logic[6:0] digital_number(
    logic[3:0] number
);
    case(number)
        4'h0:   digital_number = 7'b1111110;
        4'h1:   digital_number = 7'b0110000;
        4'h2:   digital_number = 7'b1101101;
        4'h3:   digital_number = 7'b1111001;
        4'h4:   digital_number = 7'b0110011;
        4'h5:   digital_number = 7'b1011011;
        4'h6:   digital_number = 7'b1011111;
        4'h7:   digital_number = 7'b1110000;
        4'h8:   digital_number = 7'b1111111;
        4'h9:   digital_number = 7'b1110011;
        4'ha:   digital_number = 7'b1110111;
        4'hb:   digital_number = 7'b0011111;
        4'hc:   digital_number = 7'b1001110;
        4'hd:   digital_number = 7'b0111101;
        4'he:   digital_number = 7'b1001111;
        4'hf:   digital_number = 7'b1000111;
        default:
                digital_number = 7'bx;
    endcase
endfunction

endmodule

2. Basis

C++注释风格

四种基本的值来表示硬件电路中的电平逻辑: - 0:逻辑0或"假" - 1:逻辑1或"真" - x或X:未知 - z或Z:高阻

整数数值表示方法

  • 十进制('d 或 'D),十六进制('h 或 'H),二进制('b 或 'B),八进制('o 或 'O)
  • 4'b1011 // 4bit 数值
  • 32'h3022_c0de // 32bit 的数值
  • 下划线表示分割符,增强可读性,只是给人看的
  • 最前面不分配位宽编译器会自动分配

等价操作符

  • ==!=:不能比较x或z;当操作数包含一个 x 或 z,则结果为 x(uncertain)
  • ===!==:全等比较;可以比较x或z,结果为0或1

3. Data Type

data-types

两种变量:4-state data types and 2-state data types

Nets and variables are the two main groups of data types. Distinguishing them is the most important part to understand data types in Verilog.

3.1 Nets

Nets are used to connect between hardware entities like logic gates and hence do not store any value on its own.

The most popular and widely used net in digital designs is of type wire.

3.1.1 Wire

表示硬件单元之间的物理连线,由其连接的器件的输出端持续不断地驱动。

wire: can only be driven in assign statements (详细见后面)

assign output = x;

3.2 Variables

A variable on the other hand is an abstraction of a data storage element and can hold values.

3.2.1 Reg

Verilog data-type reg can be used to model hardware registers since it can hold values between assignments.

表示存储单元;保持数据的值直到被改写。

reg: can only be driven in procedural blocks (always, initial, task, function )(详细见后面)

3.3 logic

Based on Verilog, SystemVerilog introduces logic. Except for inout port, we can use logic to replace nearly any wire or reg in Verilog.

3.4 Scalar & Vector

没有声明 range 的 wire 和 reg 默认为 1-bit wide,为scalar。声明位宽的为 vector。

// 中括号内两边都为闭区间
// 倒序;0为最低位
reg [3:0]      counter ;    //声明4bit位宽的寄存器counter
wire [32-1:0]  gpio_data;   //声明32bit位宽的线型变量gpio_data
// 以下不常用
wire [8:2]     addr ;       //声明7bit位宽的线型变量addr,位宽范围为8:2
reg [0:31]     data ;       //声明32bit位宽的寄存器变量data, 最高有效位为0

切片:

wire[9:0] data_low = data[0:9] ;
addr_temp[3:2] = addr[8:7] + 1'b1 ;

指定起始、位宽和递增/递减方向:

//下面 2 种赋值是等效的
A = data1[31-: 8] ;
A = data1[31:24] ;

//下面 2 种赋值是等效的
B = data1[0+ : 8] ;
B = data1[0:7] ;

用大括号进行拼接

A = 4'b1010 ;
B = 1'b1 ;
Y1 = {B, A[3:2], A[0], 4'h3 };  //结果为Y1='b1100_0011
Y2 = {4{B}, 3'd4};  //结果为 Y2=7'b111_1100

wire[31:0] temp1, temp2 ;
assign temp1 = { byte1[7:0], data1[31:8] };  //数据拼接
assign temp2 = { 32{1'b0} };  //赋值32位的数值0  

3.5 Array

数组大小在末尾指定。

wire [7:0]       addr_bus [3:0] ; //由4个8bit wire型变量组成的数组
wire             data_bit[7:0][5:0] ; //声明1bit wire型变量的二维数组

Array assignment or initialization:

logic[3:0] date [0:9] = '{ 4'h2, 4'h0, 4'h2, 4'h1, 4'h0, 4'h4, 4'h0, 4'h9, 4'h0, 4'h7 };

3.6 Other Notes

Conversion of real to int: 直接赋值会round而不是truncate

int'(2.2 * 3.5) // round

integer $rtoi(real_val) // use system tasks will truncate

4. Building Blocks

4.1 Module

A module is a block of Verilog code that implements a certain functionality.

module <name> ([port_list]);
    // Contents of the module
endmodule

// e.g.
// Module called "dff" has 3 inputs and 1 output port
module dff (    input d,
                            input clk,
                            input rstn,
                            output reg  q);
    // Contents of the module
    always @ (posedge clk) begin
        if (!rstn)
            q <= 0;
        else
            q <= d;
    end
endmodule

A top-level module is one which contains all other modules. A top-level module is not instantiated within any other module.

// Top-level module
module design ( [port_list]); // From design perspective, this is the top-level module
    wire    _net;
  mod1  mod_inst1   ( ... ); // since it contains all other modules and sub-modules
    mod2    mod_inst2   ( ... );
endmodule

4.2 Port

input  [net_type] [range] list_of_names;    // Input port
inout  [net_type] [range] list_of_names;    // Input & Output port
output [net_type] [range] list_of_names;    // Output port driven by a wire
output [var_type] [range] list_of_names;    // Output port driven by a variable

module ( input signed a, b,
        output c);      // unsigned by default
    wire a, b;          // a, b are signed from port declaration
    reg signed c;       // c is signed from reg declaration
endmodule

Port connection in module instantiations:

module mydesign ( input  x, y, z,     // x is at position 1, y at 2, x at 3 and
                  output o);          // o is at position 4

endmodule

module design_top;
    wire [1:0]  a;
    wire        b, c;

    mydesign d0  ( .x (a[0]),    // signal "x" in mydesign should be connected to "a[0]" in this module (design_top)
                   .y (b),       // signal "y" in mydesign should be connected to "b" in this module (design_top)
                   .z (a[1]),
                   .o (c));
endmodule

4.3 assign

Signals of type wire or a similar wire like data type requires the continuous assignment of a value.

assign <net_expression> = [drive_strength] [delay] <expression of different signals or constant value>

Rule:

  • LHS: scalar or vector of net (e.g. wire); NO reg!
  • Whenever any operand on the RHS changes in value, LHS will be updated with the new value.

(It is illegal to drive or assign reg type variables with an assign statement. This is because a reg variable is capable of storing data and does not require to be driven continuously. reg signals can only be driven in procedural blocks like initial and always.)

4.4 always

always @ (event1 or event2) begin
    [multiple statements]
end

Multiple initial and always blocks can be included in one module. But they cannot be used recursively.

All of the initial and always blocks in a module are independent and parallel! (regardless of in what order they are defined)

ATTENTION: There is an confusing error that we need to avoid:

(Ref: cannot use an input for if statement in Verilog)

Avoid writing this:

always @ (posedge clk or negedge rst) begin
  if(some_var == 0) begin
    // ...
  end
end

Because, when there are multiple conditional statements in the always conditions, the top "if-else" statement should and can only use the same conditional variables as always statement. (Don't know why.)

Instead, we can split the always block into multiple ones:

always @ (posedge clk) begin

end

always @ (negedge rst) begin

end

4.5 initial

initial will be executed once at t=0.

initial-flash-2

5. Function

function return_type function_name(
    para_type_1 para_name_1, ... // parameters
);
    // function body
  /* different return methods: */
  return 3;
  function_name = 3; // assign the value to the function name
endfunction

Last update: May 21, 2021
Authors: Co1lin (6.94%), Colin (93.06%)