Verilog笔记
assign:
assign
相当于连线,一般是将一个变量的值不间断地赋值给另一个变量,就像把这两个变量连在一起,所以习惯性的当做连线用,比如把一个模块的输出给另一个模块当输入。
对wire
型变量赋值,wire
是线网,相当于实际的连接线,如果要用assign
直接连接,就用wire
型变量。wire
型变量的值随时变化。
(1)在Verilog module
中的所有过程块(如initial
块和always
块)、连续赋值语句(如assign
语句)和实例引用都是并行的
。在同一module
中这三者出现的先后顺序没有关系
。
(2)只有连续赋值语句assign
和实例引用
语句可以独立于过程块
而存在于module
的功能定义部分。
(3)连续赋值assign
语句独立于过程块,所以不能在always
过程块中使用assign
语句。只有连续赋值语句assign
和实例引用语句
可以独立于过程块而存在于模块的功能定义部分
通过连续赋值语句描述3位加法器
module adder ( count,sum,a,b,cin );
input [2:0] a,b;
input cin;
output count;
output [2:0] sum;
assign {count,sum} = a + b + cin;
endmodule
通过连续赋值语句描述一个比较器
module compare ( equal,a,b );
output equal; //声明输出信号equal
input [1:0] a,b; //声明输入信号a,b
assign equal =(a = = b)?1:0;
/*如果a、b 两个输入信号相等,输出为1。否则为0*/
endmodule
//版本2
module compare_2 ( equal,a,b );
output equal; //声明输出信号equal
input [1:0] a,b; //声明输入信号a,b
reg equal;//
always @(a,b)
if(a==b)
equal=1;
else
equal=0;
/*如果a、b 两个输入信号相等,输出为1。否则为0*/
endmodule
//版本3
module compare_3 ( equal,a,b );
output equal; //声明输出信号equal
input [1:0] a,b; //声明输入信号a,b
reg equal;//
always @(a,b)
begin //
if(a==b)
equal=1;
else
equal=0;
end //
assign equal =(a = = b)?1:0;
/*如果a、b 两个输入信号相等,输出为1。否则为0*/
endmodule
always:
always @(a)
表示如果a有变化就执行下面的语句
always @(sl or a or b)
表示只要sl
或a
或b
,其中若有一个变化时就执行下面的语句
Verilog程序包括4个主要部分:
- 端口定义
-
I/O
说明 - 内部信号声明
- 功能定义
I/O说明的格式:
- 输入口
input[范围]
; - 输出口
output [范围]
; - 输入/输出口
inout [范围]
; -
I/O
说明也可以写在端口声明里。
内部信号说明:
-
reg[范围] 变量1,变量2…
; -
wire[范围] 变量1,变量2…
;
模块中实现逻辑功能的3种方法:
(1)assign
: assign c=a&b;
(2)用实例元件 :and #2 u1(q,a,b);
(3)用always
块
-
assign
语句是描述组合逻辑最常用的方法之一。 -
always
块既可用于描述时序逻辑,又可用于组合逻辑。
D触发器:
module new_dff(q,clk,d);
input clk,d;
output q;
reg q;
always @(posedge clk)
q<=d;
endmodule
D触发器(带异步清除端)
module new_dff2(q,clk,d,clr);
input clk,d,clr;
output q;
reg q;
always @(posedge clk or posedge clr)
begin
if(clr)
q<=0;
else
q<=d;
end
endmodule
D触发器(带异步清除端和使能端)
module new_dff3(q,clk,d,clr,en);
output q;
input clk,d,clr,en;
reg q;
always @(posedge clk or posedge clr)
begin
if(clr)
q<=0;
else if (en)
q<=d;
end
endmodule
数据类型及其常量和变量:
4种逻辑值 : 0
1
z
(高阻) x
(不定值)
常量:在程序运行过程中,其值不能被改变的量称为常量。
整数:
- 二进制整数
b
或B
- 十进制整数
d
或D
- 八进制整数
o
或O
- 十六进制整数
h
或H
数字表达方式: <位宽>'<进制><数字>
,默认位宽是32bit
4'b1110 //4位二进制数
12'habc //12位十六进制数
16'd255 //16位十进制数
负数: 一个数字可以被定义为负数,只需在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面。
-8'd5 //这个表达式代表5的补数(用八位二进制数表示)
参数(Parameter
)型:
用parameter
来定义一个标识符 代表一个常量,称为符号常量
,即标识符形式的常量,采用标识符代表一个常量可提高程序的可读性和可维护性。是一种常数型的数据,其说明格式如下:
parameter 参数名1=表达式,参数名2=表达式, …, 参数名n=表达式;
parameter
是参数型数据的确认符,确认符后跟着一个用逗号分隔开的赋值语句表。在每一个赋值语句的右边必须是一个常数表达式。也就是说,该表达式只能包含数字或先前已定义过的参数。
parameter msb=7; //定义参数msb为常量7
parameter e=25, f=29; //定义二个常数参数
parameter r=5.7; //声明r为一个实型参数
parameter byte_size=8, byte_msb=byte_size-1; //用常数表达式赋值
parameter average_delay = (r+f)/2; //用常数表达式赋值
参数型常数经常用于定义延迟时间和变量宽度。在实例引用时可通过参数传递改变在被引用模块中已定义的参数
module two_delay(a,b,c,d);
output c,d;
input a,b;
reg c,d;
parameter delay1=2,delay2=2;
always @(a)
c<= #delay1 a;
always @(b)
d<=#delay2 b;
endmodule
//调用版本1
module top_delay(a1,b1,c1,d1);
input a1,b1;
output c1,d1;
two_delay #(.delay1(5),.delay2(10)) u1(.a(a1),.b(b1),.c(c1),.d(d1));
endmodule
//调用版本2
module top_delay2(a1,b1,c1,d1);
input a1,b1;
output c1,d1;
two_delay #(5,10) u1(.a(a1),.b(b1),.c(c1),.d(d1));
endmodule
//调用版本3
module top_delay3(a1,b1,c1,d1);
input a1,b1;
output c1,d1;
two_delay #(.delay2(10)) u1(.a(a1),.b(b1),.c(c1),.d(d1));
endmodule
//调用版本4
module top_delay4(a1,b1,c1,d1);
input a1,b1;
output c1,d1;
two_delay #(10) u1(.a(a1),.b(b1),.c(c1),.d(d1)); //参数被传给了delay1
endmodule
变量:
wire变量:
wire [n-1:0] 数据名1,数据名2,…数据名i; //共有i条总线,每条总线内有n条线路
wire [n:1] 数据名1,数据名2,…数据名i;
wire a; //定义了一个一位的wire型数据
wire [7:0] b; //定义了一个八位的wire型数据
wire [4:1] c, d; //定义了二个四位的wire型数据
///////////////////////////////////////////////////////
reg是最常用的寄存器型数据。默认初始值是x:
reg [n-1:0] 数据名1,数据名2,… 数据名i;
reg [n:1] 数据名1,数据名2,… 数据名i;
reg rega; //定义了一个一位的名为rega的reg型数据
reg [3:0] regb; //定义了一个四位的名为regb的reg型数据
reg [4:1] regc, regd; //定义了两个四位的名为regc和regd的reg型数据
运算符按其功能可分为以下几类:
1) 算术运算符(+,-,×,/,%)
2) 赋值运算符(=,<=)
3) 关系运算符(>,<,>=,<=)
4) 逻辑运算符(&&,||,!)
5) 条件运算符(?:)
6) 位运算符(~,|,^,&,^~)
7) 移位运算符(<<,>>)
8) 拼接运算符({ })
9) 其它
下面是Verilog HDL中使用的关键词:
always, and, assign,begin,buf,bufif0,bufif1,case,casex,casez,cmos,deassign,default,defparam,disable,edge,else,end,endcase,endmodule,endfunction,endprimitive,endspecify, endtable, endtask, event, for, force, forever, fork, function,highz0,highz1, if,initial, inout, input,integer,join,large,macromodule,medium,module,nand,negedge,nmos,nor,not,notif0,notifl, or, output, parameter, pmos, posedge,primitive, pull0, pull1, pullup, pulldown, rcmos, reg, releses, repeat, mmos, rpmos,rtran, rtranif0,rtranif1,scalared,small,specify,specparam,strength,strong0, strong1,supply0, supply1, table, task, time, tran, tranif0, tranif1, tri, tri0, tri1, triand,trior, trireg,vectored,wait,wand,weak0,weak1,while, wire,wor, xnor, xor
块语句:
begin_end语句 通常用来标识顺序执行的语句,用它来标识的块称为顺序块
。
顺序块有以下特点:
(1) 块内的语句是按顺序执行的,即只有上面一条语句执行后下面的语句才能执行。
(2) 每条语句的延迟时间是相对于前一条语句的仿真时间而言的。
(3) 直到最后一条语句执行完,程序流程控制才跳出该语句块。
块内声明语句可以是参数声明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句。
module ex4_5;
parameter d=50;
reg[7:0] r;
reg[3:0] a;
event end_wave;
always @(end_wave)
a=4'hf;
initial
begin
a=4'h2;
end
initial
begin
#d r='h35;
#d r='hE2;
#d r='h00;
#d r='hF7;
#d ->end_wave;
end
endmodule
逻辑判断:
module counter10b(q,clk,clr);
output[3:0] q;
input clk,clr;
reg[3:0] q=4'b0000;//ModelSim仿真用,QuartusII不用初值
always @(posedge clk or posedge clr)
begin
if(clr)
q<=4'b0000;
else
begin
if (q==4'b1001)
q<=4'b0000;
else
q<=q+1'b1;
end
end
endmodule
case语句:
case语句是一种多分支选择语句,if语句只有两个分支可供选择,而实际问题中常常需要用到多分支选择,Verilog语言提供的case语句直接处理多分支选择。
case语句的所有表达式的值的位宽必须相等,只有这样控制表达式和分支表达式才能进行对应位的比较。一个经常犯的错误是用'bx, 'bz 来替代 n'bx, n'bz,这样写是不对的,因为信号x, z的缺省宽度是机器的字节宽度,通常是32位(此处 n 是case控制表达式的位宽)
module test_case;
reg[1:0] select;
initial
begin
select=2'b01;
case (select)
2'b01:$display("second 2b01...");
2'b01:$display("third 2b01...");
2'b01,2'b00:$display("2b01");
2'b11:$display("2b11");
default:$display("default...");
endcase
end
endmodule
initial
和 always
说明语句在仿真的一开始即开始执行。
initial
语句只执行一次。相反,always
语句则是不断地重复执行,直到仿真过程结束。
always
语句后面跟着的过程块是否运行,则要看它的触发条件是否满足,如满足则运行过程块一次,再次满足再运行一次,直至仿真过程结束。
在一个模块中,使用initial
和 always
语句的次数是不受限制的。
task
和function
语句可以在程序模块中的一处或多处调用。
@*
和 @(*)
,它们都表示对其后面语句块中所有输入变量的变化时敏感的。