wordpress建立商业网站,的品质网站建设,做网站的软件dw下载,湖南省做网站那个企业便宜相关阅读
Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 作为一个硬件描述语言#xff0c;Verilog HDL常常需要使用语句描述并行执行的电路#xff0c;但其实在仿真器的底层#xff0c;这些并行执行的语句是有先后顺序…相关阅读
Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 作为一个硬件描述语言Verilog HDL常常需要使用语句描述并行执行的电路但其实在仿真器的底层这些并行执行的语句是有先后顺序的然而Verilog标准并没有将这些事件调度的顺序定死而是给予了仿真器厂商一定的自由去实现自己的产品这就导致了设计者如果不遵循一定的编程习惯会导致意想不到的仿真结果下面是一些相关的规则。
4、描述时序逻辑时使用非阻塞赋值 首先以一个三级触发器为例说明描述时序逻辑时使用非阻塞赋值其电路如图1所示。 图1 一个触发器组
例1 如果使用例1所示的阻塞赋值顺序执行的阻塞赋值会导致d在时钟上升沿被直接传递到q3最后的仿真结果图2和综合结果图3都只有一级触发器。
# 例1
module example1 (q3, d, clk); output [7:0] q3; input [7:0] d; input clk; reg [7:0] q3, q2, q1; always (posedge clk) begin q1 d; q2 q1; q3 q2; end
endmodule 图2 例1的仿真结果 图3 例1的综合结果 例2 例2将例1中的三个阻塞赋值重排序了以描述一个三级触发器的行为q3首先得到q2的值随后q2再得到q1的值最后才更新q1最后的仿真结果图4和综合结果图5都是三级触发器。
# 例2
module example2 (q3, d, clk); output [7:0] q3; input [7:0] d;input clk;reg [7:0] q3, q2, q1;always (posedge clk) beginq3 q2;q2 q1; q1 d; end
endmodule 图4 例2的仿真结果 图5 例2的综合结果 虽然看起来很完美但例2其实是有问题的如果在该模块后某个触发器采样了q3或与q3有关的组合逻辑则此时q3可能使用未更新的值正确也可能使用已更新的值错误下面的例3说明了这种情况。 例3 例3在三级触发器后又加了一个触发器在时钟上升沿时q3的值会更新同时q4的值也会谁先执行是一个取决于仿真器的未定义行为。如果q4先更新则q4得到的是q3旧值正确如果q3先更新则q4得到的是q3新值错误如图6的仿真结果所示。即使图7所示的综合结果是正确的但这会造成前仿和后仿的不一致。
# 例3
module example3 (q4, d, clk); output [7:0] q4; input [7:0] d;input clk;reg [7:0] q4, q3, q2, q1;always (posedge clk) beginq3 q2;q2 q1; q1 d; endalways (posedge clk) beginq4 q3; // q4 q3; 这两种赋值都会导致竞争end
endmodule 图6 例3的仿真结果一种可能错误 图7 例3的综合结果 例4 如果理解了上面的例3那么将例3拆成三个always块的例4毫无疑问是一种会导致前仿和后仿不一致的写法因为不同always块的执行顺序是不确定的。图8展示的仿真结果表示该仿真器选择从下到上执行这三个always块因此得到了和例1一样的结果。从图9所示的综合结果来看是正确的。
# 例4
module example4 (q3, d, clk); output [7:0] q3; input [7:0] d;input clk;reg [7:0] q3, q2, q1;always (posedge clk) beginq3 q2;endalways (posedge clk) beginq2 q1;endalways (posedge clk) beginq1 d;end
endmodule 图8 例4的仿真结果一种可能错误 图9 例4的综合结果 例5 例5在例4的基础上将always块的顺序调换了图10展示的仿真结果表示该仿真器选择从下到上执行这三个always块因此得到了和例2一样的结果。从图11所示的综合结果来看是正确的。
# 例5
module example5 (q3, d, clk); output [7:0] q3; input [7:0] d;input clk;reg [7:0] q3, q2, q1;always (posedge clk) beginq1 d;endalways (posedge clk) beginq2 q1;endalways (posedge clk) beginq3 q2;end
endmodule 图10 例5的仿真结果一种可能正确 图11 例5的综合结果 上面四种使用阻塞赋值的方法中只有一种能保证仿真结果正确即使三种的综合结果是正确的。 例6 例6以非阻塞赋值重写了例1由于非阻塞赋值分两步执行首先是右侧表达式值的计算在当前仿真时间的最后才将右侧表达式值赋值给左值。因此q2得到的是q1的旧值而q3得到的也是q2的旧值如仿真结果图12所示这时的综合结果如图13所示也是正确的。
# 例6
module example6 (q3, d, clk); output [7:0] q3; input [7:0] d; input clk; reg [7:0] q3, q2, q1; always (posedge clk) begin q1 d; q2 q1; q3 q2; end
endmodule 图12 例6的仿真结果 图13 例6的综合结果
例7 例7以非阻塞赋值重写了例2但仿真结果和综合结果依旧如图12和图13所示因为此时所有值的更新都是在最后进行的不会影响右侧表达式的计算结果。
# 例7
module example7 (q3, d, clk); output [7:0] q3; input [7:0] d; input clk; reg [7:0] q3, q2, q1; always (posedge clk) beginq3 q2;q2 q1;q1 d; end
endmodule 例8 例8以非阻塞赋值重写了例4虽然不同always块的执行顺序是不确定的但这只表示右侧表达式值的计算顺序是不确定的右侧表达式值赋值给左值的顺序是不确定的这不会对结果有任何影响所有右侧表达式值赋值给左值还是发生在右侧表达式值的计算前。仿真结果和综合结果依旧如图12和图13所示。
# 例8
module example8 (q3, d, clk); output [7:0] q3; input [7:0] d;input clk;reg [7:0] q3, q2, q1;always (posedge clk) beginq3 q2;endalways (posedge clk) beginq2 q1;endalways (posedge clk) beginq1 d;end
endmodule 例9 例9以非阻塞赋值重写了例5与例8同理仿真结果和综合结果依旧如图12和图13所示。
# 例9
module example9 (q3, d, clk); output [7:0] q3; input [7:0] d;input clk;reg [7:0] q3, q2, q1;always (posedge clk) beginq1 d;endalways (posedge clk) beginq2 q1;endalways (posedge clk) beginq3 q2;end
endmodule 上面四种使用非阻塞赋值的方法中全部能保证仿真结果和综合结果正确。上面的九个例子说明了在描述时序逻辑时最好使用非阻塞赋值。
例10 例10展示了一个使用阻塞赋值实现的线性反馈移位寄存器(LFSR)关于这种结构的详细介绍可见数字IC前端学习笔记LFSR线性反馈移位寄存器。
module example10 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always (posedge clk or negedge pre_n) if (!pre_n) {q3,q2,q1} 3b111; else {q3,q2,q1} {q2,(q1^q3),q3};
endmodule 例10将所有的赋值写在了一行保证了赋值的正确但这种风格是不建议的会让debug变得更加复杂。可以发现例10无法使用例2中将阻塞赋值重排序的方法实现因为其是有互相依赖即q3依赖q2而q2依赖于q3。而且例10仍然有例3所示的前仿和后仿不一致的问题。 例11 例11以非阻塞赋值重写了例10解决了例10存在的前仿和后仿不一致的问题。
module example11 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always (posedge clk or negedge pre_n) if (!pre_n) {q3,q2,q1} 3b111; else {q3,q2,q1} {q2,(q1^q3),q3};
endmodule 例12 例12将例11拆成了一个always块中的三个非阻塞赋值仿真结果和综合结果和例11一致。
module example12 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always (posedge clk or negedge pre_n) if (!pre_n) beginq1 1b1; q2 1b1;q3 1b1;endelse beginq1 q3; q2 q1^q3;q3 q2;end
endmodule 例13 例13将例12中的三个非阻塞赋值重排序了仿真结果和综合结果和例11一致。
module example13 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always (posedge clk or negedge pre_n) if (!pre_n) beginq3 1b1; q2 1b1;q1 1b1;endelse beginq3 q2;q2 q1^q3;q1 q3; end
endmodule 例14 例14将例11拆成了三always块仿真结果和综合结果和例11一致。
module example14 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always (posedge clk or negedge pre_n) if (!pre_n) beginq3 1b1;endelse beginq3 q2;end always (posedge clk or negedge pre_n) if (!pre_n) beginq2 1b1;endelse beginq2 q1^q3;end always (posedge clk or negedge pre_n) if (!pre_n) beginq1 1b1;endelse beginq1 q3; end
endmodule 例15 例15在例14的基础上将always块的顺序调换了仿真结果和综合结果和例11一致。
module example15 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always (posedge clk or negedge pre_n) if (!pre_n) beginq1 1b1;endelse beginq1 q3; end always (posedge clk or negedge pre_n) if (!pre_n) beginq2 1b1;endelse beginq2 q1^q3;end always (posedge clk or negedge pre_n) if (!pre_n) beginq3 1b1;endelse beginq3 q2;end
endmodule 本文是基于《CUMMINGS, Clifford E., et al. Nonblocking assignments in verilog synthesis, coding styles that kill!. SNUG (Synopsys Users Group) 2000 User Papers, 2000. 》的进一步阐述感谢Clifford E. Cummings对此做出贡献。 原文链接http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf