01signal.com

클록 주기 제약 조건과 클록 객체

이 페이지는 타이밍(timing)에 관한 연재 글 모음의 일부입니다. 지금까지의 페이지에서는 타이밍 계산의 이론, 클록 주기 제약 조건, 타이밍 클로저(Timing Closure)의 원리를 다뤘습니다. 이제 타이밍 제약 조건(timing constraints)의 기술적 세부 사항을 시작할 때입니다.

Tcl 명령으로서의 create_clock 의미

지금까지의 페이지들은 모두 다음 타이밍 제약 조건을 중심으로 돌아갔습니다.

create_clock -period 4 -name clk [get_ports clk]

지금까지 이 줄의 문법에 대해 일부러 설명하지 않았습니다. 이제 이것이 실제로 무엇을 의미하는지 설명할 때가 되었습니다.

이 타이밍 제약 조건은 SDC(Synopsys Design Constraints) 형식으로 작성된 것입니다. SDC는 타이밍 제약 조건을 표현하는 가장 일반적인 형식입니다. Vivado와 Quartus를 비롯한 여러 FPGA 툴이 이 형식을 사용합니다.

SDC 파일은 본질적으로 Tcl로 작성된 스크립트(script)입니다. 따라서 SDC 파일의 내용은 단순한 정보의 모음이 아니라 짧은 컴퓨터 프로그램입니다. 다만 SDC 파일이 스크립트로서 가진 능력은 제약 조건을 작성하기 위한 작은 명령 부분 집합으로 제한됩니다. Tcl 스크립트에서 허용되는 모든 것을 SDC 파일에서 할 수 있는 것은 아닙니다.

create_clock 명령은 타이밍 제약 조건을 정의하는 데 사용됩니다. 그런데 사실 이 명령은 FPGA 툴에게 새 클록 객체(clock object)를 만들라고 지시하는 것입니다. 여기서 "객체"라는 단어는 소프트웨어 공학에서 일반적으로 쓰이는 의미 그대로입니다. 새로 만들어진 클록 객체는 고유한 속성(property)을 가진 객체로서 Tcl 인터프리터의 메모리에 저장되는 것입니다.

예를 들어 create_clock 명령에서 "-name clk"라고 쓴 부분은 "name"이라는 속성에 "clk"라는 값을 부여합니다. 이전 페이지들 중 하나에서 이 이름이 타이밍 리포트에 사용되었던 것을 기억하세요. "clk"라는 이름은 이 제약 조건(더 정확히는 이 클록 객체) 때문에 계산된 타이밍 경로들과 함께 나타났습니다.

나중에 우리는 clk_out1_clk_wiz_1, clk_out2_clk_wiz_1 같은 다른 클록 이름들을 보았습니다. 이것들은 사실 툴이 자동으로 만든 다른 클록 객체들의 이름입니다.

모든 클록을 나열하는 Tcl 명령이 있습니다. 바로 get_clocks입니다. 이전 페이지의 클록 두 개 예제를 가지고 Vivado의 Tcl 콘솔에서 세션을 실행하면 다음과 같습니다.

> get_clocks
clk clkfbout_clk_wiz_1 clk_out1_clk_wiz_1 clk_out2_clk_wiz_1

get_clocks와 그와 유사한 명령들은 다음 페이지에서 더 자세히 설명합니다.

이 객체들의 속성을 보는 것도 가능합니다. 이 모든 속성을 이해할 필요는 없습니다. 클록이 객체라는 점을 보여 주기 위해 이것을 보여 드리는 것뿐입니다. 저는 개인적으로 어떤 객체의 속성을 직접 조작해야 했던 적이 없습니다.

> report_property [get_clocks clk]
Property           Type     Read-only  Value
CLASS              string   true       clock
INPUT_JITTER       double   true       0.040
IS_GENERATED       bool     true       0
IS_PROPAGATED      bool     true       1
IS_USER_GENERATED  bool     true       0
IS_VIRTUAL         bool     true       0
NAME               string   true       clk
PERIOD             double   true       4.000
SOURCE_PINS        string*  true       clk
SYSTEM_JITTER      double   true       0.050
WAVEFORM           double*  true       0.000 2.000

