第一篇:Verilog学习心得
Verilog学习心得
因为Verilog是一种硬件描述语言,所以在写Verilog语言时,首先要有所要写的module在硬件上如何实现的概念,而不是去想编译器如何去解释这个module.比如在决定是否使用reg定义时,要问问自己物理上是不是真正存在这个register, 如果是,它的clock是什么? D端是什么?Q端是什么?有没有清零和置位?同步还是异步?再比如上面讨论的三态输出问题,首先想到的应该是在register的输出后面加一个三态门,而不是如何才能让编译器知道要“赋值”给一个信号为三态。同样,Verilog中没有“编译”的概念,而只有综合的概念。
写硬件描述语言的目的是为了综合,所以说要想写的好就要对综合器有很深的了解,这样写出来的代码才有效率。
曾经接触过motorola苏州设计中心的一位资深工程师,他忠告了一句:就是用verilog描述电路的时候,一定要清楚它实现的电路,很多人只顾学习verilog语言,而不熟悉它实现的电路,这是设计不出好的电路来的.一般写verilog code时,对整个硬件的结构应该是很清楚了,最好有详细的电路图画出,时序问题等都应该考虑清楚了。可以看着图直接写code。
要知道,最初Verilog是为了实现仿真而发明的.不可综合的Verilog语句也是很重要的.因为在实际设计电路时,除了要实现一个可综合的module外,你还要知道它的外围电路是怎样的,以及我的这个电路与这些外围电路能否协调工作.这些外围电路就可以用不可综合的语句来实现而不必管它是如何实现的.因为它们可能已经实际存在了,我仅是用它来模拟的.所以,在写verilog的时候应该要先明确我是用它来仿真的还是综合的.要是用来综合的话,就必须要严格地使用可综合的语句,而且不同的写法可能产生的电路会有很大差别,这时就要懂一些verilog综合方法的知识.就像前面说的,脑子里要有一个硬件的概念.特别是当综合报错时,就要想一想我这种写法能不能用硬件来实现,verilog毕竟还不是C,很多写法是不可实现的.要是这个module仅是用来仿真的,就要灵活得多了,这时你大可不必太在意硬件实现.只要满足它的语法,实现你要的功能就行了.有网友说关于#10 clk=~clk的问题,虽然这种语句是不可综合的,但是在做simulation和verification是常常用它在testbench中来产生一个clock信号。再比如常常用到的大容量memory, 一般是不会在片上实现的,这个时候也需要一个unsynthesizable module.mengxy所言切中肯罄。
我们设计的module的目的是为了可以综合出功能正确,符合标准的电路来。我想这是个反复的过程,就像我们在写design flow中总要注明前仿真,综合后的仿真,以及后仿真等。仿真是用来验证我们的设计的非常重要的手段。而verilog里那些看是无聊的语句这个时候就会发挥很大的作用。我想,用过verilog_xl的兄弟应该深有体会。verilog_xl里的操作,可以用verilog里的系统命令来完成。通过最近的应聘我也深有体会,很多公司看中你在写code时,是否考虑到timing, architecture,DFT等,这也说明verilog中的任何语句都非常重要的。要写代码前必须对具体的硬件有一个比较清晰的概念但是想一次完成可综合代码就太夸张了,verilog的自顶向下设计方法就是从行为建模开始的,功能验证了以后再转向可综合模型.太在意与可综合令初期设计变得太累
很同意这种看法,在做逻辑结构设计时,综合的因素是要考虑的,但是有很多东西不能考虑的过于细致,就是在设计的时候不能过于紧卡时延,面积等因素,因为这样以来综合后优化的余量就会很小,反而不利与设计的优化,如果在时延和面积要求不是很紧张的情况下,其实代码写的行为级,利用综合工具进行优化也是一种方法。偶就听说有一家很有名的公司,非常相信综合工具的优化能力,从来不作综合后仿真的,hehe.当然,如果面积和时延的要求很高,最好还是把代码写的底层一点,调用库单元时,也要充分考虑其面积和时延的因素。
Verilog与C++的类比
1.Verilog中的module对应C++中的class。它们都可以实例化。例如可以写一个FullAdder module,表示全加器这种器件。module FullAdder(a, b, cin, sum, cout);input a, b, cin;output sum, cout;
assign {cout, sum} = a + b + cin;endmodule
然后在执行8-bit补码加减运算的ALU module中实例化8个FullAdder,表示ALU用到了8个FullAdder。
module ALU(a, b, result, cout, is_add);input[7:0] a, b;input is_add;output[7:0] result;output cout;
wire[7:0] b_not = ~b;wire[7:0] b_in = is_add ? b : b_not;wire[7:0] carry;
assign carry[0] = is_add ? 1'b0 : 1'b1;
// module 实例化
// 8-bit ripple adder FullAdder fa0(a[0], b_in[0], carry[0], result[0], carry[1]);FullAdder fa1(a[1], b_in[1], carry[1], result[1], carry[2]);FullAdder fa2(a[2], b_in[2], carry[2], result[2], carry[3]);FullAdder fa3(a[3], b_in[3], carry[3], result[3], carry[4]);FullAdder fa4(a[4], b_in[4], carry[4], result[4], carry[5]);FullAdder fa5(a[5], b_in[5], carry[5], result[5], carry[6]);FullAdder fa6(a[6], b_in[6], carry[6], result[6], carry[7]);FullAdder fa7(a[7], b_in[7], carry[7], result[7], cout);endmodule
对应在C++中先写FullAdder class,然后在ALU class中以FullAdder作为data member。
class FullAdder { };
class ALU { FullAdder fa[8];};另外一点,moudle声明port的方式,像是从早期C语言的函数定义中学来的:
char* strcpy(dst, src)char* dst;char* src;{ //...}
2.Verilog中的模块调用时,指定端口可以使用名称绑定。C++在调用函数时,参数只能按顺序书写。例如memset()的原型是:
void *memset(void *s, int c, size_t n);
如果你想将某个buf清零,应该这么写: char buf[256];memset(buf, 0, sizeof(buf));
但是如果你不小心写成了: memset(buf, sizeof(buf), 0);
编译器不会报错,但运行的实际效果是根本没有对buf清零。(记得Richard Stevens的书里提到过这一点。)
在Verilog中,如果要写一个测试ALU的module,那么其中对ALU实例化的指令可以这么写:
module alu_test;reg[8:0] a_in, b_in;reg op_in;wire[7:0] result_out;wire carry_out;
ALU alu0(.a(a_in[7:0]),.b(b_in[7:0]),.is_add(op_in),.result(result_out),.cout(carry_out));//...endmodule 这样就比较容易检查接线错误。
另外,在C++中,如果所有参数类型不同,而且之间没有隐式类型转换,那么可以利用C++的强类型机制在编译期检查出这种调用错误。
3.Verilog中把大括弧{}用作bit的并置,因此语句块要用begin/end标示。Verilog中小括号()和中括号[]的作用与C++中类似,前者用于函数或模块调用,后者用于下标索引。我想如果Verilog把尖括号<>用作bit并置的话,就能把大括号{}解放出来,用作标示语句块,这样写起来更舒服一些。
4.Verilog本质上是测试驱动开发的。对于每个module都应该有对应的test bench(或称test fixture)。比较好的情况是,一个工程师写module,另一个工程师写对应的testbench,这样很容易检查出对电路功能需求理解不一致的地方。因此还可以说Verilog主张结对编程(pair programming)。例如对前面的ALU module的test bench可以写成: `timescale 1ns / 1ns module alu_test;
reg[8:0] a_in, b_in;reg op_in;
wire[7:0] result_out;wire carry_out;
ALU alu0(.a(a_in[7:0]),.b(b_in[7:0]),.is_add(op_in),.result(result_out),.cout(carry_out));
reg[9:0] get, expected;reg has_error;
initial begin has_error = 1'b0;
op_in = 1'b1;// test addition
for(a_in = 9'b0;a_in!= 256;a_in = a_in + 1)for(b_in = 9'b0;b_in!= 256;b_in = b_in + 1)begin #1;get = {carry_out, result_out};expected = a_in + b_in;if(get!== expected)begin $display(“a_in = %d, b_in = %d, expected %d, get %d”,a_in, b_in, expected, get);has_error = 1'b1;end end
op_in = 1'b0;// test subtraction //...if(has_error === 1'b0)begin $display(“ALL TESTS PASSED!”);end $finish;end endmodule
5.Verilog比起VHDL的不足之处在于,它只能定义concrete class,不能定义abstract class。也就是说interface和implementation不能分离。这在设计大型电路时就显得表现力不足。不过这关系不大,因为可以在编译时选择同一模块的不同实现版本,间接实现了接口与实现的分离。
在VHDL中,强制将接口与实现分离。对每个模块,你都得先写接口(定义输入输出信号),即ENTITY;然后至少写一份实现,即ARCHITECTURE。每个ENTITY可以有不止一份实现,例如可以有行为描述的,也有数据流描述的。然后在配置文件中选择该ENTITY到底用哪一份实现。举例来说(选自《VHDL入门·解惑·经典实例·经验总结》一书),分频器模块可以这么写,先定义其接口FreqDevider,然后定义两份实现Behavior和Dataflow:
LIBRARY IEEE;USE IEEE.Std_Logic_1164.All;
ENTITY FreqDevider IS PORT(Clock : IN Std_Logic;Clkout : OUT Std_Logic);END;
ARCHITECTURE Behavior OF FreqDevider IS SIGNAL Clk : Std_Logic;BEGIN PROCESS(Clock)BEGIN IF rising_edge(Clock)THEN Clk <= NOT Clk;END IF;END PROCESS;Clkout <= Clk;END;
ARCHITECTURE Dataflow OF FreqDevider IS--signal declarations BEGIN--processes END;在C++中,既可以写concrete class,也可以写abstract class。比Verilog和VHDL都方便。
6.Verilog和VHDL都有模板的概念,Verilog称为参数(parameter),VHDL称为类属(generic)。不过好像都只能用整数作为模板参数,不能像C++那样用类型作为模板参数。
7.目前来看,Verilog是硬件描述语言,不是硬件设计语言。在用Verilog设计电路的时候,我们是把脑子中想好的电路用Verilog“描述”出来:哪里是寄存器、哪里是组合逻辑、数据通路是怎样、流水线如何运作等等都要在脑子里有清晰的映象。然后用RTL代码写出来,经过综合器综合出的电路与大脑中的设想相比八九不离十。这就像说C语言是可移植的汇编语言,以前好的C程序员在写代码的时候,能够知道每条语句背后对应的汇编代码是什么。verilog设计经验点滴 1,敏感变量的描述完备性
Verilog中,用always块设计组合逻辑电路时,在赋值表达式右端参与赋值的所有信号都必须在always @(敏感电平列表)中列出,always中if语句的判断表达式必须在敏感电平列表中列出。如果在赋值表达式右端引用了敏感电平列表中没有列出的信号,在综合时将会为没有列出的信号隐含地产生一个透明锁存器。这是因为该信号的变化不会立刻引起所赋值的变化,而必须等到敏感电平列表中的某一个信号变化时,它的作用才表现出来,即相当于存在一个透明锁存器,把该信号的变化暂存起来,待敏感电平列表中的某一个信号变化时再起作用,纯组合逻辑电路不可能作到这一点。综合器会发出警告。Example1: 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=e |c;
end
Example2: input a,b,c;reg e,d;always @(a or b or c or d)
begin
e=d&a&b;/*d在敏感电平列表中,d变化时e立刻变化*/
d=e |c;
end
2, 条件的描述完备性
如果if语句和case语句的条件描述不完备,也会造成不必要的锁存器。Example1: if(a==1'b1)q=1'b1;//如果a==1'b0,q=? q将保持原值不变,生成锁存器!
Example2: if(a==1'b1)q=1'b1;else
q=1'b0;//q有明确的值。不会生成锁存器!Example3: reg[1:0] a,q;....case(a)
2'b00 : q=2'b00;
2'b01 : q=2'b11;//如果a==2'b10或a==2'b11,q=? q将保持原值不变,锁存器!
endcase Example4: reg[1:0] a,q;....case(a)
2'b00 : q=2'b00;
2'b01 : q=2'b11;
default: q=2'b00;//q有明确的值。不会生成锁存器!
endcase
Verilog中端口的描述
1,端口的位宽最好定义在I/O说明中,不要放在数据类型定义中; Example1: module test(addr,read,write,datain,dataout)
input[7:0] datain;input[15:0] addr;input
read,write;
output[7:0] dataout;//要这样定义端口的位宽!
wire addr,read,write,datain;
reg dataout;
Example2: module test(addr,read,write,datain,dataout)input datain,addr,read,write;output dataout;wire[15:0] addr;wire[7:0] datain;wire
read,write;reg[7:0] dataout;// 不要这样定义端口的位宽!
2,端口的I/O与数据类型的关系:
端口的I/O
端 口 的 数 据 类 型
module内部 module外部
input
wire wire或reg
output
wire或reg
wire
inout
wire
wire
3,assign语句的左端变量必须是wire;直接用“=”给变量赋值时左端变量必须是reg!Example: assign a=b;//a必须被定义为wire!******** begin a=b;//a必须被定义为reg!end
VHDL 中 STD_LOGIC_VECTOR 和 INTEGER 的区别
例如 A 是INTEGER型,范围从0到255;B是STD_LOGIC_VECTOR,定义为8位。A累加到255时,再加1就一直保持255不变,不会自动反转到0,除非令其为0;而B累加到255时,再加1就会自动反转到0。所以在使用时要特别注意!
以触发器为例说明描述的规范性 1,无置位/清零的时序逻辑
always @(posedge CLK)
begin
Q<=D;
end
2,有异步置位/清零的时序逻辑
异步置位/清零是与时钟无关的,当异步置位/清零信号到来时,触发器的输出立即被置为1或0,不需要等到时钟沿到来才置位/清零。所以,必须要把置位/清零信号列入always块的事件控制表达式。
always @(posedge CLK or negedge RESET)
begin
if(!RESET)
Q=0;
else
Q<=D;
end
3,有同步置位/清零的时序逻辑
同步置位/清零是指只有在时钟的有效跳变时刻置位/清零,才能使触发器的输出分别转换为1或0。所以,不要把置位/清零信号列入always块的事件控制表达式。但是必须在always块中首先检查置位/清零信号的电平。
always @(posedge CLK)
begin
if(!RESET)
Q=0;
else
Q<=D;
end
结构规范性
在整个芯片设计项目中,行为设计和结构设计的编码是最重要的一个步骤。它对逻辑综合和布线结果、时序测定、校验能力、测试能力甚至产品支持都有重要的影响。考虑到仿真器和真实的逻辑电路之间的差异,为了有效的进行仿真测试: 1,避免使用内部生成的时钟
内部生成的时钟称为门生时钟(gated clock)。如果外部输入时钟和门生时钟同时驱动,则不可避免的两者的步调不一致,造成逻辑混乱。而且,门生时钟将会增加测试的难度和时间。
2,绝对避免使用内部生成的异步置位/清零信号
内部生成的置位/清零信号会引起测试问题。使某些输出信号被置位或清零,无法正常测试。
3,避免使用锁存器
锁存器可能引起测试问题。对于测试向量自动生成(ATPG),为了使扫描进行,锁存器需要置为透明模式(transparent mode),反过来,测试锁存器需要构造特定的向量,这可非同一般。
4,时序过程要有明确的复位值
使触发器带有复位端,在制造测试、ATPG以及模拟初始化时,可以对整个电路进行快速复位。
5,避免模块内的三态/双向
内部三态信号在制造测试和逻辑综合过程中难于处理.补充不知你看了verilog 2001版本吗?现在的verilog在尽量往C语言的风格上靠拢。
1。敏感变量的描述完备性,我现在用always实现组合逻辑时,都是写成always@(*),这样很很好,自动把所有右端赋值信号加入。
2。module编写时这样更好: module(input [23:0] rx_data , //CRC_chk interface
output reg
CRC_en ,output reg
CRC_init , input
CRC_err);
第二篇:verilog语法学习心得(写写帮推荐)
这是我在查verilog的有符号数和无符号数时看到的,觉得很好,转载于此,共同学习
-----------------------------verilog语法学习心得
1.数字电路基础知识: 布尔代数、门级电路的内部晶体管结构、组合逻辑电路分析与设计、触发器、时序逻辑电路分析与设计
2.数字系统的构成: 传感器
AD 数字处理器
DA 执行部件
3.程序通在硬件上的执行过程:
C语言(经过编译)-->该处理器的机器语言(放入存储器)-->按时钟的节拍,逐条取出指令、分析指令、执行指令
4.DSP处理是个广泛概念,统指在数字系统中做的变换(DFT)、滤波、编码解码、加密解密、压缩解压等处理
5.数字处理器包括两部分:高速数据通道接口逻辑、高速算法电路逻辑
6.当前,IC产业包括IC制造和IC设计两部分,IC设计技术发展速度高于IC设计
7.FPGA设计的前续课程:数值分析、DSP、C语言、算法与数据结构、数字电路、HDL语言 计算机微体系结构
8.数字处理器处理性能的提高:软件算法的优化、微体系结构的优化
9.数字系统的实现方式:
编写C程序,然后用编译工具得到通用微处理器的机器指令代码,在通用微处理器上运行(如8051/ARM/PENTUIM)
专用DSP硬件处理器
用FPGA硬件逻辑实现算法,但性能不如ASIC
用ASIC实现,经费充足、大批量的情况下使用,因为投片成本高、周期长
10.FPGA设计方法: IP核重用、并行设计、层次化模块化设计、top-down思想
FPGA设计分工:前端逻辑设计、后端电路实现、仿真验证
11.matlab的应用:
matlab中有许多现成的数学函数可以利用,节省了复杂函数的编写时间
matlab可以与C程序接口
做算法仿真和验证时能很快生成有用的数据文件和表格
DSP builder可以直接将simulink模型转换成HDL代码,跳过了中间的C语言改写步骤 12.常规从算法到硬件电路的开发过程:
算法的开发
C语言的功能描述
并行结构的C语言改写
verilog的改写
仿真、验证、修正
综合、布局布线、投入实用
13.C语言改写成verilog代码的困难点:
并行C语言的改写,因为C本身是顺序执行,而不是并行执行
不使用C语言中的复杂数据结构,如指针
目前有将C语言转换成verilog的工具?
14.HDL
HDL描述方法是从电路图描述方法演化来的,相比来说更容易修改
符合IEEE标准的有verilog HDL和VHDL
VHDL由美国国防部开发,有1987和1993两个版本
verilog由cadence持有,有1995、2001、2005三个版本
verilog较VHDL更有前景:具有模拟电路描述能力、不仅可以开发电路还可以验证电路、门级以下描述比VHDL强
RTL级和门级的综合已经成熟,主要是注意行为级的综合结果,使用可综合的编程风格
SYSTEM VERILOG是VERILOG的一种延伸
15.IP核的应用:
软核soft core: 功能经过验证的、可综合的、实现后门数在5K以上的HDL代码
固核firm core: 功能经过验证的、可综合的、实现后门数在5K以上的电路结构编码文件,如edif,不可更改
硬核hard core: 功能经过验证的、可综合的、实现后门数在5K以上的电路结构版图,已带工艺参数,不可更改
16.HDL语言综合后得到EDIF,这是一种标准电路网表
EDIF经过具体工艺库匹配、布局布线、延时计算后得到网表
EDIF不可更改,作为固核存在
17.verilog特点:
区分大小写,所有关键字都要求小写
不是强类型语言,不同类型数据之间可以赋值和运算
//是单行注释
可以跨行注释
描述风格有系统级描述、行为级描述、RTL级描述、门级描述,其中RTL级和门级别与具体电路结构有关,行为级描述要遵守可综合原则
门级描述使用门级模型或者用户自定义模型UDP来代替具体基本元件,在IDE中针对不同FPGA器件已经有对应的基本元件原语
18.verlog语法要点:
module endmodule之间由两部分构成:接口描述和逻辑功能描述
IO端口种类: input output inout
相同位宽的输入输出信号可以一起声明,input[3:0] a,b;不同位宽的必须分开写
内部信号为reg类型,内部信号信号的状态: 0 1 x
z,3'bx1=3'bxx1 x/z会往左扩展
3'b1=3'b001 数字不往左扩展
逻辑功能描述中常用assign描述组合逻辑电路,always既可以描述组合逻辑电路又可以描述时序逻辑电路,还可以用元件调用方法描述逻辑功能
always之间、assign之间、实例引用之间以及它们之间都是并行执行,always内部是顺序执行
常量格式: <+/-><二进制位宽><'><进制><该进制的数值>:
默认进制为10进制
默认位宽为32位
位宽是从二进制宽度角度而言的
由位宽决定从低位截取二进制数2'hFF=2'b11,通常由被赋值的reg变量位宽决定
parameter常用于定义延迟和变量位宽,可用常量或常量表达式定义
变量种类: wire reg memory
IO信号默认为wire类型,除非指定为reg类型
wire可以用作任何输入输出端口
wire包括input output inout
wire不带寄存功能
assign赋值语句中,被赋值的信号都是wire类型
assign之所以称为连续赋值,是因为不断检测表达式的变化
reg类型可以被赋值后再使用,而不是向wire一样只能输出,类似VHDL中的buffer端口
reg类型变量初始值为x(VHDL中初始值为本类型最小值,通常是0)
always模块里被赋值的信号都必须定义为reg类型,因为always可以反复执行,而reg表示信号的寄存,可以保留上次执行的值
reg类型变量与integer变量不同,即使赋负值,实质上也是按二进制无符号数存储的,integer是有符号数
verilog中所有内部信号都是静态变量,因为它们的值都在reg中存储起来了
memory型只有一维数组,由reg型变量组成
memory初始化只能按地址赋值,不能一次性赋值
1*256的memory写法: reg mema[255:0]
mema[3]=0;
不同位宽的变量之间赋值,处理之前都以被赋值的变量位宽为准扩展或截取
A[a:b] 无论a b谁大,a总是实际电路的信号高位,b总是实际电路的信号低位
算术运算中如果有X值则结果为X
for循环中的变量另外定义成integer,因为它不是实际信号,有正负;reg则以无符号数存在
== 和!=只比较0、1,遇到z或x时结果都为x(x在if中算做假条件),结果可能是1、0、x
===和!==比较更加苛刻,包括x和z的精确比较,结果可能是0、1
&&的结果只有1'b1或1'b0两种,A&A的结果位宽则是与A相同的{1,0}为 64'h100000000,所以拼接运算中各信号一定要指定位宽
移位运算左移将保留 4'b1000<<1等于5'b10000,右移则舍弃 4'b0011等于4'b0001
数字电路里位运算应用普遍,包括按位逻辑运算、移位运算、拼接运算、缩减运算
非阻塞式赋值<=与阻塞式赋值=
阻塞:在同一个always过程中,后面的赋值语句要等待前一个赋值语句执行完,后面的语句被该赋值语句阻塞
非阻塞:在同一个always过程中,非阻塞赋值语句是同时进行的,排在后面的语句不会被该赋值语句阻塞
<=:
块结束后才能完成赋值
块内所有<=语句在always块结束时刻同时赋值
<=右边各变量的值是上一次时钟边沿时,这些变量当时的值
用于描述可综合的时序电路
=:
=语句结束之后过程always才可能结束
在always过程中,begin end块内按先后顺序立即赋值,在fork join内同时赋值(可能造成冲突)
与assign连用描述组合电路
begin end中阻塞的含义:begin...@(A)B=C...;end
如果A事件不发生则永远不能执行下去,被阻塞了
由于时钟的延时(往往在ps级),多个always(posedge)之间究竟谁先执行是个未知数
使用原则:同一个always过程块内建立时序电路用<=
纯组合逻辑电路用=,生成的电路结构最简单,执行速度最快
同一个always块内不要混用<=和=
不要在多个always块内对同一个变量赋值(多源驱动)
if else的三种形式,第三种形式适合描述优先编码器
if条件中0/x/z当成假,1当成真,非0的数值也当成真
case语句的三种: case(四种状态的比较)casez(忽略z)casex(忽略x和z,只看哪些位的信号有用)
case语句中所有表达式值的位宽必须相等,default中不能将n'bx用'bx代替
避免生成锁存器的方法: 电平触发时if后加else
case中加default
?
使用casex会将不必要的状态视为无关项,使得综合出来的电路最简单
两种特殊的括号: begin 顺序语句...end
fork 并行语句...join,其差别在于块内语句的起止时间、执行顺序、相对延时
块被命名后,其内部变量可以被调用,因为变量都是静态的(调用信号:对应电路中的一个信号线被引到另一处)
initial块只无条件执行一次
always块在满足条件时不断执行
initial常用来写测试文件,always块常用来写电路描述
always既可以描述组合逻辑电路又可以描述时序逻辑电路
always如果后面有敏感信号列表则不能用wait语句
always既可以描述电平触发又可以描述边沿触发,wait只能描述电平触发
assign常用于描述组合逻辑电路
测试文件中一般都是现initial 后always
生成语句:生成快的本质是使用循环内的一条语句代替多条重复的verilog语句,简化了用户的编程
genvar用于声明生成变量,生成变量只能用在生成快之间
仿真时,仿真器会将生成块中的代码展平,在确立后的方针代码中,生成变量是不存在的
最好是先想象出来循环生成语句被展平后的电路样子,再写相关的描述语句
task和function的区别:
task可以定义自己的仿真时间单位,function与主模块共用同一个仿真时间单位
函数不能启动任务,任务能够启动函数
函数至少要有一个输入变量,任务没有输入变量
函数返回一个值,任务不返回值
一个模块的设计包括3个部分: 电路模块的设计
测试模块的设计
设计文档的编写
设计者通过布局布线工具生成具有布线延迟的电路,再进行后仿真,得到时序分析报告
从时序分析报告中可以知道电路的实际延迟t,同步电路内每个时钟周期要大于t,从而可确定该运算逻辑的最高频率
综合器之所以能够实现加法器、乘法器是因为库中已经存在可配置的参数化器件模型
FPGA内总线宽度容易自定义,以便实现高速数据流,三态数据总线相当于数据流的控制阀门
数字系统内数据流的控制: 开关(或三态数据总线)、数据暂存部件(寄存器)、同步状态机控制(整个系统在一个时钟域内)
流水线操作pipe line:
K级流水线就是从组合逻辑的输入到输出恰好有K个寄存器组,上一级的输出是下一级的输入
流水线操作获得第一个结果的时间要比不用流水线操作的时间长,但以后结果获得时间都只需要一个时钟周期,提高了数据吞吐量
流水线操作的保证:Tclk>K*(组合逻辑延迟+触发器的建立保持时间/触发时间),即时间片段要长于最大路径延迟
体现了面积换速度的思想,在综合时考虑的是以面积小为主还是以速度为主
本质上是一种同步逻辑
同步时序逻辑和异步时序逻辑:
同步时序逻辑指所有寄存器组由唯一时钟触发
always@(posedge clk)或always@(negedage clk)
异步时序逻辑指触发条件不唯一,任意一个条件都会引起触发
always@(posedge clk or posedage reset)
目前的综合器是以同步时序逻辑综合的,因为同步时序逻辑较异步时序逻辑可靠
严格的同步要求时钟信号传递速度远远大于各部分的延迟,实际中clk要单独用线,而不要经过反相器等部件
always @(posedge..)begin...<=...end 表示同步时序逻辑(同时刻赋值)
不同速率数据接口的处理方法(异步数据的处理方法):帧同步
FIFO 双端口RAM
同步状态机:
包括moore和mealy型两种,及其反馈模型(是一种反馈控制系统,当前状态就是其内部状态变量)
状态机的开发步骤:
根据实际问题列出输入输出变量和状态数
画出状态图并化简
写出状态转移真值表得到逻辑表达式
用D触发器或JK触发器构建电路(目前用D触发器多)
verilog描述时只需要得到简化的状态图就可以描述
状态编码方式: 独热码
格雷码
状态机主体程序有单always描述方式和多always描述方式
采用case/casez/casex建立模型最好,因为x是无关态,生成的电路最简单
default: state='bx与实际情况更一致,效果等同于 default: state<=idle
只有同步状态机才能被目前的综合for语句会将所有变量的情况展开,占用巨量逻辑资源,替代办法是用计数器和case语句说明所有情况
有优先级的if else结构会消耗更多资源,建议用无优先级的case替代 模块的复用往往比代码上修改节省的资源多 PLL的分频、倍频、移相操作会增加设计精度
同步时序电路的延时:#x通常用于仿真测试,实际硬件延时是:长延迟用计数器,小延迟用D触发器,此方法用来取代延迟链
同步电路中,稳定的数据采用必须满足采样寄存器的建立和保持时间 reg类型在always中不一定综合成时序电路,也可能是组合逻辑电路 乒乓操作与作用
异步时钟域同步问题 延迟包括门延迟和线延迟
组合逻辑产生的时钟仅能应用在时钟频率较低、精度要求不高的情况下 增减敏感信号得到的结果一样
补充部分:
verilog HDL起初是作为写testbench而产生的 verilog 有1995进入IEEE标准,为IEEE-1364, 于2001年进行了扩展,为IEEE 1364-2001; verilog AMS可用于模拟电路和数字电路的综合,目前正在不断发展和完善中; verilog的标识符区分大小写,关键字使用小写; 用来进行单行注释,用* *来进行跨行注释;
标识符由字母、数字、下划线构成,并以字母开头; 关键字又叫保留字,只有小写的关键字才是保留字;
信号的状态有4种: 0 1 x z x和z在描述电路时不区分大小写,在仿真时大小写有不同意义;
常量表达式中: x z不区分大小写;
进制符号h o d b与H O D B不区分大小写; 十六进制中a~f不区分大小写; 下划线_用于提高可读性; ?在数中可以代替z; x和z的左端补位;
字符和字符串都以ASICII码形式存在,也可以当成电路内的信号; 字符串必须包含在同一行,不能分成多行书写;
如果表达式或者赋值语句中将字符串当成操作数,则字符串中的每个字符都被看成8位的ASCII值序列;
可综合的信号类型:wire reg memory 它们用来描述数字电路
不可综合的数据类型:integer real 它们只用仿真,位于testbench中
wire是连线的抽象模型,不能保存数据,其值由驱动元的值决定; wire不能用在always或initial块中; wire的默认值为高阻z;
wire的使用情形: 1.作为模块的输出端口
2.用连续赋值语句assign赋值; reg是1位寄存器(触发器)的抽象模型,可以保存数据; reg必须用在always或initial块中; reg的默认值为x;
reg的使用情形:1.阻塞赋值<= 2.非阻塞赋值= memory只能是一维的;
memory只能对每个单元分别初始化,方法:1.一个一个赋值
2.通过系统任务$readmem赋值
reg[3:0] fc;//一个4位寄存器
reg fc[3:0] //4个一位寄存器
parameter的作用:仿真开始以前对其进行赋值,整个仿真过程中保持其值不变;
关系运算符将以逻辑1或逻辑0返回比较的结果;
==!=的返回值有0 1 x三种情况,===!==的返回值只有0 1两种情况;
verilog由于是描述电路的,用于位的操作较多,有: 位逻辑操作,移位操作,并置操作,归约操作; 位逻辑运算的结果中,位数与原操作数一样多;
归约符是在原操作数的所有位上进行操作,并产生1位结果;
并置运算可以发生在bit与bit之间 bit与矢量之间 矢量与矢量之间
用于仿真的系统任务:
所有系统任务都必须在initial或always内; 所有系统任务都必须以$开头; 常见系统任务:
显示任务($diplay系列和$write系列)
监控任务($monitor系列)
探测任务($strobe系列)
文件打开、输入、关闭任务(&fopen &fclose &fdisplay...)
读取文件任务($readmemb $readmemh)
仿真结束控制任务($finish $stop)
随即信号任务($random)
过程块: initial块和always块
一个module内可以包含多个initial或always模块;
所有initial或always块在0时刻开始并行执行,各initial或always块内部顺序执行; initial过程块主要是面向testbench的,通常不具有可综合性;
always过程块在描述电路时既可以描述组合逻辑电路(电平敏感)又可以描述时序逻辑电路(边沿敏感);
写testbench时initial通常用于初始化以及顺序波形的描述,always通常用于重复波形的描述;
任务task与函数function: 为了描述模块中被多次执行的部分以及为了增强代码的易读性 verilog中的高级程序语句如for循环语句只用在写testbench中; begin end和fork join是两种特殊的括号
if语句的第三种形式适合描述优先编码器,case语句适合描述数据选择器和状态机;
case的条件表达式如果与分支项表达式长度不同,则在比较前将所有表达式都统一为这些表达式的最长长度;
casez忽略z,casex忽略z和x;
assign语句只在右端表达式发生变化时才重新计算并重新赋值,其余时间都是连续赋值; assign语句可以指定bit、vector或是任意拼接操作的结果;
assign语句是连续赋值的,用于驱动网线wire,reg类型不需要连续赋值,reg类型一旦被赋值就会一直保存;
过程赋值语句有两种:阻塞式=和非阻塞式<=,只能在过程块initial和always中使用; @对事件触发的控制与wait语句不能同时使用;
第三篇:verilog作业题
1、以结构描述方式实现下列逻辑:
F=AB+ACD(CD的非)
2、以连续赋值语句设计8位总线驱动器。
3、以always语句设计8位总线驱动器。
4、以always语句设计8位双向总线驱动器。
1、设计一个具有低电平使能端和高电平使能端的2-4译码器。
2、设计一个JK触发器。
3、设计一个24分频期,要求输出信号占空比为1:1。
4、一个挂在总线上的16位寄存器。
5、设计一个8位线编码器,输入为D7 - D0,D7优先级最高,D0最低。当Di为高电平输入时,F=1,OUT为其编码,否则F=0。
1、以结构描述方式实现下列逻辑:
Y=ABC(BC的非)+DE+ CFG(CFG的非)
2、试设计一个具有使能端ncs的2-4译码器。
3、试设计一个4位加减运算器,输入为A、B、CIN、M,输出为OUT和COUT。当M=0时执行加法运算,M=1时,执行减法运算。
4、试设计一个14分频器,要求占空比1:1。
5、试设计一个具有三态输出缓冲的8位数据寄存器。
6、试设计一个具有清0和置数功能的8位二进制加1计数器。
7、设计一个16位移位寄存器。
8、设计一个16位双向移位寄存器,当d=0时右移, d=1时左移。
1设中断请求有效电平为高电平,中断请求输入线INTR0—INTR15中INTR15优先权最高。试设计一个中断优先权编码器。当有中断请求时,INT=1,同时输出中断请求输入线的编码V;否则INT=0,V的输出任意。
2试设计一个具有同步清0和同步置数功能的8位二进制加1计数器。
3试设计一个“11010100”序列检测器.4设计一个8位双向移位寄存器。其I/O端口和控制端口包括d(移位方向控制,当d=0时右移, d=1时左移)、数据串行输入din、数据并行输入data(8位)、dout数据串行输出、数据并行输出q(8位)、同步时钟clk、并行置数load等。
5设计每周期8个采样点的锯齿波信号发生器。
6设计每周期8个采样点的正弦波信号发生器。
第四篇:学verilog小结
学习verilog一段时间 小结 学习verilog, verilog, verilog小结
一:基本
Verilog中的变量有线网类型和寄存器类型。线网型变量综合成wire,而寄存器可能综合成WIRE,锁存器和触发器。
二:verilog语句结构到门级的映射
1、连续性赋值:assign 连续性赋值语句逻辑结构上就是将等式右边的驱动左边的结点。因些连续性赋值的目标结点总是综合成由组合逻辑驱动的结点。Assign语句中的延时综合时都将忽视。
2、过程性赋值:
过程性赋值只出现在always语句中。
阻塞赋值和非阻塞赋值就该赋值本身是没有区别的,只是对后面的语句有不同的影响。
建议设计组合逻辑电路时用阻塞赋值,设计时序电路时用非阻塞赋值。
过程性赋值的赋值对象有可能综合成wire,latch,和flip-flop,取决于具体状况。如,时钟控制下的非阻塞赋值综合成flip-flop。
过程性赋值语句中的任何延时在综合时都将忽略。
建议同一个变量单一地使用阻塞或者非阻塞赋值。
3、逻辑操作符:
逻辑操作符对应于硬件中已有的逻辑门
4、算术操作符:
Verilog中将reg视为无符号数,而integer视为有符号数。因此,进行有符号操作时使用
integer,使用无符号操作时使用reg。
5、进位:
通常会将进行运算操作的结果比原操作数扩展一位,用来存放进位或者借位。如:
Wire [3:0] A,B;
Wire [4:0] C;Assign C=A+B;C的最高位用来存放进位。
6、关系运算符: 关系运算符:<,>,<=,>= 和算术操作符一样,可以进行有符号和无符号运算,取决于数据类型是reg,net还是
integer。
7、相等运算符:==,!= 注意:===和!==是不可综合的。
可以进行有符号或无符号操作,取决于数据类型
8、移位运算符:
左移,右移,右边操作数可以是常数或者是变量,二者综合出来的结果不同。
9、部分选择: 部分选择索引必须是常量。
10、BIT选择:
BIT选择中的索引可以用变量,这样将综合成多路(复用)器。
11、敏感表:
Always过程中,所有被读取的数据,即等号右边的变量都要应放在敏感表中,不然,综合时不能正确地映射到所用的门。
12、IF:
如果变量没有在IF语句的每个分支中进行赋值,将会产生latch。如果IF语句中产生了latch,则IF的条件中最好不要用到算术操作。Case语句类似。Case的条款可以是变量。
如果一个变量在同一个IF条件分支中先赎值然后读取,则不会产生latch。如果先读取,后赎值,则会产生latch。
13、循环:
只有for-loop语句是可以综合的。
14、设计时序电路时,建议变量在always语句中赋值,而在该always语句外使用,使综合时能准确地匹配。建议不要使用局部变量。
15、不能在多个always块中对同一个变量赎值
16、函数
函数代表一个组合逻辑,所有内部定义的变量都是临时的,这些变量综合后为wire。
17、任务:
任务可能是组合逻辑或者时序逻辑,取决于何种情况下调用任务。
18、Z:
Z会综合成一个三态门,必须在条件语句中赋值
19、参数化设计:
优点:参数可重载,不需要多次定义模块
四:模块优化
1、资源共享:
当进程涉及到共用ALU时,要考虑资源分配问题。可以共享的操作符主要有:关系操作符、加减乘除操作符。通常乘和加不共用ALU,乘除通常在其内部共用。
2、共用表达式: 如:C=A+B;D=G+(A+B);两者虽然有共用的A+B,但是有些综合工具不能识别.可以将第二句改为:D=G+C;这样
只需两个加法器.
3、转移代码:
如循环语句中没有发生变化的语句移出循环.
4、避免latch:
两种方法:
1、在每一个IF分支中对变量赋值。
2、在每一个IF语句中都对变量赋初值。
5:模块:
综合生成的存储器如ROM或RAM不是一种好方法。最好用库自带的存储器模块。
五、验证: 1、敏感表:
在always语句中,如果敏感表不含时钟,最好将所有的被读取的信号都放在敏感表中。
2、异步复位:
建议不要在异步时对变量读取,即异步复位时,对信号赎以常数值。
第五篇:verilog学习日志
1.解决xilinx的仿真库的编辑问题
2.模块的做法和调用方法,带参数模块的应用:两种方法modelname #(value)madelcase();
二、用defparam 改变参数。
3.Begin ……end之间是串行执行的语句。在实际的电路中各条语句并不是完全串行的。4.Fork……join之间是并行执行的语句,但是不可综合;
5.表征过程模块的四种工程模块:initial模块,always模块,task模块,function模块;一个程序中可以有多个initial模块和always模块,但是initial模块只执行一次,不可综合,而always模块是不断重复执行的,可以综合。6.数据类型两大类:线网类型和寄存器类型
7.线网类型:结构化原件之间的物理连接,默认值高阻z;寄存器表示数据的存储单元,默认值为x;寄存器类型数据保持最后一次赋值,而线型数据需要持续的驱动; 8.线网数据场用来以关键字assign指定组合逻辑信号。模块中输入输出默认为wire类型,可综合的线网类型有wire,tri,supply0,supply1;线网类型的变量赋值只能通过assign来完成,不能用于always语句。
9.寄存器类型的数据没有强弱之分,且所有的寄存器类型变量都必须明确给出类型说明(无默认状态)。10.If语句指定了一个有限编码逻辑,case结果是并行的,没有优先级,if……else占用面积小但速度较慢,case语句占用面积大,但速度较快。11.如果if……else语句块为单句则直接写就可以,如果不只一句则要用begin……end括起来。12.循环语句for,while,repeat可以综合,forever不可综合。13.对于reg型数据不能用assign赋值。
14.拼接数据时最好将数据的进制形式都写详细,否则仿真出错,不知为何?例如:parameter a = 8;b = {{a{1’b0}},c};
15.module mult_8b_for(16.a,b,q 17.);18.parameter bsize = 8;19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.input [bsize1:0] b_t;reg [bsize1)begin
if(b_t[0])
begin
q = q + a_t;//一定要使用阻塞赋值
end else 36.37.38.39.40.begin
q = q;//一定要使用阻塞赋值
end
a_t = a_t <<1;//一定要使用阻塞赋值 b_t = b_t >>1;//一定要使用阻塞赋值
41.42.end end
43.Endmodule
44.Always语句中使用非阻塞赋值“<=”时,只有在always结束后才会把右端的赋值给左边的寄存器,如果采用非阻塞赋值,则会造成循环语句只执行一次。虽然模块整体是时序逻辑,但是循环部分却是组合逻辑。
45.Task和function是综合的,不过综合出来都是组合电路。46.在第一行Task语句中不能列出端口名称。
47.Task中可以调用其他的任务或函数,也可以调用自身;
48.任务定义的结构体内部能出现always或initial过程块(可能是因为其只能综合成组合电路的缘故吧)。但是可以在always或initial中调用。调用时要与声明的端口顺序相同。且只能在过程块中调用。
49.任务的输出端口必须和寄存器类型的数据变量对应。50.Function不允许有输出端口的声明,函数定义在函数内部会隐式的定义一个寄存器变量,该寄存器变量和函数同名并且位宽也一致。函数内部不能调用任务。
51.触摸板如何禁止:下载一个UltraNav驱动按上,到控制面板鼠标里面就可以禁止触摸板了。Win7下可以直接去禁止,不用安装驱动程序。
52.保证敏感信号列表的完备性。不要在组合逻辑中引入环路。
53.同步时序电路的准则:
1、单时钟策略,单时钟沿策略。
2、避免使用门控时钟。
3、不要再子模块内部使用计数器分频产生所需时钟。推荐的方式是有一个专门的子模块来管理系统时钟。
54.同步电路在目前数字电路系统中占绝对优势,和异步电路相比有下列优势:对温度、电压、生产过程等外部参数的适应性强;可以消除毛刺和内部的歪斜的数据,能将设计频率提高的吉赫兹。但是需要更多的资源,而且其时钟翻转频率高,功耗也比较大。55.关键路径:最大的可能时钟频率是由电路中最慢的逻辑路径决定的。一种消除这种限制的方法是将复杂的运算分开成为数个简单的运算,这种技术称为pipelining。56.要输就输给追求,要嫁就嫁给幸福。
57.第九章的二级流水线仿真时结果中显示不对,不知道为何,可能是testbench写的不好,有时间一定要搞明白。58.提高工作速度的两种方式:并行方式和流水线方式。核心思想是面积换速度。59.‘timescale 1ns / 1ps 代表时延单位为1ns,时延精度为1ps。
60.亚稳态:多时钟设计中,必然存在亚稳态,当触发器的建立时间和保持时间没有达到要求是,触发器就会处于逻辑1和逻辑0之间的第3中状态。及亚稳态。
61.状态机不仅仅是一种电路,而且是一种设计思想,他可以说是一个广义时序电路,状态机是数字设计的“大脑”。
62.状态机开发流程:首先要抽象出事物的状态,再次明确状态和输入输出之间的关系,让后得到系统的状态转移图,并用verilog实现。
63.Moore状态机输出仅仅依赖于当前状态,与输入无关,下一个状态与输入和当前状态有关,64.Mealy状态机的输出与输入和当前状态有关,65.状态机的容错处理:对剩余状态进行处理。方法:转入空闲状态、转入特定状态、转入预定义的专门处理错误的状态,如报警。66.状态机设计原则:单独用一个verilog模块来描述一个有限状态机。使用代表状态名的参数parameter来给状态赋值,而不是用宏定义。在组合always块中使用阻塞赋值,在时序always块中使用非阻塞赋值,67.利用三段式设计状态机。第一段处理当前状态的赋值。第二段处理下一个状态的赋值。第三段处理输出的赋值。
68.Case后面没有begin 结尾处加endcase;
69.利用stateCAD做状态机,注意填写条件是没有标点符号,而在写输出时有标点;ise软件在10版本以后就没有自带的CAD软件了,下了一个单独运行的一样可以用。70.同步整形电路:同步电路具备最稳定的工作状态和工作能力因此经常需要将外部输入的异步信号进行同步处理(与系统时钟同步)和整形(经输入信号由不规则的波形提取为具有一个时钟周期长的脉冲信号)。313页。71.Assign只能用来给wire和output赋值,不能用来给reg类型赋值;
72.Xst:528-Multi-source in Unit
75.综合synthesize:查看综合报告、查看RTL级原理图、检查语法。实现:implement,翻译、映射、布局布线,实现之前要进行约束条件的编辑。76.