《命名规范》
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),do(do));
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