目录
注意元件的命名规范:同一层次内不能重名,不同层次之间也不能重名!
一、3种描述方式
1、结构描述(类似原理图输入)
即直接描述硬件的端口连接情况,以2选1数据选择器为例,代码如下:
module mux21(a,b,c,y); //模块名以及端口列表
input a,b,c; //输入、输出端口声明
output y;
//模块功能的描述
wire x1,x2,x3; //变量数据类型(常用的有寄存器型reg和网线型wire)
and U1(x1,a,x3);
not U2(x3,c);
and U3(x2,c,b);
or U4(y,x1,x2);
endmodule //模块以module和endmodule为引导(作用类似C语言中的花括号)
2、数据流描述(布尔代数)
不解释,仍以mux21为例,直接看示例代码:
//mux21的逻辑:y=ac'+bc
module mux21(
input a,b,c,
output y); //可以在声明端口的时候直接指明输入/输出
assign y=(a&~c)|(b&c); //assign引导连续赋值语句(不作特殊声明,则默认是wire型)
//另一种表达:
//assign y=(c?b:a); //与C语言中的含义一致
endmodule
注:(1)按位逻辑运算符:~非;|或;&与;^异或;~^(^~)同或
(2)逻辑运算符:!非;||或;&&与
3、行为描述(描述电路的功能或行为)
个人认为结构描述是口述搭电路的过程,数据流描述是在做数电作业(无意冒犯),而行为描述才真正是verilog的魅力所在(也是debug时让人血压持续走高的根源)。仍以mux21为例,代码如下:
module mux21(
input a,b,c,
output y);
reg y; //寄存器型变量,与之相对的是wire型变量
always@(a or b or c)
begin
case (c)
1'b0:y<=a; //1位二进制(binary)的0
1'b1:y<=b;
//default:y<=a;
endcase
end
//另一种always结构描述
always@(a,b,c)
begin
if(c==0) y<=a;
else y<=b;
end
endmodule
注:
(1)always@引导过程语句结构
(2)括号中为敏感表,敏感信号之间用“or”或“,”连接
(3)always引导的顺序语句中,变量必须是reg型
(4)多路分支语句case-endcase,类似真值表表达方式的描述
(5)若case/if-else不能覆盖全部情况,必须加default语句!
(6)等式运算符:==等于;!=不等于;===全等;!==不全等
二、经典组合电路、时序电路代码示例
1、组合电路
首先实现mux41,代码如下:
module mux41(
input d0,d1,d2,d3,a0,a1,
output reg y);
always @ (d0,d1,d2,d3,a0,a1)
begin
case({a1,a0}) //拼接运算符{}
2'b00:y<=d0; //2位二进制(binary)的00
2'b01:y<=d1;
2'b10:y<=d2;
2'b11:y<=d3;
endcase
end
endmodule
举一反三,实现4位的二选一数据选择器:
module mux4_21(
input [3:0] A,B,
input S,
output reg [3:0] Y);
//可以按照C语言中的数组来理解
//Y[0],Y[1],Y[2],Y[3]是相互独立的4个端口
always @ (A,B,S)
begin
if(S==0) Y<=A;
else Y<=B;
end
endmodule
2、时序电路
(1)同步复位的D触发器
代码如下:
module dff_syn(
input clk,d,rst,
output reg q);
//边沿敏感信号clk必须出现在敏感表中,always过程结构中clk不再出现
always @ (posedge clk)
begin
if(!rst) q<=0;
else q<=d;
end
endmodule
注:
(1)上升沿-posedge,下降沿-negedge
(2)rst不出现在敏感表中,说明rst是时钟同步信号
(2)异步复位的D触发器
代码如下:
module dff_asyn(
input clk,d,rst,en,
output reg q);
//异步控制信号rst必须出现在敏感表中
always @ (posedge clk or negedge rst)
begin
if(!rst) q<=0;
else if(en) q<=d;
end
endmodule
注:
(1)在always过程结构中必须表述其逻辑行为,且两种表述必须相匹配(下降沿有效-低电平有效;上升沿有效-高电平有效)
(3)敏感表中出现边沿敏感的表述,则其他类型的敏感信号不能放置,如always@(posedge clk, rst)是错误的!(原因:不特殊声明,变量默认为reg型,因此rst被判断为reg型;敏感表中已经有了posedge clk,不能再放reg rst)
3、过程中的两类赋值语句
(1)阻塞式(一般用于组合电路)
结合代码进行讲解:
always@(a,b)
begin
x=a;
y=b&x;
z=x|y;
end
//阻塞式赋值:当前语句进行完才会开始后续语句
//因此上述语句的作用等价于:
//x=a, y=b&a, z=a|(b&a)
(2)非阻塞式(一般用于时序电路)
always@(posedge clk)
begin
q1<=d;
q2<=q1;
end
//非阻塞式赋值:各个赋值语句并行
//或理解为所有赋值语句分两步执行:
//1.所有语句一起计算各自等号右边的值
//2.同时将计算的值赋给各自等号左边的变量
//因此上述语句的结果将是q1=d,q2=d
注:同一always过程结构中,两者千万不要混用!