最近在看《
通信收发信机的
verilog实现与仿真》,写写以前没有注意到的地方吧。
使用算术运算符( + 、-、 *、 /、 % )时应注意以下几点:
1. 把负数赋给 reg 或者其他无符号变量时使用 2 的补码算符;
2. 如果操作数的某一位是 X 或 Z ,则结果为 X ;
3. 模运算中使用第一个操作数的符号;
注意逻辑运算符( &&、 ||、 ! ):
1. 逻辑运算符的结果为一位1、0、X ,逻辑运算符只对逻辑进行操作;
2. 若操作数为全0,则其逻辑值为0;
3. 若操作数中只有0、X、Z ,则逻辑值为 X ;
4. 若操作数中有1位1,则逻辑值为1;
5. 逻辑反将操作数的逻辑值取反。
同步复位和异步复位:
异步复位和时钟无关,当异步复位信号到来时,即可复位而不必等待始终边沿的到来,所以必须要把复位信号加入到 always 块的敏感列表中。而同步复位是指只有在时钟边沿复位,才能正确复位,所以不用把复位信号加入到 always 块的敏感信号列表中,但是必须在 always 块中首先检查复位信号的电平。
状态机:无摩有米:输出与输入无关仅与状态有关的是摩尔状态机,输出不仅和状态有关还是输入有关的为米里状态机。
关于状态机,我们推荐使用分段式写法。因为分段式写法将同步时序逻辑和组合逻辑分开,不仅便于阅读,理解,维护,更重要的是将时序逻辑和组合逻辑分开,可非常方便的对其中的时序模块约束,以实现比较高的工作频率,而且这种结构更有利于综合器综合和布局布线。
1. 为避免锁存状态机,状态机应该具备初始化 reset 和默认 default 状态。如果整个系统有起始的状态和默认状态,那么当系统启动时,首先会进入起始状态;当系统瘫痪时,也会通过重启,进入这个起始状态。
2. 状态机也应该有个默认状态,当转移条件不满足或者状态机功能瘫痪时,要保证逻辑不会陷入死循环,而是进入默认状态。以此保证一旦后面的时序正确,状态机即可正常工作,也就是状态机具有自恢复功能。
3. 状态用 parameter 定义而不是用 `define 宏定义。因为 `define 会替换整个设计中所有的宏,而 parameter 只会定义模块内部中的参数,不会影响模块外的参数。
testbench
testbench 应该有比较全的代码覆盖率,而且能够自动的对各种情况进行测试,将结果与预期的数值进行比较,判断待测模块的功能是否正确,时序关系时候满足设计要求。
因此 testbench 首先应该产生系统需要的输入信号,同时应该能够测试输入信号的时序的正确性。
$time : 系统函数,返回当前仿真时间,一个64位无符号四舍五入到最近的时间。
$stime : 返回当前仿真时间,一个32位无符号数。
$realtime : 返回当前仿真时间,一个实数。
$monitor : 监视信号变化,若列表中参数发生变化,就显示已经变化的值,由于 $time 引起的变化不会显示。 $monitor ( $time,"a=%b b=%o c=%d d=%h",a,b,c,d );
$readmemb 和 $readmemh : 用来从文件中读取数据到指定的存储器中,这两个系统任务重,被读取的文件内容中只能包含:空白位置,注释行,二进制或者十六进制的数字,数字必须用空白位置或者注释行分隔开来。
尽量在底层模块做逻辑,在高层模块做例化,在顶层模块只能做例化,禁止出现任何胶连逻辑 glue logic ,哪怕对某个信号取反。
禁止用计数器分频后的信号做其他模块的时钟,而要改成时钟使能的方式,否则这种“时钟满天飞”的方式对设计的可靠性极为不利,也大大增加了静态时序分析的复杂性。比如
FPGA 的输入时钟为 25MHz 的,但是现在系统要通过 RS232 和 PC 通信,要以 RS232_clk 的速率发送数据,不要这么写:
always @ ( posedge RS232_clk or negedge reset_n )
begin
...........
end
而应该写成:
always @ ( posedge clk_25 or negedge reset_n )
begin
....
if ( rs232_clk == 1'b1 )
....
end
理解 data <= a+b+c+d; 和 data<=(a+b)+(c+d); 的区别。