如果总结使用verilog HDL进行建模的各种语法,可以将verilog HDL建模方式简单的归纳为三类:结构化描述方式、数据流描述方式、行为描述方式。一个模块中往往是将三种建模方式混合起来使用,来描述一个完整的功能。这三种建模方式是业界在使用verilog HDL的过程中不断归纳总结出来的,与IEEE 1364-2001的划分方式不太一样。这种简化后的归纳分类十分清晰,更利于掌握Verilog HDL的设计方法。学习笔记将会用四小节来分别介绍这三种建模方式的特点,以及如何使用三种方式混合建模。每小节都会介绍各种建模方式的基本概念,同时会给出详细的示例,便于读者对三种建模方式有一个概括性的了解。笔记中会以博主注的形式,说明一些需要注意的知识点并做一定延伸,并介绍三种建模方式的应用特点。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
[例1]
图1.一位全加器的结构图
代码:
module FA_struct (A, B, Cin, Sum, Cout);
input A;
input B;
input Cin;
output Sum;
output Cout;
wire S1, T1, T2, T3;
// -- statements -- //
xor x1 (S1, A, B);
xor x2 (Sum, S1, Cin);
and A1 (T1, A, B );
and A2 (T2, B, Cin);
and A3 (T3, A, Cin);
or O1 (Cout, T1, T2, T3 );
endmodule
该实例显示了一个全加器由两个异或门、三个与门、一个或门构成。S1、T1、T2、T3则是门与门之间的连线。代码显示了用纯结构的建模方式,其中xor 、and、or 是Verilog HDL 内置的门器件。以 xor x1 (S1, A, B) 该例化语句为例:xor 表明调用一个内置的异或门,器件名称xor ,代码实例化名x1(类似原理图输入方式)。括号内的S1,A,B 表明该器件管脚的实际连接线(信号)的名称,其中 A、B是输入,S1是输出。其他同。
[例2]
图2.两位全加器的结构图
代码:
module Four_bit_FA (FA, FB, FCin, FSum, FCout ) ;parameter SIZE = 2;
input [SIZE-1:0] FA;
input [SIZE-1:0] FB;
input FCin;
output [SIZE-1:0] FSum;
output FCout;
wire FTemp;FA_struct FA1(
.A (FA[0]),
.B (FB[0]),
.Cin (FCin) ,
.Sum (FSum[0]),
.Cout (FTemp)
);FA_struct FA2(
.A (FA[1]),
.B (FB[1]),
.Cin (FTemp) ,
.Sum (FSum[1]),
.Cout (FCount )
);endmodule 该实例用结构化建模方式进行一个两位的全加器的设计,顶层模块Four_bit_FA 调用了两个一位的全加器 FA_struct 。在这里,以前的设计模块FA_struct 对顶层而言是一个现成的器件,顶层模块只要进行例化就可以了。注意这里的例化中,端口映射(管脚的连线)采用名字关联,如 .A (FA[2]) ,其中.A 表示调用器件的管脚A,括号中的信号表示接到该管脚A的电路中的具体信号。wire 保留字表明信号Ftemp 是属线网类型(下面有具体描述)。另外,在设计中,尽量考虑参数化的问题。器件的端口映射必须采用名字关联。
文中的代码可以直接用于综合,文中图片即为Synplify综合后的RTL View做过简单修改后的图像。为了获得和博文中一样的RTL View,必须将Synplify的Implementation Option->Disable I/O Insertion选中。
assign [delay] net_name = expression;
例如:
assign #2 A = B;
在数据流描述方式中,还必须借助于HDL提供的一些运算符。如算术运算符:加(+)、减(-)等;关系运算符:大于(>),等于(==),不等于(!=)等等;按位逻辑运算符:逻辑与(&&),逻辑或(||)等;按位逻辑运算符:按位与(&)、按位或(|)等等;条件运算符:cond_expr ? expr1 : expr2;以及连接运算符:{expr1, expr2, . . .,exprN}。通过将这些运算符嵌入到连续赋值语句中,可以形成比较复杂的连续赋值语句,用来描述一些较复杂的线网变量的产生过程(即线网变量的行为)。
继续以上一节中的一位全加器为例,介绍如何用数据流描述方式来来建模:
[例1]
图1.一位全加器结构图 代码:
`timescale 1ns/100ps
module FA_flow(A,B,Cin,Sum,Cout)
input A,B,Cin;
output Sum, Cout;
wire S1,T1,T2,T3;
assign #2 S1 = A ^ B;
assign #2 Sum = S1 ^ Cin;
assign #2 T1 = A & B;
assign #2 T2 = B & Cin;
assign #2 T3 = A & Cin ;
assign #2 Cout = T1|T2|T3;
endmodule
注意,module内的各个assign 语句,是并行执行的,即各语句的执行与语句在module内出现的先后顺序无关。当assign语句右边表达式中的变量发生变化时,表达式的值会重新计算,并将结果赋值给左边的线网变量。如果赋值语句使用了时延,那么在等待时延结束后再将表达式的值赋给左边的线网变量。上例中每个assign语句都加了2个时间单位的时延,若右边表达式的值发生变化,assign语句左边的变量会在2个时间单位后获得右边表达式的新值。即,当信号A发生变化后,S1、T1、T3 也会跟着变化(A变化的2个时间单位后),S1的变化又会导致Sum的变化(A变化的4个时间单位后)。
数据流描述方式,相对行为描述方式而言,描述的都是比较简单的信号,不用类似于高级语言的高级语句(如if else,case等)就可以很容易的将信号的行为描述出来。而且通过数据流描述方式描述的电路,我们可以很容易的看出它的电路组成,如通过几个与门或者几个异或门,不需要经过很复杂的分析就可得知。对于行为描述方式,由于嵌入了大量的高级语句,我们很容易理解电路的行为,却不容易一下子看出电路的结构来。
和数据流建模方式一样,行为建模方式也需要使用到各种运算符,如算术运算符:加(+)、减(-)等;关系运算符:大于(>),等于(==),不等于(!=)等等;按位逻辑运算符:逻辑与(&&),逻辑或(||)等;按位逻辑运算符:按位与(&)、按位或(|)等等;条件运算符:cond_expr ? expr1 : expr2;以及连接运算符:{expr1, expr2, . . .,exprN}。将各种运算符和高级语句配合起来使用,可以对复杂的电路进行抽象建模,只关心电路的功能,不关心电路的具体实现。电路的具体实现过程由软件自动完成。
采用行为描述的建模方式,对电路的描述效率更高,可以用简洁的语句描述复杂的逻辑电路。下面还是以一位全加器为例,简单介绍行为建模的基本方式,让大家有一个概念。行为建模的具体细节以及一些高级语句的介绍放在后续专门的章节中介绍。
[例1]
图1.一位全加器结构图(行为建模1)
代码:
module FA_behav1(A, B, Cin, Sum, Cout );
input A,B,Cin;
output Sum,Cout;
reg Sum, Cout;
reg T1,T2,T3;
always@ ( A or B or Cin )
begin
Sum = (A ^ B) ^ Cin ;
T1 = A & B ;
T2 = B & Cin;
T3 = A & Cin;
Cout = (T1| T2) | T3;
end
endmodule
上例中,出现了一个行为建模中的关键字:reg、always、begin end。对于行为建模方式,以下几点概念必须建立:
1.只有寄存器类型的信号才可以在always和initial 语句中进行赋值,类型定义使用reg关键字声明。由于信号默认的类型
是wire,所以reg型变量必须在module中声明。
2.always 语句是一直重复执行的,由敏感表(always 语句括号内的变量)中的变量触发,每次敏感列表中的变量发生
变化时,always语句内部的各个语句都要重新执行一次。
3.always 语句从0 时刻开始。
4.在begin 和end 之间的语句是顺序执行,属于串行语句。
[ 例2] 图2.一位全加器结构图(行为建模2)
代码:
module FA_behav2(A, B, Cin, Sum, Cout );
input A,B,Cin;
output Sum,Cout;
reg Sum, Cout;
always@ ( A or B or Cin )
begin
{Cout ,Sum} = A + B + Cin ;
end
endmodule
在例2中,采用更加高级(更趋于行为级)描述方式,即直接采用“+”来描述加法。{Cout,Sum}表示对位数的扩展,因为两个1bit 相加,和有两位,低位放在Sum 变量中,进位放在Cout 中。
行为描述方式可以使用一些类似于C语言的高级语句,例如if else和case等。一段使用case语句的进行行为描述方式建模的代码,如果改为用数据流描述方式来实现(假设都是组合逻辑,组合逻辑可用数据流描述方式和行为描述方式建模,时序逻辑只能用行为描述方式建模),代码量会剧增,可读性也会很差。行为描述方式体现了verilog HDL的精髓和强大的建模能力,同时代码具有很好的可读性和抽象性,体现了高级语言的优点。当然,结构建模方式和数据建模方式也是必不可少的,应该根据需要合理的搭配使用三种不同的方式进行建模。
下面举例说明使用三种方式混合建模:
[例1]
图1.两输入4bits数值比较器代码:
`timescale 1 ns / 1 ps
//module function is:if data < datb,Value =0;else Value = 1.
module compare(Data,Datb,Value);
input [3:0] Data;
input [3:0] Datb;
output Value;
reg Value;
wire Value0;
wire Value1;
wire Value2;
wire Value3;
assign Value0 = (Data[0] ^ Datb[0]) ? Data[0] : 1'b1;
assign Value1 = (Data[1] ^ Datb[1]) ? Data[1] : 1'b1;
assign Value2 = (Data[2] ^ Datb[2]) ? Data[2] : 1'b1;
assign Value3 = (Data[3] ^ Datb[3]) ? Data[3] : 1'b1;
always@(Value0 or Value1 or Value2 or Value3)
begin
if(Value0 == 1'b1)
Value = 1'b1;
else if(Value1 == 1'b1)
Value = 1'b1;
else if(Value2 == 1'b1)
Value = 1'b1;
else if(Value3 == 1'b1)
Value = 1'b1;
else
Value = 1'b0;
end
endmodule
上例中,通过数据流描述方式和行为描述方式,描述了一个二输入4bits数值比较器。二输入4bits数值比较器的功能为:当Data大于等于Datb时,Value为1;否则Value为0。作为一个底层module,compare模块实现的是一个具体的、较小的功能,不需要例化其他更低层模块。compare模块内部,简单的逻辑可以用数据流描述方式来实现,相对复杂一些的逻辑由行为描述方式来实现,相互搭配使用,使得代码不仅可读性好,而且简洁。
[例2]
图2.三输入求最大最小值差额
代码:
//calculate the the result of the max data and min dat
module calc(Dat0,Dat1,Dat2,Result);
input [3:0] Dat0;
input [3:0] Dat1;
input [3:0] Dat2;
output [3:0] Result;
reg [3:0] MaxDat;
reg [3:0] MinDat;
wire CmpResult0;
wire CmpResult1;
wire CmpResult2;
compare U_compare_0(
.Data (Dat0),
.Datb (Dat1),
.Value(CmpResult0));
compare U_compare_1(
.Data (Dat0),
.Datb (Dat2),
.Value(CmpResult1));
compare U_compare_2(
.Data (Dat1),
.Datb (Dat2),
.Value(CmpResult2));
always@(CmpResult0 or CmpResult1 or CmpResult2 or Dat0 or Dat1 or Dat2)
begin
case({CmpResult0,CmpResult1,CmpResult2})
3'b110 : MaxDat = Dat0;
3'b111 : MaxDat = Dat0;
3'b001 : MaxDat = Dat1;
3'b011 : MaxDat = Dat1;
3'b000 : MaxDat = Dat2;
3'b100 : MaxDat = Dat2;
default : MaxDat = Dat0;
endcase
end
always@(CmpResult0 or CmpResult1 or CmpResult2 or Dat0 or Dat1 or Dat2)
begin
case({CmpResult0,CmpResult1,CmpResult2})
3'b000 : MinDat = Dat0;
3'b001 : MinDat = Dat0;
3'b100 : MinDat = Dat1;
3'b110 : MinDat = Dat1;
3'b011 : MinDat = Dat2;
3'b111 : MinDat = Dat2;
default : MinDat = Dat2;
endcase
end
assign Result = MaxDat - MinDat;
endmodule
上例中实现的功能是三数据输入计算最大最小值的差值,模块内三种建模方式都使用到了。通过例化三个数值比较器(结构化建模),比较出三个数值的相对大小,然后通过分析数值比较的结果获得最大值和最小值(使用行为描述方式),最后使用数据流描述方式完成减法运算。通过上次可以看出,对于相对复杂的逻辑,用行为描述方式更为方便,而对于简单的逻辑,使用数据流描述方式就可以了。灵活的应用三种建模方式,就可以写出高效、易读、简洁的代码了。
一周热门 更多>