ピンにフィードするクロックを取得する

author-image

投稿者:

このデザイン例は、SDC ファイルで使用でき、ピンにフィードするすべてのクロックのリストを返す、カスタム・プロシージャーを示しています。このプロシージャーは、デザイン内にある他のクロック名が不明で、派生クロックを作成する必要がある場合に役立ちます。動的 SDC 制約を使用したデザイン再利用の単純化デザイン例では、このページで説明するカスタム・プロシージャーの詳細、および使用方法例を提供しています。

プロシージャー用のコード全体は、このページの最後にあります。その前に、プロシージャーの動作方法についての完全な説明があります。SDC ファイル内で get_clocks_driving_pins カスタム・プロシージャーを使用するには、プロシージャーが定義済みであることを確認してから、その他の SDC コマンド同様に呼びます。プロシージャーが定義済みであることを確認する簡単な方法が 2 つあります。

  • プロシージャー・コードを個別の SDC ファイルに保存し、その SDC ファイルをプロジェクトに含めます。
  • get_clocks_driving_pins カスタム・プロシージャーを使用する前に、プロシージャーをコピーし、任意の SDC ファイルの最上部に貼り付けます。

個別の SDC ファイル

プロシージャー・コードを個別の SDC ファイルに保存すると、そのコードは他の制約とは別に保存され、他のプロジェクトで同様に再利用できます。個別の SDC ファイルを使用する場合、プロシージャーのある SDC ファイルをプロジェクト内のファイルリストに追加し、このプロシージャーを使用する他のあらゆる SDC ファイルより上に表示されるようにする必要があります。他の SDC ファイルより上にリスト表示することで、Quartus® II ソフトウェアが、使用前にプロシージャーを確実に定義します。

コピーと貼り付け

プロシージャーを使用するのと同じ SDC ファイル内にコピーと貼り付けすることで、プロジェクト内の SDC ファイルの数を減らせます。他のデザイナーが再利用できるようにモジュール用の SDC ファイルを作成する場合、単一の SDC ファイルにすべての制約およびサポートコードを含めて、できる限りシンプルにできます。プロシージャーを最初に使用する前に、プロシージャー・コードを SDC ファイルに含めて、使用前に定義されるようにする必要があります。通常は、この要件を満たすため、ファイルの最上部に置きます。

スクリプト操作

デザイン内でピンをフィードするすべてのクロックのリストを取得する主要なステップは、以下の 3 つです。

  1. すべてのクロックを取得し、ターゲット・ノードに、ターゲットノードからクロックへのマッピングを作成します。
  2. クロックがあるノードのリストを取得します。ノードは特定のピンへのファンイン・パス上にあります。
  3. ノードのリストから、特定のピンに最も近いノードを検索し、そのノードにあるクロックを返します。

ステップ 1.すべてのクロックを取得し、マッピングを作成する

以下の Tcl コードは、デザイン内にあるすべてのクロックを取得し、ノードからそのノードにあるクロックへのマッピングを (Tcl アレイを使用して) 作成します。

catch { array unset nodes_with_clocks }
array set nodes_with_clocks [list]

# デザイン内の各クロック上で繰り返します
foreach_in_collection clock_id [all_clocks] {

    set clock_name [get_clock_info -name $clock_id]

    # 各クロックはノードに適用されます。ターゲットノードのコレクションを取得します
    foreach_in_collection target_id [get_clock_info -targets $clock_id] {

        # クロック名をターゲットノードとアソシエイトします
        set target_name [get_node_info -name $target_id]
        lappend nodes_with_clocks($target_name) $clock_name
    }
}

仮想クロックにはターゲットがないので、マッピングが作成されません。下記のプロシージャー・コード全体では、ターゲットノードのタイプ (レジスター、ピン、セル、またはポート) に関する情報が、後日使用するために保存されます。

ステップ 2.ファンインパス内でクロックのあるノードを取得する

2 番目のステップは、特定のピンへのファンインパス上の、クロックがあるノードのサブセットを検索することです。クロックのある各ノード (ステップ 1 の検索結果) について、ノードを介した特定のピンへのファンインを取得します。ファンインがある場合、ノードはピンへのファンインパス内にあります。ファンインがない場合、ノードはピンへのファンインパス上にありません。

