时间:2024-07-26 来源:网络搜集 关于我们 0
文章
简介
本系列文章主要针对FPGA初学者编写,包括FPGA的模块书写、基础语法、状态机、RAM、UART、SPI、VGA、以及功能验证等。将每一个知识点作为一个章节进行讲解,旨在更快速的提升初学者在FPGA开发方面的能力,每一个章节中都有针对性的代码书写以及代码的讲解,可作为读者参考。
伍
第五章:赋值语句
在 Verilog HDL 中常用的几种赋值语句,在前面的章节中已经了解到两种,一种是 assign,一种是 always,还有一种常用的叫做 initial(初始化),本章中将会讲解这三种常用的赋值语句的用法。
在学习数字电路时,应该了解了组合逻辑和时序逻辑的概念,通俗说组合逻辑是完全由线与运算符组成的电路,它的运行不受时钟沿控制,而时序逻辑其实是在组合逻辑上加上寄存器,最终的结果受到时钟沿的控制。
知道了组合逻辑和时序逻辑,就能很好的区分 assign 和 always 的用法了。assign 语句只能实现组合逻辑,也就是说在 assign 赋值的语句中无法加入时钟沿,只能由线和运算符组成,所以用 assign 赋值的变量必须为 wire 型,如下所示为 assign 赋值的例子。
代码示例 1:
wire a;
wire b;
wire c;
assign c = a & b;代码分析 1:
①第 1、 2 行定义两个 wire 型变量, 此处也可以定义为 reg 型, a、b 的变量类型需要根据是由什么方式被赋值决定, 若是 a、 b 为 input,则其必须为 wire型,若是 a、 b 是在本模块中使用, 使用assign 语句得到则为 wire 型, always 语句得到则为 reg 型;
②第 3 行的 c 变量由于在 assign 中被赋值,所以必须定义成 wire 型;
③第4 行是使用 assign 语句的格式。每一个 assign 语句后面只能跟随一条语句,若是有多条语句都要在 assign 中赋值,则可以写多个 assign 语句。使用assign 进行赋值时,完成的是组合逻辑,所以赋值号必须用阻塞赋值(=)。当 a或者 b 变化时, a 与 b 运算的结果会重新赋值给 c。always 语句不仅能实现时序逻辑,而且能实现组合逻辑, 实现时序逻辑如下
所示。代码示例 2:
wire a;
wire b;
reg c;
always@(posedge clk)
c <= a&b;
代码解析 2:
①第 1、 2 行定义2个 wire 型变量;
②第 3 行定义一个 reg 型变量,由于 c 需要在 always 中被赋值,所以 c 必须定义为 reg 型;
③第 4 行中的 clk,一般是由开发板晶振提供,所以此处 clk 应该被定义 input,但是在此处被省略定义。此行中 always 后面跟随的@(posedge clk) 是该条语句的敏感信号,它表述的意思是总在遇到 clk 的上升沿时,执行该 always 中的语句,posedge 为上升沿,下降沿为 negedge。在使用 always 语句时必须要有对应的敏感信号,可以为@()形式,也可以为#延时形式,但是#延时形式写在功能文件中是无意义的, 所以尽量只写到测试文件中, @()既可以出现在功能文件也可以出现在测试文件中。always 后面没有敏感信息是不允许的;
④第 5 行中使用了非阻塞赋值号(<=), 在实现时序逻辑时,赋值号需要用非阻塞赋值号。当遇到时钟上升沿时, a 与 b 运算的结果会赋值给 c。always 实现组合逻辑代码示例如下所示。
代码示例 3:
wire a;
wire b;
reg c;
always@(a or b)
c = a & b;
代码解析 3:
①第 1、 2 行定义wire型变量;
②第 3 行定义一个 reg 型变量,由于 c 需要在 always 中被赋值,所以 c 必须定义为 reg 型。虽然在此将 c 定义成 reg 型,但是由于该代码是针对组合逻辑进行描述, 所以在综合器(ISE)综合的时候会自动将 reg 综合成 wire, 但是需要知道无论描述组合逻辑还是时序逻辑,只要在 always 中被赋值,该变量必须要定义成 reg;
③第 4 行为 always 后面是有@()形式的另外一种写法,此处括号内没有写posedge 或negedge,表明此语句不是由沿触发,而是由电平触发,所以该电路实现的是跟沿无关的组合逻辑。此处括号内的 or 可改为 , ;
④为了符合语法要求,此处第 7 行的赋值号为阻塞赋值号(=)。Always 实现组合逻辑综合后的电路和 assign 实现组合逻辑综合后的电路是一样的。当 a 或者b 有变化时,则会执行该 always 内的语句。initial 语句是初始化语句, 会在上电(电路刚刚运行时) 执行一次,不会循环执行。在电路中只有可以存储数据的寄存器才有初始化的必要,所以 initial 语句中被赋值的变量也必须为 reg。某些第三方综合软件认为 initial 是不可以被综合的,也就是说不可以被写到功能文件中的,所以为了代码的兼容性,我们尽量只在测试文件中写 initial 语句。当 initial 和 always 同用时可以很好的描述出时钟。
代码示例 4:
reg clk;
initial
begin
clk = 1b1;always #5 clk = ~clk;
end
代码解析 4:
①第 1 行定义 reg 型的 clk;
②第 2、 4 行对 clk 进行初始化,初始化的值为 1bit 的二进制数据 1;
③第 5 行中~是取反号,此处的意思 clk 在每延时 5 个时间单位,则会取反一次,如此就会产生图 1 所示的方波。图1 时钟波形
在第六章中将对Verilog HDL 中的运算符号进行讲解。
未完待续
关注我们了解更多资讯