Clock gating에 쓰는 ICG(Integrated Clock Gating) 셀은 clock을 끊을 때 생기는 glitch를 막으려고 FF 대신 latch를 쓴다. 이 글은 그 구조를 파형과 코드로 보고, RTL에서 latch를 다루는 법을 정리한다.
level-sensitive latch는 enable이 열린 동안 D를 그대로 통과시키고, edge-triggered FF는 clock edge 한 순간의 값만 capture한다. 이 차이는 앞 글에서 파형으로 다뤘다. 이번에는 그 차이가 설계에서 어떻게 쓰이는지를 본다. 칩 설계는 대부분의 자리에서 latch를 막으면서도, clock을 끊는 셀 하나에서는 일부러 latch를 고른다.
glitch는 경로마다 delay가 달라서 생기는 짧은 pulse다
glitch는 논리상으로는 나오지 않아야 할 짧은 pulse다. combinational logic의 입력이 바뀌면 신호는 여러 경로를 거쳐 출력에 닿는데, 경로마다 거치는 gate 수와 배선 길이가 달라 도착 시각이 조금씩 어긋난다. 출력이 최종 값에 자리 잡기 전의 그 짧은 틈에 잠깐 틀린 값이 나오는 것이 glitch다. 가장 단순한 예가 a와 ~a를 AND한 y다.

y = a & ~a는 논리상 늘 0이다. 그런데 a가 0에서 1로 바뀌면 inverter를 지난 ~a는 inverter delay만큼 늦게 0이 되고, 그 사이 두 입력이 모두 1이라 y에 inverter delay 폭만큼의 pulse가 생긴다. a가 1에서 0으로 돌아갈 때는 a가 먼저 0이 되므로 y는 0을 유지한다. 같은 회로라도 어느 쪽으로 바뀌느냐에 따라 glitch가 생기기도 하고 안 생기기도 한다.
데이터 경로의 glitch는 대개 문제가 되지 않는다. 받는 쪽 FF는 clock edge 순간의 값만 보므로, glitch가 다음 edge 전에 가라앉기만 하면 된다. STA가 확인하는 setup timing이 바로 이 조건이다. clock 경로에서는 사정이 다르다. clock을 받는 FF에게는 rising edge 하나하나가 capture 명령이라, clock 선에 생긴 glitch는 곧 여분의 clock edge가 된다. clock gating은 전력을 줄이려고 clock 선에 gate를 넣는 일이어서, 그 gate가 glitch를 만들지 않게 하는 것이 설계의 핵심이다. ICG 안의 latch가 맡는 일이 이것이다.
clock은 일이 없어도 매 주기 toggle한다
CMOS 회로의 dynamic power는 흔히 α·C·V²·f로 쓴다. α는 신호가 한 주기에 얼마나 자주 바뀌는지를 나타내는 switching activity다. 데이터 신호는 값이 바뀔 때만 toggle하지만 clock은 매 주기 오르고 내린다. 그래서 clock tree의 buffer와 그 끝에 달린 FF의 clock pin은 연산이 없는 순간에도 전력을 쓴다. Texas Instruments SoC 팀이 ISLPED 2008에서 발표한 자료는 clock network가 칩 전체 dynamic power의 30~50%를 쓰고, 그중 약 80%가 FF에 가장 가까운 leaf 단 driver에서 나간다고 정리했다.
clock gating은 일이 없는 register로 가는 clock을 끊어 이 소모를 줄이는 기법이다. clock을 끊으면 FF 내부의 clock 관련 switching과, 끊은 지점 아래 clock tree의 toggle이 함께 멈춘다. 문제는 clock을 어떻게 끊느냐다.
AND gate 하나로 clock을 끊으면 pulse가 잘린다
가장 단순한 방법은 clock과 enable을 AND gate 하나로 묶는 것이다(gclk = clk & en). en이 clock이 Low인 동안에만 바뀐다면 이것으로 충분하다. 그런데 en은 대개 같은 clock으로 도는 FF와 combinational logic에서 나오기 때문에, rising edge 직후, 즉 clock이 High인 구간에 바뀐다.