以下の Tcl コードは、ステップ 1 のクロックがあるすべてのノードで繰り返し、get_fanins コマンドを使用して、各ノードが特定のピンのファンインパス上にあるかどうかを判断します。ノードが特定のピンのファンインパス上にある場合、そのノードは pin_drivers リストに保存されます。

set pin_drivers [list]

# ステップ 1 で作成されたマッピングにあるすべてのノード上で繰り返します
foreach node_with_clocks [array names nodes_with_clocks] {

    # 現在のノードを介した特定のピンへのファンインを取得します
    set fanin_col [get_fanins -clock -through $node_with_clock $pin_name]

    # ファンインノードが少なくとも 1 つある場合、現在のノードは
    # 特定のピンへのファンインパス上にあるので、それを保存します。
    if { 0 < [get_collection_size $fanin_col] } {
        lappend pin_drivers $node_with_clocks
    }
}

下記のプロシージャー・コード全体では、ノードタイプに関する追加情報を使用して、get_fanins コマンド内の -through 値用のタイプ固有コレクションを指定します。

ステップ 3.特定のピンに最も近いノードを検索する

ここまでで、pin_drivers 変数には、特定のピンへのファンインパス上の、クロックがあるノードすべてのリストがあります。このステップでは、特定のピンに最も近いノードを検索します。pin_drivers リストに 1 つ以上のノードがある場合、コードはリストにある最初の 2 つのノードを選び、1 つのノードがもう一方のノードへのファンインパス上にあるかどうかを確認します。ノードがファンインパス上にある場合、最初のノードをリストから削除可能とするには、2 番目のノードよりもピンから離れている必要があります。

while { 1 < [llength $pin_drivers] } {

    # pin_drivers リストにある最初の 2 つのノードを選びます
    set node_a [lindex $pin_drivers 0]
    set node_b [lindex $pin_drivers 1]

    # node_b が node_a のファンインパス上にあるか確認します
    set fanin_col [get_fanins -clock -through $node_b $node_a]

    # 少なくとも 1 つのファンインノードがある場合、node_b は特定のピンから
    # node_a よりも離れている必要があります。
    # ファンインノードがない場合、node_b は node_a よりも
    # 特定のピンに近い必要があります。
    if { 0 < [get_collection_size] } {

        # node_a is closer to the pin.
        # node_b を pin_drivers リストから削除します
        set pin_drivers [lreplace $pin_drivers 1 1]

    } else {

        # node_b is closer to the pin.
        # node_a を pin_drivers リストから削除します
        set pin_drivers [lreplace $pin_drivers 0 0]
    }
}

# pin_drivers に残された方のノードは、特定のピンを駆動するノードです
set node_driving_pin [lindex $pin_drivers 0]

# ステップ 1 のマッピングにあるノード上のクロックを検索して返します
return $nodes_with_clocks($node_driving_pin

下記のプロシージャー・コード全体では、ノードタイプに関する追加情報を使用して、get_fanins コマンド内の -through 値用のタイプ固有コレクションを指定します。

プロシージャー・コード全体

下記は、get_clocks_driving_pin カスタム・プロシージャーの完全なコードです。これには、上記で詳細を説明していない、追加のエラーチェックおよびサポート機能が含まれています。

proc get_clocks_feeding_pin { pin_name } {

    # ステップ 1 の前に、エラーチェックを実行して、プロシージャーに渡された pin_name
    # が確実に 1 つのピンにのみ一致することを確認します。
    # 一致するピンがない場合、また 1 つのピンのみに一致していない場合は、エラーを返します。
    set pin_col [get_pins -compatibility_mode $pin_name]
    if { 0 == [get_collection_size $pin_col] } {
        return -code error "No pins match $pin_name"
    } elseif { 1 < [get_collection_size $pin_col] } {
        return -code error "$pin_name matches [get_collection_size $pin_col]\
            pins but must match only one"
    }
    
    # プロシージャーで使う変数を初期化します
    catch { array unset nodes_with_clocks }
    catch { array unset node_types }
    array set nodes_with_clocks [list]
    array set node_types [list]
    set pin_drivers [list]
    
    # Step 1.デザイン内にあるすべてのクロックを取得し、ターゲットノード上に
    # ターゲットノードからクロックへのマッピングを作成します

    # デザイン内にある各クロック上で繰り返します
    foreach_in_collection clock_id [all_clocks] {

        set clock_name [get_clock_info -name $clock_id]
        set clock_target_col [get_clock_info -targets $clock_id]
        
        # 各クロックはノードに適用されます。ターゲットノードのコレクションを取得します
        foreach_in_collection target_id [get_clock_info -targets $clock_id] {

            # クロック名をターゲットノードとアソシエイトします
            set target_name [get_node_info -name $target_id]
            lappend nodes_with_clocks($target_name) $clock_name

            # ターゲットノードのタイプを後日使用のために保存します
            set target_type [get_node_info -type $target_id]
            set node_types($target_name) $target_type
        }
    }

    # Step 2.特定のピンへのファンインパス上にある
    # クロックのあるノードのリストを取得します

    # ステップ 1 で作成したマッピングにあるすべてのノードで繰り返します
    foreach node_with_clocks [array names nodes_with_clocks] {

        # ターゲットノードのタイプを使用して get_fanins コマンド内の
        # -through 値用にタイプ特有のコレクションを作成します。
        switch -exact -- $node_types($node_with_clocks) {
            "pin" {  set through_col [get_pins $node_with_clocks] }
            "port" { set through_col [get_ports $node_with_clocks] }
            "cell" { set through_col [get_cells $node_with_clocks] }
            "reg" {  set through_col [get_registers $node_with_clocks] }
            default { return -code error "$node_types($node_with_clocks) is not handled\
                as a fanin type by the script" }
        }

        # 現在のノードを介した特定のピンへのファンインを取得します
        set fanin_col [get_fanins -clock -through $through_col $pin_name]

        # 少なくとも 1 つのファンインノードがある場合、現在のノードは
        # 特定のピンへのファンインパス上にあるので、それを保存します。
        if { 0 < [get_collection_size $fanin_col] } {
            lappend pin_drivers $node_with_clocks
        }
    }

    # ステップ 3 の前に、エラーチェックを実行して、デザイン内のクロックがあるノードの
    # 少なくとも 1 つが特定のピンへのファンインパス上に
    # あることを確認します。
    if { 0 == [llength $pin_drivers] } {
        return -code error "Can not find any node with clocks that drives $pin_name"
    }
    
    # Step 3. ステップ 2 で作成したノードのリストから、特定のピンに最も近い
    # ノードを検索し、そのノードにあるクロックを返します。

    while { 1 < [llength $pin_drivers] } {

        # pin_drivers リストにある最初の 2 つのノードを取得します
        set node_a [lindex $pin_drivers 0]
        set node_b [lindex $pin_drivers 1]
        
        # ターゲットノードのタイプを使用して、get_fanins コマンド内の
        #-through 値用のタイプ固有コレクションを作成します。
        switch -exact -- $node_types($node_b) {
            "pin" {  set through_col [get_pins $node_b] }
            "port" { set through_col [get_ports $node_b] }
            "cell" { set through_col [get_cells $node_b] }
            "reg" {  set through_col [get_registers $node_b] }
            default { return -code error "$node_types($node_b) is not handled\
                as a fanin type by the script" }
        }

        # node_b が node_a のファンインパス上にあるかどうかを確認します        
        set fanin_col [get_fanins -clock -through $through_col $node_a]

        # 少なくとも 1 つのファンインノードがある場合、node_b は node_a よりも
        # 特定のピンから離れている必要があります。
        # ファンインノードがない場合、node_b は node_a よりも
        # 特定のピンに近い必要があります。
        if { 0 < [get_collection_size $fanin_col] } {

            # node_a is closer to the pin.
            # node_b を pin_drivers リストから削除します
            set pin_drivers [lreplace $pin_drivers 1 1]

        } else {

            # node_b is closer to the pin
            # node_a を pin_drivers リストから削除します
            set pin_drivers [lrange $pin_drivers 1 end]
        }
    }
    
    # pin_drivers に残された 1 つのノードが、特定のピンを駆動するノードです
    set node_driving_pin [lindex $pin_drivers 0]

    # ステップ 1 のマッピングにあるノード上のクロックを検索して返します
    return $nodes_with_clocks($node_driving_pin)
}