> report_property [get_clocks clk_out1_clk_wiz_1]
Property           Type     Read-only  Value
CLASS              string   true       clock
EDGES              int*     true       1 2 3
EDGE_SHIFT         double*  true       0.000 2.000 4.000
INPUT_JITTER       double   true       0.000
IS_GENERATED       bool     true       1
IS_INVERTED        bool     true       0
IS_PROPAGATED      bool     true       1
IS_RENAMED         bool     true       0
IS_USER_GENERATED  bool     true       0
IS_VIRTUAL         bool     true       0
MASTER_CLOCK       clock    true       clk
NAME               string   true       clk_out1_clk_wiz_1
PERIOD             double   true       8.000
SOURCE             pin      true       pll_i/inst/mmcme3_adv_inst/CLKIN1
SOURCE_PINS        string*  true       pll_i/inst/mmcme3_adv_inst/CLKOUT0
SYSTEM_JITTER      double   true       0.050
WAVEFORM           double*  true       0.000 4.000

깨달아야 할 중요한 점은 create_clock은 단지 객체를 만들 뿐이라는 것입니다. 이 명령의 매개변수들은 그 객체의 속성을 어떻게 설정할지 결정할 뿐입니다. 예를 들어, (제가 반복해서 보여 드린 타이밍 제약 조건에서) "-period 4"라고 쓴 부분은 단지 "PERIOD"라는 속성이 값 4를 가져야 한다는 뜻입니다.

이 Tcl 명령들을 직접 시도해 보고 싶다면 FPGA 툴마다 차이가 있다는 점을 유의하세요.

Vivado에서는 Implemented Design을 연 다음에만 이러한 명령을 사용할 수 있습니다.

Quartus에서는 먼저 TimeQuest Timing Analyzer를 연 다음 Create Timing Netlist, Read SDC File, Update Timing Netlist를 차례로 클릭하세요. 그런 다음 Tcl 콘솔에서 몇 가지 명령을 시도해 보세요. 예를 들면 다음과 같습니다.

> join [ query_collection -all [ get_clocks ] ] "\n"
> get_clock_info -waveform [get_clocks clk]

"클록"이라는 단어의 의미

FPGA 툴이 "클록"이라는 단어를 사용할 때, 그것은 대개 FPGA 내부의 물리적 신호가 아니라 클록 객체를 의미합니다. 특히 타이밍 리포트에서 그렇습니다.

이전 페이지에서 제가 "이론적 클록"이라는 용어를 몇 번 사용했던 것을 기억하세요. 이것들은 사실 클록 객체입니다. 타이밍 리포트에서는 "클록"이라고 불리지만, 타이밍 분석에서의 용도를 보면 그것들이 단지 정보를 담는 그릇일 뿐임을 알 수 있습니다.

그렇다면 이 클록 객체들과 실제 신호 사이의 연결은 무엇일까요? 우리는 이미 타이밍 분석에서 클록 객체들의 이름을 보았습니다. 이것들이 어떻게 함께 동작하는 걸까요?

툴이 설계의 정적 타이밍 분석을 수행할 때 모든 경로를 검사합니다. 경로가 플립플롭에서 시작하면, 툴은 그 플립플롭의 클록 입력에 연결된 신호(즉 네트(net))를 살펴봅니다. 이 신호와 관련된 클록 객체가 있는가? 예를 들어 그 신호가 @clk라면, 관련 클록 객체는 create_clock 명령으로 "clk"라는 이름을 부여받은 객체입니다. 관련 클록 객체를 찾은 다음, 툴은 이 객체의 속성에서 필요한 정보를 가져올 수 있습니다.

경로 끝에 있는 플립플롭에 대해서도 같은 일이 일어납니다. 이제 툴은 그 경로에 해당하는 두 개의 클록 객체를 가지게 됩니다. 툴은 이 객체들의 정보를 바탕으로 타이밍 분석을 수행합니다.

