1.使用有意义的信号和变量名称
2.避免混合使用上升沿和下降沿触发的触发器
混合使用上升沿和下降沿触发的触发器可能在时钟树中引入反向器和缓冲器。一般不希望出现这种情况,因为这将在电路中引入时钟偏斜。
3.使用基本构造模块与使用连续赋值语句的对比
RTL描述中使用基本的构造模块和使用连续assign语句各有优缺点。连续assign语句是一种非常简洁的表示功能的方式,通常能生出性能很好的随机逻辑电路。然而,最终的逻辑结构并不一定是对称的。调用基本构造模块可以产生对称的设计,并且逻辑综合工具能够更有效地优化小模块。然而,调用构造模块是一种不太简洁的设计描述方式;它制约了针对设计工艺的重定向,并且通常会降低仿真器性能。
4.调用多路选择器与使用if-else或者case语句的对比
if-else和case语句常常被综合成硬件中的多路选择器。如果需要结构化实现,最好直接使用多路选择器来实现模块,因为if-else或者case语句可能使综合工具产生不可预期的随即逻辑。调用多路选择器的方式更容易控制,综合速度也更快,但它存在依赖于工艺的不利因素,并且表达多路选择器的代码比较长。另一方面,if-else和case语句可以间接地表示多路选择器,常用于建立不依赖工艺的RTL描述。
5.使用圆括号优化逻辑结构
设计者可以使用圆括号将逻辑组合起来,以便于控制最终的结构,使用圆括号也提高了Verilog描述的可读性。
6.使用算术操作符*,/和%与使用现有构造模块的对比
乘、除和取模操作在逻辑和面积上的实现代价很高。然而,这些算术操作符能够以不依赖于工艺的简明方式实现所需的功能。另一方面,设计自定义模块来完成乘、除和取模操作,可能要花费大量的时间,并且RTL描述会变得与工艺相关。
7.注意多条赋值语句对同一个变量赋值的情况
多条赋值语句对同一个变量赋值可能导致生成意料之外的电路。前面的赋值可能被忽略,只有最后一次赋值起作用。
8.显式地定义if-else或者case语句
在if-else或者case语句中必须说明各种可能的条件分支,否则可能产生电平敏感的锁存器,而不是多路选择器。
9.不要试图用function实现锁存器。
函数只可以实现组合逻辑行为,即它们只可以根据传递给函数的参数的当前值来计算一个值。因此它们不可能包含时序控制操作(无延迟控制[#],事件控制[@或wait语句],也不可能执行一个任务)。
10. 尽量不要用caseX或caseZ语句。
该语句可能会隐蔽一些bug。例如,在设计中有一个信号,该信号在某个状态的值为X。如果采用caseX(a)这样的语句,则RTL仿真系统仍能进入正确的状态,而门级仿真通常只验证很少几种情形,因此这种错误很容易被忽略。
11. 要避免在生成组合逻辑的always中使用“# 1 y=a;”这样的语句。
12.在设计中加入足够的冗余逻辑(spare gates)。这样,如果流片后发现芯片有问题,可以进行mental fix。
13. 对于FPGA设计,尽量采用独热编码;得益于ASIC设计,为节省面积,可以采用其他编码。
14. 尽量不要采用三台总线。
15. 写RTL时,尽量不要用for-loops这种结构。
尽管这种结构会使代码看起来更清楚,更简洁,但对于综合器来说,却不是好的方式。综合工具在处理这类结构时优化能力不强。因此,只有在特定场合 才使用for-loops结构。而且,在使用时,也尽量不要在for-loop结构中实现乘法器、加法器等结构。
16. 所有模块的输出最好都采用寄存器输出。
这样会增加一些寄存器,但增加的寄存器几乎不会对产品成本产生什么影响。而且,这种方式会对综合和物理设计带来极大益处。
17. 将时钟生成逻辑、复位生成逻辑与其他部分的逻辑分离开。
18. 避免在代码中加入full_case语句和parallel_case。要依靠代码而不是综合工具来消除锁存器。