en이 clock High 구간 한가운데서 1로 오르면 gclk도 그 순간 올라간다. gclk를 clock으로 쓰는 FF에게 이것은 원래 clock edge가 아닌 곳에 생긴 rising edge다. en이 High 구간 중간에 0으로 내려가면 이번에는 진행 중이던 pulse가 도중에 끊겨 폭이 모자란 runt pulse가 된다. 빨간 점선으로 두른 두 pulse가 이렇게 잘린 pulse이고, 이처럼 clock 선에 생기는 가짜 edge와 잘린 pulse를 흔히 clock glitch라고 부른다. FF에는 clock pulse 폭의 최솟값(minimum pulse width)이 정해져 있어서, 이보다 좁은 pulse에서는 capture가 되는지조차 보장할 수 없다. 반면 Low 구간의 glitch는 clock이 0이라 AND 출력에 나타나지 않는다. 결국 문제는 en이 clock High 구간에 바뀌는 경우다.
enable을 negative-level latch에 붙잡아 두면 pulse가 온전해진다
ICG 셀은 AND gate 앞에 clock이 Low일 때 transparent한 negative-level latch를 하나 둔다. latch는 clock이 Low인 동안 en을 따라가다가, clock이 High로 올라가는 순간 닫혀서 High 구간 내내 그 값을 붙잡고 있다. AND gate는 이렇게 붙잡힌 값과 clock을 곱하므로, High 구간 도중에 en이 어떻게 바뀌어도 gclk에는 영향이 없다.

기능 enable인 E와 test enable인 TE가 OR gate로 합쳐져 latch의 D로 들어간다. latch의 enable 단자에는 CP가 작은 원(inverter)을 거쳐 반전되어 들어가므로 latch는 CP가 0일 때 열린다. latch 출력(en_lat)은 다시 CP와 AND 되어 gated clock(Q)으로 나간다. TSMC 표준 셀 라이브러리에서는 이 셀에 CKLNQD 계열 이름이 붙고, 핀 이름도 E, TE, CP, Q를 쓴다. 그림 1과 같은 en을 이 셀에 넣으면 결과가 달라진다.

