verilog语法刷题知识点总结
- 1.状态机
- 2.任务和函数的区别
- 3.case,casez和casex
- 4.随机数产生关键字
- 5.运算符优先级
- 6.运算符的特殊注意点及特殊运算符
- (1)移位运算符
- (2)等式运算符
- (3)动态位宽截取运算符
- (4)求余运算符(%)
- 7.testbench知识点
- 8.乘法器
- (1)分类
- (2)串行移位加法器
- (3)并行乘法器
- (4)查找表乘法器和加法树乘法器这里不再详述
- 9.用户定义原语(UDP)
- (1)简述
- (2)语法
- (3)注意事项
- 10.generate-for注意点
- 11.标识符要求
- 12.有符号数与无符号数
- (1)**注意**
- (2)变量
- (3)有符号数和无符号数运算
- (4) #### **注意**
1.状态机
(1)三段式状态机的组成:三段式状态机,第一段用时序逻辑描述(现态);第二段用组合逻辑描述状态转移(次态);第三段用时序逻辑描述输出,第三段可以是多个always块。
(2)二段状态机中,不能为了减少代码长度,以便综合的面积,可以减少else情况,if else中少写else会产生锁存器。
2.任务和函数的区别
不同点 | 函数(function) | 任务(task) |
---|---|---|
时序逻辑 | 不能包含延时和时序控制逻辑,不能有非阻塞性赋值 | 可以包含延时和时序控制逻辑 |
输入 | 至少有一个输入 | 可以没有输入或可以有多个输入 |
输出 | 没有输出 | 可以没有输出或有多个输出 |
返回值 | 有一个返回值 | 没有返回值 |
调用关系 | 可以调用其他函数,不能调用任务 | 可以调用函数和任务 |
综合性 | 可综合 | 有的编译器可综合,有的编译器不可综合 |
3.case,casez和casex
(1)case:敏感事件表达式和各项之间的匹配是一种全等匹配,完全相同才匹配;
(2)casez:敏感事件表达式和各项之间的匹配不考虑高阻态z;
(3)casex:敏感事件表达式和各项之间的匹配不考虑高阻态z和随机状态x;
4.随机数产生关键字
(1)情况一:b>0,产生一个(-b+1)~(b-1)间的随机数;
parameter b = 60;
reg [23:0] rand;
rand = $random%b;
(2)情况二:b>0,产生一个0~(b-1)间的随机数;
parameter b = 60;
reg [23:0] rand;
rand = {$random}%b;
(2)情况二:max>0且min>0,产生一个min~max间的随机数;
parameter max = 60;
parameter min = 30;
reg [23:0] rand;
rand = min+{$random}%(max-min+1);
5.运算符优先级
6.运算符的特殊注意点及特殊运算符
(1)移位运算符
移位运算符分为分为两种,逻辑移位运算符和算数移位运算符:
A逻辑移位运算符
特点:用0填补空位
“>>”:逻辑右移运算符;
“<<”:逻辑左移运算符;
B算术移位运算符
特点:只有算术右移运算符,用原最高位填补空位,通过算术右移可以实现有符号数的除法;
“>>>”:逻辑右移运算符;
(2)等式运算符
有四个运算符:
等于:== 和 ===
不等于:!= 和 !==
区别:
== 和 != 不考虑高阻态z和未知状态x;
=== 和 !== 要考虑高阻态z和未知状态x;
注意:(与casez和casex不同)
== 和 !=:只要表达式两边有一个存在高阻态z或未知状态x,判定结果为未知状态x;
casez和casex:只要表达式中存在高阻态z或高阻态z和未知状态x时,对应位判定为匹配。
以 == 和 === 为例:
(3)动态位宽截取运算符
语法:vect[base
±
\pm
±:width]
说明:base表示起始位,+表示升序截取,-表示降序截取,width表示截取位宽;
注意:base可变但是width必须为常量;
例子:将data_in,从第3位起,正序截取4位给data_out
代码
module function_test_top(
input [7:0] data_in,
output [3:0] data_out
);
assign data_out = data_in[3+:4];
endmodule
testbench
`timescale 1ns/1ns
module function_test_tb();
reg [7:0] data_in ;
wire [3:0] data_out ;
initial begin
data_in = 8'b0000_0000;
#20;
data_in = 8'b0110_1100;
#20;
data_in = 8'b1101_0110;
#20;
data_in = 8'b1001_1100;
end
function_test_top u_function_test_top(
.data_in (data_in),
.data_out (data_out)
);
endmodule
结果:
(4)求余运算符(%)
问题:10%(3),10%(-3),(-10)%3,(-10)%(-3)四个表达式结果为多少?
答案:1,1,-1,-1
注意点:前面的数决定符号
代码:
`timescale 1ns / 1ps
module remainder(
output [7:0] out_one ,
output [7:0] out_two ,
output [7:0] out_three ,
output [7:0] out_four
);
assign out_one = 10%(3);
assign out_two = 10%(-3);
assign out_three = (-10)%(3);
assign out_four = (-10)%(-3);
endmodule
testbench
`timescale 1ns / 1ps
module remainder_tb();
wire [7:0] out_one ;
wire [7:0] out_two ;
wire [7:0] out_three ;
wire [7:0] out_four ;
remainder u_remainder(
.out_one (out_one ),
.out_two (out_two ),
.out_three (out_three ),
.out_four (out_four )
);
endmodule
结果:
7.testbench知识点
(1)初始化时用阻塞赋值和非阻塞赋值的不同
A描述
当初始化采用阻塞赋值,值的变化和时钟跳变同时发生,当前时钟边沿时可以采到数据变化的;
当初始化采用非阻塞赋值,值的变化会在时钟跳变后发生,下一个时钟边沿才能采集到数据变化;
B例子:采集一拍数据变化
a代码
module function_test_top(
input sys_clk ,
input sys_rst_n ,
input data_in ,
output reg data_out
);
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
data_out <= 1'b0;
else
data_out <= data_in;
end
endmodule
b 情况一:testbench初始化阻塞赋值
testbench
`timescale 1ns/1ns
module function_test_tb();
reg sys_clk ;
reg sys_rst_n ;
reg data_in ;
wire data_out ;
parameter T = 20;
initial begin
sys_clk = 1'b1;
sys_rst_n = 1'b0;
data_in = 1'b0;
#(2*T);
sys_rst_n = 1'b1;
#(2*T);
data_in = 1'b1;
end
// initial begin
// sys_clk <= 1'b1;
// sys_rst_n <= 1'b0;
// data_in <= 1'b0;
// #(2*T);
// sys_rst_n <= 1'b1;
// #(2*T);
// data_in <= 1'b1;
// end
always#(T/2) sys_clk = !sys_clk;
function_test_top u_function_test_top(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n),
.data_in (data_in ),
.data_out (data_out )
);
endmodule
结果:
c 情况一:testbench初始化非阻塞赋值
`timescale 1ns/1ns
module function_test_tb();
reg sys_clk ;
reg sys_rst_n ;
reg data_in ;
wire data_out ;
parameter T = 20;
// initial begin
// sys_clk = 1'b1;
// sys_rst_n = 1'b0;
// data_in = 1'b0;
// #(2*T);
// sys_rst_n = 1'b1;
// #(2*T);
// data_in = 1'b1;
// end
initial begin
sys_clk <= 1'b1;
sys_rst_n <= 1'b0;
data_in <= 1'b0;
#(2*T);
sys_rst_n <= 1'b1;
#(2*T);
data_in <= 1'b1;
end
always#(T/2) sys_clk = !sys_clk;
function_test_top u_function_test_top(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n),
.data_in (data_in ),
.data_out (data_out )
);
endmodule
结果:
(2)begin end语句块和fork join语句块的不同
A begin end
描述:串行语句块
特点:
块内语句顺序执行,块内上一条语句执行完才能执行下一条语句;
最后一条语句执行完,程序跳出该语句块。
例子:
`timescale 1ns/1ns
module function_test_tb();
reg data_a ;
reg data_b ;
reg data_c ;
initial begin
data_a = 1'b0;
data_b = 1'b0;
data_c = 1'b0;
#(20) data_a = 1'b1;
#(10) data_b = 1'b1;
#(30) data_c = 1'b1;
end
endmodule
结果:数据变化发生在20ns,30ns和60ns
B fork join
描述:并行语句块
特点:
块内语句同时执行;
耗时最长的语句块执行完后,程序跳出该语句块。
例子
`timescale 1ns/1ns
module function_test_tb();
reg data_a ;
reg data_b ;
reg data_c ;
initial fork
data_a = 1'b0;
data_b = 1'b0;
data_c = 1'b0;
#(20) data_a = 1'b1;
#(10) data_b = 1'b1;
#(30) data_c = 1'b1;
join
endmodule
结果:数据变化发生在20ns,10ns和30ns
C 两种语句块可以嵌套使用
例子:
`timescale 1ns/1ns
module function_test_tb();
reg data_a ;
reg data_b ;
reg data_c ;
initial begin
data_a = 1'b0;
data_b = 1'b0;
data_c = 1'b0;
#(10) data_a = 1'b1;
#(20) data_b = 1'b1;
#(30) data_c = 1'b1;
fork
#(10) data_a = 1'b0;
#(20) data_b = 1'b0;
#(30) data_c = 1'b0;
join
end
endmodule
结果:数据上升沿发生于10ns,30ns,60ns;数据下降沿发生于70ns,80ns,90ns。
8.乘法器
(1)分类
verilog中根据实现方法的不同,将乘法器分为串行移位乘法器、并行乘法器、查找表乘法器和加法树乘法器。
(2)串行移位加法器
优缺点:优点是资源消耗较少,缺点是比较慢(低位算了才能算高位);
实现方法:(和我们小学学的乘法运算步骤一样,只不过需要先转换为2进制)
第一步:将被乘数和乘数换算为二进制;
第二步:将被乘数乘以乘数的每一位;
第三步:将上一步的结果左移乘数的对应位;
第四步:结果相加输出;
例子:4d’11乘以4’d5
module function_test_top(
input [3:0] mul_one,
input [3:0] mul_two,
output reg [7:0] data_out
);
integer i;
always@(*)begin
data_out = 'd0;
for(i=0;i<8;i=i+1)begin
if(mul_two[i])
data_out = data_out + (mul_one<<i);
end
end
endmodule
`timescale 1ns/1ns
module function_test_tb();
reg [3:0] mul_one ;
reg [3:0] mul_two ;
wire [7:0] data_out;
initial begin
mul_one = 'd0;
mul_two = 'd0;
#20;
mul_one = 4'd11;
mul_two = 4'd5;
#20;
mul_one = 4'd6;
mul_two = 4'd3;
end
function_test_top u_function_test_top(
.mul_one (mul_one ),
.mul_two (mul_two ),
.data_out (data_out)
);
endmodule
电路:
结果:(结果以10进制数显示)
(3)并行乘法器
优缺点:优点速度快(高低位同时算),缺点是资源消耗较多;
实现方法:直接用乘法运算符实现;
例子:4d’11乘以4’d5
`timescale 1ns / 1ps
module mutplier_test(
input [3:0] mul_one,
input [3:0] mul_two,
output [7:0] data_out
);
assign data_out = mul_one*mul_two;
endmodule
testbench
`timescale 1ns / 1ps
module mutiplier_test_tb();
reg [3:0] mul_one ;
reg [3:0] mul_two ;
wire [7:0] data_out;
initial begin
mul_one = 'd0;
mul_two = 'd0;
#20;
mul_one = 4'd11;
mul_two = 4'd5;
#20;
mul_one = 4'd6;
mul_two = 4'd3;
end
mutplier_test u_mutplier_test(
.mul_one (mul_one ),
.mul_two (mul_two ),
.data_out (data_out)
);
endmodule
电路:
(4)查找表乘法器和加法树乘法器这里不再详述
9.用户定义原语(UDP)
(1)简述
用户定义原语由User Defined Primitives直接翻译得到,简称UDP。主要是用来自定义仿真使用的基本逻辑器件模块。UDP是由查找表的方法确定输出,用仿真器仿真时,速度快。只能描述能用简单真值表表示的组合或者时序逻辑。
(2)语法
primitive 元件名(输出端口名,输入端口名1,输入端口名2,输入端口名3,...... ,输入端口名n)
output 输出端口名;
input 输入端口名1,输入端口名2,输入端口名3,...... ,输入端口名n;
reg 输出端口名;
initial begin
输出端口寄存器或时序逻辑内部寄存器赋初值(0,1或X);
end
table
//输入1 输入2 输入3 输入4 ... 输入n :输出 ;
逻辑值 逻辑值 逻辑值 逻辑值 ... 逻辑值 :逻辑值 ;
逻辑值 逻辑值 逻辑值 逻辑值 ... 逻辑值 :逻辑值 ;
逻辑值 逻辑值 逻辑值 逻辑值 ... 逻辑值 :逻辑值 ;
逻辑值 逻辑值 逻辑值 逻辑值 ... 逻辑值 :逻辑值 ;
endtable
endprimitive
(3)注意事项
A 可以有多个输入端,但是只能有一个输出端;
B 所有端口只能有一位位宽;
C 在真值表中只能出现0,1,x三种逻辑,高阻态z是不允许的;
D initial语句用于时序电路内部寄存器赋初值,只允许0,1,x三种逻辑值;
E 表示时序逻辑的UDP输出必须声明位reg类型,并且可以使用initial进行初始化;
F 表示时序逻辑的UDP,状态表:<输入1><输入2>…<输入N> : <当前状态> : <下一状态>
10.generate-for注意点
(1)generate-for循环必须使用genvar关键字定义循环变量;
(2)generate-for循环中的内容必须使用begin-end包括起来,哪怕只有一句;
(3)generate-for循环begin后的名字不可以省略;
(4)generate-for循环同一个模块内的begin-end块名字不能一样。
11.标识符要求
(1)标识符可以由字母、数字、下划线和$组成;
(2)标识符必须以字母或下划线(_)开头;
(3)标识符的长度不能超过1024个字符;
(4)标识符区分大小写;
(5)标识符不能是Verilog关键字,例如"module"、"endmodule"等;
(6)标识符不能包含特殊字符,例如空格、制表符等。
请注意,虽然Verilog标识符允许使用数字,但是最好将数字用于表示信号位宽或者其它与数字相关的意义,而不是用作变量名的一部分。这有助于提高代码的可读性和可维护性
12.有符号数与无符号数
(1)注意
本题均以求余运算为例,求余运算注意点:被余数决定符号。
(2)整数
A)10进制数
特点:10进制数默认为有符号数。
例子:2%3和4294967294%3
说明:
因为10进制数默认位宽为32位,所以2和4294967294分别表示32’b0000_0000_0000_0000_0000_0000_0000_0010和32’b1111_1111_1111_1111_1111_1111_1111_1110,实际分别为有符号数2和-2,所以余3的结果为2和-2。
代码:
`timescale 1ns / 1ps
module remainder(
output [31:0] out_data_1,
output [31:0] out_data_2
);
assign out_data_1 = 2%3;
assign out_data_2 = 4294967294%3;
endmodule
`timescale 1ns / 1ps
module remainder_tb();
wire [31:0] out_data_1;
wire [31:0] out_data_2;
remainder u_remainder(
.out_data_1 (out_data_1),
.out_data_2 (out_data_2)
);
endmodule
结果:
B)未声明符号位的
特点:未声明符号位的默认为无符号数。
例子:8’b0000_0010%3和8’b1111_1110%3。
说明:
8’b0000_0010和8’b1111_1110,实际分别为无符号数2和254,所以余3的结果均为2。
代码:
`timescale 1ns / 1ps
module remainder(
output [7:0] out_data_1,
output [7:0] out_data_2
);
assign out_data_1 = (8'b0000_0010)%3;
assign out_data_2 = (8'b1111_1110)%3;
endmodule
testbench
`timescale 1ns / 1ps
module remainder_tb();
wire [7:0] out_data_1;
wire [7:0] out_data_2;
remainder u_remainder(
.out_data_1 (out_data_1 ),
.out_data_2 (out_data_2 )
);
endmodule
结果:
C)声明了符号位的
特点:声明符号位的为有符号数。
例子:(声明有符号在进制符号前加s)
8’sb0000_0010%3和8’sb1111_1110%3。
说明:
8’sb0000_0010和8’sb1111_1110%3,实际分别为有符号数2和-2,所以余3的结果为2和-2。
代码:
`timescale 1ns / 1ps
module remainder(
output [7:0] out_data_1,
output [7:0] out_data_2
);
assign out_data_1 = (8'sb0000_0010)%3;
assign out_data_2 = (8'sb1111_1110)%3;
endmodule
testbench
`timescale 1ns / 1ps
module remainder_tb();
wire [7:0] out_data_1;
wire [7:0] out_data_2;
remainder u_remainder(
.out_data_1 (out_data_1 ),
.out_data_2 (out_data_2 )
);
endmodule
结果:
(2)变量
A)注意点
一个任意类型的数(无论是有符号数还是无符号),赋值给一个变量后,其类型由被赋值的类型确定;
wire和reg类型变量,默认类型为无符号数,要定义其为有符号数要用关键字signed;
integer类型变量默认为有符号数,不用声明其类型;
B wire和reg
例子+说明:
四个无符号数8’b0000_0010,8’b1111_1110和8’b0000_0010,8’b1111_1110;
赋值给无符号变量后实际值为2,254和2,-2;
所以余3的结果为:2,2,和2,-2。
代码:
`timescale 1ns / 1ps
module remainder(
input [7:0] indata_uns_one ,
input [7:0] indata_uns_two ,
input signed [7:0] indata_sig_one ,
input signed [7:0] indata_sig_two ,
output [7:0] out_data_1 ,
output [7:0] out_data_2 ,
output [7:0] out_data_3 ,
output [7:0] out_data_4
);
assign out_data_1 = indata_uns_one%3;
assign out_data_2 = indata_uns_two%3;
assign out_data_3 = indata_sig_one%3;
assign out_data_4 = indata_sig_two%3;
endmodule
`timescale 1ns / 1ps
module remainder_tb();
reg [7:0] indata_uns_one;
reg [7:0] indata_uns_two;
reg signed [7:0] indata_sig_one;
reg signed [7:0] indata_sig_two;
wire [7:0] out_data_1;
wire [7:0] out_data_2;
wire [7:0] out_data_3;
wire [7:0] out_data_4;
initial begin
indata_uns_one = 8'b0;
indata_uns_two = 8'b0;
indata_sig_one = 8'b0;
indata_sig_two = 8'b0;
#20;
indata_uns_one = 8'b0000_0010;
indata_uns_two = 8'b1111_1110;
indata_sig_one = 8'b0000_0010;
indata_sig_two = 8'b1111_1110;
end
remainder u_remainder(
.indata_uns_one (indata_uns_one),
.indata_uns_two (indata_uns_two),
.indata_sig_one (indata_sig_one),
.indata_sig_two (indata_sig_two),
.out_data_1 (out_data_1 ),
.out_data_2 (out_data_2 ),
.out_data_3 (out_data_3 ),
.out_data_4 (out_data_4 )
);
endmodule
结果
(3)有符号数和无符号数运算
规则:
两个无符号数,按照无符号数规则运算;
两个有符号数,按照无符号数规则运算;
一个有符号数一个无符号数,按照无符号数规则运算。
例子+说明:
情况一:两个无符号数8’b1111_1110和8’b0000_0011,实际值为254和3,求余结果为8‘b0000_0010即2;
情况二:两个有符号数8’b1111_1110和8’b0000_0011,实际值为-2和3,求余结果为8‘b1111_1110即-2;
情况二:有符号数8’b1111_1110和无符号数8’b0000_0011,实际值为-2和3,均转换为无符号数为254和3,求余结果为8‘b0000_0010即-2。
(4) #### 注意
所有运算分两步看:以(3)情况二为例
第一步:在求赋值前的值,两个有符号数-3余3为-2,即8’b1111_1110;
第二步:由被赋值变量看形式,8’b1111_1110是无符号数254还是有符号数-2,由存储他的变量out_data_2决定。out_data_2是无符号变量,结果8’b1111_1110就是无符号数254;out_data_2是有符号变量,结果8’b1111_1110就是有符号数-2。