资源描述
有关verilog 算法及建模
1 有关C、Veilog HDL、VHDL 1
1.1 C与Verilog 1
1.2 Verilog HDL与VHDL 3
2 有关Verilog 中的一些语法 5
2.1 运算符 5
2.2 Wire、reg、tri、memory型 6
2.3 assign和always语句 6
2.4 阻塞赋值和非阻塞赋值 8
2.5 Verilog中使用的一些关键字 9
2.6 其他 13
3 有关Verilog建模 15
3.1软核、固核、硬核 15
3.2 模块设计流程 15
3.3 门级结构 16
3.4 基本逻辑运算 16
3.5 控制逻辑 16
3.6 同步和异步 17
3.7 有限状态机 18
3.8 综合的一般原则 21
4 有关总线和时钟 22
4.1 I2C 双向二线制串行总线协议 22
4.2时钟发生器的形成(分频的另外一种实现方式) 22
1 有关C、Veilog HDL、VHDL
1.1 C与Verilog
C 语言配合Verilog HDL 来设计算法的硬件电路块时考虑的三个主要问题:
为什么选择C 语言与Verilog HDL 配合使用?
C 语言与Verilog HDL 的使用有何限制?
如何利用C 来加速硬件的设计和故障检测?
1) 为什么选择C 语言与Verilog 配合使用
首先,C 语言很灵活,查错功能强,还可以通过PLI(编程语言接口)编写自己的系任务直接与硬件仿真器(如Verilog-XL)结合使用。C 语言是目前世界上应用最为广泛的一种编程语言,因而C 程序的设计环境比Verilog HDL 的完整。此外,C 语言可应用于许多领域,有可靠的编译环境,语法完备,缺陷较少。
比较起来, Verilog 语言只是针对硬件描述的,在别处使用(如用于算法表达等)并不方便。而且Verilog 的仿真、综合、查错工具等大部分软件都是商业软件,与C 语言相比缺乏长期大量的使用,可靠性较差,亦有很多缺陷。所以,只有在C 语言的配合使用下,Verilog 才能更好地发挥作用。
解决方法:面对上述问题,最好的方法是C 语言与Verilog 语言相辅相成,互相配合使用。这就是既要利用C 语言的完整性,又要结合Verilog 对硬件描述的精确性,来更快更好地设计出符合性能要求的硬件电路系统。利用C 语言完善的查错和编译环境,设计者可以先设计出一个功能正确的设计单元,以此作为设计比较的标准。然后,把C 程序一段一段地改写成用并型结构(类似于Verilog)描述的C 程序,此时还是在C 的环境里,使用的依然是C 语言。如果运行结果都正确,就将C 语言关键字用Verilog 相应的关键字替换,进入Verilog 的环境。将测试输入同时加到C 与Verilog 两个单元,将其输出做比较。这样很容易发现问题的所在,然后更正,再做测试,直至正确无误。剩下的工作就交给后面的设计工程师继续做。
2) C 语言与Verilog 语言互相转换中存在的问题
这样的混合语言设计流程往往会在两种语言的转换中会遇到许多难题。例如, 怎样把
C 程序转换成类似Verilog 结构的C 程序,来增加并行度,以保证用硬件实现时运行速度达到设计要求;又如怎样不使用C 中较抽象的语法:例如迭代,指针,不确定次数的循环等等,也能来表示算法(因为转换的目的是要用可综合的Verilog 语句来代替C程序中的语句,而可用于综合的Verilog 语法是相当有限的,往往找不到相应的关键字来替换)。
C 程序是一行接一行依次执行的,属于顺序结构,而Verilog 描述的硬件是可以在同一
时间同时运行的,属于并行结构。这两者之间有很大的冲突。而Verilog 的仿真软件也是顺序执行的,在时间关系上同实际的硬件是有差异的,可能会出现一些无法发现的问题。
Verilog 可用的输出输入函数很少。C 语言的花样则很多,转换过程中会遇到一些困难。
C 语言的函数调用与Verilog 中模块的调用也有区别。C 程序调用函数是没有延时特性的,一个函数是唯一确定的,对同一个函数的不同调用是一样的。而Verilog 中对模块的不同调用是不同的,即使调用的是同一个模块,必须用不同的名字来指定。Verilog的语法规则很死,限制很多,能用的判断语句有限。仿真速度较慢,查错功能差,错误信息不完整。仿真软件通常也很昂贵,而且不一定可靠。C 语言没有时间关系,转换后的Verilog 程序必须要能做到没有任何外加的人工延时信号,也就是必须表达为有限状态机,即RTL 级的Verilog,否则将无法使用综合工具把Verilog 源代码转化为门级逻辑。
3) 如何利用C 语言来加快硬件的设计和查错
下表中列出了常用的C 与Verilog 相对应的关键字与控制结构
下表中,列出了C 与Verilog 相对应的运算符
从上面的讨论我们可以总结如下:
C 语言与Verilog 硬件描述语言可以配合使用,辅助设计硬件
C 语言与Verilog 硬件描述语言很象,只要稍加限制,C 语言的程序很容易转成Verilog
的程序
1.2 Verilog HDL与VHDL
Verilog HDL和VHDL都是用于逻辑设计的硬件描述语言,并且都已成为IEEE标准。VHDL是在1987年成为IEEE标准,Verilog HDL则在1995年才正式成为IEEE标准。之所以VHDL比Verilog HDL早成为IEEE标准,这是因为VHDL是美国军方组织开发的,而Verilog HDL 则是从一个普通的民间公司的私有财产转化而来,基于Verilog HDL的优越性,才成为的IEEE标准,因而有更强的生命力。
VHDL 其英文全名为VHSIC Hardware Description Language,而VHSIC则是Very High Speed Integerated Circuit的缩写词,意为甚高速集成电路,故VHDL其准确的中文译名为甚高速集成电路的硬件描述语言。
Verilog HDL和VHDL作为描述硬件电路设计的语言,其共同的特点在于:
能形式化地抽象表示电路的结构和行为、支持逻辑设计中层次与领域的描述、可借用高级语言的精巧结构来简化电路的描述、具有电路仿真与验证机制以保证设计的正确性、支持电路描述由高层到低层的综合转换、硬件描述与实现工艺无关(有关工艺参数可通过语言提供的属性包括进去)、便于文档管理、易于理解和设计重用。
但是Verilog HDL和VHDL又各有其自己的特点。由于Verilog HDL早在1983年就已推出,至今已有十三年的应用历史,因而Verilog HDL拥有更广泛的设计群体,成熟的资源也远比VHDL丰富。与VHDL相比VerilogHDL的最大优点是:它是一种非常容易掌握的硬件描述语言,只要有C语言的编程基础,通过二十学时的学习,再加上一段实际操作,一般同学可在二至三个月内掌握这种设计技术。而掌握VHDL设计技术就比较困难。这是
因为VHDL不很直观,需要有Ada编程基础,一般认为至少需要半年以上的专业培训,才能掌握VHDL的基本设计技术。目前版本的Verilog HDL和VHDL在行为级抽象建模的覆盖范围方面也有所不同。一般认为Verilog HDL在系统级抽象方面比VHDL略差一些,而在门级开关电路描述方面比VHDL强得多。下面图1-3是Verilog HDL和VHDL建模能力的比较图示供读者参考:
Verilog HDL较为适合系统级(System)、算法级(Alogrithem)、寄存器传输级(RTL)、逻辑级(Logic)、门级(Gate)、电路开关级(Switch)设计,而对于特大型(几百万门级以上)的系统级(System)设计,则VHDL更为适合,由于这两种HDL语言还在不断地发展过程中,它们都会逐步地完善自己。
美国和中国台湾地区逻辑电路设计和制造厂家大都以Verilog HDL 为主,中国大陆地区目前学习使用VHDL 的较多。到底选用VHDL 或是Verilog HDL 來配合C 一起用,就留給各位
自行去決定。但从学习的角度来看,Verilog HDL 比較簡單,也與C 语言较接近,容易掌握。从使用的角度,支持Verilog 硬件描述语言的半导体厂家也较支持VHDL 的多。
2 有关Verilog 中的一些语法
2.1 运算符
位运算符
1) ~ //取反
2) & //按位与
3) | //按位或
4) ^ //按位异或
5) ^~ //按位同或(异或非)
逻辑运算符
在Verilog HDL语言中存在三种逻辑运算符:
1) && 逻辑与
2) || 逻辑或
3) ! 逻辑非
等式运算符
在Verilog HDL语言中存在四种等式运算符:
1) == (等于)
2) != (不等于)
3) === (等于)
4) !== (不等于)
"=="和"!="又称为逻辑等式运算符。其结果由两个操作数的值决定。由于操作数中某些位可能是不定值x和高阻值z,结果可能为不定值x。
而"==="和"!=="运算符则不同,它在对操作数进行比较时对某些位的不定值x和高阻值z也进行比较,两个操作数必需完全一致,其结果才是1,否则为0。"==="和"!=="运算符常用于case表达式的判别,所以又称为"case等式运算符"。
位移运算符
左移:右边的添0
右移:左边的添0,移除的位舍去
举例:
4’b1001<<1 = 5’b10010; 4’b1001<<2 = 6’b100100;
1<<6 = 32’b1000000; 4’b1001>>1 = 4’b0100; 4’b1001>>4 = 4’b0000;
位拼接运算符
1.{a,b[3:0],w,3’b101}也可以写成为{a,b[3],b[2],b[1],b[0],w,1’b1,1’b0,1’b1}
2.{4{w}} //这等同于{w,w,w,w}
3.{b,{3{a,b}}} //这等同于{b,a,b,a,b,a,b}
负数:
一个数字可以被定义为负数,只需在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面。注意减号不可以放在位宽和进制之间也不可以放在进制和具体的数之间。见下例:-8'd5 //这个表达式代表5的补数(用八位二进制数表示)
2.2 Wire、reg、tri、memory型
wire型变量通常是用来表示单个门驱动或连续赋值语句驱动的网络型数据,tri型变量则用来表示多驱动器驱动的网络型数据。如果wire型或tri型变量没有定义逻辑强度(logic strength),在多驱动源的情况下,逻辑值会发生冲突从而产生不确定值。
wire型数据常用来表示用于以assign关键字指定的组合逻辑信号。Verilog程序模块中输入输出信号类型缺省时自动定义为wire型。
寄存器是数据储存单元的抽象。寄存器数据类型的关键字是reg,通过赋值语句可以改变寄存器储存的值,其作用与改变触发器储存的值相当。reg类型数据的缺省初始值为不定值x。
reg型数据常用来表示用于“always”模块内的指定信号,常代表触发器。通常,在设计中要由“always”块通过使用行为描述语句来表达逻辑关系。在“always”块内被赋值的每一个信号都必须定义成reg, reg型只表示被定义的信号将用在“always”块内,理解这一点很重要。并不是说reg型信号一定是寄存器或触发器的输出。虽然reg型信号常常是寄存器或触发器的输出,但并不一定总是这样。
Verilog HDL通过对reg型变量建立数组来对存储器建模,可以描述RAM型存储器,ROM存储器和reg文件。数组中的每一个单元通过一个数组索引进行寻址。在Verilog语言中没有多维数组存在。 memory型数据是通过扩展reg型数据的地址范围来生成的。
reg [7:0] mema[255:0];
这个例子定义了一个名为mema的存储器,该存储器有256个8位的存储器。该存储器的地址范围是0到255。
尽管memory型数据和reg型数据的定义格式很相似,但要注意其不同之处。如一个由n个1位寄存器构成的存储器组是不同于一个n位的寄存器的。见下例:
reg [n-1:0] rega; //一个n位的寄存器
reg mema [n-1:0]; //一个由n个1位寄存器构成的存储器组
一个n位的寄存器可以在一条赋值语句里进行赋值,而一个完整的存储器则不行。见下例:
rega =0; //合法赋值语句
mema =0; //非法赋值语句
如果想对memory中的存储单元进行读写操作,必须指定该单元在存储器中的地址。下面的写法是正确的。
mema[3]=0; //给memory中的第3个存储单元赋值为0。
2.3 assign和always语句
模块中最重要的部分是逻辑功能定义部分。有三种方法可在模块中产生逻辑。
1).用“assign”声明语句
采用“assign”语句是描述组合逻辑最常用的方法之一。
如: assign a = b & c;
2).用实例元件
如: and and_inst( q, a, b );
采用实例元件的方法象在电路图输入方式下,调入库元件一样。键入元件的名字和相连的引脚即可,表示在设计中用到一个跟与门(and)一样的名为and_inst的与门,其输入端为a, b,输出为q。要求每个实例元件的名字必须是唯一的,以避免与其他调用与门(and)的实例混淆。
3).用“always”块
“always”块既可用于描述组合逻辑也可描述时序逻辑。
如果用Verilog模块实现一定的功能,首先应该清楚哪些是同时发生的,哪些是顺序发生的。上面三个例子分别采用了“assign”语句、实例元件和“always”块。这三个例子描述的逻辑功能是同时执行的。也就是说,如果把这三项写到一个 VeriIog 模块文件中去,它们的次序不会影响逻辑实现的功能。这三项是同时执行的,也就是并发的。
然而,在“always”模块内,逻辑是按照指定的顺序执行的。“always”块中的语句称为“顺序语句”,因为它们是顺序执行的。请注意,两个或更多的“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[<msb>:<lsb>]<identifier>。
6) 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
边沿触发的always块常常描述时序逻辑,如果符合可综合风格要求可用综合工具自动转换为表示时序逻辑的寄存器组和门级逻辑,而电平触发的always块常常用来描述组合逻辑和带锁存器的组合逻辑,如果符合可综合风格要求可转换为表示组合逻辑的门级逻辑或带锁存器的组合逻辑。一个模块中可以有多个always块,它们都是并行运行的。
2.4 阻塞赋值和非阻塞赋值
1.非阻塞(Non_Blocking)赋值方式( 如 b <= a; ) ,在描述时序逻辑的always 块中用非阻塞赋值,则综合成时序逻辑的电路结构。
1) 块结束后才完成赋值操作。
2) b的值并不是立刻就改变的。
3) 这是一种比较常用的赋值方法。(特别在编写可综合模块时)
2.阻塞(Blocking)赋值方式( 如 b = a; ) ,在描述组合逻辑的always 块中用阻塞赋值,则综合成组合逻辑的电路结构。
1) 赋值语句执行完后,块才结束。
2) b的值在赋值语句执行完后立刻就改变的
3) 可能会产生意想不到的结果。
到目前为止,前面所举的例子中的"always"模块内的reg型信号都是采用
下面的这种赋值方式:b <= a;
这种方式的赋值并不是马上执行的,也就是说"always"块内的下一条语句执行后,b并不等于a,而是保持原来的值。"always"块结束后,才进行赋值。而另一种赋值方式阻塞赋值方式,b = a;
这种赋值方式是马上执行的。也就是说执行下一条语句时,b已等于a。
[例1]:always @( posedge clk )
begin
b<=a;
c<=b;
end
[例1] 中的"always"块中用了非阻塞赋值方式,定义了两个reg型信号b和c,clk信号的上升沿到来时,b就等于a,c就等于b,这里应该用到了两个触发器。请注意:赋值是在"always"块结束后执行的,c应为原来b的值。这个"always"块实际描述的电路功能如下图所示:
[例2]: always @(posedge clk)
begin
b=a;
c=b;
end
[例2]中的 "always"块用了阻塞赋值方式。clk信号的上升沿到来时,将发生如下的变化:b马上取a的值,c马上取b的值(即等于a),生成的电路图如下所示只用了一个触发器来寄存器a的值,又输出给b和c。这大概不是设计者的初衷,如果采用[例1]所示的非阻塞赋值方式就可以避免这种错误
关于赋值语句的编写规则:
1) 时序电路建模时,用非阻塞赋值。
2) 锁存器电路建模时,用非阻塞赋值。
3) 用always 块建立组合逻辑模型时,用阻塞赋值。
4) 在同一个always 块中建立时序和组合逻辑电路时,用非阻塞赋值。
5) 在同一个always 块中不要既用非阻塞赋值又用阻塞赋值。
6) 不要在一个以上的always 块中为同一个变量赋值。
7) 用$strobe 系统任务来显示用非阻塞赋值的变量值
8) 在赋值时不要使用 #0 延迟
2.5 Verilog中使用的一些关键字
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
所以,在对变量进行命名的时候,避免使用以上的名字,以免造成混乱而使系统无法进行争取识别 。
下面主要介绍几种常用的。
1.Repeat 语句
在repeat语句中,其表达式通常为常量表达式。下面的例子中使用repeat循环语句及加法和移位操作来实现一个乘法器。
parameter size=8,longsize=16;
reg [size:1] opa, opb;
reg [longsize:1] result;
begin: mult
reg [longsize:1] shift_opa, shift_opb;
shift_opa = opa;
shift_opb = opb;
result = 0;
repeat(size)
begin
if(shift_opb[1])
result = result + shift_opa;
shift_opa = shift_opa <<1;
shift_opb = shift_opb >>1;
end
end
也可以用for语句来实现:
for( bindex=1; bindex<=size; bindex=bindex+1 )
if(opb[bindex])
result = result + (opa<<(bindex-1));
2.task和function说明语句的不同点
task和function说明语句分别用来定义任务和函数。利用任务和函数可以把一个很大的程序模块分解成许多较小的任务和函数便于理解和调试。输入、输出和总线信号的值可以传入、传出任务和函数。任务和函数往往还是大的程序模块中在不同地点多次用到的相同的程序段。学会使用task和function语句可以简化程序的结构,使程序明白易懂,是编写较大型模块的基本功
任务和函数有些不同,主要的不同有以下四点:
1) 函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位。
2) 函数不能启动任务,而任务能启动其它任务和函数。
3) 函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量。
4) 函数返回一个值,而任务则不返回值。
函数的目的是通过返回一个值来响应输入信号的值。任务却能支持多种目的,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。Verilog HDL模块使用函数时是把它当作表达式中的操作符,这个操作的结果值就是这个函数的返回值。下面让我们用例子来说明:
例如,定义一任务或函数对一个16位的字进行操作让高字节与低字节互换,把它变为另一个字(假定这个任务或函数名为: switch_bytes)。
任务返回的新字是通过输出端口的变量,因此16位字字节互换任务的调用源码是这样的:
switch_bytes(old_word,new_word);
任务switch_bytes把输入old_word的字的高、低字节互换放入new_word端口输出,而函数返回的新字是通过函数本身的返回值,因此16位字字节互换函数的调用源码是这样的:
new_word = switch_bytes(old_word);
3.系统任务 $random
这个系统函数提供了一个产生随机数的手段。当函数被调用时返回一个32bit的随机数。它是一个带符号的整形数。
$random一般的用法是:$ramdom % b ,其中 b>0.它给出了一个范围在(-b+1):(b-1)中的随机数。
下面给出一个产生随机数的例子:
reg[23:0] rand;
rand = $random % 60;//(-60+1:60-1)=(-59:59)
上面的例子给出了一个范围在-59到59之间的随机数,下面的例子通过位并接操作产生一个值在0到59之间的数。
reg[23:0] rand;
rand = {$random} % 60;
$random产生宽度随机的随机脉冲序列的测试信号源,在电路模块的设计仿真时非常有用。
例:`timescale 1ns/1ns
module random_pulse( dout );
output [9:0] dout;
reg dout;
integer delay1,delay2,k;
initial
begin
#10 dout=0;
for (k=0; k< 100; k=k+1)
begin
delay1 = 20 * ( {$random} % 6);// delay1 在0到100ns间变化
delay2 = 20 * ( 1 + {$random} % 3);// delay2 在20到60ns间变化
#delay1 dout = 1 << ({$random} %10);
//dout的0--9位中随机出现1,并出现的时间在0-100ns间变化
#delay2 dout = 0;//脉冲的宽度在在20到60ns间变化
end
end
endmodule
4.宏定义 `define
用一个指定的标识符(即名字)来代表一个字符串,它的一般形式为:
`define 标识符(宏名) 字符串(宏内容)
如:`define signal string
它的作用是指定用标识符signal来代替string这个字符串,在编译预处理时,把程序中在该命令以后所有的signal都替换成string。这种方法使用户能以一个简单的名字代替一个长的字符串,也可以用一个有含义的名字来代替没有含义的数字和符号,因此把这个标识符(名字)称为“宏名”,在编译预处理时将宏名替换成字符串的过程称为“宏展开”。
宏定义的八点说明:
1) 宏名可以用大写字母表示,也可以用小写字母表示。建议使用大写字母,以与变量名相
区别。
2) 宏定义不是Verilog HDL语句,不必在行末加分号。如果加了分号会连分号一起进行置
换。如:
[例2]:module test;
reg a, b, c, d, e, out;
`define expression a+b+c+d;
assign out = `expression + e;
...
endmodule
经过宏展开以后,该语句为:assign out = a+b+c+d;+e;显然出现语法错误。
3) `define命令可以出现在模块定义里面,也可以出现在模块定义外面。宏名的有效范围
为定义命令之后到原文件结束。通常,`define命令写在模块定义的外面,作为程序的一部分,在此程序内有效。
4) 使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。而且记住一
个宏名要比记住一个无规律的字符串容易,这样在读程序时能立即知道它的含义,当需
要改变某一个变量时,可以只改变 `define命令行,一改全改。如例1中,先定义WORDSIZE
代表常量8,这时寄存器data是一个8位的寄存器。如果需要改变寄存器的大小,只需把
该命令行改为:`define WORDSIZE 16。这样寄存器data则变为一个16位的寄存器。
由此可见使用宏定义,可以提高程序的可移植性和可读性。
5.时间尺度 `timescale
`timescale命令用来说明跟在该命令后的模块的时间单位和时间精度。使用`timescale命令可以在同一个设计里包含采用了不同的时间单位的模块。例如,一个设计中包含了两个模块,其中一个模块的时间延迟单位为ns,另一个模块的时间延迟单位为ps。EDA工具仍然可以对这个设计进行仿真测试。
`timescale 命令的格式如下:
`timescale<时间单位>/<时间精度>
$time与$realtime
$time: 返回的是整型数
$realtime:返回的是实型数
6. 用户定义的原语(UDP)
由于UDP是用查表的方法来确定其输出的,用仿真器进行仿真时,对它的处理速度较对一般用户编写的模块快得多。与一般的用户模块比较,UDP更为基本,它只能描述简单的能用真值表表示的组合或时序逻辑。UDP模块的结构与一般模块类似,只是不用module而改用primitive关键词开始,不用endmodule而改用endprimitive关键词结束
定义UDP的语法:
primitive 元件名(输出端口名,输入端口名1,输入端口名2,…)
output 输出端口名;
input 输入端口名1, 输入端口名2,…;
reg 输出端口名;
initial begin
输出端口寄存器或时序逻辑内部寄存器赋初值(0,1,或 X);
end
table
//输入1 输入2 输入3 … : 输出
逻辑值 逻辑值 逻辑值 … : 逻辑值 ;
逻辑值 逻辑值 逻辑值 … : 逻辑值 ;
逻辑值 逻辑值 逻辑值 … : 逻辑值 ;
… … … … : … ;
endtable
endprimitive
注意点:
1) UDP只能有一个输出端,而且必定是端口说明列表的第一项。
2) UDP可以有多个输入端,最多允许有10个输入端。
3) UDP所有端口变量必须是标量,也就是必须是1位的。
4) 在UDP的真值表项中,只允许出现0、1、X三种逻辑值,高阻值状态Z是不允许出现的。
5) 只有输出端才可以被定义为寄存器类型变量。
6) initial语句用于为时序电路内部寄存器赋初值,只允许赋0、1、X三种逻辑值,缺省值为X。
值得注意的是:往往在Verilog中使用C中常使用的一些关键字时,不能进行正常的综合,这主要是由于Verilog的局限性所致,在这方面,需要查阅一些Verilog可综合代码风格的书籍。
???疑问:
在使用for 、reapeat、while等关键字实现某些功能的时候,相对而言很直观、简介,但在综合的时候,出现错误,有待讨论。
2.6 其他
1.优先级别
2.关于程序中生成锁存器的问题
右边的例子很明确,程序中的case语句有default项,指明了如果sel不取00或11时,编译器或仿真器应赋给q的值。程序所示情况下,q赋为0,因此不需要锁存器。
以上就是怎样来避免偶然生成锁存器的错误。如果用到if语句,最好写上else项。如果用case语句,最好写上default项。遵循上面两条原则,就可以避免发生这种错误,使设计者更加明确设计目标,同时也增强了Verilog程序的可读性。
3.关于赋值
1) 对一个寄存器型(reg)和整型(integer)变量给定位的赋值只允许在一个always块内进行,如在另一always块也对其赋值,这是非法的。
2) 把某一信号值赋为'bx,综合器就把它解释成无关状态,因而综合器为其生成的硬件电路最简洁。
3 有关Verilog建模
3.1软核、固核、硬核
我们把功能经过验证的、可综合的、实现后电路结构总门数在5000门以上的Verilog HDL模型称之为“软核”(Soft Core)
我们把在某一种现场可编程门阵列(FPGA)器件上实现的,经验证是正确的总门数在5000门以上电路结构编码文件,称之为“固核”。
我们把在某一种专用半导体集成电路工艺的(ASIC)器件上实现的经验证是正确的总门数在5000门以上的电路结构掩膜,称之为“硬核”。
MASK(掩膜):(单片机掩膜是指程序数据已经做成光刻版,在单片机生产的过程中把程序做进去。优点是:程序可靠、成本低。缺点:批量要求大,每次修改程序就需要重新做光刻板,不同程序不能同时生产,供货周期长。
在半导体制造中,许多芯片工艺步骤采用光刻技术,用于这些步骤的图形“底片”称为掩膜(也称作“掩模”),其作用是:在硅片上选定的区域中对一个不透明的图形模板掩膜,继而下面的腐蚀或扩散将只影响选定的区域)
显而易见,在具体实现手段和工艺技术尚未确定的逻辑设计阶段,软核具有最大的灵活性,它可以很容易地借助EDA综合工具与其它外部逻辑结合为一体。当然,由于实现技术的不确定性,有可能要作一些改动以适应相应的工艺。相比之下固核和硬核与其它外部逻辑结合为一体的灵活性要差得多,特别是电路实现工艺技术改变时更是如此。而近年来电路实现工艺技术的发展是相当迅速的,为了逻辑电路设计成果的积累,和更快更好地设计更大规模的电路,发展软核的设计和推广软核的重用技术是非常有必要的
3.2 模块设计流程
主要由两大主要功能部分组成:
1)设计开发:即从编写设计文件-->综合到布局布线-->投片生成这样一系列步骤。
2)设计验证:也就是进行各种仿真的一系列步骤,如果在仿真过程中发现问题就返回设计输入进行修改。
自顶向下的设计过程中在每一层次划分时都要对某些目标作优化,TOP_DOWN的设计过程是理想的设计过程,它的缺点是得到的最小单元不标准,制造成本可能很高。从底向上的设计过程全采用标准基本单元,通常比较经济,但有时可能不能满足一些特定的指标要求。复杂数字逻辑电路和系统的设计过程通常是这两种设计方法的结合,设计时需要考虑多个目
标的综合平衡。
系统设计:至顶向下
底层电路:至底向上
3.3 门级结构
and 与门
nand 与非门
nor 或非门
or 或门
xor 异或门
xnor 异或非门
buf 缓冲器
not 非门
3.4 基本逻辑运算
全加器
Xi 、Yi 表示两个加数,Si 表示和,Ci -1 表示来自低位的进位、Ci 表示向高
位的进位。从真值表很容易写出逻辑表达式如下:
全加器和Si 的表达式也可以表示为:
S i = P i ⊕ Ci 其中P i = Xi ⊕ Yi ( 5 . 1 )
Ci = P i·Ci - 1 + Gi 其中Gi = Xi·Yi ( 5 . 2 )
参考清华大学出版社出版的刘宝琴老师编写的《数字电路与系统》,可以很容易地写出超前进位形成电路的逻辑,在这里不再详细介绍。
16位超前进位加法器原理图
Verilog实现加法器:assign {C, Sum } = X + Y;
Verilog实现乘法器:assign Product = X * Y;
3.5 控制逻辑
组合逻辑:输出只是当前输入逻辑电平的函数(有延时),与电路的原始状态无关的
逻辑电路。也就是说,当输入信号中的任何一个发生变化时,输出都有可能会根据其
变化而变化,但与电路目前所处的状态没有任何关系。
组合逻辑:由与、或、非门组成的网络。常用的组合电路有:多路器、数据通路开
关、加法器、乘法器….
时序逻辑:输出不只是当前输入的逻辑
展开阅读全文