Get Clocks Feeding a Pin

author-image

By

This design example shows a custom procedure, which you can use in SDC files, that returns a list of all clocks that feed a pin. The procedure can be helpful if you need to create generated clocks without knowing other clock names in a design. The Simplify Design Reuse with Dynamic SDC Constraints design example provides more details and an example of how to use the custom procedure described on this page.

The full code for the procedure is at the bottom of the page, following a complete explanation of how the procedure works. To use the get_clocks_driving_pins custom procedure in an SDC file, ensure the procedure has been defined, then call it like any other SDC command. There are two easy ways to ensure the procedure has been defined before use:

  • Save the procedure code in a separate SDC file and include the SDC file in the project.
  • Copy and paste the procedure code at the top of any SDC file, before the get_clocks_driving_pins custom procedure is used.

Separate SDC File

Saving the procedure code in a separate SDC file keeps the code separate from the rest of your constraints and makes it simpler to reuse in other projects. If you use a separate SDC file, you must add the SDC file with the procedure to the list of files in the project, and it must appear above any SDC files that use the procedure. Listing it above other SDC files ensures that the Quartus® II software defines the procedure before using it, whenever SDC files are read.

Copy and Paste

Copying and pasting the procedure code in the same SDC file where it is used results in fewer SDC files in a project. If you are creating an SDC file for a module that will be reused by other designers, it may be simplest to include all constraints and supporting code in a single SDC file. You must include the procedure code in the SDC file before the first use of the procedure so it is defined before use. Typically, you would put it at the top of the file to satisfy this requirement.

Script Operation

Getting a list of all clocks in a design that feed a pin takes three main steps:

  1. Get all clocks and create a mapping from their target nodes to the clocks on the target nodes.
  2. Get a list of nodes with clocks on them that are on the fanin path to the specified pin.
  3. From that list of nodes, find the node closest to the specified pin and return the clocks on that node.

Step 1. Get All Clocks and Create Mapping

The following Tcl code gets all clocks in the design and creates the mapping (with a Tcl array) from a node to the clocks on the node.

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

# Iterate over each clock in the design
foreach_in_collection clock_id [all_clocks] {

    set clock_name [get_clock_info -name $clock_id]

    # Each clock is applied to nodes. Get the collection of target nodes
    foreach_in_collection target_id [get_clock_info -targets $clock_id] {

        # Associate the clock name with its target node
        set target_name [get_node_info -name $target_id]
        lappend nodes_with_clocks($target_name) $clock_name
    }
}

Virtual clocks have no targets, so no mapping is ever made with a virtual clock. In the full procedure code listed below, information about the type of the target nodes (register, pin, cell, or port) is saved for later use.

Step 2. Get Nodes with Clocks in Fanin Path

The second step is to find the subset of nodes with clocks that are on the fanin path to the specified pin. For each node with clocks (found in step 1), get the fanin to the specified pin through the node. If there is a fanin, the node is in the fanin path to the pin. If there is no fanin, the node is not on the fanin path to the pin.

The following Tcl code iterates through all nodes with clocks from step 1 and uses the get_fanins command to determine whether each node is on the fanin path of the specified pin. If the node is on the fanin path of the specified pin, the node is saved in the pin_drivers list.

set pin_drivers [list]

# Iterate over all nodes in the mapping created in step 1
foreach node_with_clocks [array names nodes_with_clocks] {

    # Get any fanins to the specified pin through the current node
    set fanin_col [get_fanins -clock -through $node_with_clock $pin_name]

    # If there is at least one fanin node, the current node is on the
    # fanin path to the specified pin, so save it.
    if { 0 < [get_collection_size $fanin_col] } {
        lappend pin_drivers $node_with_clocks
    }
}

The full procedure code listed below uses additional information about the node type to specify a type-specific collection for the -through value in the get_fanins command.

Step 3. Find Node Closest to Specified Pin

The pin_drivers variable now has a list of all the nodes with clocks that are on the fanin path to the specified pin. This step finds the node closest to the specified pin. While there is more than one node in the pin_drivers list, the code takes the first two nodes in the list and checks whether one is on the fanin path to the other. If it is on the fanin path, the first node must be further away from the pin than the second node, so it can be removed from the list.

while { 1 < [llength $pin_drivers] } {

    # Get the first two nodes in the pin_drivers list
    set node_a [lindex $pin_drivers 0]
    set node_b [lindex $pin_drivers 1]

    # Check whether node_b is on the fanin path of node_a
    set fanin_col [get_fanins -clock -through $node_b $node_a]

    # If there is at least one fanin node, node_b must be further
    # away from the specified pin than node_a is.
    # If there is no fanin node, node_b must be closer to the
    # specified pin than node_a is.
    if { 0 < [get_collection_size] } {

        # node_a is closer to the pin.
        # Remove node_b from the pin_drivers list
        set pin_drivers [lreplace $pin_drivers 1 1]

    } else {

        # node_b is closer to the pin.
        # Remove node_a from the pin_drivers list
        set pin_drivers [lreplace $pin_drivers 0 0]
    }
}

# The one node left in pin_drivers is the node driving the specified pin
set node_driving_pin [lindex $pin_drivers 0]

# Look up the clocks on the node in the mapping from step 1 and return them
return $nodes_with_clocks($node_driving_pin

The full procedure code listed below uses additional information about the node type to specify a type-specific collection for the -through value in the get_fanins command.

Full Procedure Code

The complete code for the get_clocks_driving_pin custom procedure is listed below. It includes additional error checking and support features that are not described in detail above.

proc get_clocks_feeding_pin { pin_name } {

    # Before step 1, perform an error check to ensure that pin_name
    # passed in to the procedure matches one and only one pin.
    # Return an error if it does not match one and only one pin.
    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"
    }
    
    # Initialize variables used in the procedure
    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. Get all clocks in the design and create a mapping from
    # the target nodes to the clocks on the target nodes

    # Iterate over each clock in the design
    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]
        
        # Each clock is applied to nodes. Get the collection of target nodes
        foreach_in_collection target_id [get_clock_info -targets $clock_id] {

            # Associate the clock name with its target node
            set target_name [get_node_info -name $target_id]
            lappend nodes_with_clocks($target_name) $clock_name

            # Save the type of the target node for later use
            set target_type [get_node_info -type $target_id]
            set node_types($target_name) $target_type
        }
    }

    # Step 2. Get a list of nodes with clocks on them that are on the
    # fanin path to the specified pin

    # Iterate over all nodes in the mapping created in step 1
    foreach node_with_clocks [array names nodes_with_clocks] {

        # Use the type of the target node to create a type-specific
        # collection for the -through value in the get_fanins command.
        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" }
        }

        # Get any fanins to the specified pin through the current node
        set fanin_col [get_fanins -clock -through $through_col $pin_name]

        # If there is at least one fanin node, the current node is on the
        # fanin path to the specified pin, so save it.
        if { 0 < [get_collection_size $fanin_col] } {
            lappend pin_drivers $node_with_clocks
        }
    }

    # Before step 3, perform an error check to ensure that at least one
    # of the nodes with clocks in the design is on the fanin path to
    # the specified pin.
    if { 0 == [llength $pin_drivers] } {
        return -code error "Can not find any node with clocks that drives $pin_name"
    }
    
    # Step 3. From the list of nodes created in step 2, find the node
    # closest to the specified pin and return the clocks on that node.

    while { 1 < [llength $pin_drivers] } {

        # Get the first two nodes in the pin_drivers list
        set node_a [lindex $pin_drivers 0]
        set node_b [lindex $pin_drivers 1]
        
        # Use the type of the target node to create a type-specific
        # collection for the -through value in the get_fanins command.
        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" }
        }

        # Check whether node_b is on the fanin path of node_a        
        set fanin_col [get_fanins -clock -through $through_col $node_a]

        # If there is at least one fanin node, node_b must be further
        # away from the specified pin than node_a is.
        # If there is no fanin node, node_b must be closer to the
        # specified pin than node_a is.
        if { 0 < [get_collection_size $fanin_col] } {

            # node_a is closer to the pin.
            # Remove node_b from the pin_drivers list
            set pin_drivers [lreplace $pin_drivers 1 1]

        } else {

            # node_b is closer to the pin
            # Remove node_a from the pin_drivers list
            set pin_drivers [lrange $pin_drivers 1 end]
        }
    }
    
    # The one node left in pin_drivers is the node driving the specified pin
    set node_driving_pin [lindex $pin_drivers 0]

    # Look up the clocks on the node in the mapping from step 1 and return them
    return $nodes_with_clocks($node_driving_pin)
}