数字IC前端学习笔记:锁存器的综合

发布时间:2026/9/6 17:35:43
数字IC前端学习笔记:锁存器的综合 相关阅读数字IC前端专栏https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482锁存器是一种时序逻辑与触发器相比面积更小同时也可以放宽常见设计中的沿到沿时序要求但它的存在会使静态时序分析(STA)变得更加复杂因此懂得什么样的设计会综合出锁存器是很重要的本文就将对此进行详细介绍。显式锁存器显式锁存器指的是在连续赋值语句或组合逻辑always结构中将信号显式赋值给自己时出现的锁存器一般显式锁存器是有意为之的。连续赋值语句最直观的锁存器图1 D型锁存器// 例1 module NonLatch1 ( input wire d, input wire en, output wire q, output wire qbar ); wire set, rst; assign set !(en d); assign rst !(en !d); assign q !(set qbar); assign qbar !(rst q); endmodule图2是例1在Design Compiler 2024中的综合结果可以看出即使可以综合综合工具并没有推断出D锁存器而是综合为存在组合环(Combinational Loop)或者说反馈的结构。图2 例1的综合结果使用条件操作符// 例2 module NonLatch2 ( input wire d, input wire en, output wire q, output wire qbar ); assign q en ? d : q; assign qbar !q; endmodule图3是例2的综合结果可以看出综合工具也没有推断出D锁存器而是综合为存在组合环(Combinational Loop)或者说反馈的多路选择器。图3 例2的综合结果always结构需要注意的是这里指的是组合逻辑always结构而不是边沿控制的时序逻辑always块对于后者综合工具会推断出触发器而不是锁存器。使用条件操作符// 例3 module Latch1 ( input wire d, input wire en, output reg q, output reg qbar ); always (*) begin q en ? d : q; qbar !q; end endmodule图4是例3的综合结果可以看出综合工具将其综合为D锁存器。图4 例3的综合结果使用if-else语句// 例4 module Latch2 ( input wire d, input wire en, output reg q, output reg qbar ); always (*) begin if (en) begin q d; qbar ~d; end else begin q q; qbar qbar; end end endmodule图5是例4的综合结果可以看出综合工具将其综合为D锁存器但与图4有一点区别原因是发生了在优化过程中发生了相位反转和触发器合并。图5 例4的综合结果需要注意的是如果在例4中将else分支的q和qbar换为!q和!qbar则会综合出组合逻辑环(Timing Loop)而不是锁存器。隐式锁存器隐式锁存器指的是在always结构中没有将自己显式赋值给自己但由于意料之外的因素出现的锁存器。不完整的敏感列表// 例5 module NonLatch3 ( input wire a, input wire b, input wire c, output reg y ); always (a) begin y a b c; end endmodule例5使用组合逻辑always结构描述了一个三输入与门但敏感列表中只有a而不含b和c这导致了只有a发生变化时才会激活always结构。对于以前的综合工具这会导致锁存器的产生对于现在的综合工具其对组合逻辑always结构的综合不依赖敏感列表因此不会有锁存器产生但这会导致前/后仿真的不一致因为对于仿真工具根据敏感列表进行判断因此行为就像是锁存器。图6是例5的综合结果可以看出综合工具将其综合为三输入与门。图6 例5的综合结果实际上由于不完整的敏感列表带来的风险如此巨大Verilog 2001标准新加了一个*通配符来形成一个完整的敏感列表。使用通配符意味着always结构中RHS表达式、函数和任务调用的参数、case语句的case expression和case item、if语句的expression信号将自动加入敏感列表如例6所示。// 例6 module NonLatch4 ( input wire a, input wire b, input wire c, output reg y ); always (*) begin y a b c; end endmodule不完整的if语句// 例7 module Latch3 ( input wire a, input wire b, output reg y ); always (*) begin if (a) y b; end endmodule例7中的if语句缺少else分支即当a为0时输出保持不变这其实与例4等价只不过例4是显式赋值这是一个锁存器的行为图7是例7的综合结果可以看出综合工具将其综合为D锁存器。图7 例7的综合结果解决方法1保持if语句完整且在每个分支中对所有信号的赋值如例8所示。// 例8 module NonLatch5 ( input wire a, input wire b, output reg y ); always (*) begin if (a) y b; else y 1b0; end endmodule解决方法2在always结构的开头对所有信号赋初值如例9所示。// 例9 module NonLatch6 ( input wire a, input wire b, output reg y ); always (*) begin y 1b0; if (a) y b; end endmodule不完整的case语句// 例10 module Latch4 ( input wire [1:0] sel, input wire a, input wire b, output reg y ); always (*) begin case (sel) 2b00: y 1b0; 2b01: y 1b1; endcase end endmodule与例7类似例10中的case语句缺少case item即当sel[1]为1时输出保持不变这是一个锁存器的行为图8是例10的综合结果可以看出综合工具将其综合为D锁存器。图8 例10的综合结果解决方法1保持case语句完整且在每个分支中对所有信号的赋值或者干脆使用case default如例11所示。// 例11 module NonLatch7 ( input wire [1:0] sel, input wire a, input wire b, output reg y ); always (*) begin case (sel) 2b00: y a; 2b01: y b; 2b01: y 1b1; 2b01: y 1b0; // default: y 1b0; endcase end endmodule解决方法2在always结构的开头对所有信号赋初值如例12所示。// 例12 module NonLatch8 ( input wire [1:0] sel, input wire a, input wire b, output reg y ); always (*) begin y 1b0; case (sel) 2b00: y a; 2b01: y b; endcase end endmodule根本原因以上这些无意间综合出的锁存器产生的根本原因是出现了在某些情况下需要保持信号值不变的需求而又使用了组合逻辑always结构因此只能使用锁存器实现功能。思考一个问题如果不使用本文提到的这些解决方法不完整的if语句和case语句一定会综合出锁存器吗例13会综合出锁存器吗// 例13 module Latch_or_Not ( input wire sel, input wire a, input wire b, output reg y ); reg [1:0] sel_with_constraint; always (*) begin if (sel) sel_with_constraint 2b00; else sel_with_constraint 2b01; end always (*) begin case (sel_with_constraint) 2b00: y a; 2b01: y b; endcase end endmodule图9是例13的综合结果如果这与你想的不一样想想锁存器产生的根本原因而不是孤立地看待if语句和case语句。图9 例13的综合结果下面再来看一个例子例14会综合出组合逻辑环(Timing Loop)吗// 例14 module popcount_100 ( input wire [99:0] in, output reg [6:0] count ); integer i; always (*) begin count 0; for (i 0; i 100; i i 1) begin if (in[i] 1b1) count count 1b1; end end endmodule虽然在for语句中循环中的count信号参与的计算结果显式赋值给自己是描述组合环逻辑环的代码风格但如今的Design Compiler已经可以处理这种风格的RTL代码并将其转化为下面这种意思。count_0 0; count_1 in[0] ? count_0 1 : count_0; count_2 in[1] ? count_1 1 : count_1; count_3 in[2] ? count_2 1 : count_2; count count_3;