@@ -302,7 +302,7 @@ void TimingGraphBuilder::build(bool allow_dangling_combinational_nodes) {
302
302
// Break any combinational loops (i.e. if the graph is not a DAG)
303
303
fix_comb_loops ();
304
304
305
- // Levelize the graph (i.e. determine its topological ordering and record
305
+ // Levelize the graph (i.e. determine its topological ordering and record
306
306
// the level of each node) for the timing analyzer
307
307
tg_->levelize ();
308
308
}
@@ -388,6 +388,16 @@ void TimingGraphBuilder::add_block_to_timing_graph(const AtomBlockId blk) {
388
388
* bad practise, but it happens). We detect such cases and also create a
389
389
* SOURCE (and leave any combinational inputs to that node disconnected).
390
390
*/
391
+
392
+ auto clock_generator_tnodes = create_block_timing_nodes (blk);
393
+ create_block_internal_data_timing_edges (blk, clock_generator_tnodes);
394
+ create_block_internal_clock_timing_edges (blk, clock_generator_tnodes);
395
+ }
396
+
397
+ // Constructs the timing graph nodes for the specified block
398
+ //
399
+ // Returns the set of created tnodese which are clock generators
400
+ std::set<tatum::NodeId> TimingGraphBuilder::create_block_timing_nodes (const AtomBlockId blk) {
391
401
std::set<std::string> output_ports_used_as_combinational_sinks;
392
402
393
403
// Create the tnodes corresponding to input pins
@@ -501,6 +511,10 @@ void TimingGraphBuilder::add_block_to_timing_graph(const AtomBlockId blk) {
501
511
netlist_lookup_.set_atom_pin_tnode (output_pin, tnode, BlockTnode::EXTERNAL);
502
512
}
503
513
514
+ return clock_generator_tnodes;
515
+ }
516
+
517
+ void TimingGraphBuilder::create_block_internal_clock_timing_edges (const AtomBlockId blk, const std::set<tatum::NodeId>& clock_generator_tnodes) {
504
518
// Connect the clock pins to the sources and sinks
505
519
for (AtomPinId pin : netlist_.block_pins (blk)) {
506
520
for (auto blk_tnode_type : {BlockTnode::EXTERNAL, BlockTnode::INTERNAL}) {
@@ -511,46 +525,78 @@ void TimingGraphBuilder::add_block_to_timing_graph(const AtomBlockId blk) {
511
525
512
526
auto node_type = tg_->node_type (tnode);
513
527
514
- if (node_type == NodeType::SOURCE || node_type == NodeType::SINK) {
515
- // Look-up the clock name on the port model
516
- AtomPortId port = netlist_.pin_port (pin);
517
- const t_model_ports* model_port = netlist_.port_model (port);
518
-
519
- VTR_ASSERT_MSG (!model_port->clock .empty (), " Sequential pins must have a clock" );
520
-
521
- // Find the clock pin in the netlist
522
- AtomPortId clk_port = netlist_.find_port (blk, model_port->clock );
523
- VTR_ASSERT (clk_port);
524
- VTR_ASSERT_MSG (netlist_.port_width (clk_port) == 1 , " Primitive clock ports can only contain one pin" );
525
-
526
- AtomPinId clk_pin = netlist_.port_pin (clk_port, 0 );
527
- VTR_ASSERT (clk_pin);
528
-
529
- // Convert the pin to it's tnode
530
- NodeId clk_tnode = netlist_lookup_.atom_pin_tnode (clk_pin);
531
- VTR_ASSERT (clk_tnode);
532
-
533
- // Determine the type of edge to create
534
- // This corresponds to how the clock (clk_tnode) relates
535
- // to the data (tnode). So a SOURCE data tnode should be
536
- // a PRIMTIVE_CLOCK_LAUNCH (clock launches data), while
537
- // a SINK data tnode should be a PRIMITIVE_CLOCK_CAPTURE
538
- // (clock captures data).
539
- tatum::EdgeType type;
540
- if (node_type == NodeType::SOURCE) {
541
- type = tatum::EdgeType::PRIMITIVE_CLOCK_LAUNCH;
542
- } else {
543
- VTR_ASSERT (node_type == NodeType::SINK);
544
- type = tatum::EdgeType::PRIMITIVE_CLOCK_CAPTURE;
545
- }
528
+ if (node_type != NodeType::SOURCE && node_type != NodeType::SINK) continue ;
529
+
530
+ VTR_ASSERT_SAFE (node_type == NodeType::SOURCE || node_type == NodeType::SINK);
531
+
532
+ // Look-up the clock name on the port model
533
+ AtomPortId port = netlist_.pin_port (pin);
534
+ const t_model_ports* model_port = netlist_.port_model (port);
535
+
536
+ VTR_ASSERT_MSG (!model_port->clock .empty (), " Sequential pins must have a clock" );
537
+
538
+ // Find the clock pin in the netlist
539
+ AtomPortId clk_port = netlist_.find_port (blk, model_port->clock );
540
+ VTR_ASSERT (clk_port);
541
+ VTR_ASSERT_MSG (netlist_.port_width (clk_port) == 1 , " Primitive clock ports can only contain one pin" );
542
+
543
+ AtomPinId clk_pin = netlist_.port_pin (clk_port, 0 );
544
+ VTR_ASSERT (clk_pin);
545
+
546
+ // Convert the pin to it's tnode
547
+ NodeId clk_tnode = netlist_lookup_.atom_pin_tnode (clk_pin);
548
+ VTR_ASSERT (clk_tnode);
549
+
550
+ // Determine the type of edge to create
551
+ // This corresponds to how the clock (clk_tnode) relates
552
+ // to the data (tnode). So a SOURCE data tnode should be
553
+ // a PRIMTIVE_CLOCK_LAUNCH (clock launches data), while
554
+ // a SINK data tnode should be a PRIMITIVE_CLOCK_CAPTURE
555
+ // (clock captures data).
556
+ tatum::EdgeType type;
557
+ if (node_type == NodeType::SOURCE) {
558
+ type = tatum::EdgeType::PRIMITIVE_CLOCK_LAUNCH;
559
+ } else {
560
+ VTR_ASSERT (node_type == NodeType::SINK);
561
+ type = tatum::EdgeType::PRIMITIVE_CLOCK_CAPTURE;
562
+ }
563
+
564
+ // Add the edge from the clock to the source/sink
565
+ tg_->add_edge (type, clk_tnode, tnode);
566
+ }
567
+ }
568
+
569
+ // Connect the combinational edges from clock input pins
570
+ //
571
+ // These are typically used to represent clock buffers
572
+ for (AtomPinId src_clock_pin : netlist_.block_clock_pins (blk)) {
573
+ NodeId src_tnode = netlist_lookup_.atom_pin_tnode (src_clock_pin, BlockTnode::EXTERNAL);
574
+
575
+ if (!src_tnode) continue ;
576
+
577
+ // Look-up the combinationally connected sink ports name on the port model
578
+ AtomPortId src_port = netlist_.pin_port (src_clock_pin);
579
+ const t_model_ports* model_port = netlist_.port_model (src_port);
580
+
581
+ for (const std::string& sink_port_name : model_port->combinational_sink_ports ) {
582
+ AtomPortId sink_port = netlist_.find_port (blk, sink_port_name);
583
+ if (!sink_port) continue ; // Port may not be connected
584
+
585
+ // We now need to create edges between the source pin, and all the pins in the
586
+ // output port
587
+ for (AtomPinId sink_pin : netlist_.port_pins (sink_port)) {
588
+ // Get the tnode of the sink
589
+ NodeId sink_tnode = netlist_lookup_.atom_pin_tnode (sink_pin, BlockTnode::EXTERNAL);
546
590
547
- // Add the edge from the clock to the source/sink
548
- tg_-> add_edge (type, clk_tnode, tnode );
591
+ tg_-> add_edge (tatum::EdgeType::PRIMITIVE_COMBINATIONAL, src_tnode, sink_tnode);
592
+ VTR_LOG ( " Adding edge from '%s' (tnode: %zu) -> '%s' (tnode: %zu) to allow clocks to propagate \n " , netlist_. pin_name (src_clock_pin). c_str (), size_t (src_tnode), netlist_. pin_name (sink_pin). c_str (), size_t (sink_tnode) );
549
593
}
550
594
}
551
595
}
596
+ }
552
597
553
- // Connect the combinational edges from input pins
598
+ void TimingGraphBuilder::create_block_internal_data_timing_edges (const AtomBlockId blk, const std::set<tatum::NodeId>& clock_generator_tnodes) {
599
+ // Connect the combinational edges from data input pins
554
600
//
555
601
// These edges may represent an intermediate (combinational) sub-path of a
556
602
// timing path (i.e. between IPINs and OPINs), the start of a timing path (i.e. SOURCE
@@ -560,7 +606,7 @@ void TimingGraphBuilder::add_block_to_timing_graph(const AtomBlockId blk) {
560
606
// Note that the creation of these edges is driven by the 'combinationl_sink_ports' specified
561
607
// in the architecture primitive model
562
608
for (AtomPinId src_pin : netlist_.block_input_pins (blk)) {
563
- // Note that we have already created all the relevant nodes, and appropriately labelled them as
609
+ // Note that we have already created all the relevant nodes, and appropriately labelled them as
564
610
// internal/external. As a result, we only need to consider the 'internal' tnodes when creating
565
611
// the edges within the current block.
566
612
NodeId src_tnode = netlist_lookup_.atom_pin_tnode (src_pin, BlockTnode::INTERNAL);
@@ -617,34 +663,6 @@ void TimingGraphBuilder::add_block_to_timing_graph(const AtomBlockId blk) {
617
663
}
618
664
}
619
665
}
620
-
621
- // Connect the combinational edges from clock pins
622
- //
623
- // These are typically used to represent clock buffers
624
- for (AtomPinId src_clock_pin : netlist_.block_clock_pins (blk)) {
625
- NodeId src_tnode = netlist_lookup_.atom_pin_tnode (src_clock_pin, BlockTnode::EXTERNAL);
626
-
627
- if (!src_tnode) continue ;
628
-
629
- // Look-up the combinationally connected sink ports name on the port model
630
- AtomPortId src_port = netlist_.pin_port (src_clock_pin);
631
- const t_model_ports* model_port = netlist_.port_model (src_port);
632
-
633
- for (const std::string& sink_port_name : model_port->combinational_sink_ports ) {
634
- AtomPortId sink_port = netlist_.find_port (blk, sink_port_name);
635
- if (!sink_port) continue ; // Port may not be connected
636
-
637
- // We now need to create edges between the source pin, and all the pins in the
638
- // output port
639
- for (AtomPinId sink_pin : netlist_.port_pins (sink_port)) {
640
- // Get the tnode of the sink
641
- NodeId sink_tnode = netlist_lookup_.atom_pin_tnode (sink_pin, BlockTnode::EXTERNAL);
642
-
643
- tg_->add_edge (tatum::EdgeType::PRIMITIVE_COMBINATIONAL, src_tnode, sink_tnode);
644
- VTR_LOG (" Adding edge from '%s' (tnode: %zu) -> '%s' (tnode: %zu) to allow clocks to propagate\n " , netlist_.pin_name (src_clock_pin).c_str (), size_t (src_tnode), netlist_.pin_name (sink_pin).c_str (), size_t (sink_tnode));
645
- }
646
- }
647
- }
648
666
}
649
667
650
668
void TimingGraphBuilder::add_net_to_timing_graph (const AtomNetId net) {
0 commit comments