收藏 分享(赏)

Verilog学习心得.docx

上传人:hwpkd79526 文档编号:6627630 上传时间:2019-04-18 格式:DOCX 页数:15 大小:27.27KB
下载 相关 举报
Verilog学习心得.docx_第1页
第1页 / 共15页
Verilog学习心得.docx_第2页
第2页 / 共15页
Verilog学习心得.docx_第3页
第3页 / 共15页
Verilog学习心得.docx_第4页
第4页 / 共15页
Verilog学习心得.docx_第5页
第5页 / 共15页
点击查看更多>>
资源描述

1、 Verilog 学习心得因为 Verilog 是一种硬件描述语言,所以在写 Verilog 语言时,首先要有所要写的 module 在硬件上如何实现的概念,而不是去想编译器如何去解释这个 module. 比如在决定是否使用 reg 定义时,要问问自己物理上是不是真正存在这个 register, 如果是,它的 clock 是什么? D 端是什么?Q 端是什么?有没有清零和置位?同步还是异步?再比如上面讨论的三态输出问题,首先想到的应该是在 register 的输出后面加一个三态门,而不是如何才能让编译器知道要“赋值”给一个信号为三态。同样,Verilog 中没有“编译”的概念,而只有综合的概念

2、。 写硬件描述语言的目的是为了综合,所以说要想写的好就要对综合器有很深的了解,这样写出来的代码才有效率。 曾经接触过 motorola 苏州设计中心的一位资深工程师, 他忠告了一句:就是用 verilog 描述电路的时候,一定要清楚它实现的电路 ,很多人只顾学习 verilog 语言,而不熟悉它实现的电路, 这是设计不出好的电路来的.一般写 verilog code 时,对整个硬件的结构应该是很清楚了,最好有详细的电路图画出,时序问题等都应该考虑清楚了。可以看着图直接写 code。 要知道 ,最初 Verilog 是为了实现仿真而发明的.不可综合的 Verilog 语句也是很重要的.因为在实际

3、设计电路时,除了要实现一个可综合的module 外,你还要知道它的外围电路是怎样的, 以及我的这个电路与这些外围电路能否协调工作.这些外围电路就可以用不可综合的语句来实现而不必管它是如何实现的.因为它们可能已经实际存在了,我仅是用它来模拟的.所以,在写 verilog 的时候应该要先明确我是用它来仿真的还是综合的. 要是用来综合的话,就必须要严格地使用可综合的语句 ,而且不同的写法可能产生的电路会有很大差别,这时就要懂一些 verilog 综合方法的知识.就像前面说的,脑子里要有一个硬件的概念. 特别是当综合报错时,就要想一想我这种写法能不能用硬件来实现,verilog 毕竟还不是 C,很多写

4、法是不可实现的.要是这个 module 仅是用来仿真的, 就要灵活得多了,这时你大可不必太在意硬件实现. 只要满足它的语法, 实现你要的功能就行了.有网友说关于#10 clk=clk 的问题,虽然这种语句是不可综合的,但是在做 simulation 和 verification 是常常用它在 testbench 中来产生一个 clock 信号。再比如常常用到的大容量 memory, 一般是不会在片上实现的,这个时候也需要一个 unsynthesizable module. mengxy所言切中肯罄。我们设计的 module 的目的是为了可以综合出功能正确,符合标准的电路来。我想这是个反复的过程

5、,就像我们在写 design flow 中总要注明前仿真,综合后的仿真,以及后仿真等。仿真是用来验证我们的设计的非常重要的手段。而 verilog 里那些看是无聊的语句这个时候就会发挥很大的作用。我想,用过 verilog_xl 的兄弟应该深有体会。verilog_xl 里的操作,可以用 verilog 里的系统命令来完成。 通过最近的应聘我也深有体会,很多公司看中你在写 code 时,是否考虑到 timing,architecture,DFT 等,这也说明 verilog 中的任何语句都非常重要的。 要写代码前必须对具体的硬件有一个比较清晰的概念但是想一次完成可综合代码就太夸张了,veril

6、og 的自顶向下设计方法就是从行为建模开始的,功能验证了以后再转向可综合模型. 太在意与可综合令初期设计变得太累 很同意这种看法,在做逻辑结构设计时,综合的因素是要考虑的,但是有很多东西不能考虑的过于细致,就是在设计的时候不能过于紧卡时延,面积等因素,因为这样以来综合后优化的余量就会很小,反而不利与设计的优化,如果在时延和面积要求不是很紧张的情况下,其实代码写的行为级,利用综合工具进行优化也是一种方法。偶就听说有一家很有名的公司,非常相信综合工具的优化能力,从来不作综合后仿真的,hehe.当然,如果面积和时延的要求很高,最好还是把代码写的底层一点,调用库单元时,也要充分考虑其面积和时延的因素。

