@@ -649,6 +649,30 @@ void RepairDesign::findBufferSizes()
649649 });
650650}
651651
652+ // / Gain buffering: Make a buffer tree to satisfy fanout and cap constraints
653+ // /
654+ // / Purpose: Reduce fanout count and total loading capacitance.
655+ // /
656+ // / @param net The net to be buffered
657+ // / @param drvr_pin The driver pin of the net
658+ // / @param max_fanout Maximum fanout allowed per buffer (gain buffering
659+ // / constraint)
660+ // /
661+ // / Algorithm:
662+ // / 1. Sinks are collected and sorted by timing criticality (required time).
663+ // / 2. Sinks are grouped based on driving cell's max fanout and max buffer
664+ // / load capacity (e.g., 9 * input cap of the largest buffer).
665+ // / 3. Insert the smallest buffer satisfying the following criteria.
666+ // / "buffer_input_cap > accumulated_load_cap / gain ratio"
667+ // / 4. The new buffer's input is treated as a new sink and re-added to the
668+ // / queue to recursively build the tree.
669+ // / 5. Incremental timing update.
670+ // /
671+ // / Why use required time instead of slack?
672+ // / - Arrival times change as the buffer tree is built, while required time
673+ // / does not change. So required times is a more stable metric for bottom-up
674+ // / construction and critical path isolation.
675+ // /
652676bool RepairDesign::performGainBuffering (Net* net,
653677 const Pin* drvr_pin,
654678 int max_fanout)
@@ -701,7 +725,7 @@ bool RepairDesign::performGainBuffering(Net* net,
701725 }
702726 };
703727
704- // Collect all sinks
728+ // 1. Collect all sinks
705729 std::vector<EnqueuedPin> sinks;
706730
707731 NetConnectedPinIterator* pin_iter = network_->connectedPinIterator (net);
@@ -744,8 +768,8 @@ bool RepairDesign::performGainBuffering(Net* net,
744768 }
745769 std::ranges::sort (sinks, PinRequiredHigher (network_));
746770
747- // Iterate until we satisfy both the gain condition and max_fanout
748- // on drvr_pin
771+ // 2. Iterate until we satisfy both the gain condition and max_fanout
772+ // on drvr_pin
749773 while (sinks.size () > max_fanout
750774 || (has_driver_cin && load > cin * gate_gain)) {
751775 float load_acc = 0 ;
@@ -766,102 +790,47 @@ bool RepairDesign::performGainBuffering(Net* net,
766790
767791 // Find the smallest buffer satisfying the gain condition on
768792 // its output pin
769- auto size = buffer_sizes_.begin ();
770- for (; size != buffer_sizes_.end () - 1 ; size++) {
771- if (bufferCin (*size) > load_acc / resizer_->buffer_sizing_cap_ratio_ ) {
793+ auto buf_cell = buffer_sizes_.begin ();
794+ for (; buf_cell != buffer_sizes_.end () - 1 ; buf_cell++) {
795+ if (bufferCin (*buf_cell)
796+ > load_acc / resizer_->buffer_sizing_cap_ratio_ ) {
772797 break ;
773798 }
774799 }
775800
776- if (bufferCin (*size ) >= 0 .9f * load_acc) {
801+ if (bufferCin (*buf_cell ) >= 0 .9f * load_acc) {
777802 // We are getting dimishing returns on inserting a buffer, stop
778803 // the algorithm here (we might have been called with a low gain value)
779804 break ;
780805 }
781806
782- // Get scope of driver, put any new buffers in that scope
783- sta::Pin* driver_pin = nullptr ;
784- odb::dbModule* driver_parent = db_network_->getNetDriverParentModule (
785- net, driver_pin, db_network_->hasHierarchy ());
786- odb::dbModInst* parent_mod_inst = driver_parent->getModInst ();
787- Instance* parent;
788- if (parent_mod_inst) {
789- parent = db_network_->dbToSta (parent_mod_inst);
790- } else {
791- parent = db_network_->topInstance ();
792- }
793-
794- // note any hierarchical nets.
795- // and move them to the output of the buffer.
796- odb::dbModNet* driver_mod_net = db_network_->hierNet (driver_pin);
797- if (driver_mod_net) {
798- // only disconnect the modnet, we hook it to the output of the buffer.
799- db_network_->disconnectPin (driver_pin,
800- db_network_->dbToSta (driver_mod_net));
801- }
802-
803- Net* new_net = db_network_->makeNet (parent);
804- dbNet* net_db = db_network_->staToDb (net);
805- dbNet* new_net_db = db_network_->staToDb (new_net);
806- new_net_db->setSigType (net_db->getSigType ());
807- // TODO: Propagate NDR settings
808- if (net_db->getNonDefaultRule ()) {
809- new_net_db->setNonDefaultRule (net_db->getNonDefaultRule ());
810- }
811-
812- const Point drvr_loc = db_network_->location (drvr_pin);
813-
814- // create instance in driver parent
815- Instance* inst = resizer_->makeBuffer (*size, " gain" , parent, drvr_loc);
816-
817807 LibertyPort *size_in, *size_out;
818- (*size)->bufferPorts (size_in, size_out);
819- Pin* buffer_ip_pin = nullptr ;
820- Pin* buffer_op_pin = nullptr ;
821- resizer_->getBufferPins (inst, buffer_ip_pin, buffer_op_pin);
822- db_network_->connectPin (buffer_ip_pin, net);
823-
824- // connect the buffer output to the new flat net and any modnet
825- // Keep the original input net driving the buffer.
826- // Update the hierarchical net/flat net correspondence because
827- // the hierarhical net is moved to the output of the buffer.
828-
829- db_network_->connectPin (
830- buffer_op_pin, new_net, db_network_->dbToSta (driver_mod_net));
831-
832- repaired_net = true ;
833- inserted_buffer_count_++;
834- if (graphics_) {
835- dbInst* db_inst = db_network_->staToDb (inst);
836- graphics_->makeBuffer (db_inst);
837- }
838-
808+ (*buf_cell)->bufferPorts (size_in, size_out);
839809 int max_level = 0 ;
810+ Pin* new_input_pin = nullptr ;
811+
812+ // 3. Insert a new buffer
813+ PinSet group_set (db_network_);
840814 for (auto it = sinks.begin (); it != group_end; it++) {
841- Pin* sink_pin = it->pin ;
842- LibertyPort* sink_port = network_->libertyPort (it->pin );
843- Instance* sink_inst = network_->instance (it->pin );
844- load -= sink_port->capacitance ();
815+ group_set.insert (it->pin );
845816 max_level = std::max (it->level , max_level);
846-
847- odb::dbModNet* sink_mod_net = db_network_->hierNet (sink_pin);
848- // rewire the sink pin, taking care of both the flat net
849- // and the hierarchical net. Update the hierarchical net
850- // flat net correspondence
851- db_network_->disconnectPin (sink_pin);
852- db_network_->connectPin (sink_pin,
853- db_network_->dbToSta (new_net_db),
854- db_network_->dbToSta (sink_mod_net));
855817 if (it->level == 0 ) {
856- Pin* new_pin = network_->findPin (sink_inst, sink_port);
857- tree_boundary.push_back (graph_->pinLoadVertex (new_pin));
818+ tree_boundary.push_back (graph_->pinLoadVertex (it->pin ));
858819 }
859820 }
860821
861- Pin* new_input_pin = buffer_ip_pin;
822+ Instance* inst = resizer_->insertBufferBeforeLoads (
823+ net, &group_set, *buf_cell, nullptr , " gain" );
824+ if (inst) {
825+ repaired_net = true ;
826+ inserted_buffer_count_++;
827+ Pin* buffer_op_pin = nullptr ;
828+ resizer_->getBufferPins (inst, new_input_pin, buffer_op_pin);
829+ }
862830
863- Delay buffer_delay
864- = resizer_->bufferDelay (*size, load_acc, resizer_->tgt_slew_dcalc_ap_ );
831+ // 4. New buffer input pin is enqueued as a new sink
832+ Delay buffer_delay = resizer_->bufferDelay (
833+ *buf_cell, load_acc, resizer_->tgt_slew_dcalc_ap_ );
865834
866835 auto new_pin = EnqueuedPin{new_input_pin,
867836 (group_end - 1 )->required_path ,
@@ -873,8 +842,11 @@ bool RepairDesign::performGainBuffering(Net* net,
873842 std::ranges::upper_bound (sinks, new_pin, PinRequiredHigher (network_)),
874843 new_pin);
875844
845+ load -= load_acc;
876846 load += size_in->capacitance ();
877847 }
848+
849+ // 5. Incremental timing update
878850 sta_->ensureLevelized ();
879851 sta::Level max_level = 0 ;
880852 for (auto vertex : tree_boundary) {
0 commit comments