一、Verilog关键字、注释符以及转义符
1、关键词
设计:module、input、output、inout、parameter、wire、reg、always、assign、 begin、or、end、posedge/negedge、case、defualt、endcase、if、else、for、integer、 generate、genvar、endgenerate、function、endfunction、task、endtask、endmodule
验证:initial、forever、fork、join、wait
2、注释符
//This is a single line comment.
//This is a single line comment.
//This is a single line comment.
/*
This is a multiline comment.
This is a multiline comment.
This is a multiline comment.
*/
3、标识符和转义符
二、数值
不定态(x或X):信号延迟没到达寄存器,可能是不定态;未初始化的寄存器也会出 现不定态;三态门会出现不定态等。
高阻态(z或Z):电路的一种输出状态既不是高电平也不是低电平,如果加入下一级 等于没接,门电路上拉管和下拉管都截止时,输出端就相当于浮空就产生高阻态。
高阻态应用:在总线连接的结构上。总线上挂有多个设备,设备总线以高阻 的形式连接。这样在设备不占用总线时自动释放总线,以方便其他设备获得总线 的使用权;高阻态可以认为是电阻无穷大,在一定程度上增加芯片的抗电压能力。
三、数据类型
数据类型可以分为两大类:物理数据类型(线网型及寄存器型)和抽象数据类型(整型、时间型及参数型)。
1、物理数据类型
(1)线网(wire)
wire a; //Declare the 1-bit wide line variable a
wire [3:0] a; //Declare the 4-bit wide line variable a
wire 类型表示硬件单元之间的物理连线,由其连接的器件输出端连续驱动。如果没有驱动元件连接到 wire 型变量,缺省值一般为 "Z"。 线网型还有其他数据类型,包括 wand,wor,wri,triand,trior,trireg 等。
(2)寄存器(reg)
reg a; //Declare the 1-bit wide line variable a
reg [3:0] a; //Declare the 4-bit wide line variable a
寄存器(reg)用来表示存储单元,它会保持数据原有的值,直到被改写。reg与wire区别在于,reg会保持最后一次赋值,而wire需要持续的驱动。一般情况下reg默认初始值为“X”,因此要赋予初值。
(3)存储器
reg[31:0] mem[0:1023]; //1Kbyte memory, 32 bits wide
存储器变量可以描述RAM型、ROM型以及reg文件,数值中的每一个单元通过一个数组索引进行寻址。
2、抽象数据类型
(1)整数型(integer)
reg [31:0] data1 ;
reg [3:0] byte1 [7:0];
integer j ; //整型变量,用来辅助生成数字电路
always@* begin
for (j=0; j<=3;j=j+1) begin
byte1[j] = data1[(j+1)*8-1 : j*8];
//把data1[7:0]…data1[31:24]依次赋值给byte1[0][7:0]…byte[3][7:0]
end
end
(2)实型(real)
real data1 ;
integer temp ;
initial begin
data1 = 2e3 ;
data1 = 3.75 ;
end
initial begin
temp = data1 ; //temp 值的大小为3
end
实数用关键字 real 来声明,可用十进制或科学计数法来表示。实数声明不能带有范围,默认值为 0。如果将一个实数赋值给一个整数,则只有实数的整数部分会赋值给整数。
(3)时间型(time)
time current_time ;
initial begin
#100 ;
current_time = $time ; //current_time 的大小为 100
end
Verilog 使用特殊的时间寄存器 time 型变量,对仿真时间进行保存。其宽度一般为 64 bit,通过调用系统函数 $time 获取当前仿真时间。
(4)参数型(parameter)
parameter data_width = 10'd32 ;
parameter i=1, j=2, k=3 ;
parameter 可以在其他模块传递,localparam 局部参数本模块用。
四、运算符
算术运算符(+,-,×,/, %)、赋值运算符(=, <=)、关系运算符(>, <, >=, <=)、逻辑运算符(&&, ||, !)、条件运算符( ?, : )、位运算符(~, |, ^, &, ^~)、移位运算符(<<, >>)、拼接运算符({ })、归约运算(~, |, ^, &, ^~)。
==和===区别:
五、过程结构与连续赋值
assign、always、initial 以及例化的模块都是并行执行
1、连续赋值assign
assign LHS_target = RHS_expression;
LSH_target 必须是 wire 线类型,RHS_expression 没有要求
2、过程结构always
一直重复执行:
always #10 clk = ~clk; //产生 50Mhz 时钟
条件触发执行:
//当信号 a 或 b 值发生变化时执行里面语句
always @(a or b) begin … end
//当 clk 上升沿到来或者 rst下降沿到来执行里面的语句
always @(posedge clk or negedge rst) begin … end
//当a或b信号发生变化执行里面的语句;LSH_target 必须是 reg 寄存器类型,RHS_expression 没有要求
always @(a or b) begin
LHS_target = RHS_expression ;
end
3、过程结构initial
只执行一次,不可综合 initial begin … end ,里面的语句顺序顺序执行。
//LSH_target 必须是 reg 寄存器类型,RHS_expression 没有要求。
initial begin
LHS_target = RHS_expression ;
end
六、阻塞和非阻塞赋值
阻塞赋值:顺序执行,执行完上一条语句才可以执行下一条用“=”;
非阻塞赋值:一个过程块中的语句同时执行用“<=”。
例子:交换 a,b 两个寄存器的值
阻塞赋值:
reg temp;
always @(posedge clk) begin
temp = a ;
a = b ;
b = temp ;
end
非阻塞赋值:
always @(posedge clk) begin
a <= b ;
b<=a ;
end
使用情况:
组合逻辑使用阻塞赋值,always @(*) begin 阻塞赋值 end
时序逻辑使用非阻塞赋值,always @(posedge clk or negedge rst) begin 非阻塞 end
七、条件语句
1、if...else... 语句(有优先级)
module mux4to1(
input [1:0] sel,
input [1:0] p0,
input [1:0] p1,
input [1:0] p2,
input [1:0] p3,
output reg[1:0] sout);
always @(*) begin
if (sel == 2'b00) sout= po;
else if (sel == 2'b01) sout=p1;
else if (sel == 2'b10) sout=p2;
else sout=p3;
end
endmodule
2、case...endcase语句
module mux4to1(
input [1:0] sel ,
input [1:0] p0 ,
input [1:0] p1 ,
input [1:0] p2 ,
input [1:0] p3 ,
output reg[1:0] sout);
always @(*) begin
case(sel)
2'b00: sout_t = p0 ;
2'b01: sout_t = p1 ;
2'b10: sout_t = p2 ;
2'b11: sout_t = p3 ;
endcase
end
endmodule
八、循环语句
1、for语句
一般不用在设计环节,用在功能仿真写测试环节。
for (integer i; i<=5; i++) begin
赋值语句;
end
2、while语句
一般不用在设计环节,用在功能仿真写测试环节。
while(判断条件) begin
赋值语句;
end
3、repeat语句
一般不用在设计环节,用在功能仿真写测试环节。
//赋值语句运行 5 次
repeat (5) begin
赋值语句;
end
4、forever语句
不可综合语句
//生成一个100Mhz时钟
reg clk ;
initial begin
clk = 0 ;
forever begin
clk = ~clk ;
#5 ;
end
end
5、generate...endgenerate循环块
generate
for (genvar i=0; i<=2; i=i+1) begin: and_gate
and gate(.a(a[i]), .b(b[i]), .c(c[i]));
end
endgenerate
等价于以下代码:
and_gate[0] gate(.a(a[0]), .b(b[0]), .c(c[0]));
and_gate[1] gate(.a(a[1]), .b(b[1]), .c(c[1]));
and_gate[2] gate(.a(a[2]), .b(b[2]), .c(c[2]));
九、函数和任务
1、函数function…endfunction
使用规则:
不含有任何延迟、时序或时序控制逻辑;
至少有一个输入变量 input;
只有一个返回值默认是寄存器类型,且没有输出 output;
只能使用阻塞赋值;函数可以调用其他函数,但是不能调用任务。
//定义一个一个按位与的函数
function[3:0] yu;
input[3:0] a;
input[3:0] b;
begin
yu = a & b;
end
endfunction
reg[3:0] reg_a, reg_b;
wire[3:0] result;
assign result = yu(reg_a, reg_b); //调用yu函数
2、任务 task…endtask
一般不用在设计环节,用在功能仿真写测试环节。
使用规则:
任务调用语句只能出现过程块中;
可以有多个或者没有输出 output;
可以有多个或者没有输入 input;
任务没有返回值;任务可以调用任务和函数。
module add4bit(
input wire[3:0] A,
input wire[3:0] B,
input wire CIN,
output reg[3:0] SUM,
output reg COUT
);
reg[1:0] s0, s1, s2, s3;
task ADD; // 定义一个半加器
input wire a, b, cin;
output reg[1:0] c;
reg s, cout;
begin
s = a ^ b ^ c;
cout = a & b | (a ^ b) & cin;
c = {cout, s};
end
endtask
always @(*) begin
ADD (A[0], B[0], CIN, s0); //调用任务
ADD (A[1], B[1], s0[1], s1);
ADD (A[2], B[2], s1[1], s2);
ADD (A[3], B[3], s2[1], s3);
SUM = {s0[0], s1[0], s2[0], s3[0]};
COUT = s3[1];
end
endmodule
十、模块的例化和传参
带参数的模块:
module add
#(
parameter AW = 8,
parameter DW = 16,
)
(
input a,
output b
);
/*
功能描述
*/
endmodule
带参数例化:
add #(16, 32) add0(.a(a), .b(b));
十一、verilog测试常用函数
(1)延时
#5; //延时5个时间单位
(2)打印输出
$display(); //跟printf 函数一样
(3)显示时间
$time;
(4)结束仿真
$finish;
(5)读取函数
$readmemh(); //16 进制读取
$readmemb(); //二进制读取
(6)随机函数
$random;
a = {$random %6}; //产生 0~5 的 32 位随机数。
(7)仿真信息转存
$dumpfile(“tb.vcd”); //把仿真信息放在 tb.vcd 文件中
$dumpvars(0, top); //转存 top 层下面的所有信号
$dumpvars(1, top); //只转存 top 层下面的第一层信号
$dumpvars(2, top); //只转存 top 层下面的两层信号信号
十二、verilog三种建模描述方式
1、数据流建模
输入信号经过组合逻辑电路传到输出时类似于数据流动,而不会在其中存储。可以通过连续赋值语句对这种特性进行建模,这种建模方式通常被称为数据流建模。
例:1bit 全加器
assign sum = a ^ b ^ cin; // 本位 sum = a⊕b⊕c
assign co = a & b | (a ^ b) & cin; // 进位 co = a&b|(a⊕b)&c
2、行为级建模
VerilogHDL支持设计者从电路外部行为的角度对其进行描述,因此行为级建模是从一个层次很高的抽象角度来表示电路的。其目标不是对电路的具体硬件结构进行说明,它是为了综合以及仿真的目的而进行的。
例:1bit 全加器
assign {cout, sum} = a+b+cin;
3、结构化建模
结构描述方式就是将硬件电路描述成一个分级子模块系统,通过逐层调用这些子模块构成功能复杂的数字逻辑电路和系统的一种描述方式。
根据所调用子模块的不同抽象级别,可以将模块的结构描述方式分成如下三类:
(1)模块级建模:通过调用由用户设计生成的低级子模块来对硬件电路结构进行说明。这种情况下的模块由低级模块的实例组成。
(2)门级建模:通过调用VerilogHDL内部的基本门级元件来对硬件电路的结构进行说明。这种情况下的模块由基本门级元件的实例组成。
(3)开关级建模:通过调用VerilogHDL内部的基本开关元件来对硬件电路的结构进行说明。这种情况下的模块由基本开关级元件的实例组成。
例:1bit 全加器
//与门、或门和异或门封装好,直接调用
wire W1, W2, W3;
xor gate1 (.A(a), .B(b), .Z(W1));
xor gate2 (.A(W1), .B(cin), .Z(sum));
and gate3 (.A(a), .B(b), .Z(W2));
and gate4 (.A(W1), .B(cin), .Z(W3));
or gate6 (.A(W2), .B(W3), .Z(cout));