7、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(

8、a, b, result, cout, is_add); input7:0 a, b; input is_add; output7:0 result; output cout; wire7:0 b_not = b; wire7:0 b_in = is_add ? b : b_not; wire7:0 carry; assign carry0 = is_add ? 1b0 : 1b1; / module 实例化 / 8-bit ripple adder FullAdder fa0(a0, b_in0, carry0, result0, carry1); FullAdder fa1(a1, b_i

9、n1, carry1, result1, carry2); FullAdder fa2(a2, b_in2, carry2, result2, carry3); FullAdder fa3(a3, b_in3, carry3, result3, carry4); FullAdder fa4(a4, b_in4, carry4, result4, carry5); FullAdder fa5(a5, b_in5, carry5, result5, carry6); FullAdder fa6(a6, b_in6, carry6, result6, carry7); FullAdder fa7(a

10、7, b_in7, carry7, result7, cout); endmodule 对应在 C+中先写 FullAdder class,然后在 ALU class 中以 FullAdder 作为 data member。 class FullAdder ; class ALU FullAdder fa8; ; 另外一点,moudle 声明 port 的方式,像是从早期 C 语言的函数定义中学来的:char* strcpy(dst, src)char* dst;char* src; / .2. Verilog 中的模块调用时,指定端口可以使用名称绑定。C+在调用函数时,参数只能按顺序书写。例

