vpr: Allow creation of clock to output pin edges in the timing graph

These edges are used to represent black-box buffers in the clock
network.
diff --git a/vpr/src/timing/PreClusterDelayCalculator.h b/vpr/src/timing/PreClusterDelayCalculator.h
index 24a1b3a..54e97e6 100644
--- a/vpr/src/timing/PreClusterDelayCalculator.h
+++ b/vpr/src/timing/PreClusterDelayCalculator.h
@@ -101,8 +101,9 @@
         VTR_ASSERT_MSG((src_node_type == tatum::NodeType::IPIN && sink_node_type == tatum::NodeType::OPIN)
                            || (src_node_type == tatum::NodeType::SOURCE && sink_node_type == tatum::NodeType::SINK)
                            || (src_node_type == tatum::NodeType::SOURCE && sink_node_type == tatum::NodeType::OPIN)
+                           || (src_node_type == tatum::NodeType::CPIN && sink_node_type == tatum::NodeType::OPIN)
                            || (src_node_type == tatum::NodeType::IPIN && sink_node_type == tatum::NodeType::SINK),
-                       "Primitive combinational delay must be between {SOURCE, IPIN} and {SINK, OPIN}");
+                       "Primitive combinational delay must be between {SOURCE, IPIN} and {SINK, OPIN}, or CPIN/OPIN");
 
         //Primitive internal combinational delay
         AtomPinId input_pin = netlist_lookup_.tnode_atom_pin(src_node);
diff --git a/vpr/src/timing/atom_delay_calc.inl b/vpr/src/timing/atom_delay_calc.inl
index 407602c..ec32aa9 100644
--- a/vpr/src/timing/atom_delay_calc.inl
+++ b/vpr/src/timing/atom_delay_calc.inl
@@ -10,8 +10,10 @@
 inline float AtomDelayCalc::atom_combinational_delay(const AtomPinId src_pin, const AtomPinId sink_pin, const DelayType delay_type) const {
     VTR_ASSERT_MSG(netlist_.pin_block(src_pin) == netlist_.pin_block(sink_pin), "Combinational primitive delay must be between pins on the same block");
 
-    VTR_ASSERT_MSG(   netlist_.port_type(netlist_.pin_port(src_pin)) == PortType::INPUT 
-                   && netlist_.port_type(netlist_.pin_port(sink_pin)) == PortType::OUTPUT,
+    auto src_pin_type = netlist_.port_type(netlist_.pin_port(src_pin));
+    auto sink_pin_type = netlist_.port_type(netlist_.pin_port(sink_pin));
+    VTR_ASSERT_MSG((src_pin_type == PortType::INPUT && sink_pin_type == PortType::OUTPUT)
+                   || (src_pin_type == PortType::CLOCK && sink_pin_type == PortType::OUTPUT),
                    "Combinational connections must go from primitive input to output");
 
     //Determine the combinational delay from the pb_graph_pin.
diff --git a/vpr/src/timing/timing_graph_builder.cpp b/vpr/src/timing/timing_graph_builder.cpp
index 86ff555..4d4ac33 100644
--- a/vpr/src/timing/timing_graph_builder.cpp
+++ b/vpr/src/timing/timing_graph_builder.cpp
@@ -267,7 +267,7 @@
         }
     }
 
-    //Connect the combinational edges
+    //Connect the combinational edges from input pins
     for (AtomPinId src_pin : netlist_.block_input_pins(blk)) {
         //Combinational edges go between IPINs and OPINs for combinational blocks
         //and between the internal SOURCEs and SINKS for sequential blocks
@@ -327,6 +327,34 @@
             }
         }
     }
+
+    //Connect the combinational edges from clock pins
+    //
+    //These are typically used to represent clock buffers
+    for (AtomPinId src_clock_pin : netlist_.block_clock_pins(blk)) {
+        NodeId src_tnode = netlist_lookup_.atom_pin_tnode(src_clock_pin, BlockTnode::EXTERNAL);
+
+        if (!src_tnode) continue;
+
+        //Look-up the combinationally connected sink ports name on the port model
+        AtomPortId src_port = netlist_.pin_port(src_clock_pin);
+        const t_model_ports* model_port = netlist_.port_model(src_port);
+
+        for (const std::string& sink_port_name : model_port->combinational_sink_ports) {
+            AtomPortId sink_port = netlist_.find_port(blk, sink_port_name);
+            if (!sink_port) continue; //Port may not be connected
+
+            //We now need to create edges between the source pin, and all the pins in the
+            //output port
+            for (AtomPinId sink_pin : netlist_.port_pins(sink_port)) {
+                //Get the tnode of the sink
+                NodeId sink_tnode = netlist_lookup_.atom_pin_tnode(sink_pin, BlockTnode::EXTERNAL);
+
+                tg_->add_edge(tatum::EdgeType::PRIMITIVE_COMBINATIONAL, src_tnode, sink_tnode);
+                VTR_LOG("Adding edge from '%s' (%zu) -> '%s' (%zu)\n", netlist_.pin_name(src_clock_pin).c_str(), size_t(src_tnode), netlist_.pin_name(sink_pin).c_str(), size_t(sink_tnode));
+            }
+        }
+    }
 }
 
 void TimingGraphBuilder::add_net_to_timing_graph(const AtomNetId net) {