From:http://blog./space.php?uid=121854&do=blog&id=1432
8.0 流水线建模 图二示意了一个简单的时序(sequential)流水线寄存器。
从例5到例8列举了一个工程师可能选用的4种使用阻塞赋值为它建模的方案。
module pipeb1 (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 Example 5 - Bad blocking-assignment sequential coding style #1
(外注:综合报告:WARNING: Signal <q1> is assigned but never used. WARNING:Signal <q2> is assigned but never used.) 在例5里面,接连的“阻塞赋值”命令将使得输入D连续地覆盖所有寄存器输出(在下一个posedge clk到来时)。即在每一个clk边沿,输入值被无延迟地传到q3的输出。这很明显并没有建立一个流水线而只是为一个寄存器建模------实际综合结果将是上面的图3。
module pipeb2 (q3, d, clk); output [7:0] q3; input [7:0] d; input clk; reg [7:0] q3, q2, q1;
q3 = q2; q2 = q1; q1 = d; end endmodule Example 6 - Bad blocking-assignment sequential coding style #2 - but it works! 上面的pipeb2里面,阻塞赋值被仔细地安排了次序以使得行为仿真正确。这种建模同样也可以得到正确的综合结果。(外注:Found 3-bit shift register for signal <q3>. Summary: inferred 8 Shift register(s). )
在下面的例3里,“阻塞赋值”被安排在不同的always块里面。这样Verilog标准允许以任意的次序 来仿真执行3个always块-------这也许会使得该流水线仿真结果产生错误,因为这产生了Verilog竞争条件。由不同的always块执行顺序会产生不同的结果。尽管这样,它的综合结果将是正确的! 这就意味着综合前仿真和综合后仿真不匹配。Pipeb4或者其它的类似always块同样也许会产生仿真与综合不匹配的结果------综合结果是对的,但是仿真结果也许不正确。(外注:pipeb4只是又颠倒了一下次序,对实际仿真次序却不产生决定作用.)
module pipeb3 (q3, d, clk); output [7:0] q3; input [7:0] d; input clk; reg [7:0] q3, q2, q1; always @(posedge clk) q1=d; always @(posedge clk) q2=q1; always @(posedge clk) q3=q2; endmodule Example 7 - Bad blocking-assignment sequential coding style #3
module pipeb4 (q3, d, clk); output [7:0] q3; input [7:0] d; input clk; reg [7:0] q3, q2, q1; always @(posedge clk) q2=q1; always @(posedge clk) q3=q2; always @(posedge clk) q1=d; endmodule Example 8 - Bad blocking-assignment sequential coding style #4
假如每一个上面的例子都改用“非阻塞赋值”那么将会都能得到正确的仿真结果,并综合出想要的流水线逻辑。
module pipen1 (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 Example 9 - Good nonblocking-assignment sequential coding style #1
module pipen2 (q3, d, clk); output [7:0] q3; input [7:0] d; input clk; reg [7:0] q3, q2, q1; always @(posedge clk) begin q3 <= q2; q2 <= q1; q1 <= d; end endmodule Example 10 - Good nonblocking-assignment sequential coding style #2
module pipen3 (q3, d, clk); output [7:0] q3; input [7:0] d; input clk; reg [7:0] q3, q2, q1; always @(posedge clk) q1<=d; always @(posedge clk) q2<=q1; always @(posedge clk) q3<=q2; endmodule Example 11 - Good nonblocking-assignment sequential coding style #3
module pipen4 (q3, d, clk); output [7:0] q3; input [7:0] d; input clk; reg [7:0] q3, q2, q1; always @(posedge clk) q2<=q1; always @(posedge clk) q3<=q2; always @(posedge clk) q1<=d; endmodule Example 12 - Good nonblocking-assignment sequential coding style #4
从上面的流水线编码风格例子可以看出: 仅一个“阻塞赋值”的描述可以保证仿真正确。 三个“阻塞赋值”的描述可以得到正确综合结果。 四个“非阻塞赋值”描述都可以保证仿真结果正确。 四个“非阻塞赋值”描述都可以得到正确综合结果。(原文这一条是“阻塞赋值”大概是有误?) 虽然,如果限制在一个always块里面,并小心地组织好一个always块里面阻塞赋值的次序(外注:一个always块里面的几个“阻塞赋值”是按照陈述的次序串行仿真执行的,综合执行次序也是?)同样可能会正确地为流水线建模;但是另一方面,我们可以很容易地使用“非阻塞赋值”来为上面的流水线建模------它们既可以正确仿真也可以正确综合。
9.0 阻塞赋值 & 简单例子
有许多将Verilog和Verilog综合的书,它们举了很多成功地利用“阻塞赋值”为一些简单的时序电路建模的小例子。例13是一个在大多数Verilog书本里用来为一个触发器(flip-flop)建模的例子(这是简单而有缺陷的阻塞赋值建模,但是它确实可以工作):
module dffb (q, d, clk, rst); output q; input d, clk, rst; reg q; always @(posedge clk) if (rst) q = 1'b0; else q = d; endmodule Example 13 - Simple flawed blocking-assignment D-flipflop model - but it works!
如果工程师们想把所有的模块(module)都集中到一个always里面描述,“阻塞赋值”可以用来正确地为所需要的逻辑建模、仿真和综合。但是不幸的是这个原因导致了喜欢在其它情况下也使用“阻塞赋值”的习惯,并且更复杂的时序always块将会产生竞争条件------在前面已经详细阐述过。
module dffx (q, d, clk, rst); output q; input d, clk, rst; reg q; always @(posedge clk) if (rst) q <= 1'b0; else q <= d; endmodule Example 14 - Preferred D-flipflop coding style with nonblocking assignments
应该努力养成使用“非阻塞赋值”为 所有的 时序逻辑建模的习惯------象上面的例14一样------即使是为了对付任何一个简单的模块。
下面考虑一下一个稍微复杂的时序逻辑,一个线性反馈移位寄存器(Linear Feedback shift-Register)或称之为LFSR。
10.0 为时序反馈建模 (Sequential feedback modeling)
一个LFSR是一种带反馈环路(feedback loop)的时序逻辑。反馈环路(feedback loop)为工程师们带来了一个难题使得他们试图使用细心组织次序的“阻塞赋值”来为它正确建模,如下面的例子:
module lfsrb1 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; wire n1; assign n1 = q1 ^ q3; always @(posedge clk or negedge pre_n) if (!pre_n) begin q3 = 1'b1; q2 = 1'b1; q1 = 1'b1; end else begin q3 = q2; q2 = n1; q1 = q3; end endmodule Example 15 - Non-functional LFSR with blocking assignments(外注:综合报告―――> Register <q1> equivalent to <q3> has been removed Found 1-bit register for signal <q3>. Found 1-bit xor2 for signal <n1>. Found 1-bit register for signal <q2>.) Summary: inferred 2 D-type flip-flop(s). 没有办法通过调整描述次序的方法来正确建模除非引入一个临时的变量( 外注:例如引入“wire n2”――> module xxxxx (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; wire n1,n2; assign n1 = q1 ^ q3; assign n2 = q3; always @(posedge clk or negedge pre_n) if (!pre_n) begin q3 = 1'b1; q2 = 1'b1; q1 = 1'b1; end else begin q3 = q2; q2 = n1; q1 = n2; end endmodule
这样可以得到正确的综合结果: Found 1-bit register for signal <q3>. Found 1-bit xor2 for signal <n1>. Found 1-bit register for signal <q1>. Found 1-bit register for signal <q2>. Summary: inferred 3 D-type flip-flop(s).)。 可以通过把所有赋值弄到一个等式的方式(one-line equations)来避免使用临时变量,例如下面的例16所示。但是现在编码显得更难于理解尤其当涉及的表达式更大更长时,编写代码和调试都变得比较困难,因此不鼓励使用这种风格。
module lfsrb2 (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} = 3'b111; else {q3,q2,q1} = {q2,(q1^q3),q3}; endmodule Example 16 - Functional but cryptic LFSR with blocking assignments
如果把例15和例16的阻塞赋值(blocking assignment)都替换为非阻塞赋值(nonblocking assignment),如下面例17和18所示,那么所有的仿真都将如我们对一个LFSR所期望的那样。 module lfsrn1 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; wire n1; assign n1 = q1 ^ q3; always @(posedge clk or negedge pre_n) if (!pre_n) begin q3 <= 1'b1; q2 <= 1'b1; q1 <= 1'b1; end else begin q3 <= q2; q2 <= n1; q1 <= q3; end endmodule Example 17 - Functional LFSR with nonblocking assignments
module lfsrn2 (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} <= 3'b111; else {q3,q2,q1} <= {q2,(q1^q3),q3}; endmodule Example 18 - Functional but cryptic LFSR with nonblocking assignments
根据8.0段例子pipeline和10.0段例子LFSR,我们推荐对所有时序逻辑建模时使用非阻塞赋值(nonblocking assignment)。相似的分析也将显示出对latch建模时使用非阻塞赋值(nonblocking assignment)是最安全的。
#1: 当为时序逻辑建模,使用“非阻塞赋值”。 #2: 当为锁存器(latch)建模,使用“非阻塞赋值”。
11.0 组合逻辑―使用阻塞赋值(blocking assignment)
用Verilog可以有很多种方法为组合逻辑建模,但是当使用always块来为组合逻辑建模时,应该使用阻塞赋值(blocking assignment)。
如果在某个always块里面只有一个赋值(表达),那么使用阻塞或者非阻塞赋值都可以正确工作。但是如果您对养成好的编码习惯有兴趣的话,还是要“总是用阻塞赋值对组合逻辑建模”。
一些设计师建议非阻塞赋值不应该只为编写时序逻辑,它也可以用来编写组合逻辑。当然对于简单的组合逻辑always块这是可以的,但是对于在一个always块里面含有多个赋值陈述,例如例19含有and-or的陈述,使用了不含延迟(delay)的非阻塞赋值会造成仿真不正确,或者要使仿真正确您需要另外的添加敏感事件列表(sensitivity list entries),和“多登入路径”(multiple passes)来贯穿always 块以使得仿真正确。接下来的问题是从仿真需要多长时间来看,这是低效率的(外注:即降低仿真的performance)。
例19的y输出建立在3个依次执行的陈述上(外注:tmp1 <= a & b; tmp2 <= c & d; y <= tmp1 | tmp2;)。由于非阻塞赋值的LHS变量值更新是在对RHS表达式估值之后,所以tmp1和tmp2的值仍然是该always块上一个登入口的值而不是在这一个仿真时间步(simulation time step)结束时被更新的值。因此y的值将受旧的tmp1和tmp2影响,而不是这次扫描过的always块内被更新的值。
module ao4 (y, a, b, c, d); output y; input a, b, c, d; reg y, tmp1, tmp2; always @(a or b or c or d) begin tmp1 <= a & b; tmp2 <= c & d; y <= tmp1 | tmp2; end endmodule Example 19 - Bad combinational logic coding style using nonblocking assignments
例20与例19是一样的,不同之处在于tmp1和tmp2被添加到事件列表中去了。如第7段(section 7.0)中所述,在“非阻塞赋值更新事件队列”中当非阻塞赋值更新LHS变量时,always块将会“自触发”并使用最新的tmp1和tmp2来更新y输出。现在y输出值正确了因为增加使用了两条“登入路径”(two passes)贯穿整个always块。使用更多的“登入路径”来贯穿always块等于降低仿真器的性能,因此如果可以有合理的一些代码变化可以取代这种用法的话,就尽量避免这种用法。
module ao5 (y, a, b, c, d); output y; input a, b, c, d; reg y, tmp1, tmp2; always @(a or b or c or d or tmp1 or tmp2) begin tmp1 <= a & b; tmp2 <= c & d; y <= tmp1 | tmp2; end endmodule Example 20 - Inefficient multi-pass combinational logic coding style with nonblocking assignments
发展一个好的习惯可以避免使用“多登入路径”(multiple passes)贯穿always块,即使用阻塞赋值为组合逻辑建模。
module ao2 (y, a, b, c, d); output y; input a, b, c, d; reg y, tmp1, tmp2; always @(a or b or c or d) begin tmp1 = a & b; tmp2 = c & d; y = tmp1 | tmp2; end endmodule Example 21 - Efficient combinational logic coding style using blocking assignments
例21与例19一样,不同之处只在于用阻塞赋值替代了非阻塞赋值。这保证了在一个“登入路径”贯穿always后y输出的正确(guarantee that the y-output assumes the correct value after only one pass through the always block?)。因此有下面的编码方针:
#3: 当用always块为组合逻辑建模,使用“阻塞赋值” |
|