Verilog学习笔记

笔记来自正点原子官方教学视频

时间:2025/2/1

更新时间:2025/4/18

【第一期】手把手教你学领航者&启明星ZYNQ之FPGA开发篇【真人出镜】FPGA教学视频教程_哔哩哔哩_bilibili

补充内容为博主后续学习中遇到的新内容,更新方式为遇到一点更新一点。

逻辑值

        逻辑0:表示低电平,对应电路的GND;

        逻辑1:表示高电平,对应电路的VCC;

        逻辑X:表示未知电平,可能是高电平,也可能是低电平;

        逻辑Z:表示高阻态,外部无激励信号,是一个悬空状态。

数字进制格式

        二进制表示如下:4’b0101 表示4位二进制数字0101

        十进制表示如下:4’d2 表示4位十进制数字2(二进制0010)

        十六进制表示如下:4’ha 表示4位十六进制数字a(二进制1010)

        一般来说,当未指明数据的位宽时,默认为32位宽。

16’b1001_1010_1010_1001 = 16’h9AA9  这里的下划线没有含义,只是为了更好的可读性。

标识符

        标识符( identifier)用于定义模块名、端口名、信号名等。

        命名规则如下:

                ①、标识符可以是任意一组字母、数字、$符号和_(下划线)符号的组合;

                ②、但标识符的第一个字符必须是字母或者下划线;

                ③、标识符是区分大小写的;

标识符推荐写法

        1、不建议大小写混合使用;

        2、普通内部信号建议全部小写;

        3、信号命名最好体现信号的含义,简洁、清晰、易懂;

        以下是一些推荐的写法:   

                ①、用有意义的有效的名字如 sum 、cpu_addr等。     

                ②、用下划线区分词,如cpu_addr。     

                ③、采用一些前缀或后缀,比如时钟采用clk前缀:clk_50,clk_cpu;

数据类型

        在 Verilog 语言中,主要有三大类数据类型:     寄存器数据类型、线网数据类型 和 参数数据类型。

        从名称中,我们可以看出,真正在数字电路中起作用的数据类型应该是:寄存器数据类型和线网数据类型。

寄存器类型

        寄存器类型:寄存器表示一个抽象的数据存储单元,通过赋值语句可以改变寄存器储存的值。寄存器数据类型的关键字是 reg,reg 类型数据的默认初始值为不定值 x 。

//reg define 
reg    [31 : 0 ]    delay_cnt;
reg                 key_reg;  // 位宽为1
reg    [7  : 0 ]    example[13:0];  // example是一个包含14个 8位寄存器 的数组,如 example[0] 表示数组中的第一个元素,这个元素是一个 8位寄存器

// 定义方法
// 数据类型 位宽 变量名
// 若没有指明位宽,则默认位宽为1

        reg 类型的数据只能在 always 语句和 initial 语句中被赋值。      

        如果该过程语句描述的是时序逻辑,即always语句带有时钟信号,则该寄存器变量对应为触发器;      

        如果该过程语句描述的是组合逻辑,即always语句不带有时钟信号,则该寄存器变量对应为硬件连线

        当未指定数据类型时,默认为wire类型。

线网类型

        线网数据类型表示结构实体(例如门)之间的物理连线。线网类型的变量不能储存值,它的值是由驱动它的元件所决定的。

         驱动线网类型变量的元件有门、连续赋值语句、assign等。 如果没有驱动元件连接到线网类型的变量上,则该变量就是高阻的,即其值为z。

        线网数据类型包括 wire 型和 tri 型,其中最常用的就是 wire 类型。

// wire define
wire    key_flag

参数类型

        参数其实就是一个常量,在 Verilog HDL 中用 parameter 定义常量。我们可以一次定义多个参数,参数与参数之间需要用逗号隔开。每个参数定义的右边必须是一个常数表达式。

// parameter define
parameter    H_SYNC    =    11'd41;
parameter    H_BACK    =    11'd41;
parameter    H_DISP    =    11'd41;
parameter    H_FRONT   =    11'd41;
parameter    H_TOTAL   =    11'd41;

        参数型数据常用于定义状态机的状态数据位宽延迟大小等。

        采用标识符来代表一个常量可以提高程序的可读性和可维护性。

        在模块调用时,可通过参数传递来改变被调用模块中已定义的参数。

运算符

        Verilog中的操作符按照功能可以分为下述类型:     

                1、算术运算符     

                2、关系运算符     

                3、逻辑运算符     

                4、条件运算符     

                5、位运算符     

                6、移位运算符     

                7、拼接运算符

算术运算符

符号

使用方法

说明

备注

a + b 

加上 b

a - b 

减去 b

a * b 

乘以 b

a / b 

除以 b

这是整除

a % b 

模除 b

    

关系运算符     

符号

使用方法

说明

a > b 

大于 b

a < b 

小于 b

<= 

a >= b 

大于等于 b

>= 

a<= b 

小于等于 b

== 

a == b 

等于 b

!= 

a != b 

不等于 b

逻辑运算符     

符号

使用方法

说明

!a 

a的非

如果a0,那么a的非是1

&& 

a && b 

与上 b

如果ab都为1a&&b结果才为1,表示真。

|| 

a || b 

或上 b

如果a或者b有一个为1a||b结果为1,表示真。

条件运算符     

符号

使用方法

说明

? : 

a ? b : c 

如果 为真,就选择 b,否则选择 c

        举例:

result = (a >= b) ? a : b;

位运算符     

符号

使用方法

说明

~a 

 的每个位进行取反

a & b 

 的每个位与 相同的位进行相与

a | b 

 的每个位与 相同的位进行相或

a ^ b 

 的每个位与 相同的位进行异或

移位运算符   

符号

使用方法

说明

<< 

a << b 

 左移 

>> 

a >> b 

 右移 

        两种移位运算都用0来填补移出的空位。

        左移时,位宽增加。例:4’b1001 << 2 = 6’b100100;

        右移时,位宽不变。例:4’b1001 >> 1 = 4’b0100; 

        

拼接运算符

符号

使用方法

说明

{} 

