FPGA高手设计实战真经100则——摘记

来源:互联网 发布:平安淘宝信用卡 看电影 编辑:程序博客网 时间:2024/05/02 02:04

《命名规范》

1、  文件头:必须包含正确的版权信息和声明。可以包含简短说明、设计工程师名字和电子邮箱、版本几对该文件进行更改的列表。

格式:/*--------------------------------------------------------------------------------------------------------------

                    说明……

------------------------------------------------------------------------------------------------------------------*/

2、  一个文件实现一个verilog模块,模块名称与文件名相匹配。

//file: my_module.v

module my_module;

//module implementation

endmodule        //my_module

3、  标示符命名两种风格:

a.  大小写混合,没有下划线,每个单词第一个字母大写,如reg MyRegister;

b.  全部小写,字与字之间用下划线(推荐),reg my_register;

4、  常用后缀:

_p,_n:正极性或负极性,或两个差分信号

_ff,_q:寄存器输出

_c:组合门驱动的信号

_cur:现态

_next:次态

_tb:测试平台

_en:使能

5、  时钟信号:wireclk50;    wire clk_200_p,clk_200_n;      wire clk_en;

6、  复位信号:regreset;//高电平有效       reg reset_n;//低电平有效

7、  端口名称:_i,_o等等。

 

《连接模块实例的端口》

有两种方法:通过名称和通过顺序。推荐用通过名称方法,允许更多灵活性,例如可以省略一个未连接的端口。虽然代码量稍大,但更具有可读性且不容易出错,尤其适合具有数百个端口的大型模块。

个人写法:module名 m(.旧端口(新端口),……);

 

《使用可变的比特范围选择》

看例子:assign qout[24+:8] = din[7-:8];

等价于  assign qout[24:31] = din[7:0];

这种方法不容易出错,可以防止胃口不匹配情况。

 

《使用函数》

module function_example(input a,b, outputfunc_out);

         functionfunc_xor;

         inputa,b;

         begin

                   func_xor= a^b;

         end

         endfunction

         assignfunc_out = func_xor(a,b);

endmodule       //function_example

建议使用函数来实现组合逻辑和其他需非阻塞幅值的操作(如同步逻辑)。

 

《使用generate块》

使得模块、函数、变量、信号和连续幅值的多次例化更加容易。

genvar I;

generate

         for(i=0;i<32;i=i+1)

         begin

                   my_modulem(.clk(clk),.di(di[i]),do(do[i]));

         end

         end

endgenerate

关于generate的理解,百度了很多,以下回答我觉得可以接受的:

“1.
只能说在一定的条件下可以替代,因为generate的用法还是很宽泛的,她和module可以说是一个等级的。
在generate中的要求和在module中很类似,因为generate就是生成一个电路,电路结构就是你在generate中表述的内容。所以if,while这些语句都必须在generate中再嵌入一个initial或always块,然后在这些块中描述电路特征
至于实例调用的问题,可以被实例调用的必须是一个完整的语句块,有I/O,有语句,那些if,while等语句单独是不能存在的,所以也就不能实例调用(换句话说,如果你吧一个if写成一个function或者一个module,那么就可以调用了)
2.
我纠正一下自己的回答,说是执行语句可能有些不恰当
可以独立存在于generate块或者module的应当是变量声明,常量定义,assign赋值,门级语句,块声明,实例调用方法(I/O匹配表)
可能有些遗漏,不过也差不多了,像ifelse,while,for,case这类的语句都是高级语句,是不能独立出现的,必须放在initial或always块中

 

《阻塞和非阻塞语句》

1、  他们永远不应该混在同一个always块中,否则会导致不可预知的综合和仿真后果。虽然一般情况不会产生警告。

2、  在always块中的阻塞赋值是按着顺序执行的。因此建议不要使用多个阻塞赋值修改一个always块内同一个变量。

 

《寄存器初始化》

有三种方法:

1、  initial块中初始化,但只在仿真有用,综合时忽略。

2、  声明期间初始化。           reg my_reg = 1’b0;

3、  使用复位reset进行初始化 (if……else……)

 

《verilog和VHDL混合使用》

只要所有边界规则得以满足,就能直接在一起使用。Xilinx规则:

1、  混合使用仅仅限于设计单元的例化。VHDL设计中可以例化verilog模块,verilog设计中可以例化VHDL实体。任何VHDL和verilog之间的其他类型混用是不支持的。

2、  Verilog中例化VHDL,将模块名定义为与要例化的VHDL实体相同,并执行常规的verilog例化。大小写敏感。

3、  VHDL中例化verilog模块,定义VHDL元件名与想要例化的verilog模块名相同。

 

《时钟设计方案》

1、  使用专用的时钟资源。

内部产生的时钟是组合逻辑或寄存器输出。组合逻辑产生的时钟可能有毛刺,会被错误的当成时钟边沿造成错误。因此不要使用组合逻辑的输出做时钟。

其次内部产生的时钟使用通用布线资源,与专用时钟布线相比延迟较长,会导致时钟偏移增加,满足时序的过程更加困难。若大量使用,问题突出。

因此尽可能使用专用时钟资源(dcm,pll等)。

2、  使用时钟单个边沿。使用两个边沿带来的问题是由于时钟占空比可能不总是50%,会对电路产生影响。

3、  频率高情况下(比如高于100M)使用差分时钟,相比单端时钟优势是共模噪声一直,抗噪声性能好。高速逻辑下首选。

