이 페이지는 타이밍(timing)에 관한 연재 글 모음의 일부입니다. 타이밍 제약 조건(timing constraints)의 이론을 간단히 소개하고, 클록 주기 제약 조건에 대한 첫 페이지를 살펴본 데 이어, 이번에는 이 제약 조건이 적용되는 몇 가지 실제적인 시나리오를 보겠습니다.
다음 단계: PLL 사용하기
이전 페이지에 나온 예에서는 외부 클록 핀이 로직에 직접 연결되어 있었습니다. 실제 설계에서는 대개 PLL을 사용하여 로직에서 쓰는 클록을 만듭니다. 가장 당연한 이유는 로직이 필요로 하는 주파수가 외부 클록의 주파수와 다르기 때문입니다. 하지만 PLL을 사용하면 외부 클록의 불완전성, 특히 지터(jitter)를 정리해 주는 효과도 있습니다.
PLL을 Verilog 코드로 설계에 추가하면 다음과 같습니다.
module top(
input clk,
input foo,
output reg bar_reg
);
reg foo_reg;
reg bar;
wire pll_clk;
clk_wiz_0 pll_i
(.clk_in1(clk),
.clk_out1(pll_clk));
always @(posedge pll_clk)
begin
foo_reg <= foo;
bar <= !foo_reg;
bar_reg <= bar;
end
endmodule
앞선 예와 같지만, 이번에는 플립플롭들의 클록이 @clk 대신 @pll_clk입니다. 이 PLL은 Vivado의 Clocking Wizard IP로 생성했으며, Verilog 코드에서는 clk_wiz_0이라는 이름의 모듈로 사용됩니다.
이 예에서 Clocking Wizard는 250MHz 기준 클록을 받아 출력 포트(@clk_out1)에서 125MHz 클록을 만들도록 설정되어 있습니다. 예를 간단하게 유지하기 위해 clk_wiz_0에는 리셋 입력이나 "locked" 출력이 없습니다. 실제 설계에서는 대개 이러한 포트들을 활성화하여 사용하는 것이 좋습니다.
clk_wiz_0의 또 한 가지 특징은 위상 정렬(phase alignment) 옵션이 켜져 있으므로 @pll_clk의 클록 엣지가 @clk의 클록 엣지에 맞춰진다는 점입니다. 이 옵션은 설계에 외부 클록과 동기되는 I/O 포트가 있을 때 유용합니다. 위상 정렬이 활성화되어 있으면 외부 클록과 내부 클록 사이의 타이밍 관계가 예측 가능해지기 때문입니다. 외부 클록을 기준으로 하는 타이밍 요구사항을 충족해야 하는 I/O 포트에서 특히 유용합니다.
Xilinx의 용어를 정확히 하자면, Xilinx FPGA에는 두 종류의 PLL이 있습니다. 하나는 PLL이라 부르고, 다른 하나는 MMCM이라 부릅니다. 이 차이는 이 예제의 목적과는 무관합니다. clk_wiz_0은 MMCM이지만, 이해하기 쉽도록 여기서는 PLL이라는 용어로 부르겠습니다.
여기서 PLL에 대해 이야기하는 내용은 시중에 판매되는 모든 FPGA에도 적용된다는 점을 다시 강조할 가치가 있습니다. 이 예는 Vivado로 보여 주고 있지만, clk_wiz_0과 똑같이 동작하는 PLL은 어떤 FPGA에서든 생성할 수 있습니다.
PLL이 있을 때의 타이밍 제약 조건
PLL이 있을 때 타이밍 제약 조건에 대해 가장 중요한 점은, 특별히 추가로 해 줄 일이 없다는 것입니다. 타이밍 제약 조건은 외부 핀(이 예에서는 @clk)을 기준으로 작성하며, PLL이 다른 주파수의 클록을 만들면 그 점을 고려하는 것은 툴의 몫입니다.
다시 한 번 말하지만, PLL을 사용한다고 해서 타이밍 제약 조건을 추가로 작성할 필요가 생기지는 않습니다. 만약 추가로 작성해야겠다는 생각이 든다면 설계에 문제가 있을 가능성이 높으며, 타이밍 제약 조건을 추가해 "해결"해도 실제 문제는 해결되지 않습니다.
그러므로 이전과 마찬가지로 타이밍 제약 조건은 아래 한 줄뿐입니다.
create_clock -period 4.000 -name clk [get_ports clk]
Quartus를 사용하는 분이라면 이 페이지에서 설명하는 함정을 알아두시기 바랍니다.
PLL이 있을 때의 타이밍 리포트
이제 이전 페이지의 예와 동일한 경로(path)에 대한 타이밍 리포트를 살펴보겠습니다. 유일한 차이는 위에서처럼 PLL이 추가되었다는 것입니다. 아래에서는 관련 경로에 대한 분석을 타이밍 리포트에 나타나는 순서대로 보여 줍니다. 먼저 분석의 요약입니다.
Slack (MET) : 7.288ns (required time - arrival time) Source: foo_reg_reg/C (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_0 {rise@0.000ns fall@4.000ns period=8.000ns}) Destination: bar_reg__0/D (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_0 {rise@0.000ns fall@4.000ns period=8.000ns}) Path Group: clk_out1_clk_wiz_0 Path Type: Setup (Max at Slow Process Corner) Requirement: 8.000ns (clk_out1_clk_wiz_0 rise@8.000ns - clk_out1_clk_wiz_0 rise@0.000ns) Data Path Delay: 0.669ns (logic 0.382ns (57.100%) route 0.287ns (42.900%)) Logic Levels: 1 (LUT1=1) Clock Path Skew: -0.048ns (DCD - SCD + CPR) Destination Clock Delay (DCD): -0.715ns = ( 7.285 - 8.000 ) Source Clock Delay (SCD): -0.616ns Clock Pessimism Removal (CPR): 0.051ns Clock Uncertainty: 0.062ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE Total System Jitter (TSJ): 0.071ns Discrete Jitter (DJ): 0.103ns Phase Error (PE): 0.000ns Clock Net Delay (Source): 1.389ns (routing 0.002ns, distribution 1.387ns) Clock Net Delay (Destination): 1.218ns (routing 0.002ns, distribution 1.216ns)
눈에 띄는 차이점이 몇 가지 있습니다. 먼저 Requirement가 이전의 4ns가 아니라 8ns입니다. PLL 출력이 125MHz이므로 예상할 수 있는 결과입니다. 툴이 이 출력에 대해 클록 주기가 8ns인 타이밍 제약 조건을 자동으로 추가로 만들었습니다. 클록 주기가 4ns 길어졌고 데이터 경로는 그대로이므로 슬랙(slack)도 약 4ns 늘어났습니다.
자동으로 생성된 제약 조건을 보여 주는 또 다른 표시는 이 리포트의 Path Group이 "clk_out1_clk_wiz_0"라고 되어 있다는 점입니다. 이전에는 "clk"였습니다. 실제로 이 리포트에서는 이전에 "clk"라고 쓰였던 모든 위치에 "clk_out1_clk_wiz_0"라고 표시됩니다. 자동 생성된 타이밍 제약 조건이 타이밍 리포트에 어떻게 반영되는지는 아래에서 여러 클록을 다루는 맥락에 설명하겠습니다.
PLL 때문에 생긴 작은 변화로 Clock Uncertainty가 0.062ns로 늘어났습니다. 이전 예에서는 0.035ns였습니다. Discrete Jitter가 이제 0이 아니라 0.103ns이기 때문입니다.
이제 타이밍 분석 자체로 넘어가겠습니다. 이번에는 소스 클록 경로(Source Clock Path), 데이터 경로(Data Path), 목적지 클록 경로(Destination Clock Path)를 실제 리포트에 나오는 그대로 한 번에 보여 드립니다.
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock clk_out1_clk_wiz_0 rise edge)
0.000 0.000 r
AG12 0.000 0.000 r clk (IN)
net (fo=0) 0.000 0.000 pll_i/inst/clkin1_ibuf/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.738 0.738 r pll_i/inst/clkin1_ibuf/INBUF_INST/O
net (fo=1, routed) 0.105 0.843 pll_i/inst/clkin1_ibuf/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.049 0.892 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
net (fo=1, routed) 0.975 1.867 pll_i/inst/clk_in1_clk_wiz_0
MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
-4.474 -2.607 r pll_i/inst/mmcme3_adv_inst/CLKOUT0
net (fo=1, routed) 0.501 -2.106 pll_i/inst/clk_out1_clk_wiz_0
BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.101 -2.005 r pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=3, routed) 1.389 -0.616 pll_clk
SLICE_X49Y58 FDRE r foo_reg_reg/C
------------------------------------------------------------------- -------------------
SLICE_X49Y58 FDRE (Prop_EFF2_SLICEL_C_Q)
0.138 -0.478 f foo_reg_reg/Q
net (fo=1, routed) 0.241 -0.237 foo_reg
SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O)
0.244 0.007 r bar__0_i_1/O
net (fo=1, routed) 0.046 0.053 p_0_in
SLICE_X49Y58 FDRE r bar_reg__0/D
------------------------------------------------------------------- -------------------
(clock clk_out1_clk_wiz_0 rise edge)
8.000 8.000 r
AG12 0.000 8.000 r clk (IN)
net (fo=0) 0.000 8.000 pll_i/inst/clkin1_ibuf/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.515 8.515 r pll_i/inst/clkin1_ibuf/INBUF_INST/O
net (fo=1, routed) 0.066 8.581 pll_i/inst/clkin1_ibuf/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.034 8.615 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
net (fo=1, routed) 0.873 9.488 pll_i/inst/clk_in1_clk_wiz_0
MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
-3.934 5.554 r pll_i/inst/mmcme3_adv_inst/CLKOUT0
net (fo=1, routed) 0.422 5.976 pll_i/inst/clk_out1_clk_wiz_0
BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.091 6.067 r pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=3, routed) 1.218 7.285 pll_clk
SLICE_X49Y58 FDRE r bar_reg__0/C
clock pessimism 0.051 7.336
clock uncertainty -0.062 7.274
SLICE_X49Y58 FDRE (Setup_DFF2_SLICEL_C_D)
0.067 7.341 bar_reg__0
-------------------------------------------------------------------
required time 7.341
arrival time -0.053
-------------------------------------------------------------------
slack 7.288
이전 예와 비교하면 경로에는 단 하나의 차이만 있습니다. 외부 클록 핀과 글로벌 클록 버퍼 사이에 PLL(리포트에는 MMCME3_ADV_X1Y0로 표시됨)이 삽입되었습니다.
이 PLL은 극적인 영향을 줍니다. 소스 클록 경로에서 PLL의 지연은 −4.474ns이고, 목적지 클록 경로에서 같은 지연은 −3.934ns입니다. 이 음의 지연은 PLL이 클록 엣지를 조정해서 글로벌 클록이 입력 핀의 클록보다 약간 일찍 나타나게 한다는 뜻입니다. 글로벌 클록 버퍼 출력에서의 클록 총 지연은 소스 클록 경로에서 −0.616ns입니다. 목적지 클록 경로에서 같은 지연은 7.285ns입니다. 이는 두 번째 클록 엣지(8ns)보다 0.715ns 이릅니다.
다시 말해, 외부 클록 핀과 (로직에 전달되는) 글로벌 클록 사이의 시간 차는 두 클록 경로에서 거의 동일합니다. 한쪽 클록 경로는 최대 지연으로 계산하고 다른쪽 클록 경로는 최소 지연으로 계산했는데도 전체 결과가 거의 같습니다.
이것은 우연이 아닙니다. PLL은 글로벌 클록 출력을 기준으로 위상을 맞추기 때문에, 입력 클록 핀과의 관계가 유지되도록 글로벌 클록 버퍼 입력의 위상을 조정합니다. 최소 지연과 최대 지연의 차이는 PLL이 보상합니다. 타이밍 계산에서는 이 사실이 가장 빠른 경우와 가장 느린 경우 사이의 차이가 0.1ns에 불과하다는 점으로 반영됩니다. 이전 예에서는 PLL이 없었기 때문에 이 차이가 훨씬 컸습니다(0.575ns. 이전 페이지의 "클록 페시미즘 제거" 참조).
글로벌 클록이 외부 입력보다 약 0.6ns 앞서도록 조정되는 이유가 다른 값이 아니라 바로 이것인지는 별개의 이야기입니다. 이렇게 하면 많은 경우 I/O 핀과 관련된 타이밍 제약 조건을 달성하기 쉬워지기 때문에 툴이 자동으로 이 선택을 합니다. 그렇지만 모든 FPGA에는 이 지연을 조정할 수 있는 옵션이 있습니다.
두 개의 관련 클록
로직 설계에서는 하나보다 많은 클록이 필요한 경우가 꽤 자주 있습니다. FPGA 설계에 여러 클록이 존재한다는 것은 그 자체로 하나의 주제이며, 클록 도메인(clock domain) 입문에서 다룹니다. 클록 도메인과 타이밍이라는 두 주제는 서로 떼어낼 수 없으므로, 여기를 계속 읽기 전에 그 입문 글을 (가볍게라도) 읽어 보는 것이 좋습니다. 두 주제가 밀접하게 연관되어 있기 때문에 그 입문 글과 이 연재 사이에는 내용이 겹치는 부분도 있습니다.
아래 논의에서는 "신호 X가 @clk와 동기된다" 같은 표현을 자주 사용하겠습니다. 이 말은 X가 이름이 "clk"인 클록의 상승 엣지에 응답해서만 값이 바뀌는 플립플롭의 출력이라는 뜻입니다(비동기 리셋(asynchronous reset)은 예외지만 지금은 무관합니다). 당연히 두 신호가 같은 클록에 응답하는 플립플롭의 출력이라면 그 두 신호는 "같은 클록에 동기된다"고 말합니다.
이제 같은 PLL에서 생성되는 두 개의 클록을 살펴보겠습니다. 대부분의 경우 이 두 클록은 관련 클록(related clocks)으로 간주되기 때문에 흥미롭습니다. 그 이유를 이해하기 위해, 한 플립플롭은 두 클록 중 하나에 동기되고 다른 플립플롭은 두 번째 클록에 동기된다고 가정해 보겠습니다. 이 경우 두 플립플롭 사이에 신호를 연결하는 것은 마치 같은 클록에 동기된 것처럼 처리해도 문제없습니다. FPGA 툴이 그 경로에 대한 타이밍 요구사항이 충족되도록 보장해 줍니다.
여러 클록을 사용하는 방법을 더 잘 이해하려면 관련 클록과 무관한 클록(unrelated clocks)에 대한 별도 페이지가 있습니다. 그 페이지는 클록 도메인 크로싱(clock domain crossing)에 대한 입문입니다. 여기서는 관련 클록의 타이밍 측면, 특히 그러한 클록들과 관련된 타이밍 리포트에 초점을 맞추겠습니다.
아래 타이밍 리포트를 만들기 위해 예제에서 다른 PLL(즉 Clocking Wizard IP)을 사용했습니다. 이 새 PLL의 이름은 clk_wiz_1입니다. 사용된 Verilog 코드는 다음과 같습니다.
module top(
input clk,
input foo,
output reg bar_reg
);
reg foo_reg;
reg bar;
wire pll_clk_8, pll_clk_6;
clk_wiz_1 pll_i
(.clk_in1(clk),
.clk_out1(pll_clk_8),
.clk_out2(pll_clk_6));
always @(posedge pll_clk_8)
foo_reg <= foo;
always @(posedge pll_clk_6)
begin
bar <= !foo_reg;
bar_reg <= bar;
end
이전과 마찬가지로 이 PLL의 입력(@clk)은 250MHz이지만 출력이 두 개입니다. 하나는 @pll_clk_8에 연결되며 125MHz로 동작합니다(따라서 클록 주기는 8ns이고, 그 이름도 여기서 나왔습니다). 이것은 이전 예의 @pll_clk와 정확히 같습니다. 두 번째 출력은 @pll_clk_6에 연결되며 클록 주기가 6ns, 즉 약 166.67MHz입니다.
@pll_clk_8과 @pll_clk_6은 같은 PLL에서 생성되었으므로 관련 클록입니다.
clk_wiz_1도 위상 정렬 옵션이 켜져 있습니다. 특히 이전 예와 일관성을 유지하기 위해서입니다. 그리고 혹시 의심이 들었다면, 여전히 타이밍 제약 조건은 이전과 똑같이 하나뿐입니다.
create_clock -period 4.000 -name clk [get_ports clk]
클록 요약 이해하기
모든 클록에 대한 정보는 타이밍 리포트의 시작 부분에 요약되어 있습니다. 클록이 여러 개일 때 이 요약이 더 유용하기 때문에 지금에서야 소개합니다. 모든 FPGA 툴은 리포트에 이런 종류의 요약을 생성하며, 이 부분을 확인하는 것은 언제나 좋은 습관입니다.
-------------------------------------------------------------------------
| Clock Summary
| -------------
-------------------------------------------------------------------------
Clock Waveform(ns) Period(ns) Frequency(MHz)
----- ------------ ---------- --------------
clk {0.000 2.000} 4.000 250.000
clk_out1_clk_wiz_1 {0.000 4.000} 8.000 125.000
clk_out2_clk_wiz_1 {0.000 3.000} 6.000 166.667
clkfbout_clk_wiz_1 {0.000 2.000} 4.000 250.000
이 부분의 가장 큰 장점은 이해하기 쉽고, 타이밍 제약 조건과 관련된 가장 흔한 실수, 즉 "클록 주파수가 제대로 지정되었는가"를 쉽게 확인할 수 있다는 점입니다.
이 클록 요약을 보면 타이밍 제약 조건이 올바르게 해석되었음을 알 수 있습니다. clk라는 이름의 외부 클록이 하나 정의되어 있고, 그 주기는 4ns입니다. 그 외에 파생 클록(derived clock)이 세 개 있습니다. clk_out1_clk_wiz_1, clk_out2_clk_wiz_1, clkfbout_clk_wiz_1입니다. 이름이 암시하듯이 이들은 clk_wiz_1이라는 PLL 때문에 자동으로 생성된 클록입니다.
처음 두 클록(clk_out1_clk_wiz_1과 clk_out2_clk_wiz_1)은 PLL의 두 출력입니다. 세 번째 클록(clkfbout_clk_wiz_1)은 PLL이 글로벌 클록의 위상을 외부 클록에 맞추는 데 사용합니다. clkfbout_clk_wiz_1의 주파수는 @clk와 같습니다.
위상 정렬 옵션이 켜져 있으면 PLL의 피드백 클록(feedback clock, clkfbout_clk_wiz_1)이 글로벌 클록 버퍼에 연결됩니다. PLL은 항상 입력 클록과 피드백 클록 사이를 동기화하므로(이 클록들을 정렬하거나 둘 사이의 지연을 일정하게 유지함), clkfbout_clk_wiz_1이 글로벌 클록이라는 사실은 입력 클록과 다른 출력 클록 사이의 지연도 일정하게 보장합니다.
중요한 점은 clk_out1_clk_wiz_1, clk_out2_clk_wiz_1, clkfbout_clk_wiz_1이 실제로 존재하는 클록을 규정하는 것이 아니라는 사실입니다. 이들은 소프트웨어가 타이밍 계산을 위해 사용하는 클록의 기호일 뿐입니다. 이 클록들이 이론적이라는 것을 보여 주는 한 가지는 클록 요약에 표시된 파형입니다. 이 파형은 각 클록의 듀티 사이클(duty cycle)을 반영합니다. 그런데 네 클록(clk와 세 개의 이론적 클록) 모두 상승 엣지가 정확히 0ns에 있다는 사실 자체는 아무 의미가 없습니다. 특히 이 네 클록이 완벽하게 정렬되어 있다는 뜻은 아닙니다. 설령 정렬되어 있더라도 실제 정렬은 완벽하지 않으며, 이는 타이밍 계산을 통해 드러납니다.
이 이론적 클록들이 어떻게 사용되는지는 아래에서 두 클록이 관련된 타이밍 계산을 다룰 때 설명하겠습니다.
이 이론적 클록들에 대해 또 한 가지 중요한 점은, 이것들을 만들기 위한 명시적인 타이밍 제약 조건이 없다는 것입니다. clk_out1_clk_wiz_*의 생성은 설계 소스 어디에도, 툴이 만든 파일 어디에도 존재하지 않습니다. 대부분의 경우 그러한 타이밍 제약 조건을 직접 쓰는 것은 올바르지 않습니다. 그렇게 하면 클록 경로의 지연이 제대로 반영되지 않기 때문입니다. 이런 제약 조건을 쓰면 툴이 클록 사이의 상대 타이밍을 잘못 계산하게 되고, 관련 클록 도메인 사이의 경로도 제대로 계산되지 않습니다.
따라서 다시 한 번 강조하지만, 타이밍 제약 조건 하나만으로 PLL의 모든 출력 클록에 대한 제약 조건이 자동으로 생성되어야 합니다.
관련 클록 두 개가 있을 때의 타이밍 리포트
위 Verilog 코드에서 알 수 있듯이 @foo_reg는 @pll_clk_8에 동기되고, @bar는 @pll_clk_6에 동기됩니다. 따라서 @bar를 갱신하는 구문에는 클록 도메인 크로싱이 포함됩니다.
bar <= !foo_reg;
두 클록은 관련 클록이므로, 툴은 아래 계산을 통해 @bar의 타이밍 요구사항(tsu에 관한)이 충족되도록 보장합니다.
Slack (MET) : 0.475ns (required time - arrival time)
Source: foo_reg_reg/C
(rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_1 {rise@0.000ns fall@4.000ns period=8.000ns})
Destination: bar_reg__0/D
(rising edge-triggered cell FDRE clocked by clk_out2_clk_wiz_1 {rise@0.000ns fall@3.000ns period=6.000ns})
Path Group: clk_out2_clk_wiz_1
Path Type: Setup (Max at Slow Process Corner)
Requirement: 2.000ns (clk_out2_clk_wiz_1 rise@18.000ns - clk_out1_clk_wiz_1 rise@16.000ns)
Data Path Delay: 1.160ns (logic 0.307ns (26.466%) route 0.853ns (73.534%))
Logic Levels: 1 (LUT1=1)
Clock Path Skew: -0.250ns (DCD - SCD + CPR)
Destination Clock Delay (DCD): -0.681ns = ( 17.319 - 18.000 )
Source Clock Delay (SCD): -0.600ns = ( 15.400 - 16.000 )
Clock Pessimism Removal (CPR): -0.169ns
Clock Uncertainty: 0.182ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE
Total System Jitter (TSJ): 0.071ns
Discrete Jitter (DJ): 0.103ns
Phase Error (PE): 0.120ns
Clock Net Delay (Source): 1.369ns (routing 0.002ns, distribution 1.367ns)
Clock Net Delay (Destination): 1.208ns (routing 0.002ns, distribution 1.206ns)
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock clk_out1_clk_wiz_1 rise edge)
16.000 16.000 r
AG12 0.000 16.000 r clk (IN)
net (fo=0) 0.000 16.000 pll_i/inst/clkin1_ibuf/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.738 16.738 r pll_i/inst/clkin1_ibuf/INBUF_INST/O
net (fo=1, routed) 0.105 16.843 pll_i/inst/clkin1_ibuf/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.049 16.892 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
net (fo=1, routed) 0.975 17.867 pll_i/inst/clk_in1_clk_wiz_1
MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
-4.438 13.429 r pll_i/inst/mmcme3_adv_inst/CLKOUT0
net (fo=1, routed) 0.501 13.930 pll_i/inst/clk_out1_clk_wiz_1
BUFGCE_X1Y1 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.101 14.031 r pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=1, routed) 1.369 15.400 pll_clk_8
SLICE_X49Y58 FDRE r foo_reg_reg/C
------------------------------------------------------------------- -------------------
SLICE_X49Y58 FDRE (Prop_EFF_SLICEL_C_Q)
0.139 15.539 f foo_reg_reg/Q
net (fo=1, routed) 0.807 16.346 foo_reg
SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O)
0.168 16.514 r bar__0_i_1/O
net (fo=1, routed) 0.046 16.560 p_0_in
SLICE_X49Y58 FDRE r bar_reg__0/D
------------------------------------------------------------------- -------------------
(clock clk_out2_clk_wiz_1 rise edge)
18.000 18.000 r
AG12 0.000 18.000 r clk (IN)
net (fo=0) 0.000 18.000 pll_i/inst/clkin1_ibuf/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.515 18.515 r pll_i/inst/clkin1_ibuf/INBUF_INST/O
net (fo=1, routed) 0.066 18.581 pll_i/inst/clkin1_ibuf/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.034 18.615 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
net (fo=1, routed) 0.873 19.488 pll_i/inst/clk_in1_clk_wiz_1
MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT1)
-3.890 15.598 r pll_i/inst/mmcme3_adv_inst/CLKOUT1
net (fo=1, routed) 0.422 16.020 pll_i/inst/clk_out2_clk_wiz_1
BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.091 16.111 r pll_i/inst/clkout2_buf/O
X2Y0 (CLOCK_ROOT) net (fo=2, routed) 1.208 17.319 pll_clk_6
SLICE_X49Y58 FDRE r bar_reg__0/C
clock pessimism -0.169 17.150
clock uncertainty -0.182 16.967
SLICE_X49Y58 FDRE (Setup_DFF2_SLICEL_C_D)
0.067 17.034 bar_reg__0
-------------------------------------------------------------------
required time 17.034
arrival time -16.560
-------------------------------------------------------------------
slack 0.475
앞서 말했듯이 타이밍 계산은 가상의 실험입니다. 가상의 스톱워치가 클록 엣지와 함께 출발합니다. 이전 예에서 이 스톱워치는 0ns에 출발했습니다. 그러나 이번 계산에서는 16ns의 클록 엣지와 함께 출발합니다. 두 클록의 주기가 같지 않기 때문입니다. 이 계산은 첫 번째 클록과 두 번째 클록 사이에서 최악의 조합에 대해 수행됩니다.
첫 번째 클록의 상승 엣지는 0ns, 8ns, 16ns, 24ns, ...에 있습니다. 두 번째 클록의 상승 엣지는 0ns, 6ns, 12ns, 18ns, 24ns, ...에 있습니다. 따라서 첫 번째 클록과 두 번째 클록 사이의 간격이 가장 작은 경우는 첫 번째 클록의 16ns와 두 번째 클록의 18ns입니다. 이번 타이밍 계산이 검사하는 상황이 바로 이것입니다.
이것은 데이터 경로가 약 2ns로 제한된다는 뜻이며, 대략 500MHz에 해당합니다. 매우 엄격한 요구사항입니다. 다행히 데이터 경로에 LUT가 하나뿐이고 두 플립플롭이 같은 슬라이스(slice)에 있기 때문에 이 목표를 달성하기는 쉬웠습니다. 그럼에도 이 예는 관련 클록들의 주파수를 서로 잘 어울리도록 선택하는 것이 왜 중요한지 보여 줍니다. 주파수를 선택할 때 한쪽이 다른 쪽의 배수가 되도록 하는 경우가 많습니다. 그렇게 하면 이 예의 2ns 간격 같은 상황을 피할 수 있습니다.
참고로, 서로 잘 어울리는 주파수를 선택할 수 없다면, 두 클록을 무관한 클록(unrelated clocks)으로 취급하는 방법이 해결책이 될 수 있습니다.
파생 클록
앞에서 clk_out1_clk_wiz_1과 clk_out2_clk_wiz_1은 이론적 클록이라고 말했습니다. 타이밍 리포트에는 이 사실이 이렇게 반영됩니다. 두 경로 모두 출발점이 외부 핀(AG12)입니다. 어떻게 그럴 수 있을까요? 실제로 이 핀의 신호는 기준 클록(reference clock)이지, 이 두 클록 중 하나가 아닙니다.
예를 들어 clk_out1_clk_wiz_1을 보겠습니다. 이 계산의 기본 아이디어는 마치 외부 핀에 125MHz 클록이 있는 것처럼 가정하는 것입니다. 실제로 125MHz 클록은 PLL 출력, 즉 MMCME3_ADV_X1Y0의 출력에만 존재합니다. 그러나 PLL의 주파수 변환을 타이밍 계산에 직접 반영하는 대신 이론적 클록을 사용합니다. 이 클록은 0ns에서 시작하는 이상적인 파형을 가집니다. PLL은 주파수를 바꾸지 않고 지연만 추가하는 것처럼 취급됩니다.
따라서 이론적 클록(예: clk_out1_clk_wiz_1)은 클록의 파형(주파수, 듀티 사이클, 지터 등)을 규정합니다. 그러나 이 이론적 클록이 FPGA에 실제 신호로 존재하는 다른 클록들과의 타이밍 관계를 규정하지는 않습니다.
그렇다면 @pll_clk_6과 @pll_clk_8이 실제로 정렬되어 있다는 것을 어떻게 알 수 있을까요? 답은 이 클록들의 실제 타이밍과 이상적 타이밍을 비교하는 데 있습니다. 예를 들어 위 계산에서 clk_out1_clk_wiz_1의 이론적 클록 엣지는 16ns에 있습니다. 반면 clk_out2_clk_wiz_1의 클록 엣지는 18ns에 있어 2ns 더 늦습니다. 이제 글로벌 클록 트리 출력의 클록 엣지를 비교해 보겠습니다. 타이밍 리포트에 따르면 첫 번째 클록의 엣지는 15.400ns에 도착합니다. 두 번째 클록 엣지에 대해서는 리포트가 17.319ns라고 말합니다. 따라서 계산에 따르면 시간 차이는 2ns가 아니라 1.919ns입니다. 이상적인 차이보다 0.081ns 짧을 뿐입니다. 그러므로 두 클록은 확실히 정렬되어 있습니다.
클록이 하나뿐이었던 이전 예와 비교해 보겠습니다. 두 클록 엣지 사이의 이상적인 시간 차이는 클록 주기, 즉 8ns였습니다. 그러나 관련 타이밍 리포트를 보면 첫 번째 클록 엣지는 −0.616ns에 도착했고, 두 번째 클록 엣지는 7.285ns에 도착했습니다. 이상적인 시간 차이는 8ns였지만 실제로는 7.901ns였습니다. 즉 두 번째 클록 엣지가 예상보다 0.099ns 일찍 도착한 것입니다.
따라서 관련 클록 두 개의 경우 이상적 시간 차이로부터의 편차는 0.081ns입니다. 클록이 하나뿐인 경우에는 그 편차가 약 0.099ns로 거의 같았습니다. 두 경우 모두 편차가 생기는 이유는 첫 번째 클록 엣지 계산에는 최대 지연을, 두 번째 클록 엣지 계산에는 최소 지연을 사용하기 때문입니다.
결론적으로 @pll_clk_6과 @pll_clk_8은 마치 하나의 클록일 때와 거의 같은 정밀도로 서로 정렬되어 있습니다.
참고로, PLL의 위상 정렬을 활성화하는 옵션이 없어도 이 클록들은 서로 정렬되었을 것입니다. PLL의 출력들은 보통 어차피 정렬되어 있기 때문입니다. 이 정렬은 팬아웃(fan-out)과 관계없이 거의 동일한 지연을 갖는 글로벌 클록 버퍼를 사용함으로써 보장됩니다. 다시 말해 각 클록 버퍼가 몇 개의 로직 요소에 연결되어 있든, PLL 출력에서 목적지까지의 지연은 대략 같습니다.
관련 클록에 대해 알게 된 점
이 타이밍 리포트의 분석은 두 관련 클록 도메인 사이의 경로가 한 클록 도메인 내부의 경로와 대체로 동등함을 보여 줍니다. 그렇지만 완전히 같지는 않습니다. 특히 클록 불확실성(clock uncertainty)이 0.062ns에서 0.182ns로 늘어났습니다. 각 클록마다 자체 지터가 있고, 정렬도 완벽하지 않기 때문입니다.
이 타이밍 리포트는 또한 두 클록의 정렬이 타이밍 계산에 어떻게 반영되는지 보여 주었습니다.
FPGA 설계에 클록 도메인 크로싱이 있을 때는 타이밍 리포트에서 이 클록들 사이의 상호작용을 살펴보는 것이 좋습니다. 목적은 툴이 클록들을 우리가 기대하는 방식대로 취급하는지 확인하는 것입니다. 가장 중요한 두 가지 확인 사항은 다음과 같습니다.
- 로직이 두 클록을 관련 클록으로 간주하는 경우: 이 클록들 사이의 모든 경로에 타이밍 계산이 존재하는지 확인합니다.
- 로직이 두 클록을 무관한 클록으로 간주하는 경우: 이 두 클록 사이의 경로에는 어떤 타이밍 계산도 수행되지 않는지 확인합니다.
Vivado는 Clock Interaction Report를 생성할 수 있습니다. 이 리포트는 설계의 클록 도메인 크로싱과 각 크로싱이 툴에서 어떻게 처리되는지를 그래픽으로 보여 줍니다. 다른 FPGA 툴에도 비슷한 기능이 있습니다. 예를 들어 Quartus Pro의 CDC Viewer가 그렇습니다. 가능하다면 이 리포트를 생성해서 살펴볼 것을 권장합니다.
또 한 가지 살펴볼 것은 클록들이 정렬되어 있는지 여부입니다. 설계가 실수로 정렬되지 않은 클록을 사용하면 타이밍 제약 조건을 달성하는 데 불필요한 어려움이 생길 수 있습니다. 예를 들어 @clk와 @pll_clk_8 사이에 경로가 있으면, 툴은 타이밍 제약 조건을 적용하여 이 경로가 확실히 동작하도록 합니다. 그러나 여기서 주의할 점은 @clk는 PLL로 들어가는 기준 클록이고, @pll_clk_8은 그 PLL의 출력이라는 점입니다. 따라서 이 두 클록은 정렬되어 있지 않습니다. 그 결과 툴은 이 경로의 타이밍 제약 조건을 달성하려고 불필요하게 힘을 쓸 수 있습니다. 그 불필요한 노력 때문에 다른 경로들이 타이밍 제약 조건을 달성하지 못할 수도 있습니다.
그리고 다시 한번, 클록 도메인이라는 주제는 이 연재에서 다룹니다.
thold도 중요합니다
지금까지 보여 드린 모든 타이밍 계산은 tsu 요구사항과 관련된 것입니다. 이 요구사항에 주목하는 것은 자연스러운 일입니다. 툴이 타이밍 제약 조건을 달성하지 못할 때는 거의 항상 적어도 하나의 경로가 tsu 요구사항을 충족하지 못했기 때문입니다.
그렇지만 thold도 염두에 두는 것이 중요합니다. 이 요구사항을 충족하기 위해 툴은 때때로 라우팅 지연을 인위적으로 추가하여 데이터 경로를 일부러 느리게 만듭니다. 따라서 thold 요구사항이 타이밍 제약 조건 달성 실패의 이유로 직접 언급되는 경우는 드물지만, 실제로는 이 요구사항이 숨은 실패 원인일 수 있습니다.
실제로 두 플립플롭을 연결하는 배선의 라우팅 지연에서도 관련된 일이 일어났습니다. 클록이 하나뿐이었던 모든 타이밍 리포트에서 이 배선은 같은 슬라이스(SLICE_X49Y58) 안에 있었습니다. 따라서 이 배선의 지연은 모든 리포트에서 0.241ns였습니다. 그런데 경로에 클록이 두 개가 관여하면 이 지연이 0.807ns로 늘어났습니다.
설명하자면, 0.241ns는 같은 슬라이스에 배치된 두 플립플롭 사이에서 얻을 수 있는 최소 지연입니다. 더 긴 지연(0.807ns)은 이 배선을 다르게 라우팅한 결과입니다. 이것은 우연이 아닙니다. 툴이 thold 요구사항을 충족시키려고 일부러 배선을 길게 만들었습니다. 이는 리포트의 "Data Path Delay" 행에도 반영됩니다. 로직 지연이 26.5%에 불과하고 나머지는 라우팅 지연입니다. 그것만으로도 무언가가 일어났다는 신호입니다. 이에 대해서는 아래에서 더 자세히 설명합니다.
thold 요구사항 분석
이 연재의 이론 페이지에서 기억하시겠지만, thold는 클록 엣지 이후에 두 번째 플립플롭의 데이터 입력이 안정적으로 유지되어야 하는 시간입니다. thold가 위반되는 상황을 설명해 보겠습니다. 클록 엣지가 첫 번째 플립플롭에 도착하고, 두 번째 플립플롭에도 거의 같은 시각에 클록 엣지가 도착합니다(이 클록 엣지들은 같은 클록에서 오거나 서로 다른 클록에서 올 수 있습니다). 첫 번째 플립플롭은 자신의 클록 엣지에 응답하여 일정 지연(clock-to-output) 후 출력을 갱신합니다. 그런데 그 갱신된 값이 두 번째 플립플롭에 너무 일찍 도달합니다. 그 결과 두 번째 플립플롭은 이전 값을 안정적으로 샘플링하지 못합니다. 새 값이 두 번째 플립플롭이 클록 엣지에 반응하는 것을 끝마칠 시간을 갖기 전에 도착해 버린 것입니다. 다시 말해 thold 요구사항이 위반됩니다.
두 플립플롭에 같은 클록이 사용될 때, thold 위반은 주로 클록 스큐(clock skew) 때문에 발생할 수 있습니다. 클록 엣지가 첫 번째 플립플롭에 더 일찍 도착하면 첫 번째 플립플롭이 너무 일찍 출력을 바꿀 가능성이 생깁니다. 그러면 갱신된 신호가 두 번째 플립플롭에 충분히 빨리 도달하여 thold 요구사항을 위반할 수 있습니다. 두 플립플롭에 도달하는 것은 같은 클록 엣지이므로, 클록 주파수나 클록 지터의 크기는 이 경우 아무런 의미가 없습니다. 오직 서로 다른 지연, 즉 클록 스큐만이 역할을 합니다.
하지만 아래의 타이밍 분석에서는 마지막 예, 즉 @pll_clk_8과 @pll_clk_6이라는 두 클록이 관여하는 예를 계속 사용하겠습니다. 클록이 하나인 경우의 타이밍 분석도 비슷하지만, 그다지 흥미롭지는 않습니다. 클록이 하나이면 thold 요구사항을 충족하는 것이 너무 쉽기 때문입니다.
따라서 우리가 살펴볼 타이밍 리포트는 두 개의 관련 클록에 대해 만들어졌습니다. 이 두 클록은 주파수가 다르며, 앞에서와 마찬가지로 첫 번째 클록의 엣지 시각과 두 번째 클록의 엣지 시각 사이에는 여러 조합이 있습니다. 그러나 tsu 계산과 달리 thold의 최악의 경우는 두 클록 엣지가 모두 0ns에 있을 때입니다. thold 위반은 두 클록 엣지가 거의 동시에 발생할 때 일어나므로, 이보다 더 나쁜 조합은 없습니다.
그럼 이제 이러한 이해를 바탕으로 타이밍 리포트를 살펴보겠습니다.
Slack (MET) : 0.093ns (arrival time - required time) Source: foo_reg_reg/C (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_1 {rise@0.000ns fall@4.000ns period=8.000ns}) Destination: bar_reg__0/D (rising edge-triggered cell FDRE clocked by clk_out2_clk_wiz_1 {rise@0.000ns fall@3.000ns period=6.000ns}) Path Group: clk_out2_clk_wiz_1 Path Type: Hold (Min at Fast Process Corner) Requirement: 0.000ns (clk_out2_clk_wiz_1 rise@0.000ns - clk_out1_clk_wiz_1 rise@0.000ns) Data Path Delay: 0.458ns (logic 0.104ns (22.707%) route 0.354ns (77.293%)) Logic Levels: 1 (LUT1=1) Clock Path Skew: 0.127ns (DCD - SCD - CPR) Destination Clock Delay (DCD): -0.542ns Source Clock Delay (SCD): -0.248ns Clock Pessimism Removal (CPR): -0.421ns Clock Uncertainty: 0.182ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE Total System Jitter (TSJ): 0.071ns Discrete Jitter (DJ): 0.103ns Phase Error (PE): 0.120ns Clock Net Delay (Source): 0.495ns (routing 0.002ns, distribution 0.493ns) Clock Net Delay (Destination): 0.576ns (routing 0.002ns, distribution 0.574ns) Location Delay type Incr(ns) Path(ns) Netlist Resource(s) ------------------------------------------------------------------- ------------------- (clock clk_out1_clk_wiz_1 rise edge) 0.000 0.000 r AG12 0.000 0.000 r clk (IN) net (fo=0) 0.000 0.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.339 0.339 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.025 0.364 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.015 0.379 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.405 0.784 pll_i/inst/clk_in1_clk_wiz_1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0) -1.721 -0.937 r pll_i/inst/mmcme3_adv_inst/CLKOUT0 net (fo=1, routed) 0.167 -0.770 pll_i/inst/clk_out1_clk_wiz_1 BUFGCE_X1Y1 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.027 -0.743 r pll_i/inst/clkout1_buf/O X2Y0 (CLOCK_ROOT) net (fo=1, routed) 0.495 -0.248 pll_clk_8 SLICE_X49Y58 FDRE r foo_reg_reg/C ------------------------------------------------------------------- ------------------- SLICE_X49Y58 FDRE (Prop_EFF_SLICEL_C_Q) 0.049 -0.199 f foo_reg_reg/Q net (fo=1, routed) 0.343 0.144 foo_reg SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O) 0.055 0.199 r bar__0_i_1/O net (fo=1, routed) 0.011 0.210 p_0_in SLICE_X49Y58 FDRE r bar_reg__0/D ------------------------------------------------------------------- ------------------- (clock clk_out2_clk_wiz_1 rise edge) 0.000 0.000 r AG12 0.000 0.000 r clk (IN) net (fo=0) 0.000 0.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.595 0.595 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.042 0.637 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.022 0.659 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.457 1.116 pll_i/inst/clk_in1_clk_wiz_1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT1) -2.474 -1.358 r pll_i/inst/mmcme3_adv_inst/CLKOUT1 net (fo=1, routed) 0.209 -1.149 pll_i/inst/clk_out2_clk_wiz_1 BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.031 -1.118 r pll_i/inst/clkout2_buf/O X2Y0 (CLOCK_ROOT) net (fo=2, routed) 0.576 -0.542 pll_clk_6 SLICE_X49Y58 FDRE r bar_reg__0/C clock pessimism 0.421 -0.121 clock uncertainty 0.182 0.061 SLICE_X49Y58 FDRE (Hold_DFF2_SLICEL_C_D) 0.056 0.117 bar_reg__0 ------------------------------------------------------------------- required time -0.117 arrival time 0.210 ------------------------------------------------------------------- slack 0.093
먼저 몇 가지 분명한 차이점이 있습니다. Path Type이 이전의 Setup이 아니라 Hold입니다. 예상된 결과입니다. 또 "Min at Fast Process Corner"라고 표시되어 있는데, 이는 셋업 경로에서의 표시와 정반대입니다. 즉 이 계산은 데이터 경로에 최대 지연이 아니라 최소 지연을 사용합니다. 이것은 데이터 입력이 너무 일찍 바뀌는 상황에 대한 최악의 경우 계산에 적합합니다.
Requirement가 0ns입니다. 홀드 경로(hold path)에서는 전형적인 값입니다. 두 클록의 주파수는 여기서 역할을 하지 않습니다. 검사하는 시나리오는 두 클록 모두 같은 시각에 상승 엣지를 갖는 경우입니다.
클록 경로에 관해서는, 소스 클록 경로의 각 구성 요소 지연이 목적지 클록 경로의 같은 지연보다 일관되게 짧다는 점(더 길지 않다는 점)을 주목하세요. 이것 역시 이 계산의 목적과 일치합니다. 최악의 경우는 두 번째 플립플롭에 도달하는 클록 엣지에 비해 데이터가 너무 일찍 바뀌는 상황이기 때문입니다. 멀티 코너 타이밍 분석은 이전 페이지에서 설명했습니다.
하지만 이 타이밍 리포트에서 가장 중요한 점은 슬랙이 작다는 것입니다. 0.093ns에 불과합니다. 이것은 종종 툴이 요구사항을 충족시키기 위해 노력을 기울여야 했다는 신호입니다. 그렇지만 슬랙이 작다고 해서 반드시 해결해야 할 문제가 있었다는 뜻은 아닙니다.
thold에 대한 타이밍 분석을 할 때 작은 슬랙은 꽤 흔합니다. 그렇다면 왜 이 경로가 의심스러울까요? 주된 이유는 앞에서 언급했듯이 같은 슬라이스(SLICE_X49Y58) 안에 있는 두 플립플롭 사이의 라우팅 지연이 더 크기 때문입니다. 아마도 배치 및 배선(place and route) 과정의 초기 단계에서 소프트웨어가 이 두 플립플롭 사이의 thold 요구사항을 충족하지 못할 것임을 발견했을 가능성이 높습니다. 그럼 어떻게 수정했을까요?
thold 요구사항을 충족하지 못한다는 것은 두 번째 플립플롭의 클록 엣지에 비해 데이터 신호가 너무 일찍 도착한다는 뜻입니다. 이는 데이터 경로에 지연을 인위적으로 추가하여 해결합니다. 그 결과 두 번째 플립플롭의 데이터 입력 값이 조금 더 늦게 바뀝니다. 툴은 아마 두 플립플롭 사이의 배선을 길게 만들었을 것입니다. 그러면 라우팅 지연이 늘어나 thold 문제가 해결됩니다. 이렇게 지연을 늘리면 tsu 요구사항을 충족하는 데 문제가 생길 수도 있지만, 이 경우에는 그런 문제가 없었습니다. 이 경로에 대한 타이밍 제약 조건이 달성되었기 때문입니다(tsu와 thold 요구사항이 모두 충족됨).
그럼에도 이 예는 thold 문제를 해결해야 할 필요성이 겉보기에는 무관해 보이는 tsu 문제를 어떻게 만들 수 있는지 보여 줍니다. 경로의 로직 지연 대비 라우팅 지연 비율이 매우 낮을 때 유념해야 할 점입니다. 물론 라우팅 지연이 커지는 데는 다른 가능한 이유도 있습니다. 특히 팬아웃(fan-out)이 큰 경우가 그렇습니다. 하지만 팬아웃이 낮은 경우(이 예처럼 팬아웃이 1인 경우), 큰 라우팅 지연이 thold 문제를 해결하려고 툴이 일부러 넣은 것인지 돌아볼 가치가 있습니다.
그런데 왜 두 개의 클록이 관여할 때만 thold 문제가 있었을까요? 답은 클록이 두 개이면 두 플립플롭 각각에 클록 엣지가 도착하는 시각에 대한 불확실성이 더 커지기 때문입니다. 여기에는 여러 요인이 기여합니다. 특히 클록 스큐와 지터가 그렇습니다. thold 계산은 0ns에서 0ns 사이에서 이루어지기 때문에, 클록 엣지 도착 시각의 작은 불확실성에도 더 민감합니다.
따라서 두 관련 클록과 관련된 불확실성 때문에 thold 요구사항을 충족하기 위한 보정 조치가 필요한 경우가 종종 있습니다. 물론 이러한 보정은 툴이 자동으로 수행합니다. 그렇지만 타이밍 제약 조건을 달성하는 데 문제가 있을 때는 이런 보정이 일어났을 수 있음을 인식하는 것이 중요합니다.
요약
이 연재의 마지막 두 페이지에서는 특정한 하나의 타이밍 제약 조건에서 나온 타이밍 리포트의 예들을 보여 주었습니다. 또한 이 모든 리포트는 아주 단순한 로직 예와 관련된 것이었습니다. 이 예들이 타이밍의 기초를 이해하는 데 도움이 되었기를 바랍니다.
이 시점에서는 자신이 설계한 타이밍 리포트를 직접 살펴보면서, 같은 원리가 LUT가 하나보다 많은 경로에도 어떻게 적용되는지 확인해 보는 것이 좋습니다.
다음 페이지에서는 타이밍 문제와 그 해결 방법에 대한 논의를 시작합니다.