前言
参考书籍:《Verilog HDL 数字设计与综合》第二版,本文档为第6章的学习笔记。由于本章也讲述的建模方式。当电路较大时,Verilog支持用户从数据流的角度对电路进行建模。数据流建模意味着根据数据在寄存器之间的流动和处理过程对电路进行描述,而不是直接对电路的逻辑门进行实例引用。计算机可以通过辅助工具将数据流级转换成门级结构,这个过程也称为逻辑综合。
在数字设计领域,RTL(Register Transfer Leveel,寄存器传输级)通常是指数据流级建模和行为级建模(下节会讲)的结合。
学习目标
- 掌握连续赋值语句(assign)、对于连续赋值语句的限制及隐式赋值语句
- 赋值延迟、隐式赋值延迟以及用于连续赋值语句的线网声明延迟
- 定义表达式、操作符和操作数
- 列表解释所有类型的操作符
- 用数据流对实际数字电路进行建模
6.1 连续赋值语句
连续赋值语句必须以关键词assign开始,其语法如下:
continuous_assign ::= assign [drive_strength] [delay3]
list_of_net_assignments;
list_of_net_assignments ::= net_assignment{ , net_assignment}
net_assignment ::= net_lvalue = expression
语法中:[drive_strength] 驱动强度是可选项,其默认值为strong1和strong0。
[delay3]延迟值是可选项,可以指定延迟时间 " #10 "表示10单位的时间延迟
- 连续赋值语句在左值必须是一个标量或向量线网(wire),或者是标量或向量线网的拼接,而不能是向量或向量寄存器(reg);
- 连续赋值语句总是处于激活状态。只要任意一个操作数发生变化,表达式就会立即重新计算,并且讲结果赋值给等号左边线网;
- 操作数(=号右边)可以是标量或向量的线网或者寄存器,也可以是函数调用;
- 赋值延迟用于控制对线网赋值的时间,根据仿真时间单位进行说明。
连续赋值语句举例
//连续赋值语句举例
//线网变量赋值
wire out, i1, i2;
assign out = i1 & i2;
//向量线网的连续赋值语句
wire [15:0] addr;
reg [15:0] addr1_bits, addr2_bits
assign addr[15:0] = addr1_bits ^ addr2_bits;
//拼接操作
assign {c_out, sum[3:0]} = a[3:0] + b[3:0] + c_in;
6.1.1 隐式连续赋值
线网可以在声明变量的时候对其进行赋值,由于线网只能被声明一次,因此对线网的隐式赋值也只能有一次。下面进行举例:
//普通的连续赋值
wire out;
assign out = in1 & in2;
//使用隐式连续赋值语句
wire out = in1 & in2;
6.1.2 隐式线网声明
如果一个信号名被用在连续赋值语句的左侧,那么编译器认为该信号是一个隐式声明的线网。
//连续赋值,out为线网型
wire i1, i2;
assign out = i1 & i2;
6.2 延迟
指定赋值延迟的方法有三种:普通赋值延迟、隐式赋值延迟、线网声明延迟
- 普通赋值延迟
如果设置10个单位的赋值语句,即当等号右侧数值发生变化时,经过十个时间单位才进行赋值,即惯性延迟。当脉冲宽度小于赋值延迟的输入变化也不会对输出产生影响。
assign #10 out = in1 & in2;
-
隐式连续赋值延迟
wire #10 out = in1 & in2;
- 线网声明延迟
wire #10 out;
6.3 表达式、操作符和操作数
和C语言类似理解,,因为后面会介绍,这里不详细介绍了。
6.4操作符类型
要分清逻辑运算符和按位运算符,分清等价运算符与赋值运算符。
6.5 数据流级建模举例
6.5.1 四选一多路选择器
方法一:使用逻辑等式
//用数据流描述的四选一多路选择器模块,采用逻辑方程
//用来与门级描述的模型进行比较
module mux4_1(out, i0, i1, i2, i3, s1, s0);
output out;
input i0, i1, i2, i3;
input s1, s0;
assign out = (~s1 & ~s0 & i0)|
(~s1 & s0 & i1)|
(s1 & ~s0 & i2)|
(s1 & s0 & i3);
endmodule
方法二:使用条件操作符
//用数据流描述的四选一多路选择器模块,采用条件操作语句
//用来与门级描述的模型进行比较
module mux4_1(out, i0, i1, i2, i3, s1, s0);
output out;
input i0, i1, i2, i3;
input s1, s0;
//采用嵌套的条件操作语句
assign out = s1 ? (s0 ? i3 : i2) : (s0 ? i1 : i0);
endmodule
6.5.2 四位全加器
方法一:数据流操作符
module fulladd4(sum, c_out, a, b, c_in);
output [3:0] sum;
output c_out;
input [3:0] a,b;
input c_in;
//指定全加器的功能
assign {c_out, sum} = a + b + c_cin
endmodule
方法二:带超前进位的全加器
比较麻烦不建议使用
6.5.3 脉动进位计数器
程序块
//边沿D触发器
module edge_dff(q, qbar, d, clk, clear);
output q, qbar;
input d, clk, clear;
wire s, sbar, r, rbar, cbar;
assign cbar = ~clear;
assign sbar = ~(rbar & s),
s = ~(sbar & cbar & ~clk),
r = ~(rbar & ~clk & s),
rbar = ~(r & cbar & d);
assign q = ~(s & qbar),
qbar = ~(q & r & cbar);
endmodule
//T触发器
module T_FF(q, clock, reset);
output q;
input clock,reset;
//调用边沿触发的D触发器
//输出q反向后到输入d
//由于qbar未用到,所以直接将其悬空不需要连接
edge_dff edff(q, , ~q, clock, reset);
endmodule
//顶层模块四位脉动计数器
module counter4(Q, clock, clear);
output [3:0] Q ;
input clock, clear;
T_FF tf0(Q[0], clock, clear);
T_FF tf1(Q[1], Q[0], clear);
T_FF tf2(Q[2], Q[1], clear);
T_FF tf3(Q[3], Q[2], clear);
endmodule
逻辑综合和后的RTL视图
激励块
module tb_counter4();
reg CLOCK, CLEAR;
wire [3:0] Q;
initial
$monitor($time, "count Q=%b, clear=%b", Q[3:0], CLEAR);
counter4 c(Q, CLOCK, CLEAR);
initial begin
CLEAR = 1'b1;
#200 CLEAR = 1'b0;
#400 CLEAR = 1'b1;
#60 CLEAR = 1'b0;
end
initial begin
CLOCK = 1'b0;
forever #20 CLOCK = ~CLOCK;
end
/* initial begin
#400 $finish;
end */
endmodule
总结
关于Verilog HDL语言的其他内容请参考,本人Verilog专栏:
https://blog.csdn.net/arm_qiao/category_11744094.htmlhttps://blog.csdn.net/arm_qiao/category_11744094.html关于数字电路技术基础请参考专栏:
https://blog.csdn.net/arm_qiao/category_11744079.htmlhttps://blog.csdn.net/arm_qiao/category_11744079.html关于FPGA开发教程请参考专栏: