4.5.1 always块言语辅导准则
运用always块进行可归纳的代码规划时需求留意以下几个问题。
(1)每个always块只能有一个事情操控“@(event-expression)”,而且要紧跟在always关键字后边。
(2)always块能够表明时序逻辑或许组合逻辑,也能够用always块既表明电平灵敏的通明锁存器又一起表明组合逻辑。可是不引荐运用这种描绘办法,由于这简略发生过错和剩余的电平灵敏的通明锁存器。
(3) 带有posedge 或 negedge 关键字的事情表达式表明沿触发的时序逻辑;没有posedge 或negedge关键字的表明组合逻辑或电平灵敏的锁存器,或许两种都表明。在表明时序和组合逻辑的事情操控表达式中如有多个沿和多个电平,其间有必要用关键字“or” 衔接 。
(4)每个表明时序逻辑的always块只能由一个时钟跳变沿触发,置位或复位最好也由该时钟跳变沿触发。
(5)每个在always块中赋值的信号都必需界说成reg型或整型。整型变量缺省为32bit,运用Verilog操作符可对其进行二进制求补的算术运算。归纳器还支撑整型量的规模阐明,这样就答应发生不是32位的整型量,句法结构如下:
integer[
(6)always块中应该防止组合反应回路。每次履行always块时,在生成组合逻辑的always块中赋值的一切信号必需都有清晰的值;不然需求规划者在规划中参加电平灵敏的锁存器来坚持赋值前的最终一个值。
只要这样,归纳器才干正常生成电路。假如不这样做,归纳器会发出正告,提示规划中插入了锁存器。假如在规划中存在归纳器以为不是电平灵敏锁存器的组合回路时,归纳器会发出过错信息(例如规划中有异步状况机时)。
用always块规划纯组合逻辑电路时,在生成组合逻辑的always块中,参加赋值的一切信号都有必要有清晰的值,即在赋值表达式右端参加赋值的信号都必需在always @(灵敏电平列表)中列出。
假如在赋值表达式右端引用了灵敏电平列表中没有列出的信号,那么在归纳时,将会为该信号发生一个隐含的通明锁存器。这是由于该信号的改变不会马上引起所赋值的改变,而有必要比及灵敏电平列表中某一个信号改变时,它的效果才显现出来。
也便是相当于存在着一个通明锁存器把该信号的改变暂存起来,待灵敏电平列表中某一个信号改变时复兴效果,纯组合逻辑电路不可能做到这一点。这样,归纳后所得电路现已不是纯组合逻辑电路了。这时归纳器会发出正告提示规划中插入了锁存器,如下所示:
input a,b,c;
reg e,d;
always @(a or b or c) begin
e = d a b; //由于d没有在灵敏电平列表中,所以d改变时,e不能马上改变,
//要比及a或b或c改变时才表现出来。这便是说实际上相当于存在
//一个电平灵敏的通明锁存器在起效果, 把d信号的改变锁存其间
d = e | c;
end
(7)对一个寄存器型(reg)或整型(integer)变量的赋值只答应在一个always块内进行,假如在另一always块也对其赋值,这是不合法的。
(8)把某一信号值赋为’bx,归纳器就把它解说成无关状况,因此归纳器为其生成的硬件电路最简练。
4.5.2 可归纳风格的Verilog HDL模块实例
1.组合逻辑电路规划实例
例4.6:8位带进位端的加法器的规划实例(运用简略的算法描绘)。
module adder_8(cout,sum,a,b,cin); //模块声明
output cout;
output [7:0] sum;
input cin;
input[7:0] a,b; //端口声明
assign {cout,sum} = a + b + cin; //加法器算法
endmodule
例4.7:指令译码电路的规划实例(运用电平灵敏的always块来规划组合逻辑)。
‘define plus 3’d0 //操作码的宏界说
‘define minus 3’d1
‘define band 3’d2
‘define bor 3’d3’
‘define unegate 3’d4
module alu(out,opcode,a,b); //模块声明
output [7:0] out;
input [2:0] opcode;
input [7:0] a,b; //端口声明
reg [7:0] out; //寄存器声明
always @(opcode or a or b) begin //用电平灵敏的always块描绘组合逻辑
case(opcode)
‘plus: out = a + b; //算术运算
‘minus: out = a – b;
‘band: out = a b; //位运算
‘bor: out = a | b;
‘unegate: out = ~a; //单目运算
default: out = 8’hx;
endcase
end
endmodule
例4.8:比较后重组信号的组合逻辑(运用task和电平灵敏的always块规划)。
module sort4(ra,rb,rc,rd,a,b,c,d); //模块声明
output [t:0] ra, rb, rc, rd;
input [t:0] a, b, c, d; //端口声明
reg [t:0] ra, rb, rc, rd;
reg [t:0] va, vb, vc, vd; //寄存器声明
parameter t=3; //参数声明
always @(a or b or c or d) begin //用电平灵敏的always块描绘组合逻辑
{va,vb,vc,vd}={a,b,c,d};
sort2(va,vc); //信号重组
sort2(vb,vd);
sort2(va,vb);
sort2(vc,vd);
sort2(vb,vc);
{ra,rb,rc,rd}={va,vb,vc,vd};
end
task sort2; //x与y交换使命
inout [t:0] x,y;
reg [t:0] tmp;
if(x > y) begin
tmp = x; //运用暂时变量tmp保存x的值
x = y;
y = tmp;
end
endtask
endmodule
例4.9:比较器的规划实例(运用赋值句子规划组合逻辑)。
module compare(equal,a,b); //模块声明
output equal;
input [size-1:0] a,b; //端口声明
parameter size=1; //参数声明
assign equal =(a==b)? 1 : 0; //比较器
endmodule
例4.10:3-8译码器规划实例(运用赋值句子规划组合逻辑)。
module decoder(out,in); //模块声明
output [7:0] out;
input [2:0] in; //端口声明
assign out = 1’b1in; //把最低位的1左移 in(依据从in口输入的值)位
//将移位成果赋予out
endmodule
例4.11:3-8编码器的规划实例。
编码器规划方案一。
module encoder1(none_on,out,in); //模块声明
output none_on;
output [2:0] out;
input [7:0] in; //端口声明
reg [2:0] out;
reg none_on; //寄存器声明
always @(in) begin: local //in有改变时,触发
integer i; //变量声明
out = 0;
none_on = 1; //初始化
for( i=0; i8; i=i+1 ) begin //for循环句子
if( in[i] ) begin //将in中值为1的位编码
out = i;
none_on = 0;
end
end
end
endmodule
编码器规划方案二。
module encoder2 ( none_on,out2,out1,out0,h,g,f,e,d,c,b,a); //模块声明
input h,g,f,e,d,c,b,a;
output none_on,out2,out1,out0; //端口声明
wire [3:0] outvec; //向量声明
assign outvec = //运用assign句子完成输出向量赋值
h ? 4’b0111 : g ? 4’b0110 : f ? 4’b0101:
e ? 4’b0100 : d ? 4’b0011 : c ? 4’b0010 :
b ? 4’b0001 : a ? 4’b0000 : 4’b1000;
assign none_on = outvec[3]; //运用assign句子进行编码
assign out2 = outvec[2];
assign out1 = outvec[1];
assign out0 = outvec[0];
endmodule
编码器规划方案三。
module encoder3 ( none_on,out2,out1,out0,h,g,f,e,d,c,b,a); //模块声明
input h,g,f,e,d,c,b,a;
output none_on,out2,out1,out0; //端口声明
wire [3:0] outvec; //向量声明
assign {none_on,out2,out1,out0} = outvec; //与上例的编码方法共同
always @( a or b or c or d or e or f or g or h) begin
if(h) outvec=4’b0111; //运用if_else句子完成向量赋值
else if(g) outvec=4’b0110; //共9个分支,其间向量的低3位有8种编码方法
else if(f) outvec=4’b0101;
else if(e) outvec=4’b0100;
else if(d) outvec=4’b0011;
else if(c) outvec=4’b0010;
else if(b) outvec=4’b0001;
else if(a) outvec=4’b0000;
else outvec=4’b1000;
end
endmodule
例4.12:多路器的规划实例。
运用assign赋值句子、case句子或if-else句子能够生成多路器电路。假如条件句子(case或if-else)平分支条件是互斥的话,归纳器能自动地生成并行的多路器。
多路器规划方案一。
modul emux1(out,a,b,sel); //模块声明
output out;
input a,b,sel; //端口声明
//运用assign句子查看输入信号sel的值
assign out = sel ? a : b; //当sel为1时,out为a;不然为b
endmodule
多路器规划方案二。
module mux2( out,a,b,sel); //模块声明
output out;
input a,b,sel; //端口声明
reg out;
always @(a or b or sel) begin //用电平触发的always块来规划多路器的组合逻辑
case( sel ) //运用case句子查看输入信号sel的值
1’b1: out = a; //假如为1,输出out为a
1’b0: out = b; //假如为0,输出out为b
default: out = ‘bx; //默许状况
endcase
end
endmodule
多路器规划方案三。
module mux3( out,a,b sel); //模块声明
output out;
input a, b, sel; //端口声明
reg out;
always @( a or b or sel ) begin
if( sel ) //运用if_else句子查看输入信号sel的值
out = a; //假如为1,输出out为a
else
out = b; //假如为0,输出out为b
end
endmodule
例4.13:奇偶校验位生成器规划实例。
module parity( even_numbits,odd_numbits,input_bus); //模块声明
output even_numbits, odd_numbits;
input [7:0] input_bus; //端口声明
assign odd_numbits = ^input_bus; //当input_bus中1的个数为奇数时,输出为1
assign even_numbits = ~odd_numbits; //此刻输出even_numbits为0
endmodule
例4.14:三态输出驱动器规划实例(用接连赋值句子树立三态门模型)。
三态输出驱动器规划方案一。
module trist1( out,in,enable); //模块声明
output out;
input in, enable; //端口声明
assign out = enable? in: ‘bz; //运用assign句子判别enable的值
endmodule
三态输出驱动器规划方案二。
module trist2( out,in,enable ); //模块声明
output out;
input in,enable; //端口声明
bufif1 mybuf1(out, in, enable); //bufif1是一个 Verilog门级原语(primitive)
//经过实例化该原语,完成三态门的调用
endmodule
例4.15:三态双向驱动器规划实例。
module bidir(tri_inout,out,in,en,b); //模块声明
inout tri_inout;
output out;
input in,en,b; //端口声明
assign tri_inout = en? in : ‘bz; //三态门的输入为in
assign out = tri_inout ^ b; //三态门的输出为b
endmodule
2.时序逻辑电路规划实例
例4.16:触发器规划实例。
module dff( q,data,clk); //模块声明
output q;
input data,clk; //端口声明
reg q;
always @( posedge clk ) begin //边际检测
q = data; //经过always句子,完成触发器
end
endmodule
例4.17:电平灵敏型锁存器规划实例一(assign句子)。
module latch1( q,data,clk); //模块声明
output q;
input data,clk; //端口声明
assign q = clk ? data : q; //经过assign句子,完成的是一个锁存器
endmodule
例4.18:带置位和复位端的电平灵敏型锁存器规划实例二(assign句子)。
module latch2( q,data,clk,set,reset); //模块声明
output q;
input data,clk,set,reset; //端口声明
assign q= reset ? 0 : ( set? 1:(clk? data : q ) );
//经过assign句子,完成的是一个锁存器
//其间,set为置位端,reset为复位端
//在clk为高电平时,锁存data,不然坚持q值
endmodule
例4.19:电平灵敏型锁存器规划实例三(always块)。
module latch3( q, data, clk); //模块声明
output q;
input data,clk; //端口声明
reg q;
always @(clk or data) begin //电平检测
if(clk) //clk为高电平时,q锁存data值
q = data;
end
endmodule
留意 有的归纳器会发生一个正告信息,提示将发生了一个电平灵敏型锁存器。由于此例中规划的便是一个电平灵敏型锁存器,所以这个正告信息是没有问题的。
例4.20:移位寄存器规划实例。
module shifter( din,clk,clr,dout); //模块声明
input din,clk,clr;
output [7:0] dout; //端口声明
reg [7:0] dout;
always @(posedge clk) begin
if(clr) //清零
dout = 8’b0;
else begin
dout = dout1; //左移一位
dout[0] = din; //把输入信号放入寄存器的最低位
end
end
endmodule
例4.21:8位计数器规划实例一。
module counter1( out, cout, data, load, cin, clk); //模块声明
output [7:0] out;
output cout;
input [7:0] data;
input load, cin, clk; //端口声明
reg [7:0] out;
always @(posedge clk) begin //边际检测
if( load ) //加载信号检测
out = data;
else
out = out + cin;
end
assign cout= out cin; //只要当out[7:0]的一切各位都为1
//而且进位cin也为1时才干发生进位cout
endmodule
例4.22:8位计数器规划实例二。
module counter2( out, cout, data, load, cin, clk); //模块声明
output [7:0] out;
output cout;
input [7:0] data;
input load, cin, clk; //端口声明
reg [7:0] out;
reg cout;
reg [7:0] preout; //寄存器声明
always @(posedge clk) begin //边际检测
out = preout; //触发器
end
//核算计数器和进位的下一个状况,为进步功能,load不该影响进位
always @( out or data or load or cin ) begin
{cout, preout} = out + cin; //进位操作
if(load) preout = data; //判别加载信号
end
endmodule