그리고 당연히, 이 절차는 플립플롭뿐만 아니라 모든 순차 요소(sequential element)에 동일하게 적용됩니다.

이것을 이해하는 것이 왜 중요할까요? 여러 이유가 있지만, 그중 하나는 타이밍 리포트에 "클록이 없는 레지스터가 있다"는 오류 메시지가 가끔 나타나기 때문입니다. 대개 이것은 클록 입력에 아무것도 연결되지 않은 플립플롭이 있다는 뜻이 아닙니다. 오히려 툴이 그 클록 입력과 관련된 클록 객체를 찾지 못했다는 뜻입니다. 다시 말해 툴이 이 플립플롭의 클록 입력에 대한 어떤 정보도 찾지 못했다는 것입니다. 따라서 문제는 대개 로직 설계에 있는 것이 아니라, 타이밍 제약 조건이 빠져 있거나(또는 잘못 작성되었거나) 하는 것입니다.

다시 한번 말할 가치가 있습니다. 타이밍 리포트에 "클록"이라고 적혀 있어도, 로직 설계에 그런 이름의 신호가 존재한다는 뜻이 아니라 그런 이름으로 클록 객체가 생성되었다는 뜻입니다. 그러면 그 신호가 무엇인지는 어떻게 알 수 있을까요? 그것이 다음 주제입니다.

이 클록은 누구의 클록인가?

타이밍 리포트를 읽기 어렵게 만드는 것 중 하나가 클록의 이름입니다. 로직 설계에 있는 클록 신호 대부분은 PLL로 만들어지며, 타이밍 리포트에 나타나는 이름이 별로 도움이 되지 않을 수 있다는 것을 우리는 이미 보았습니다. 대부분의 FPGA 툴은 SDC 파일에 명령을 추가하여 클록 객체의 이름을 바꾸는 것을 허용하지만, 대부분의 프로젝트에서 그렇게 하지 않습니다. 그리고 골든 룰 #4는 프로젝트에 특수한 일을 삼가라는 것입니다.

클록의 소스가 IP 블록(예: 기가비트 트랜시버, PCIe 블록 또는 온칩 프로세서 코어)일 때는 이름 문제가 훨씬 더 어려워집니다. 이 경우 클록의 이름은 그것이 어디서 왔고 무엇과 관련이 있는지에 대해 거의 알려 주지 않는 경우가 많습니다.

그럼 이 문제는 어떻게 해결할까요? 가장 단순한 상황, 즉 클록의 이름이 우리 자신의 SDC 파일에 있는 create_clock 명령에서 비롯된 경우부터 시작하겠습니다. 이것은 앞서 반복해서 본 바로 그 타이밍 제약 조건입니다.

create_clock -period 4 -name clk [get_ports clk]

이 명령의 마지막 부분은 "[get_ports clk]"입니다. Tcl 언어에서 대괄호는 그 내용을 Tcl 명령으로 실행하고, 그 결과를 대괄호 자리에 사용하라는 뜻입니다.

get_ports 명령은 "clk"라는 이름의 I/O 포트를 찾습니다. 이 명령의 결과는 그 포트를 나타내는 객체입니다. 따라서 위의 create_clock 명령에서 이 객체는 이 명령의 인자(argument)가 됩니다. 이것이 create_clock이 클록 객체와 실제 신호 사이의 연결을 만드는 방식입니다.

포트의 이름과 객체의 이름이 둘 다 "clk"라는 점에 유의하세요. 둘이 같을 필요는 없지만 그렇게 하는 것이 권장됩니다. 객체의 이름은 타이밍 리포트에 나타납니다. 따라서 포트의 이름이 대개 가장 좋은 선택입니다.

신호를 식별하는 데 네트 객체나 핀 객체를 사용하는 것도 가능합니다. 이것은 IP 블록을 대신하여 자동으로 생성되는 타이밍 제약 조건에서 흔히 쓰입니다. 그러나 자신의 제약 조건에서 이렇게 해야겠다는 생각이 든다면, 무언가 잘못하고 있을 가능성이 높습니다.