11、如 memset()的原型是: void *memset(void *s, int c, size_t n); 如果你想将某个 buf 清零,应该这么写: char buf256; memset(buf, 0, sizeof(buf); 但是如果你不小心写成了: memset(buf, sizeof(buf), 0); 编译器不会报错,但运行的实际效果是根本没有对 buf 清零。(记得Richard Stevens 的书里提到过这一点。) 在 Verilog 中,如果要写一个测试 ALU 的 module,那么其中对 ALU实例化的指令可以这么写: module alu_test; reg8:

12、0 a_in, b_in; reg op_in; wire7:0 result_out; wire carry_out; ALU alu0(.a(a_in7:0), .b(b_in7:0), .is_add(op_in), .result(result_out), .cout(carry_out); / . endmodule 这样就比较容易检查接线错误。 另外,在 C+中,如果所有参数类型不同,而且之间没有隐式类型转换,那么可以利用 C+的强类型机制在编译期检查出这种调用错误。 3. Verilog 中把大括弧用作 bit 的并置,因此语句块要用 begin/end标示。Verilog 中小

13、括号 ()和中括号 的作用与 C+中类似,前者用于函数或模块调用,后者用于下标索引。我想如果 Verilog 把尖括号 用作 bit 并置的话,就能把大括号解放出来,用作标示语句块,这样写起来更舒服一些。4. Verilog 本质上是测试驱动开发的 。对于每个 module 都应该有对应的 test bench(或称 test fixture)。比较好的情况是,一个工程师写module,另一个工程师写对应的 testbench,这样很容易检查出对电路功能需求理解不一致的地方。因此还可以说 Verilog 主张结对编程(pair programming)。例如对前面的 ALU module 的

14、test bench 可以写成: timescale 1ns / 1ns module alu_test; reg8:0 a_in, b_in; reg op_in; wire7:0 result_out; wire carry_out; ALU alu0(.a(a_in7:0), .b(b_in7:0), .is_add(op_in), .result(result_out), .cout(carry_out); reg9:0 get, expected; reg has_error; initial begin has_error = 1b0; op_in = 1b1; / test ad

15、dition for (a_in = 9b0; a_in != 256; a_in = a_in + 1) for (b_in = 9b0; 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 = 1b1; end end op

16、_in = 1b0; / test subtraction / . if (has_error = 1b0) begin $display(“ALL TESTS PASSED!“); end $finish; end endmodule 5. Verilog 比起 VHDL 的不足之处在于,它只能定义 concrete class,不能定义 abstract class。也就是说 interface 和 implementation 不能分离。这在设计大型电路时就显得表现力不足。不过这关系不大,因为可以在编译时选择同一模块的不同实现版本,间接实现了接口与实现的分离。 在 VHDL 中,强制将接

17、口与实现分离。对每个模块,你都得先写接口(定义输入输出信号),即 ENTITY;然后至少写一份实现,即 ARCHITECTURE。每个 ENTITY 可以有不止一份实现,例如可以有行为描述的,也有数据流描述的。然后在配置文件中选择该 ENTITY 到底用哪一份实现。举例来说(选自VHDL 入门解惑经典实例经验总结一书),分频器模块可以这么写,先定义其接口 FreqDevider,然后定义两份实现 Behavior 和 Dataflow:LIBRARY IEEE; USE IEEE.Std_Logic_1164.All; ENTITY FreqDevider IS PORT ( Clock :

18、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 E

19、ND; 在 C+中,既可以写 concrete class,也可以写 abstract class。比 Verilog 和 VHDL 都方便。 6. Verilog 和 VHDL 都有模板的概念,Verilog 称为参数(parameter) ,VHDL 称为类属(generic)。不过好像都只能用整数作为模板参数,不能像 C+那样用类型作为模板参数。 7. 目前来看,Verilog 是硬件描述语言,不是硬件设计语言。在用 Verilog 设计电路的时候,我们是把脑子中想好的电路用 Verilog“描述”出来:哪里是寄存器、哪里是组合逻辑、数据通路是怎样、流水线如何运作等等都要在脑子里有清晰的

20、映象。然后用 RTL 代码写出来,经过综合器综合出的电路与大脑中的设想相比八九不离十。这就像说 C 语言是可移植的汇编语言,以前好的 C 程序员在写代码的时候,能够知道每条语句背后对应的汇编代码是什么。 verilog 设计经验点滴1,敏感变量的描述完备性Verilog 中, 用 always 块设计组合逻辑电路时,在赋值表达式右端参与赋值的所有信号都必须在 always (敏感电平列表)中列出,always中 if 语句的判断表达式必须在敏感电平列表中列出。如果在赋值表达式右端引用了敏感电平列表中没有列出的信号,在综合时将会为没有列出的信号隐含地产生一个透明锁存器。这是因为该信号的变化不会立

21、刻引起所赋值的变化,而必须等到敏感电平列表中的某一个信号变化时,它的作用才表现出来,即相当于存在一个透明锁存器,把该信号的变化暂存起来,待敏感电平列表中的某一个信号变化时再起作用,纯组合逻辑电路不可能作到这一点。综合器会发出警告。Example1:input a,b,c;reg e,d;always (a or b or c)begine=d /*d 没有在敏感电平列表中,d 变化时 e 不会立刻变化,直到 a,b,c 中某一个变化*/d=e |c;endExample2:input a,b,c;reg e,d;always (a or b or c or d)begine=d /*d 在敏感

22、电平列表中,d 变化时 e 立刻变化*/d=e |c;end2, 条件的描述完备性如果 if 语句和 case 语句的条件描述不完备,也会造成不必要的锁存器。Example1:if (a=1b1) q=1b1;/如果 a=1b0,q=? q 将保持原值不变,生成锁存器!Example2:if (a=1b1) q=1b1;else q=1b0;/q 有明确的值。不会生成锁存器!Example3:reg1:0 a,q;case (a)2b00 : q=2b00;2b01 : q=2b11;/如果 a=2b10 或 a=2b11,q=? q 将保持原值不变,锁存器!endcaseExample4:r

23、eg1:0 a,q;case (a)2b00 : q=2b00;2b01 : q=2b11;default: q=2b00;/q 有明确的值。不会生成锁存器!endcaseVerilog 中端口的描述1,端口的位宽最好定义在 I/O 说明中,不要放在数据类型定义中;Example1:module test(addr,read,write,datain,dataout)input7:0 datain;input15:0 addr;input read,write;output7:0 dataout; /要这样定义端口的位宽!wire addr,read,write,datain;reg data

24、out;Example2:module test(addr,read,write,datain,dataout)input datain,addr,read,write;output dataout;wire15:0 addr;wire7:0 datain;wire read,write;reg7:0 dataout; / 不要这样定义端口的位宽!2,端口的 I/O 与数据类型的关系:端口的 I/O 端 口 的 数 据 类 型module 内部 module 外部input wire wire 或 regoutput wire 或 reg wireinout wire wire3,assign

25、 语句的左端变量必须是 wire;直接用 “=“给变量赋值时左端变量必须是 reg!Example:assign a=b; /a 必须被定义为 wire!*begina=b; /a 必须被定义为 reg!endVHDL 中 STD_LOGIC_VECTOR 和 INTEGER 的区别例如 A 是 INTEGER 型,范围从 0 到 255; B 是 STD_LOGIC_VECTOR,定义为 8 位。A 累加到 255 时,再加 1 就一直保持 255 不变,不会自动反转到 0,除非令其为 0;而 B 累加到 255 时,再加 1 就会自动反转到 0。所以在使用时要特别注意!以触发器为例说明描述

26、的规范性1,无置位/清零的时序逻辑always ( posedge CLK)beginQ=D;end2,有异步置位/清零的时序逻辑异步置位/清零是与时钟无关的,当异步置位/清零信号到来时,触发器的输出立即被置为 1 或 0,不需要等到时钟沿到来才置位/清零。所以,必须要把置位/清零信号列入 always 块的事件控制表达式。always ( posedge CLK or negedge RESET)beginif (!RESET)Q=0;elseQ=D;end3,有同步置位/清零的时序逻辑同步置位/清零是指只有在时钟的有效跳变时刻置位/清零,才能使触发器的输出分别转换为 1 或 0。所以,不要

27、把置位/清零信号列入 always 块的事件控制表达式。但是必须在 always 块中首先检查置位/清零信号的电平。always ( posedge CLK )beginif (!RESET)Q=0;elseQ=D;end结构规范性在整个芯片设计项目中,行为设计和结构设计的编码是最重要的一个步骤。它对逻辑综合和布线结果、时序测定、校验能力、测试能力甚至产品支持都有重要的影响。考虑到仿真器和真实的逻辑电路之间的差异,为了有效的进行仿真测试:1,避免使用内部生成的时钟内部生成的时钟称为门生时钟(gated clock)。如果外部输入时钟和门生时钟同时驱动,则不可避免的两者的步调不一致,造成逻辑混乱

28、。而且,门生时钟将会增加测试的难度和时间。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 );

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 实用文档 > 工作总结

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报