相關閱讀
數字IC前端專欄https://blog.csdn.net/weixin_45791458/category_12173698.html?spm=1001.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 @(*) beginq = en ? d : q;qbar = !q;endendmodule
????????圖4是例3的綜合結果,可以看出,綜合工具將其綜合為D鎖存器。?
圖4?例3的綜合結果
使用if-else語句
// 例4
module Latch2 (input wire d,input wire en,output reg q,output reg qbar
);always @(*) beginif (en) beginq = d; qbar = ~d;endelse beginq = q; qbar = qbar;endendendmodule
????????圖5是例4的綜合結果,可以看出,綜合工具將其綜合為D鎖存器,但與圖4有一點區別,原因是發生了在優化過程中發生了相位反轉和寄存器合并。??
圖5?例4的綜合結果
隱式鎖存器
? ? ? ? 隱式鎖存器指的是在always結構中,沒有將自己顯式賦值給自己,但由于意料之外的因素出現的鎖存器。
不完整的敏感列表
// 例5
module NonLatch3 (input wire a,input wire b,input wire c,output reg y
);always @(a) beginy = a & b & c;endendmodule
? ? ? ? 例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 @(*) beginy = a & b & c;endendmodule
不完整的if語句
// 例7
module Latch3 (input wire a,input wire b,output reg y
);always @(*) beginif (a) y = b;endendmodule
? ? ? ? 例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 @(*) beginif (a) y = b;else y = 1'b0;endendmodule
解決方法2
? ? ? ? 在always結構的開頭對所有信號賦初值,如例9所示。
// 例9
module NonLatch6 (input wire a,input wire b,output reg y
);always @(*) beginy = 1'b0;if (a) y = b;endendmodule
不完整的case語句
// 例10
module Latch4 (input wire [1:0] sel,input wire a,input wire b,output reg y
);always @(*) begincase (sel)2'b00: y = 1'b0;2'b01: y = 1'b1;endcase
endendmodule
? ? ? ? 與例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 @(*) begincase (sel)2'b00: y = a;2'b01: y = b;2'b01: y = 1'b1;2'b01: y = 1'b0;// default: y = 1'b0;endcase
endendmodule
解決方法2
????????在always結構的開頭對所有信號賦初值,如例12所示。
// 例12
module NonLatch8 (input wire [1:0] sel,input wire a,input wire b,output reg y
);always @(*) beginy = 1'b0;case (sel)2'b00: y = a;2'b01: y = b;endcase
endendmodule
根本原因
? ? ? ? 以上這些無意間綜合出的鎖存器產生的根本原因是出現了在某些情況下需要保持信號值不變的需求,而又使用了組合邏輯always結構,因此只能使用鎖存器實現功能。
? ? ? ? 思考一個問題:如果不使用本文提到的這些解決方法,不完整的if語句和case語句一定會綜合出鎖存器嗎?例13會綜合出鎖存器嗎?
// 例12
module Latch_or_Not (input wire sel,input wire a,input wire b,output reg y
);reg [1:0] sel_with_constraint;always @(*) beginif (sel) sel_with_constraint = 2'b00;else sel_with_constraint = 2'b01;
endalways @(*) beginy = 1'b0;case (sel_with_constraint)2'b00: y = a;2'b01: y = b;endcase
endendmodule
????????圖9是例13的綜合結果,如果這與你想的不一樣,想想鎖存器產生的根本原因,而不是孤立地看待if語句和case語句。
圖9 例13的綜合結果