따라서 SDC 파일에 create_clock 명령을 사용했다면 클록 객체가 어떤 신호와 관련되는지 쉽게 알 수 있습니다. 그렇다면 툴이 자동으로 만든 클록 객체는 어떨까요?

이 경우 클록을 알아보는 가장 좋은 방법은 타이밍 리포트를 보는 것입니다. 예를 들어 이전 페이지의 예에서 어떤 클록 객체가 @pll_clk_8과 관련되어 있을까요? 이를 알아내는 쉬운 방법은 타이밍 리포트에서 텍스트 검색을 하는 것입니다. "pll_clk_8"을 검색하면 다음 부분을 찾을 수 있습니다.

Location          Delay type                Incr(ns)  Path(ns)    Netlist Resource(s)
--------------------------------------------------------------  -------------------
                  (clock clk_out1_clk_wiz_1 rise edge)
                                              16.000    16.000
AG12                                           0.000    16.000  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  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  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  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  pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=1, routed)           1.369    15.400  pll_clk_8
SLICE_X49Y58      FDRE                                          foo_reg_reg/C

이것은 clk_out1_clk_wiz_1에 대한 소스 클록 경로(Source Clock Path)이며, 질문에 대한 답이 여기 있습니다.

또 다른 방법은 Tcl 명령으로 정보를 얻는 것입니다. 정확한 방법은 FPGA 툴마다 다릅니다. Vivado에서는 Implemented Design을 연 다음 다음과 같은 명령을 사용할 수 있습니다.

> get_clocks -of_objects [ get_nets pll_clk_8 ]
clk_out1_clk_wiz_1

이 방법은 네트의 이름을 알아야 합니다. 때로는 이 예처럼 단순하지만, 때로는 이 네트의 이름을 찾아내야 합니다. FPGA 툴은 대개 GUI로 이 작업을 할 수 있는 방법을 제공합니다. Tcl 명령을 사용할 수도 있습니다.

사실, Tcl에 대한 몇 가지 예가 Tcl을 제대로 다루는 법을 배우는 것이 얼마나 중요한지 확신시켜 주었기를 바랍니다. 이것이 다음 페이지의 주제입니다.

get_port 사용의 중요성

위의 예에서 create_clock 명령은 get_port를 사용하여 클록 객체와 물리적 입력 핀을 연결합니다. 앞서 말했듯이 이 연결은 어떤 로직 요소들이 이 클록(또는 그로부터 파생된 클록들)에 연결되어 있는지 아는 데 필요합니다.

그런데 get_port를 사용하는 것만이 유일한 방법은 아닙니다. 예를 들어 글로벌 클록 버퍼의 출력 핀을 가리킬 수도 있습니다. 다음과 같은 식입니다.

create_clock -name clk -period 4 [get_pins my_BUFG_inst/O]

차이점은 툴이 글로벌 클록 버퍼의 출력 핀을 클록의 원점으로 간주한다는 것입니다. 다시 말해 클록 경로의 계산이 이 위치에서 시작됩니다. 이 원점에서 첫 번째 클록 엣지는 0ns에 발생하므로, 이 출력 핀이 시간 기준이 됩니다.

이것은 합법적인 타이밍 제약 조건이지만 두 가지 중요한 단점이 있습니다.

따라서 가능하면 항상 get_port를 사용해야 합니다. 그렇지 않으면 그 클록의 타이밍은 자기 자신에 대한 관계를 제외하고는 모두 알 수 없는 것으로 간주해야 합니다.


이 페이지에서는 Tcl 명령이 많이 등장했지만 충분히 설명하지는 못했습니다. 다음 페이지에서 그 공백을 메우겠습니다.

이 페이지는 영어 원문을 기계 번역한 것입니다. 의문이 드는 부분이 있으면 원문을 참조하시기 바랍니다.
Copyright © 2021-2026. All rights reserved. (dcc38493)