Verilog Basics (1) Verilog Basic Syntax and Notes

Basic knowledge

 0.1 Module (Module)

        A module in Verilog can be regarded as a black box with input and output ports. The black box has input and output interfaces (signals), and a certain function is achieved by performing certain operations on the input in the box. (similar to functions in C language)

Figure 1 Schematic diagram of the module

0.1.1 Module description

The top-module (top_module) structure shown in Figure 1 can be described in Verilog as:

module  top_module(
    input a,
    input b,
    output out
);

   ....... 

endmodule
  • A module starts with module and ends with endmodule
  • top_module is the module name
  • input : is the input port
  • output: is the output port
  • All code must be in the module module !

 Similarly, the sub-module (mod_a) structure shown in Figure 1 can be described in Verilog language as:

module  top_module(
    input in1,
    input in2,
    output out
);

   ....... 

endmodule

Note : Each module should be in a separate block in a .v file, and the module name is the file name (canonical code!)

0.1.2 Module input and output signals

  • output: output
  • input: input

The input and output ports of the module can see the signal of the module. If the signal type is not written, the default is the wire type signal !

// 以下这两个语句本质是一直的
input a;

input wire a;

In addition to wire-type signals, there are also reg-type signals, see section 1.4 for details !

0.1.3 Module instantiation

        As shown in Figure 1, the two input ports of the top_module are connected to the input ports of the sub-module (mod_a). How to use the function of the mod_a module in the top_module module? This requires module instantiation. Top_module can be regarded as the main function in the C language, and the secondary module mod_a can be regarded as an ordinary function, so that other functions can be called in the main function to complete the corresponding functions!

The way to instantiate mod_a in top_module is:

Module instantiation syntax : module name instance name (defines the signal connected to the port);

module  top_module(
    input a,
    input b,
    output out
);

	mod_a instance2 (.in1(a), .in2(b), .out(out));

endmodule
  • Instantiate in port order defined by mod_a: mod_a instance1 (a, b, out);
  • Instantiate by mod_a port name : mod_a instance2 (.in1(a), .in2(b), .out(out)); (this is recommended)

0.2 Logic blocks (always, generate)

0.2.1 always logic block

        The always block can build combinational logic blocks and sequential logic blocks. Complex logic operations need to be in this logic block, such as if, case, for, etc.

(1) Combination logic block

module top_module();

    always @(*) begin
        ....
    end

endmodule
  • Trigger immediately when any signal in the always logic block changes , execute the statement between begin - end
  • begin - end is used to combine multiple statements into a code block , and can be omitted when there is only one statement

(1) Sequential logic circuit

module top_module();

    always @(posedge clk) begin
        ....
    end

endmodule
  • Trigger on rising edge of clk signal
  • posedge: rising edge
  • negedge: falling edge

0.2.2 generate logic block

generate is mainly used in conjunction with the for loop. The main uses are:

  • Repeat operation on multiple bits in a vector
  • Multiple repeated instantiations of the same module (main purpose)

(1) Operation vector

module top_module(input [7:0] in,  output [7:0] out);
    genvar i;        // genvar i; 也可以定义在generate内部
    generate
        for(i=0; i<8; i++) begin: bit
             assign out[i]=^in[8-1:i];
        end
    endgenerate
endmodule

(2) The module is repeatedly instantiated multiple times

module  top_module(
    input a,
    input b,
    output out
);
    genvar i;
    generate
        for(i=0; i<8; i++)  begin: gen_mod_a   //  gen_mod_a 为每个begin_end的结构的名称
            mod_a instance2 (.in1(a), .in2(b), .out(out));
        end
    endgenerate
endmodule
  • Note: When the module is instantiated multiple times, the name of each begin_end structure must be written (gen_mod_a)
  • The simulator will identify the generated structure by gen_mod_a: gen_mod_a[0], gen_mod_a[1]....

0.3 Assignment method

There are three assignment methods in Verilog: continuous assignment, blocking assignment, and non-blocking assignment

0.3.1 Continuous assignment (assign)

assign x = y;
  • This statement means to connect the two signals of x and y, the real physical connection !
  • cannot be used in always blocks

0.3.2 Blocking assignment (=)

// 组合块
always @(*)  begin
	out1 = a ;
    a = b ;
    out2 = a ;
end
  • Blocking assignment in a combined always block
  • Execution order: Execute in sequence according to the order in the begin_end statement block. The above output results are: out1 = a, out2 = b

 0.3.3 Nonblocking assignment (<=)

