上节内容从总体上讲解了顺序语句的特点,always过程,函数 function,任务task等在循序语句描述中又各有特点,本节内容以always过程为主进行详细讲解。
-
always顺序语句结构
由always 引导的顺序语句,又称为always 过程语句。一般由begin…end包起来的一条或多条语句组成的。多条语句之间是按顺序执行的。
其定义的格式如下:
always@(...) begin ... end
由关键字always@引导,后跟括号(…)用来描述always块启动,所需激励的变量。例如:
always@(a,b) begin c = a & b; end
其中a , b , c是always块内需要使用的变量,当括号()内的变量任何一个发生变化,always内的过程语句就会执行一次。因此这些变量又称为敏感量。由这些敏感量组成了always需要的所有变化检测量表,称为敏感表。如:(a,b)就是敏感表。只要敏感表中任何一个变量的值有变化,always过程就会启动。
always块是由begin…end隔离的,当只有一条语句时,begin和end也可以省略。如:
always@(a,b) c = a & b; // 或者用下面的格式
always@(a or b) c = a & b;
在Verilog 2001的语法中,也允许不指定敏感量,由编译系统根据always过程使用的变量自动推断。如:
always@(*) //不具体指定敏感量,由*替代敏感量,由编译系统推断。 c = a & b; 一般系统分析always内行为确定敏感表的组成,如本例经过分析后的敏感表为(a,b). always@(*) //不具体指定敏感量,由*替代敏感量,由编译系统推断。 c <= a & b; 一般系统分析always内行为确定敏感表的组成,如本例经过分析后的敏感表为(a,b,c). 可见不同的赋值语句,(*)所对应的敏感表也是不同的。
2. always 过程内语句的特点
- always过程内的语句只能对reg型变量赋值
always过程内的语句只能对reg型变量赋值,不能对wire变量赋值,也不能使用assign赋值语句。上节内容已经介绍,过程之间不能相互嵌套。
- always过程内的语句是顺序执行的
always 过程内的语句是顺序执行,因此对过程内的语句书写顺序是有要求的。见上节内容“Verilog顺序语句”
3. assign 与 always 比较。
-
对敏感量的处理
assign 是连续赋值语句,对赋值语句右边表达式的变量实时监测,一旦变量的值变化即评估表达式的值,并进行赋值。而always是对敏感表中的敏感量实时监测,一旦敏感量的值发生变化,即启动always过程。从这一点上看,二者是一致的,可以认为assign 赋值语句就是缩小版的always过程。但对于非阻塞赋值语句,always@(*)的敏感表也包含被赋值变量,因此可以引起always过程的重入。
例:
wire [3:0] a, b, c; assign c = a & b; wire [3:0] a, b; reg [3:0] c; always@(*) c = a & b;
上例中,除了在assign赋值语句中要求c是wire 型变量,always要求reg 型变量外,其结果可以认为是相同的。
-
always过程可以处理更复杂的过程
- always过程内允许同一个变量多次赋值,而且语句执行具有顺序性。
- 有更复杂的判断即分支语句可以在always过程中使用,如if …else if …else(见分支选择语句),case… endcase(见分支选择语句),?语句等。而assign 赋值语句只能使用较简单的问号(?)语句(见分支选择语句)。
- always过程内可以同时处理多个if、case块 ,if, case 等可以相互嵌套。可给多个变量赋值,同一个变量可以多次赋值。
- always过程支持时钟边沿激励(如 posedge 与negedge),assign赋值语句不支持边沿检测激励。
例 1: 十进制加1计数器
module dec_count ( input clk, input reset, output [3:0] count ); reg [3:0] count_r; assign count = count_r; always@ (posedge clk or posedge reset)//clk信号的上升沿成为过程的检测对象。 if(reset) count_r = 0; else begin if(count_r == 9) count_r = 0; else count_r = count_r + 1; end endmodule
Verilog 2001版本中的语法允许在 module端口中直接定义寄存器型输出端口,因此也可以改成下面的写法。
module dec_count ( input clk, input reset, output reg [3:0] count ); always@ (posedge clk or posedge reset) if(reset) count = 0; else begin if(count == 9) count = 0; else count = count + 1; end endmodule
4.always过程语句中register的模型
-
组合逻辑模型
由简单的赋值语句组成always过程是组合逻辑过程,此时reg类型起到的作用与wire类型变量类似。如: always@(*) c=a&b;
例2:3-8译码器:
module dec3to8n ( input [2:0] in, output reg [7:0] nout ); always@(in) case (in) 0: nout = 8'b1111_1110; 1: nout = 8'b1111_1101; 2: nout = 8'b1111_1011; 3: nout = 8'b1111_0111; 4: nout = 8'b1110_1111; 5: nout = 8'b1101_1111; 6: nout = 8'b1011_1111; default: nout = 8'b0111_1111; endcase endmodule
例2中虽然在always过程中使用case语句,但依然是组合逻辑。
正逻辑的3/8译码器的程序如下:
module dec3to8 ( input [2:0] in, output reg [7:0] out ); always@(in) case (in) 0: out = 8'b0000_0001; 1: out = 8'b0000_0010; 2: out = 8'b0000_0100; 3: out = 8'b0000_1000; 4: out = 8'b0001_0000; 5: out = 8'b0010_0000; 6: out = 8'b1000_0000; default: out = 8'b1000_00000; endcase endmodule
-
锁存器模型
由于在顺序语句的 reg类型数据,在条件满足时更新数据,在条件撤销后具有保持原数据的能力,因此很容易描述锁存器。锁存器的用途非常广泛,著名的单片机芯片MCS8051的P0口就是利用锁存器实现地址/数据复用。
例3:锁存器(Latch)
module ALE ( input ALE, input [7:0] P0, input rd, output reg [7:0] addr, output reg [7:0] data ); reg [7:0] data_tmp; always @(ALE,P0) begin if(ALE) addr = P0; else data_tmp = P0; end always@( posedge rd) data<=data_tmp; endmodule
上面的程序描述的功能是,在ALE为高电平时,addr(地址)随P0变化而改变,在ALE的下降沿时,将P0的值锁存在地址线(addr)上。在ALE为低电平时,数据(data)随P0的值而改变,在ALE的上升沿前通过读信号的上升沿将数据捕获,这是FPGA与单片机8051或早期Intel CPU 8088的接口设计。
-
触发器模型
在时钟边沿的驱动下,寄存器的模型一般都等效为触发器,可建模为D边沿触发器,JK边沿触发器等。由于D 触发器的模型较为简单,因此一般都是以D边沿触发器作为参考模型。如:例1就可以以D触发器作为参考模型进行分析。
5. always 时钟模型
在always模型中还有一种简单的模型,该模型不需要敏感表,多由于生成仿真用的时钟。如:
always #5 clk=~clk;
由于该过程没有敏感量,默认为不需要敏感量的变化激励就可以运行,因此一定要有延迟语句保证不会陷入0延迟的死循环中,这一点与forever的行为相似。
如 always clk=~clk; 该语句是错误的,要么编译语法错误,要么引起仿真死循环,仿真不出任何波形等一系列难以理解的现象。当然正确使用该语句,还需要将变量正确初始化。
真确写法如下:
`timescale 1 ns /1 ps module tb(); reg clk = 0; //声明时初始化,或在initial 过程中初始化 always #5 clk = ~clk; endmodule
由于在综合时会自动忽略延迟,因此”always #5 clk=~clk;“语句,一定是不可综合的语句。
更多的模型建模内容请参照后续文章“阻塞与非阻塞赋值语句”的相关文章.
对应视频:
老师好,在练习时我遇到一个问题,请老师解答:
1、always(posedge clk or posedge reset) 与 always@(posedge clk , posedge areset),这两种表达方式的效果相同吗? “or”与“,”的意义是一样的吗?
是的, 两种写法都是可以的
老师好,在学习中遇到一点疑问,请老师解答:
1、always @(*) 与 always @ * ,这两种表达方式都可以吗?表达的含义一样吗?
经过测试,Vivado和Quartus都承认这两种写法,没有区别,推荐使用always @(*)