옅은 파란 띠가 clk이 Low여서 latch가 transparent한 구간이다. en이 High 구간 한가운데서 1로 올라도 latch는 닫혀 있어 en_lat은 0을 유지하고, gclk도 그대로 0이다. clock이 Low로 내려가 latch가 열리면 그제야 en_lat이 1이 되고, 다음 rising edge부터 gclk에 온전한 pulse가 나간다. en이 High 구간 도중 0으로 내려갈 때도 진행 중인 pulse는 끝까지 나가고, 그다음 pulse부터 사라진다. Low 구간에 en이 흔들리면 latch가 열려 있으니 en_lat도 따라 흔들리지만, 그동안 clock이 0이라 AND 출력은 0으로 남는다. 어느 경우든 gclk에는 온전한 pulse가 나가거나 아예 나가지 않거나 둘 중 하나다.
두 회로를 나란히 simulation해 보면 차이가 숫자로 나온다. 아래 testbench는 AND gating과 뒤에 나오는 SystemVerilog ICG model을 같은 clk, en으로 구동하고, 두 gated clock의 pulse마다 시작과 끝 시각, 폭을 출력한다. clock 주기는 10ns(High 5ns)이고, en이 바뀌는 시점은 그림 1, 3과 같다.
`timescale 1ns/1ns
module tb_icg;
logic clk = 1'b0, en = 1'b0, te = 1'b0;
logic gclk_and, gclk_icg;
time t_and, t_icg;
assign gclk_and = clk & en; // plain AND gating
icg_model u_icg (.clk(clk), .en(en), .te(te), .gclk(gclk_icg));
always #5 clk = ~clk; // 10ns period, rising edges at 5, 15, 25, ...
initial begin
#17 en = 1'b1; // t=17: 2ns into the high phase of the 2nd cycle
#20 en = 1'b0; // t=37: 2ns into the high phase of the 4th cycle
#14 en = 1'b1; // t=51: short glitch in the low phase of the 5th cycle
#1 en = 1'b0;
#20 $finish;
end
// print every gated clock pulse with its width
initial forever begin
@(posedge gclk_and) t_and = $time;
@(negedge gclk_and) $display("AND gating: pulse %0t-%0t ns, width %0t ns", t_and, $time, $time - t_and);
end
initial forever begin
@(posedge gclk_icg) t_icg = $time;
@(negedge gclk_icg) $display("ICG model : pulse %0t-%0t ns, width %0t ns", t_icg, $time, $time - t_icg);
end
endmoduleICG model 코드는 다음 절에 있다. 나는 두 파일을 내가 만들고 있는 오픈소스 RTL 시뮬레이터 vitamin의 vita로 돌렸다. vita는 파일 이름만 넘기면 compile, elaborate, simulation을 한 번에 한다(만든 과정은 vitamin 연재에 적었다).
$ vita icg_model.sv tb_icg.sv
AND gating: pulse 17-20 ns, width 3 ns
AND gating: pulse 25-30 ns, width 5 ns
ICG model : pulse 25-30 ns, width 5 ns
AND gating: pulse 35-37 ns, width 2 ns
ICG model : pulse 35-40 ns, width 5 ns
simulation ended (Finish) at time 72
errors=0 warnings=0 notes=0AND gating은 2번째 주기의 17ns에서 3ns짜리, 4번째 주기의 35ns에서 2ns짜리 잘린 pulse를 낸다. 같은 입력을 받은 ICG model은 3번째와 4번째 주기에 5ns 폭의 pulse만 내고, 5번째 주기 Low 구간의 glitch는 두 회로 모두 내보내지 않는다. iverilog로 돌리려면 아래 명령을 쓰면 되고, 같은 다섯 줄이 나온다. Verilator 5.052의 –binary –timing으로 돌려도 결과는 같다.
$ iverilog -g2012 -o sim icg_model.sv tb_icg.sv && vvp sim처음 돌렸을 때 vita는 결과와 함께 경고 하나를 냈다.
warning[VITA-W1018] W-PP-TIMESCALE-MIXED: some modules have a `timescale and these do not: icg_model — IEEE 1800 §3.14.2.2 requires all or none, and other tools refuse to elaborate the mixed form (they take the 1ns/1ns base here)testbench에는 `timescale이 있는데 icg_model에는 없었다는 뜻이다. SystemVerilog 표준은 `timescale이 어떤 module에는 있고 어떤 module에는 없는 상태를 오류로 다루도록 권한다. 같은 코드를 iverilog는 아무 말 없이 받아들였고, Verilator는 TIMESCALEMOD 경고를 내고 빌드를 멈췄다. 그래서 다음 절의 ICG model 코드 첫 줄에 `timescale 1ns/1ns를 넣었고, 고친 뒤에는 위처럼 경고 없이 끝난다. 틀린 결과를 조용히 내는 대신 문제를 드러내는 것(correct-or-loud)이 vitamin을 만들며 지키는 원칙인데, 이 예제에서도 그 경고가 먼저 잡았다.
negedge FF로도 막을 수 있지만 timing 여유가 반으로 줄어든다
glitch만 막는 것이 목적이라면 latch 대신 falling edge에서 en을 capture하는 FF를 써도 된다. FF 출력은 clock이 Low로 내려갈 때만 바뀌니 AND gate 입장에서는 결과가 같다. 이 방법이 표준이 되지 못한 이유는 timing이다.
en을 만드는 logic은 보통 rising edge에서 출발한다. falling edge FF가 이 값을 받으려면 계산이 같은 주기의 falling edge 전에 끝나야 해서, 쓸 수 있는 시간이 반 주기로 줄어든다. negative-level latch는 clock이 Low인 동안 계속 열려 있다가 다음 rising edge에 닫히므로, en은 다음 rising edge 직전(latch의 setup time 전)까지만 도착하면 된다. 거의 한 주기를 쓸 수 있는 셈이다. 크기도 latch 쪽이 작다. FF는 latch 두 개를 이어 붙인 구조라서 같은 일을 하는 데 latch 하나보다 트랜지스터가 더 든다.
| 구분 | AND gate만 | negedge FF + AND | negative-level latch + AND (ICG) |
|---|---|---|---|
| High 구간에 en이 바뀌면 | 잘린 pulse, 가짜 edge | 영향 없음 | 영향 없음 |
| en이 도착해야 하는 시점 | clock Low 구간 안 | 같은 주기 falling edge 전(반 주기) | 다음 rising edge 전(약 한 주기) |
| 추가 소자 | 없음 | FF 하나 | latch 하나 |
ICG의 동작을 RTL로 옮기면 이렇다
아래는 ICG의 동작을 simulation용으로 옮긴 behavioral model이다. 실제 칩에서는 이 코드를 합성하지 않고 라이브러리의 ICG 셀을 그대로 instance한다(이유는 뒤에서 본다). 같은 회로를 Verilog와 SystemVerilog로 한 번씩 썼고, 아래 설명에서 괄호 안이 SystemVerilog 쪽 차이다.
`timescale 1ns/1ns
module icg_model (
input wire clk,
input wire en, // functional enable
input wire te, // test enable (scan mode)
output wire gclk
);
reg en_lat;
// negative-level latch: transparent while clk is low, holds while clk is high
always @(clk or en or te)
if (!clk) en_lat <= en | te;
assign gclk = clk & en_lat;
endmodule`timescale 1ns/1ns
module icg_model (
input logic clk,
input logic en, // functional enable
input logic te, // test enable (scan mode)
output logic gclk
);
logic en_lat;
// negative-level latch: transparent while clk is low, holds while clk is high
always_latch
if (!clk) en_lat <= en | te;
assign gclk = clk & en_lat;
endmoduleen과 te를 OR로 합친 값을 latch가 clk이 0인 동안에만 받아 두고, 그 값과 clk을 AND해서 gclk을 만든다. Verilog에서는 always block의 sensitivity list에 clk, en, te를 모두 적고 if 문에 else를 두지 않는 방식으로 latch를 쓴다(SystemVerilog는 always_latch로 쓴다. sensitivity list는 툴이 자동으로 만들고, 블록 안의 코드가 latch가 아니면 툴이 경고한다). 앞의 testbench가 불러 쓰는 icg_model이 이 SystemVerilog 쪽이다. assignment는 두 코드 모두 non-blocking(<=)으로 썼는데, 이 선택에는 기준마다 이견이 있어서 글 끝에서 다시 다룬다.
오픈소스 SoC인 OpenTitan에도 같은 모델이 있다. OpenTitan의 generic prim_clock_gating module은 always_latch 하나와 assign 하나로 된 이 구조 그대로이고, FPGA나 특정 공정으로 갈 때는 이 module 자리를 그 target의 clock gating 구현으로 바꿔 끼운다.
RTL에는 enable만 쓰고, ICG는 합성 툴이 넣는다
RTL 설계자가 register마다 ICG를 직접 그리지는 않는다. 평소처럼 enable 달린 register를 쓰면 된다.
module en_reg (
input wire clk,
input wire en,
input wire [7:0] d,
output reg [7:0] q
);
// plain enable register: no clock gating in RTL
always @(posedge clk)
if (en) q <= d;
endmoduleen이 1인 clock edge에서만 d를 받고, 0이면 q를 그대로 둔다(SystemVerilog라면 always_ff @(posedge clk)로 쓴다). 합성 툴은 이 코드를 en이 d와 q 중 하나를 고르는 mux와 FF로 해석한다. q가 mux를 거쳐 다시 FF로 돌아오는 이 mux-feedback 구조를 찾으면, 툴은 mux를 없애고 같은 en으로 ICG를 하나 넣어 register 묶음 전체의 clock을 끊는다.

위 칸(before)에서는 mux가 en에 따라 d와 되돌아온 q 중 하나를 골라 register에 넣고(8bit이면 bit마다 하나씩, mux 8개), clock은 매 주기 register에 들어간다. 아래 칸(after)에서는 mux가 모두 빠지고, ICG 하나가 en이 0인 동안 register 전체의 clock을 막는다. mux가 빠지니 datapath의 delay와 면적도 줄어든다.
다만 register 폭이 좁으면 ICG와 enable 배선이 아끼는 전력보다 비싸질 수 있다. 그래서 Design Compiler의 set_clock_gating_style 같은 설정으로 최소 bit 폭을 정해 두고, 그보다 좁은 register는 mux를 그대로 둔다. 이렇게 register 묶음 단위로 들어가는 것을 흔히 fine-grained gating이라 부른다. block 전체가 쉬는 동안 그 block의 clock을 통째로 끊는 coarse-grained gating은 설계자가 RTL에 ICG 셀, 또는 그것을 감싼 wrapper module을 직접 instance해서 넣는다.
같은 회로를 전용 셀로 쓰는 이유
논리만 보면 OR, latch, AND를 따로 놓아도 같은 회로다. 그래도 ICG를 라이브러리의 전용 셀로 쓰는 이유는 clock path가 일반 신호와 다르게 다뤄지기 때문이다.
첫째, 툴이 이 셀을 clock gating 셀로 알아본다. Liberty 파일에서 ICG 셀에는 clock_gating_integrated_cell 속성이 붙는다. 값이 latch_posedge_precontrol이면 latch 기반이고, rising edge register용이며, test control 로직이 latch 앞에 있다는 뜻이다. 합성 툴은 이 속성을 보고 ICG를 고르고, CTS(Clock Tree Synthesis)는 이 셀을 clock tree의 일부로 보고 그 아래 FF들과 함께 skew를 맞춘다. latch와 AND gate가 흩어져 있으면 툴은 이것을 평범한 logic으로 본다.
둘째, 내부 timing이 이미 검증돼 있다. latch 출력이 AND gate에 닿는 시점과 clock이 AND gate에 닿는 시점이 조금만 어긋나도 gclk에 좁은 pulse가 생길 수 있다. 전용 셀은 이 내부 경로를 foundry가 설계하고 characterization까지 끝낸 상태로 나온다. STA는 셀 바깥에서 E 핀이 latch가 닫히기 전, 즉 다음 rising edge 전에 도착하는지를 setup/hold로 확인하면 된다. 셀 없이 gate로 clock을 끊으면 STA가 그 gate에 clock gating check라는 검사를 따로 걸어야 한다.
셋째, test가 망가지지 않게 한다. 양산 test에서는 FF들을 scan chain으로 엮어 값을 넣고 빼는데(scan shift), 이 동안 모든 FF에 clock이 들어가야 한다. en이 0이라 clock이 끊겨 있으면 그 register들은 chain에서 빠지고 fault coverage가 떨어진다. TE는 이 경우를 위한 핀이다. scan mode에서 TE를 1로 올리면 en과 상관없이 clock이 통과한다. TE를 latch 앞에서 OR하는 precontrol 구조에서는 TE도 en과 같은 glitch 보호를 받는다.
ICG가 더하는 비용
clock gating이 공짜는 아니다. ICG는 clock path에 셀 하나를 더 넣으므로, ICG를 거치는 FF와 거치지 않는 FF 사이에 insertion delay 차이가 생기고, CTS가 buffer를 더 넣어 skew를 맞춰야 한다. en을 만드는 logic은 다음 rising edge 전에 ICG에 도착해야 하는 새 timing path가 된다.
simulation에서도 조심할 점이 있다. reset 직후 en이 X이면 en_lat와 gclk도 X가 된다. Verilog는 0에서 X로 바뀌는 것도 posedge로 치기 때문에, 실제 칩에서는 clock이 끊겼을 수도 있는 register가 simulation에서는 값을 capture한다. 위 model에 en을 X로 둔 채 iverilog로 돌려 보면 첫 rising edge에서 gclk가 0에서 X가 되고, 그 gclk를 쓰는 FF가 d 값을 그대로 받는다. en을 만드는 FF에 reset을 거는 이유가 여기에도 있다.
ICG 밖에서는 latch를 되도록 쓰지 않는다
ICG처럼 목적이 분명한 자리를 빼면, 일반 logic에서는 latch를 되도록 쓰지 않는다. lowRISC의 Verilog coding style guide도 latch 사용을 권하지 않고, 가능하면 FF를 쓰라고 적는다. 이유는 세 가지로 모인다.
첫째는 STA다. FF는 clock edge마다 경로를 끊어 주므로 STA는 FF에서 다음 FF까지 한 주기 안에 도착하는지만 보면 된다. latch는 열려 있는 동안 데이터가 그대로 통과하므로, 앞 단에서 늦게 도착한 데이터가 latch를 지나 다음 단의 시간을 빌려 쓸 수 있다(time borrowing). 고속 설계에서는 이 성질을 일부러 쓰기도 하지만, 경로가 한 주기에서 끝나지 않고 다음 단으로 이어지므로 분석과 timing closure가 복잡해진다.
둘째는 glitch다. latch가 열려 있는 동안 combinational logic에서 생긴 glitch는 그대로 출력으로 나간다. FF는 edge 순간의 값만 보므로 그 밖의 glitch를 걸러 낸다. 셋째는 test다. scan chain은 FF를 scan FF로 바꿔 엮는데, latch는 이 방식으로 바로 엮이지 않는다. test mode에서 transparent로 열어 두는 식으로 따로 처리해야 하고, 그만큼 fault coverage를 챙기기 어렵다.
의도하지 않은 latch는 빠진 분기에서 생긴다
실제로 문제가 되는 latch는 대부분 설계자가 쓰려고 한 적이 없는 latch다. combinational always block에서 어떤 분기에 출력을 assign하지 않으면, 그 조건에서는 출력이 이전 값을 유지해야 하므로 합성 툴은 거기에 latch를 inference한다. if에 else가 없거나 case에 빠진 값이 있을 때가 대표적이다.
module latch_bad (
input logic sel,
input logic [7:0] a,
output logic [7:0] y
);
always_comb begin
if (sel)
y = a;
// no else: y has to keep its old value when sel is 0
end
endmodule$ verilator --lint-only -Wall latch_bad.sv
%Warning-LATCH: latch_bad.sv:6:5: Latch inferred for signal 'y' (not all control paths of combinational always assign a value)
: ... Suggest use of always_latch for intentional latchesVerilator 5.052로 lint를 돌리면 이 코드에 LATCH 경고가 붙는다. 모든 control path가 y를 assign하지 않아 latch가 inference됐다는 뜻이고, 의도한 latch라면 always_latch를 쓰라고 권한다. always_comb 대신 Verilog의 always @*로 써도 같은 경고가 나온다.
고치는 방법은 모든 경로에서 값이 정해지게 하는 것이다. else나 case의 default를 채워도 되고, 블록 첫 줄에서 출력에 default assignment를 먼저 해 두면 뒤에 분기가 늘어나도 빠지는 경로가 생기지 않는다. 아래처럼 고친 코드는 같은 lint에서 경고 없이 통과한다.
module latch_fixed (
input logic sel,
input logic [7:0] a,
output logic [7:0] y
);
always_comb begin
y = '0; // default assignment first
if (sel)
y = a;
end
endmodulecase 문에 빠진 값이 있을 때 synthesis pragma인 full_case를 붙여 latch를 막는 방법도 쓰였다. Cummings는 이 pragma가 합성 툴에만 작용하고 simulator는 무시해서 simulation과 합성 결과가 달라질 수 있다며, parallel_case와 함께 ‘evil twins’라고 불렀다. default assignment는 simulator와 합성 툴이 같은 코드를 같은 뜻으로 읽으므로 이런 문제가 없다.
의도한 latch는 always_latch로 드러낸다
ICG model처럼 latch가 필요한 곳에는 SystemVerilog의 always_latch를 쓴다. IEEE 1800은 always_comb, always_latch, always_ff를 따로 두고, 블록 안의 동작이 이름과 맞지 않으면 툴이 경고하도록 권고한다. always_comb에서 latch가 생기면 앞의 LATCH 경고가 나오고, 반대로 always_latch 안의 코드가 모든 분기에서 값을 assign해 latch가 아니게 되면 Verilator는 NOLATCH 경고를 낸다. 코드를 읽는 사람에게도 이 latch는 의도한 것이라는 표시가 된다.
latch 안의 assignment를 blocking으로 쓸지 non-blocking으로 쓸지는 기준마다 다르다. Cummings는 latch도 non-blocking으로 모델링하라고 권하고, lowRISC style guide도 latch에는 always_latch와 non-blocking을 쓰고 blocking은 쓰지 말라고 적는다. 그런데 Verilator 5.052는 위 SystemVerilog ICG model의 always_latch 안 <=에 COMBDLY 경고를 붙이고, combinational process 안의 non-blocking이라 blocking으로 실행한다고 알린다(Verilog 쪽 always @(clk or en or te)에는 경고가 없다). 그리고 같은 lowRISC가 관리하는 OpenTitan의 prim_clock_gating도 실제 코드는 always_latch 안에서 blocking(=)을 쓴다.
ICG에서는 어느 쪽으로 써도 결과가 같다. latch 출력은 clock이 0일 때만 바뀌고, 그동안 AND 출력은 0으로 묶여 있어서, 다른 block이 그 값을 언제 읽느냐가 gclk에 영향을 주지 않는다. 그래서 이 선택은 팀이 쓰는 lint 툴의 규칙에 맞추면 된다. 다만 latch 출력을 같은 time step에 다른 always block이 읽는 구조라면, simulation race를 피하는 데는 non-blocking 쪽이 안전하다.
정리하면 칩 설계에서 latch는 막아야 할 대상이면서 clock gating을 가능하게 하는 부품이기도 하다. 일반 logic에서는 STA와 glitch, test 때문에 latch를 피하고, 빠진 분기로 생기는 latch는 lint로 잡는다. clock을 끊는 자리에서는 latch가 Low 구간에 열리고 High 구간에 값을 붙잡는 성질을 그대로 이용해, en에 한 주기 가까운 여유를 주면서 gclk에는 온전한 pulse만 내보낸다. 그 latch를 foundry가 검증한 셀 하나로 묶은 것이 ICG이고, RTL 설계자는 대부분 enable만 쓰고 셀을 넣는 일은 합성 툴에 맡긴다.
참고 자료
- vitamin, open-source RTL simulator (vita)
- Jairam S. et al. (Texas Instruments), Clock Gating for Power Optimization in ASIC Design Cycle: Theory & Practice (ISLPED 2008)
- Synopsys, Liberty Reference Manual, clock_gating_integrated_cell
- SiliconPath, Power-Aware Synthesis: Clock Gating and Multi-Vt
- lowRISC, Verilog Coding Style Guide (Latches)
- OpenTitan, prim_generic/rtl/prim_clock_gating.sv
- Verilator, Errors and Warnings (LATCH, NOLATCH, COMBDLY)
- Clifford E. Cummings, Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!
- Clifford E. Cummings, "full_case parallel_case", the Evil Twins of Verilog Synthesis (SNUG 1999)
- IEEE Standard for SystemVerilog (IEEE Std 1800), 9.2.2 always_comb, always_latch, always_ff