マルチクロックドメインにおけるSDC制約とタイミング対応手法

本設計ではAXIバスに400MHz、AHBに200MHz、APBに100MHzの同期クロックを用いており、これらはPLLから生成された400MHzクロックを段階的に2分周して得られる派生クロックである。さらにJTAG用の非同期1MHzクロックも存在し、全体として複数のクロックドメインを扱う必要がある。

派生クロックの制約において重要なのは、RTL内で分周クロックをどのように生成し、どのノードに制約をかけるかである。単純にモジュール出力ポートに分周クロックを接続し、そのポートにcreate_generated_clockを適用すると、ツールが内部パスにバッファを挿入する可能性があり、結果として位相ずれが発生し同期関係が崩れる。これを避けるためには、分周ロジックをDFFで明示的に実装し、そのQ出力端子に対して直接クロック制約をかけるのが適切である。ASIC設計では標準セルのDFFを使用し、FPGAではVivadoなどのプリミティブDFFをインスタンス化するのが一般的である。

非同期クロック間の制約については、set_clock_groups -asynchronousを用いるのが最も簡潔かつ確実な方法である。特に3つ以上のクロックグループを扱う場合、個別にset_false_pathを設定するよりも管理が容易である。ただし、Tcl変数をリストとして渡す際には注意が必要で、文字列結合ではなく明示的なリスト変数を用いることで正しくget_clocksが認識される。

以下に該当するRTLとSDC制約の例を示す:

assign clk_400 = clk;

wire clk_200_inv;
wire clk_100_inv;

// 2分周レジスタ(第1段)
assign clk_200_inv = ~clk_200;
DFCNQD0BWP7D5T20P96CPD u_div_reg0 (
    .D(clk_200_inv),
    .CP(clk),
    .CDN(rst_n),
    .Q(clk_200)
);

// 2分周レジスタ(第2段)
assign clk_100_inv = ~clk_100;
DFCNQD0BWP7D5T20P96CPD u_div_reg1 (
    .D(clk_100_inv),
    .CP(clk_200),
    .CDN(rst_n),
    .Q(clk_100)
);
# クロック定義
set Tclk_axi 2.5   ; # 400 MHz
set Tclk_ahb 5.0   ; # 200 MHz
set Tclk_apb 10.0  ; # 100 MHz
set Tclk_jtag 1000 ; # 1 MHz

set clk_axi "clk_400"
set clk_ahb "clk_200"
set clk_apb "clk_100"
set clk_jtag "clk_jtag"

set uncertainty_pct 0.05

create_clock -name $clk_axi -period $Tclk_axi [get_ports clk]
set_clock_uncertainty -setup [expr $Tclk_axi * $uncertainty_pct] [get_clocks $clk_axi]

create_generated_clock -name $clk_ahb \
    -source [get_pins u_clk_rst_gen/u_div_reg0/CP] \
    -divide_by 2 \
    -master_clock $clk_axi \
    [get_pins u_clk_rst_gen/u_div_reg0/Q]

create_generated_clock -name $clk_apb \
    -source [get_pins u_clk_rst_gen/u_div_reg1/CP] \
    -divide_by 2 \
    -master_clock $clk_ahb \
    [get_pins u_clk_rst_gen/u_div_reg1/Q]

create_clock -name $clk_jtag -period $Tclk_jtag [get_ports jtag_tck]
set_clock_uncertainty -setup [expr $Tclk_jtag * $uncertainty_pct] [get_clocks $clk_jtag]

set_clock_transition 0.4 [all_clocks]

set sync_clks [list $clk_axi $clk_ahb $clk_apb]
set async_clks [list $clk_jtag]

set_clock_groups -asynchronous \
    -group [get_clocks $sync_clks] \
    -group [get_clocks $async_clks]

この構成では、分周回路内部でホールド違反が発生することがある。具体的には、DFFのQ出力がフィードバックされてD入力に接続される経路において、データ到着時間が保持時間要件を満たさないケースが報告された。これはQ→D間の遅延が小さすぎるために生じる問題であり、解決策としてQ出力とD入力の間に複数段のバッファを挿入することで意図的に遅延を追加し、ホールド条件を満たすように調整できる。

修正後のRTL例:

assign clk_200_inv_raw = ~clk_200;

wire clk_fb0, clk_fb1, clk_fb2;
BUFFD0BWP7D5T20P96CPD buf0 (.I(clk_200_inv_raw), .Z(clk_fb0));
BUFFD0BWP7D5T20P96CPD buf1 (.I(clk_fb0), .Z(clk_fb1));
BUFFD0BWP7D5T20P96CPD buf2 (.I(clk_fb1), .Z(clk_fb2));

DFCNQD0BWP7D5T20P96CPD u_div_reg0 (
    .D(clk_fb2),
    .CP(clk),
    .CDN(rst_n),
    .Q(clk_200)
);

このようにして、手動でホールド違反を解消することが可能である。もちろん、物理合成ツールによる自動バッファ挿入にも対応可能だが、タイミングクリティカルなクロックパスでは明示的な制御が望ましい場合が多い。

タグ: SDC timing constraints multi-clock domain ASIC design clock generation

5月31日 07:26 投稿