4、  避免使用门控时钟。根本原因在于fpga采用专用的低延迟时钟网络,只有整个时钟系统运行在该网络时,才能达到最高性能。而通过组合逻辑的门控时钟信号运行在普通布线资源上,大大降低性能,甚至带来保持时间问题。

5、  不建议将时钟信号作为通用逻辑的控制、复位(即判断)或数据输入。

 

《跨时钟域及时钟同步电路》

实现不同时钟域之间数据传输时,亚稳态需要考虑,即不是0也不是1的过渡状态,会导致数据破坏。解决方案:

1、  两个时钟使用两个寄存器进行同步。注意,两个寄存器之间不要有组合逻辑,否则毛刺会影响后面的寄存器。还要注意,后寄存器的时钟必须比前寄存器的高。反之,数据会被错过。这个挺好理解。一般还要附加时序约束。以覆盖两个时钟域之间的所有路径。

一般包括时钟周期约束以及之间的路径约束。

2、  使用FIFO。缺点两个,一个是需要相当多的逻辑和嵌入式存储器资源。另一个是增加了延迟。

3、  采用格雷码编码技术,每次码字只有一位发生变化,可用于数据总线同步。

4、  制定握手协议。一般过程是:发送源时钟域的请求,目标域接收请求,执行同步,捕捉数据,并送回一个确认。源时钟域接收确认,准备发送下一个数据。好处是它并不需要对数据同步,可以节省大量逻辑资源。缺点是每次传输数据,交换请求、确认的过程增加了延迟。

 

《计算FIFO深度》

使用模式之一是生产者和消费者之间缓冲数据。整个生产者的数据速率不应超过消费者处理数据的速率。FIFO不是旨在克服速率的差异。无论FIFO多深,如果生产者的数据传输速率始终比消费者高,那么终会溢出。FIFO旨在通过缓冲多余数据的方式,克服暂时的生产者——消费者数据速率差异。

公式:L = T*(P-C)。

 

《带符号的算术运算》

例如wire signed [15:0] val;    //unsigned是可选的。

1、  移位运算符,逻辑的表示为<<、>>,带符号的活算术的表示为<<<、>>>。

其中<<和<<<,将左操作数,移位的位数由右操作数给出。最右边的位用零填补。

>>将左操作数向右移位,移位的位数由右操作数给出。空出的位用零填补。

>>>要根据结果类型判断。如果结果赋给不带符号数,则>>>将用零填补空出位置。如果结果赋给符号数,则运算符要对最左边的符号进行符号扩展(理解)。

2、  算术运算。如果所有操作数都是带符号的,结果将使用带符号的算术进行计算。如果有一个操作数是不带符号的,则使用不带符号的算术运算。

 

《复位方案》

1、  异步复位

a、  缺点:造成设计偶发错误,难以发现解决,比如复位延迟问题。另外如果复位信号由组合逻辑驱动,那么异步复位会受到毛刺影响。

b、  优点:不需要时钟一直有效,可能减少损耗;设计的快速物理实现,更为宽松的时序约束。为了满足这些约束,布局布线工具花费的时间和努力更少。

2、  同步复位

建议使用。保证了复位只发生在有效时钟边沿。因此对于复位信号上的小毛刺来说,时钟作为滤波器使用;从设计的逻辑利用率角度来看非常有利;有助于执行不同的面积优化等等。

3、  没有复位:设计不灵活,只能上电初始化。

 

《时序约束》

1、  PERIOD(周期)约束。用于设计始终,并且定义了周期。

NET “clk1”TNM_NET = clk1;

TIMESPEC TS_ clk1=PERIOD “clk1” 5 ns HIGH 50%;

2、  OFFSET IN/OUT(输入/输出偏离)约束。规定了外部输入始终和数据输入/输出引脚之间的时序关系。

NET “data_in” OFFSET = IN 5 ns VALID 5 ns BEFORE “clk1”RISING;

规定了data_in在clk1上升沿之前最多有效5ns。

Net “data_out” OFFSET = OUT 4.1 ns AFTER “clk2”;

规定了data_out在clk2变化后最多有效4.1ns。

OFFSET约束适用于与特定IO相连的信号,或者与多个或全部IO相连的一组信号,不能用于内部信号。这个约束规定了fpga输入和输出相对于时钟的有效时序范围。该范围不是固定的、可预见的,每次设计都会发生变化。因此建议在IO快中布局输入和输出寄存器(经常读到这样的程序),以确保每次设计的时序延迟都相同。

3、  MAXDELAY(最大延迟)和MAXSKEW(最大偏移)

NET “reset” MAXDELAY = 1 ns;

NET “reset” MAXSKEW = 2ns;

4、  FROM:TO约束,规定两个逻辑组之间的时序约束。

TIMESPEC  “TS_CDC_1”= FROM “clk1” TO “clk2” 5ns;

规定了从clk1到clk2组的路径不超过5ns。

TIMESPEC  ” TS_CDC_1”= FROM “clk1” TO “clk2” TIG;

从clk1到clk2组间的所有路径排除在时序分析之外。

5、  TIG:忽略时序。

NET “data_in” TIG;

6、  约束优先级由低到高:

PERIOD,OFFSET IN/OUT,FROM:TO,MAXDELAY,TIG

 

0 0
原创粉丝点击