0%

Verilog基础学习笔记

资源

HDLBits 基础题目学习

Github 上有哪些优秀的 Verilog/FPGA 项目?

Complex Digital Systems MIT课程

基础

语法

相比于VHDL的entity,Verilog的设计由module组成,其基本表示如下(Verilog-2001和Verilog-1995支持的写法略有不同)

1
2
3
4
5
6
7
8
module top_module ( output out ); 
// Verilog-2001
endmodule

module top_module ( out );
output out;
// Verilog-1995
endmodule

每一个模块由module…endmodule组成,module后面跟的是模块名称以及参数,括号内包含了输入或输出参数,分别以input或output关键字修饰。Verilog的行末如同C语言,都需要带上分号。

关键字

  • module / endmodule: 模块定义,模块的连接可以按名称或者按参数顺序,如

    1
    2
    3
    4
    5
    module top_module(input in, output out);
    //module定义:module top_module(input a, output o);
    module_m m1(.a(in),.o(out)); //使用名称连接
    module_m m2(in,out);// 使用参数顺序
    endmodule
  • assign: 用于连续赋值,相当于硬件连线操作,非单个时刻的事件

    例如将模块输入输出接在一起

    1
    2
    3
    module top_module(input in, output out);
    assign out = in;
    endmodule
  • input / output / inout: 端口输入/输出/双向定义类型

  • wire: wire类型变量相当于物理连线,只能被assign赋值

  • reg: reg类型变量相当于寄存器,只能在always和initial语句块中被赋值

  • generate: 生成代码块,总是以generate...endgenerate组成,例如

    1
    2
    3
    4
    5
    6
    genvar i;
    generate
    for(i=0;i<100;i++)begin:gen
    //...generate by i
    end
    endgenerate

    其中begin:gen的gen为必须的代码块名称,for循环内部可引用i自动生成

运算符

  • 位运算符:输出与输入数据位数等长,包括&, |, ~, ^,分别代表与,或,非以及异或

  • 逻辑运算符:即把两侧都看作一个单独的值进行操作,输出只有一位,其中包括&&, || , !,分别代表逻辑与,逻辑或,以及逻辑非

  • 关系运算符:> < <= >= == !=

  • 拼接运算符:用于拼接多个符号,如{a,b},花括号前可带数字代表重复,例如{{2{a}},{2{b}}}等同于aabb拼接在一起

  • 移位运算符: << >>

  • 归约操作符:只有一个操作数的逻辑运算符,表示对某个向量操作数逐位进行操作,最终输出一位的结果,其中包括包括:归约与(&),归约与非(~&),归约或(|),归约或非(~|),归约异或(^),归约同或(~^)。

    1
    2
    A = 4'b1010 ;
    &A ; //相当于 1 & 0 & 1 & 0 = 1'b0,可用来判断变量A是否全1
  • 双目运算符:同C语言里的双目运算符,形如condition_expression ? true_expression : false_expression

特性

Vector

类似C语言的数组,Verilog变量可以被声明为多个元素的vector,其格式为

1
type [upper:lower] vector_name;

例如wire [7:0] w;为8bit的名为w的wire(最高位在第七位),访问变量可以只截取部分,但是不能逆定义顺序访问,如用w[0:7]访问刚才定义的w是非法的。

数值表示

Verilog的四种电平:0,1,x(未知),z(高阻)

通常数值表示以位宽'类型 数值来表示,例如

1
2
3'b000 //二进制
4'hef //十六进制

位宽即开头数字不一定需要指定,但是'是必须的,类型有b,h,d,o分别代表二进制,十六进制,十进制,八进制。数值中间可以增加下划线(对数值本身无影响)

带参数例化

可以在声明模块时定义参数,用#号开头即可声明参数列表,但是例化时参数列表在模块名之前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module m //#号开头表示参数列表
#( parameter p1 = 0,
parameter p2 = 0)
( //以下为端口列表
input clk,
input x,
output y
);

endmodule

//例化时指定参数
m #(.p1(2),.p2(2))
m_name(
.clk(clk),
.x(x),
.y(y)
);

第二种办法是在模块内部定义parameter,再用defparam a.para=xxx指定某个类型的模块的参数。但这种办法一般不建议使用。

赋值类型

  • 连续赋值:使用assign关键字的赋值,赋值左值必须是wire类型,赋值过程相当于硬件连线,时间上连续,语句形如assign x = y; ,多用于组合逻辑的门级描述。

  • 过程阻塞赋值:在always @(*)语句块内的赋值,赋值左值必须是reg类型,其他与连续赋值基本相似,语句形如x=y;,多用于组合逻辑的行为描述

  • 过程无阻塞赋值:在always @(posedge clk)内的赋值,即带时钟信号的赋值,赋值左值必须为reg类型,相当于触发器锁存行为,语句形如x <= y;,多用于时序逻辑的行为描述。

    Blocking vs. Non-Blocking Assignment

    There are three types of assignments in Verilog:

    • Continuous assignments (assign x = y;). Can only be used when not inside a procedure (“always block”).
    • Procedural blocking assignment: (x = y;). Can only be used inside a procedure.
    • Procedural non-blocking assignment: (x <= y;). Can only be used inside a procedure.

    In a combinational always block, use blocking assignments. In a clocked always block, use non-blocking assignments. A full understanding of why is not particularly useful for hardware design and requires a good understanding of how Verilog simulators keep track of events. Not following this rule results in extremely hard to find errors that are both non-deterministic and differ between simulation and synthesized hardware.