// 时序块
always @(posedge clk)  begin
	out1 <= a ;
    a <= b ;
    out2 <= a ;
end
  • Use non-blocking assignments in sequential always blocks
  • Execution order: all statements in begin_end are executed in parallel , the above output results are: out1 = a, out2 = a


Chapter 1 Basic Grammar

1.1 Identifiers

(1) Purpose: Identifiers are used to define constants, variables, signals, ports, parameter names, module names, etc.

(2) Composition: any combination of letters, numbers, $, and underscores

(3) Notes:

  • Case sensitivity (Verilog and verilog are different)
  • The first character can only be a letter or an underscore (123demo is an illegal identifier)

1.2 Logical Values ​​and Logical Operations

1.2.1 Logical value

There are 4 logical values ​​in Verilog: 0, 1, x, z

  • 0: low level
  • 1: High level
  • x: means the state is unknown
  • z: Indicates high resistance state

Note: z and x here are not case-sensitive (X, Z can also be used)

1.2.2 Logical operations

(1) Logical operators: && (and), == (equal), || (or), != (not equal)

  • Such as m&&n: judge whether m and n are all true (if not 0 is true ), if true, output 1'b1, otherwise output 1'b0 (4'b1010&4'b0101 = 1'b1 )
  • The final output result is only 1 bit

(2) Bitwise operators: &, |, ~, ^, ~&, ~^, ~| 

  • Such as m&n: it is to perform the bitwise AND operation of each bit of m and each bit of n (4'b1010&4'b0101 = 4'b0000 )
  • The output result is the same as the number of bits of m/n

(3) Reduction operators: &, |, ~, ^, &, ~^, ~| 

  • When there is only one parameter involved in the operation (& is the unary operator), it means the reduction and operation, that is, the internal operation of the vector
&a [3:0] // AND:a[3]&a[2]&a[1]&a [0]相当于(a[3:0]== 4'hf)
|b [3:0] // OR: b[3]|b[2]|b[1]|b [0]相当于(b[3:0]!= 4'h0)
^c [2:0] // XOR:c[2]^c[1]^c[0]
  • 即(&4’b0101 = 0&1&0&1 = 1'b0 )
  • The final output result is only 1 bit

1.3 Representation of constants

Similar to the C language, there are three types of constants: integer, real and string.

1.3.1 Representing integer constants as decimal integers

(1) Positive number: directly write 10 to represent a decimal integer with a bit width of 32 bits (system default)

(2) Negative numbers: -10 needs to be represented in two's complement, with one more sign bit ( 1 1010)

(3) Express in scientific notation: 12.345e3 means 12345

1.3.2 Representing integer constants in radix

[换算成二进制数后的位宽]'[数制符号][与数制对应的值]

(1) Binary (b): 8'b1000_1100      

(2) Hexadecimal (h): 8'h8c

(3) Octal (o): 8'o214

(4) Decimal (d): 8'140

Precautions:

  • When representing binary, it is best to write an underscore every 4 bits to enhance readability: e.g. 8'b1000_1100 is the same as 8'b10001100
  • When x is encountered in radix notation : 4 x's in hexadecimal, 3 x's in octal  
  • When the bit width is greater than the binary number, the left side is automatically filled with 0, and when the bit width is smaller than the binary number, 2 is truncated from the left !

1.3.3 Strings (with double quotes)

(1) Each character is represented by an 8-bit ASCII code value, that is, 1byte storage space is required

(2) For example: "Hello world"   string is composed of 11 ASCII symbols and requires 11byte storage space

1.3 Annotation method

There are two main types of comments in Verilog: line comments (//) and block comments (/* .... */). The representation method is consistent with the C language!

// 行注释

/*
        块注释

*/

1.4 Variables (wire, reg)

There are two main types of variables in Verilog: wire and reg

1.4.1 wire

(1) Wire type (wire): Indicates the physical connection between circuits, and the variables defined by wire can also be regarded as signal ports

(2) When two wire signals are assigned consecutively, they will be mapped to real physical connections in the logic block. At this time, the changes of these two signal ports are synchronized !

wire a;

wire b;

assign b = a;    // 表示a与b之间生成实际的物理连线

1.4.2 reg

(1) Register type (reg): Represents an abstract data storage unit

(2) reg has the function of maintaining the state at a certain point in time

1.4.3 Usage and Precautions

(1) The variables assigned in the always and initial statements (the variables to the left of the assignment number ) are all reg variables

(2) The variable assigned in the assign statement is a wire type variable

1.5 Vectors and Parameters (Constants)

1.5.1 parameter parameter (constant)

(1) Parameter is a kind of constant, which usually appears inside the module and is often used to define the state, data bit width, etc.

parameter STATE = 1'b0;

(2) Only works on the declared file, and can be changed flexibly !

(3) Local parameter localparam, only used in this module

localparam  STATE= 1'b1’;

(4) The name of the parameter is generally uppercase to distinguish other variables 

1.5.2 Vector (vector)

vector (vector) is a collection of a group of signals , which can be regarded as a wire signal with a bit width exceeding 1 bit.

(1) Definition:

格式:input/output  wire/reg [upper:lower] vector_name

//输入输出型
input [7:0] a,b,
output reg [7:0] out

// 模块中间向量
wire [7:0] c, e;
reg [7:0] d;
  • [upper:lower] defines the bit width, such as [7:0] means the bit width is 8 bits, that is, upper=7, lower=0
  • vector_name can write multiple vectors at once

1.5.3 Vector Chip Select

  • a[3:0] 0~4 bit data of vector a
  • b[n] nth bit data of vector b
  • c[-1:-2] The lowest 2 bits of the vector c
  • c[0:3] The highest 4-bit data of the vector c

Multiplexer application: implement a 256-to-1 selector, the sel signal is used as the selection signal, when sel = 0, select in[3:0], when sel = 1, select in[7:4], and so on.

module top_module (
	input [1023:0] in,
	input [7:0] sel,
	output [3:0] out
);
	assign out = {in[sel*4+3], in[sel*4+2], in[sel*4+1], in[sel*4+0]};

	// assign out = in[sel*4 +: 4];		
	// assign out = in[sel*4+3 -: 4];	
endmodule
  • The input of the chip select signal sel is an n-bit binary number, which will be automatically converted into a decimal number when participating in the operation and acting as an index .
  • The signal segment selected for this question is: in[sel*4+3: sel*4] , but this does not conform to Verilog's chip selection grammar rules, so it should be written as:
    in[sel*4 +: 4]   means that the index starts from sel* The high 4bit signal starting from 4
    in[sel*4+3 -: 4] indicates the low 4bit signal  starting from sel*4+3
  • Or directly select each bit you need , and then use { } to concatenate into a new vector:
    {in[sel*4+3], in[sel*4+2], in[sel*4+1], in[ sel*4+0]}

Reference article: HDLBits: Learning Verilog Online (Thirteen · Problem 60-64) - Knowing (zhihu.com)

1.6 Ternary expressions

(1) Like the C language, Verilog also has ternary expressions :

condition ? if_true : if_false

When the condition is true, the expression evaluates to if_true, otherwise the expression evaluates to if_false.

(2) Application

(sel ? b : a)   // 一个二选一MUX,通过sel的值选择a或者b

always @(posedge clk)         // 一个T触发器
  q <= toggle ? ~q : q;

assign out = ena ? q : 1'bz;  // 三态缓冲器

(3) Reference article:  HDLBits: Learning Verilog Online (8. Problem 35-39) - Zhihu (zhihu.com)

1.7 Branch Statements (if-else, case)

1.7.1 if-else statement

(1) The most common form : (Advantage: all possible outputs are written, there is no unknown level output !)

if(<条件表达式 1>)
    语句或语句块 1;
else if(<条件表达式 2>)
    语句或语句块 2;
    ………
else
    语句或语句块 n;

(2) It is not recommended to use if-else nesting, there will be priority problems, resulting in logical confusion,

(3)  All if-else statements should be written in the form of (1) !

(4) Compare sequentially according to the conditional expression, there is a priority !

1.7.2 case statement

(1) Written form:

case(<控制表达式>)
    <分支语句 1> : 语句块 1;
    <分支语句 2> : 语句块 2;
    <分支语句 3> : 语句块 3;
    ………
    <分支语句 n> : 语句块 n;

    default : 语句块 n+1;
endcase

If the values ​​of <control expression> and <branch statement n> are equal, the corresponding statement is executed, otherwise the statement after default is executed!

(2) Jump out of the case statement structure immediately after executing a branch statement , and terminate the execution of the case statement.

(3)  The values ​​of <branch statement n> must be different from each other !

(4) End the case statement block with encase

(5) There is no priority between the branch statements !

(6) Specific application: Use case statement to build a multiplexer, (take a 9-to-1 multiplexer as an example)

module top_module( 
    input [15:0] a, b, c, d, e, f, g, h, i,
    input [3:0] sel,
    output [15:0] out );
    always @(*) begin
        case(sel)
            4'h0:begin out = a; end
            4'h1:begin out = b; end
            4'h2:begin out = c; end
            4'h3:begin out = d; end
            4'h4:begin out = e; end
            4'h5:begin out = f; end
            4'h6:begin out = g; end
            4'h7:begin out = h; end
            4'h8:begin out = i; end
            default: out = 16'hffff;
        endcase
    end
endmodule

1.8 for loop statement

(1) Written form:

integer i;
always @(*)  begin 
    for(i=0; i<n; i++)  begin: for_name
        <循环语句>
    end
end
  • Execute <loop statement> n times
  • for_name is the name of each loop

1.9 Relational operators (>, <, >=, <=)

  • Returns 1 if the result of the operation is true
  • Returns 0 if the result of the operation is false
  • If an operand value is indeterminate (x) , the return value is x

2.0 Concatenation operator ({ , })

2.0.1 Splicing

Use a pair of curly braces and commas to form the " { , } " concatenation operator, and the comma-separated data are sequentially concatenated into new data !

wire [1:0] a;
wire [3:0] b;
wire [5:0] c;
wire [11:0] d = {a, b, c} 

2.0.2 Shift by splicing

Splicing on the left to move right, and splicing on the right to move left!

always @(posedge clk) begin
    if(rst_n == 1'b0)
        out <= 4'b0;
    else
        out <= {in, out[3:1]};    // 右移
end

2.0.2 Repeated operations in connectors

Syntax:  {number of repetitions{vector}}

{3{a}} = {a, a, a}
{3'd5, {2{3'd6}}}   // 9'b101_110_110.

3 binary full adder

  • a, b are input 1bit data
  • cin is the carry input of the previous adder
  • cout is the carry output of this adder
  • sum = a+b

Code:

module add1 (
	input a,
	input b,
	input cin,	
	output sum,
	output cout
);
	assign sum = a^b^cin;
	assign cout = (a&b) | (a&cin) | (b&cin);
endmodule

4 hexadecimal full adder

The hexadecimal full adder is shown in the figure above, which can be composed of 16 binary full adders in the previous section.

The hexadecimal full adder code implemented in Verilog is:

module add16 (	
	input [15:0] a,
	input [15:0] b,
	input cin,
	
	output [15:0] sum,
	output cout
);
	wire [16:0] Add_cin;
	assign Add_cin[0] = cin;    // 上图中第一个二进制加法器进位输入为0 assign Add_cin[0] = 1b'0;

//  用 generate 进行模块多次实例化
// generate 应用范围:对矢量(vector)多个位重复操作,模块重复实例化
	genvar i;
	generate
		for(i=0; i<16; i++) begin: gen_add16		// gen_add16 为每个begin_end的结构,仿真器会通过他来标识生成结构,gen_add16[0],gen_add16[1]....
			add1 Add16(.a(a[i]), .b(b[i]), .cin(Add_cin[i]), .sum(sum[i]), .cout(Add_cin[i+1]));
		end
	
	endgenerate
	
	assign cout = Add_cin[16];

endmodule

5 Parameter passing in modules

5.1 Defining a module that can pass parameters

module counter
// 参数传递
#(
    parameter COUNT_MAX = 25'd24_999_999,
    parameter STATE     = 1'b0            // 多个参数用逗号隔开
)
(
    input  wire  sys_clk,
    output reg led_out
);
// 代码主体
endmodule

5.2 Instantiation of a block with parameters

// 参数传递
#(
   .COUNT_NUM( 25'd24_999_999),        // 传入参数
   .STATE(1'b0)
)
counter1_init      // 实例化模块的名称位置
(
    .sys_clk   (sys_clk),
    .led_out(led_out)
); 

 

References:

[1] Wildfire "FPGA Verilog Development Practical Guide": [Wildfire] FPGA Verilog Development Practical Guide - Based on Altera EP4CE10 Journey Pro Development Board - [Wildfire] FPGA Verilog Development Practical Guide - Based on Altera EP4CE10 Journey Pro Development Board Documentation (embedfire .com) https://doc.embedfire.com/fpga/altera/ep4ce10_pro/en/latest/index.html

[2] HDLBits Chinese guide: HDLBits Chinese guide-zhihu (zhihu.com)

Guess you like

Origin blog.csdn.net/jac_chao/article/details/123744724