{ a,b

 a  b 拼接起来,作为一个新信号

        举例:

c = { a, b[3:0] };

         若a为8位,则拼接后的c为12位宽,高8位为a,低4位为b。即:

c[11:0] = { a[7:0], b[3:0] };

位拼接操作符

符号

使用方法

说明

+:

信号名[起始位 +: 宽度]表示从起始位开始,向高位方向提取子向量
-:信号名[起始位 -: 宽度]表示从起始位开始,向低位方向提取子向量

         例子:

wire [63:0] axil_awaddr;
wire [31:0] sub_axil_awaddr;

assign sub_axil_awaddr = axil_awaddr[32 +: 32];
// axil_awaddr[32 +: 32] 表示从第 32 位开始,提取 32 位的子向量,即 axil_awaddr[63:32]

Verilog程序框架

Verilog注释

        Verilog中有两种注释方式。一种是以 // 开头的语句,它表示以//开始到本行结束都属于注释语句。

        另一种是以“/*”符号开始,“*/” 结束,在两个符号之间的语句都是注释语句,因此可扩展到多行。

Verilog关键字

        以下只列出常用关键字:

关键字

含义

module

模块开始定义

input

输入端口定义

output

输出端口定义

inout

双向端口定义

parameter

信号的参数定义

wire

wire信号定义

reg

reg信号定义

always

产生Reg信号语句的关键字

assign

产生wire信号语句的关键字

begin

语句的起始标志

end

语句的结束标志

edge/posedge/negedge

时序电路的标志

case

Case语句起始标记

default

Case语句的默认分支标志

endcase

Case语句结束标记

if

if/else语句标记

else

if/else语句标记

for

for语句标记

endmodule

模块结束定义

Verilog程序框架

        Verilog 的基本设计单元是“模块”(block)。 一个模块是由两部分组成的,一部分描述接口,另一部分描述逻辑功能。

           

        每个Verilog程序包括4个主要的部分:     端口定义、IO说明、内部信号声明、功能定义。

模块的结构

        功能定义部分有三种方法:

                ①、assign语句     描述组合逻辑

                ②、always语句     描述组合/时序逻辑

                ③、例化实例元件     如:and #2 u1(q,a,b);

        上述三种逻辑功能是并行的。

        注意: 在always块中,逻辑是顺序执行的。 而多个always块之间是并行的。

模块的调用

        在模块调用时,信号通过模块端口在模块之间传递。

        端口连接时注意位宽一致。

        注:输出端口只能连接wire类型数据。

结构语句

        initial always。

        initial 语句它在模块中只执行一次。 它常用于测试文件的编写,用来产生仿真测试信号(激励信号),或者用于对存储器变量赋初值。

        上图中,#20的含义为:延时20个单位时间后,再对sys_rst_n进行赋值。

        always 语句一直在不断地重复活动。 但是只有和一定的时间控制结合在一起才有作用。

        always 的时间控制可以是沿触发,也可以是电平触发; 可以是单个信号,也可以是多个信号,多个信号中间要用关键字 or 连接。 always 语句后紧跟的过程块是否运行,要看它的触发条件是否满足。

        以下是沿触发的always块:

        沿触发的 always 块常常描述时序逻辑行为。

        由关键词 or 连接的多个事件名或信号名组成的列表称为“敏感列表”。 

        以下是电平触发的always块:

        电平触发的 always 块常常描述组合逻辑行为。 

        如果组合逻辑块语句的输入变量很多,那么编写敏感列表会很烦琐并且容易出错。

        以上两个图中的代码是等价的,@( * )表示对后面语句块中所有输入变量的变化都是敏感的。

赋值语句

        Verilog HDL 语言中,信号有两种赋值方式:

                ①、阻塞赋值(blocking),如 b = a;     

                ②、非阻塞赋值(Non_Blocking),如 b <= a;

        阻塞赋值只有一个步骤:计算 RHS 并更新 LHS 。

        非阻塞赋值有两个步骤:

                ①、赋值开始的时候,计算 RHS ;   

                ②、赋值结束的时候,更新 LHS 。

        以下以阻塞赋值为例:

        所谓阻塞的概念是指,在同一个always块中,后面的赋值语句是在前一句赋值语句结束后才开始赋值的。

        以下以非阻塞赋值为例:

        所谓非阻塞的概念是指,在计算非阻塞赋值的RHS以及更新LHS期间,允许其他的非阻塞赋值语句同时计算RHS和更新LHS。

        非阻塞赋值只能用于对寄存器类型的变量进行赋值,因此只能用在initial块和always块等过程块中。

总结

        在描述组合逻辑always 块中用阻塞赋值 = ,综合成组合逻辑的电路结构; 这种电路结构只与输入电平的变化有关系。

        在描述时序逻辑always 块中用非阻塞赋值 <=,综合成时序逻辑的电路结构; 这种电路结构往往与触发沿有关系,只有在触发沿时才可能发生赋值的变化。

        注意:在同一个always块中不要既用非阻塞赋值又用阻塞赋值,不允许在多个always块中对同一个变量进行赋值!

条件语句

if 条件语句

        条件语句必须在过程块中使用。     

        过程块语句是指由initial和always语句引导的块语句。

        if 语句有如下注意事项:

                ①、允许一定形式的简写,如: if(a) 等同于 if(a == 1) if(!a)等同于 if(a != 1)。

                ②、if语句对表达式的值进行判断,若为0,x,z,则按假处理;若为1,按真处理。

                ③、if和else后面的操作语句可以用begin和end包含多个语句。

                ④、允许if语句的嵌套。

case 语句

        1、分支表达式的值互不相同;

        2、所有表达式的位宽必须相等;        不能用 ’bx 来代替 n’bx。

        3、casez   比较时,不考虑表达式中的高阻值;

        4、casex   不考虑高阻值z 和 不定值x。

状态机(State Machine)       

        有限状态机(Finite State Machine,简称FSM),指在有限个状态之间按一定规律转换的时序电路。

        下面介绍两种有限状态机:

        Mealy 状态机

        状态寄存器由一组触发器组成,用来记忆状态机当前所处的状态,状态的改变只发生在时钟的跳变沿。 

        状态是否改变、如何改变,取决于组合逻辑F的输出,F是当前状态和输入信号的函数。

        状态机的输出是由输出组合逻辑G提供的,G也是当前状态和输入信号的函数。

        

        Moore 状态机

状态机的设计

        状态机的设计可以遵循四段论:

                ①、状态空间定义

                ②、状态跳转

                ③、下个状态判断

                ④、各个状态下的动作

状态空间定义

        此步骤需定义状态空间当前状态以及下一状态。注意位宽保持一致

  

        左右两种方式都可以,右边的定义方式称为:独热码。 即每个状态只有一个寄存器位置位,译码逻辑简单。

状态跳转

 下一个状态判断(使用组合逻辑)

各个状态下的动作

        有以下两种写法:

        一种是直接使用assign:

        另一种是使用组合逻辑: 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值