// -*- C++ -*- // // This file is part of the Coriolis Software. // Copyright (c) UPMC/LIP6 2008-2010, All Rights Reserved // // =================================================================== // // $Id$ // // x-----------------------------------------------------------------x // | | // | C O R I O L I S | // | K a t a b a t i c - Routing Toolbox | // | | // | Author : Jean-Paul CHAPUT | // | E-mail : Jean-Paul.Chaput@asim.lip6.fr | // | =============================================================== | // | C++ Module : "./KatabaticEngine.cpp" | // | *************************************************************** | // | U p d a t e s | // | | // x-----------------------------------------------------------------x #include #include #include "hurricane/DebugSession.h" #include "hurricane/Bug.h" #include "hurricane/Error.h" #include "hurricane/Warning.h" #include "hurricane/DataBase.h" #include "hurricane/Technology.h" #include "hurricane/Layer.h" #include "hurricane/BasicLayer.h" #include "hurricane/NetExternalComponents.h" #include "hurricane/Cell.h" #include "crlcore/Utilities.h" #include "crlcore/Catalog.h" #include "crlcore/Measures.h" #include "crlcore/AllianceFramework.h" #include "katabatic/Session.h" #include "katabatic/AutoContact.h" #include "katabatic/AutoSegment.h" #include "katabatic/GCell.h" #include "katabatic/GCellGrid.h" #include "katabatic/KatabaticEngine.h" namespace { using namespace Hurricane; bool isTopAndBottomConnected ( Segment* segment, set& layers ) { ltrace(88) << "* Potential Null Length: " << segment << endl; ltracein(88); Contact* source = dynamic_cast(segment->getSource()); Contact* target = dynamic_cast(segment->getTarget()); layers.clear (); if ( source ) { forEach ( Hook*, ihook, source->getBodyHook()->getSlaveHooks() ) { ltrace(88) << "* Slave: " << (*ihook)->getComponent() << endl; if ( (*ihook)->getComponent() == segment ) continue; layers.insert ( (*ihook)->getComponent()->getLayer() ); } } if ( target ) { forEach ( Hook*, ihook, target->getBodyHook()->getSlaveHooks() ) { ltrace(88) << "* Slave: " << (*ihook)->getComponent() << endl; if ( (*ihook)->getComponent() == segment ) continue; layers.insert ( (*ihook)->getComponent()->getLayer() ); } } size_t supplemental = (layers.find(segment->getLayer()) == layers.end()) ? 1 : 0; if ( source->getAnchor() || target->getAnchor() ) supplemental++; #if 0 bool bottomConnect = false; bool topConnect = false; if ( source ) { if ( segment->getLayer() != source->getLayer() ) { if ( source->getLayer()->getTop() == segment->getLayer() ) { bottomConnect = true; ltrace(88) << "Source bottom connected: " << source << endl; } else { topConnect = true; ltrace(88) << "Source top connected: " << source << endl; } } forEach ( Hook*, ihook, source->getBodyHook()->getSlaveHooks() ) { ltrace(88) << "* Slave: " << (*ihook)->getComponent() << endl; if ( (*ihook)->getComponent() == segment ) continue; if ( (*ihook)->getComponent()->getLayer() == segment->getLayer() ) { if ( bottomConnect ) topConnect = true; else bottomConnect = true; break; } } } if ( target ) { if ( segment->getLayer() != target->getLayer() ) { if ( target->getLayer()->getTop() == segment->getLayer() ) { bottomConnect = true; ltrace(88) << "Target bottom connected: " << target << endl; } else { topConnect = true; ltrace(88) << "Target top connected: " << target << endl; } } forEach ( Hook*, ihook, target->getBodyHook()->getSlaveHooks() ) { ltrace(88) << "* Slave: " << (*ihook)->getComponent() << endl; if ( (*ihook)->getComponent() == segment ) continue; if ( (*ihook)->getComponent()->getLayer() == segment->getLayer() ) { if ( bottomConnect ) topConnect = true; else bottomConnect = true; break; } } } #endif ltraceout(88); //return topConnect && bottomConnect; return layers.size()+supplemental > 2; } } // End of anonymous namespace. namespace Katabatic { using namespace std; using Hurricane::tab; using Hurricane::ltracein; using Hurricane::ltraceout; using Hurricane::inltrace; using Hurricane::DebugSession; using Hurricane::ForEachIterator; using Hurricane::Bug; using Hurricane::Error; using Hurricane::Warning; using Hurricane::DataBase; using Hurricane::Technology; using Hurricane::Layer; using Hurricane::BasicLayer; using Hurricane::NetExternalComponents; using CRL::AllianceFramework; using CRL::Catalog; using CRL::CatalogProperty; using CRL::Measures; using CRL::addMeasure; using CRL::getMeasure; // ------------------------------------------------------------------- // Global Variables. const char* missingKTBT = "%s :\n\n" " Cell %s do not have any Katabatic (or not yet created).\n"; const char* badMethod = "%s :\n\n" " No method id %ud (Cell %s).\n"; const char* lookupFailed = "Katabatic::Extension::getDatas(Segment*) :\n\n" " Cannot find AutoSegment associated to %s (internal error).\n"; // ------------------------------------------------------------------- // Class : "Katabatic::KatabaticEngine". Name KatabaticEngine::_toolName = "Katabatic"; KatabaticEngine* KatabaticEngine::get ( const Cell* cell ) { return static_cast(ToolEngine::get(cell,staticGetName())); } const Name& KatabaticEngine::staticGetName () { return _toolName; } const Name& KatabaticEngine::getName () const { return _toolName; } KatabaticEngine::KatabaticEngine ( Cell* cell ) : ToolEngine (cell) , _timer () , _state (StateCreation) , _destroyBaseContact(true) , _destroyBaseSegment(false) , _demoMode (false) , _warnGCellOverload (false) , _configuration (Configuration::getDefault()->clone()) , _gcellGrid (NULL) , _routingNets () { addMeasure ( cell, "Gates" , AllianceFramework::getInstancesCount(cell,AllianceFramework::IgnoreFeeds |AllianceFramework::Recursive) ); } void KatabaticEngine::_postCreate () { ToolEngine::_postCreate (); } void KatabaticEngine::createDetailedGrid () { _gcellGrid = GCellGrid::create ( this ); Session::revalidate (); addMeasure ( getCell(), "GCells", _gcellGrid->getGCellVector()->size() ); } KatabaticEngine* KatabaticEngine::create ( Cell* cell ) { ltrace(90) << "KatabaticEngine::create() - " << cell << endl; KatabaticEngine* katabatic = new KatabaticEngine ( cell ); katabatic->_postCreate (); return katabatic; } KatabaticEngine::~KatabaticEngine () { delete _configuration; } void KatabaticEngine::_preDestroy () { ltrace(90) << "Katabatic::_preDestroy ()" << endl; ltracein(90); if ( getState() < Katabatic::StateGutted ) setState ( Katabatic::StatePreDestroying ); _gutKatabatic (); _state = StateGutted; ltrace(89) << "About to delete base class ToolEngine." << endl; ToolEngine::_preDestroy (); ltrace(89) << "Exiting Katabatic::_preDestroy()." << endl; ltraceout(90); cmess2 << " - GCells := " << GCell::getAllocateds() << endl; cmess2 << " - AutoContacts := " << AutoContact::getAllocateds() << endl; cmess2 << " - AutoSegments := " << AutoSegment::getAllocateds() << endl; cmess2 << " - SegmentEnds := " << AutoContact::getSegmentEndAllocateds() << endl; } void KatabaticEngine::_gutKatabatic () { Session::open ( this ); _destroyBaseContact = false; _destroyBaseSegment = false; if ( _state == StateDriving ) { ltracein(90); ltrace(90) << "Saving AutoContacts/AutoSegments." << endl; cmess1 << " o Driving Hurricane data-base." << endl; cmess1 << Dots::asSizet(" - AutoSegments",AutoSegment::getAllocateds()) << endl; cmess1 << Dots::asSizet(" - AutoContacts",AutoContact::getAllocateds())<< endl; forEach ( Net*, inet, _cell->getNets() ) _saveNet ( *inet ); //_autoContactLut.clear (); ltraceout(90); } if ( _state < StateGutted ) { ltrace(90) << "Gutting Katabatic." << endl; _state = StateGutted; _destroyBaseContact = true; _destroyAutoSegments (); _destroyAutoContacts (); if ( _gcellGrid ) { _gcellGrid->destroy (); _gcellGrid = NULL; } } Session::close (); } AutoSegment* KatabaticEngine::_lookup ( Segment* segment ) const { AutoSegmentLut::const_iterator it = _autoSegmentLut.find ( segment ); if ( it == _autoSegmentLut.end() ) return NULL; return (*it).second; } void KatabaticEngine::_link ( AutoSegment* autoSegment ) { if ( _state > StateActive ) return; _autoSegmentLut [ autoSegment->getSegment() ] = autoSegment; } void KatabaticEngine::_unlink ( AutoSegment* autoSegment ) { if ( _state > StateDriving ) return; AutoSegmentLut::iterator it = _autoSegmentLut.find ( autoSegment->getSegment() ); if ( it != _autoSegmentLut.end() ) _autoSegmentLut.erase ( it ); } AutoContact* KatabaticEngine::_lookup ( Contact* contact ) const { AutoContactLut::const_iterator it = _autoContactLut.find ( contact ); if ( it == _autoContactLut.end() ) { return NULL; } return (*it).second; } void KatabaticEngine::_link ( AutoContact* autoContact ) { if ( _state > StateActive ) return; _autoContactLut [ autoContact->base() ] = autoContact; } void KatabaticEngine::_unlink ( AutoContact* autoContact ) { if ( _state > StateActive ) return; AutoContactLut::iterator it = _autoContactLut.find ( autoContact->getContact() ); if ( it != _autoContactLut.end() ) _autoContactLut.erase ( it ); } void KatabaticEngine::xmlWriteGCellGrid ( const string& fileName ) { ofstream file ( fileName.c_str() ); xmlWriteGCellGrid ( file ); file.close (); } void KatabaticEngine::xmlWriteGCellGrid ( ostream& o ) { if ( _gcellGrid ) _gcellGrid->_xmlWrite ( o ); else cerr << Error("Cannot dump GCellGrid: not allocated yet.") << endl; } void KatabaticEngine::startMeasures () { _timer.resetIncrease (); _timer.start (); } void KatabaticEngine::stopMeasures () { _timer.stop (); } void KatabaticEngine::printMeasures ( const string& tag ) const { cmess1 << " - Done in " << Timer::getStringTime(_timer.getCombTime()) << " [+" << Timer::getStringMemory(_timer.getIncrease()) << "]." << endl; cmess1 << " (raw measurements : " << _timer.getCombTime() << "s [+" << (_timer.getIncrease()>>10) << "Ko/" << (_timer.getMemorySize()>>10) << "Ko])" << endl; if ( not tag.empty() ) { addMeasure ( getCell(), tag+"T", _timer.getCombTime () ); addMeasure ( getCell(), tag+"S", (_timer.getMemorySize() >> 20) ); } } bool KatabaticEngine::_check ( const char* message ) const { bool coherency = true; if ( message ) cerr << " o checking Katabatic DB (" << message << ")." << endl; AutoSegmentLut::const_iterator it = _autoSegmentLut.begin (); AutoSegmentLut::const_iterator end = _autoSegmentLut.end (); for ( ; it != end ; it++ ) coherency = coherency && it->second->_check(); vector::const_iterator itGCell = _gcellGrid->getGCellVector()->begin(); vector::const_iterator endGCell = _gcellGrid->getGCellVector()->end(); for ( ; itGCell != endGCell ; itGCell++ ) { vector::const_iterator itAutoContact = (*itGCell)->getContacts()->begin(); vector::const_iterator endAutoContact = (*itGCell)->getContacts()->end(); for ( ; itAutoContact != endAutoContact ; itAutoContact++ ) { (*itAutoContact)->checkTopology (); } } if ( message ) cerr << " - completed." << endl; return coherency; } void KatabaticEngine::refresh ( bool openSession ) { if ( _gcellGrid ) _gcellGrid->updateContacts ( openSession ); } void KatabaticEngine::_destroyAutoSegments () { ltrace(90) << "Katabatic::_destroyAutoSegments ()" << endl; size_t expandeds = 0; AutoSegmentLut::iterator it = _autoSegmentLut.begin (); AutoSegmentLut::iterator end = _autoSegmentLut.end (); for ( ; it != end ; it++ ) { if ( !it->second->isCollapsed() ) expandeds++; it->second->destroy (); } if ( _state == StateDriving ) cerr << " - Expandeds := " << expandeds << endl; _autoSegmentLut.clear (); } void KatabaticEngine::_destroyAutoContacts () { ltrace(90) << "Katabatic::_destroyAutoContacts ()" << endl; AutoContactLut::iterator it = _autoContactLut.begin (); AutoContactLut::iterator end = _autoContactLut.end (); for ( ; it != end ; it++ ) it->second->destroy (); _autoContactLut.clear (); } Configuration* KatabaticEngine::getConfiguration () { return _configuration; } void KatabaticEngine::loadGlobalRouting ( unsigned int method, NetSet& nets ) { if ( _state < StateGlobalLoaded ) throw Error ("KatabaticEngine::loadGlobalRouting() : global routing not present yet."); if ( _state > StateGlobalLoaded ) throw Error ("KatabaticEngine::loadGlobalRouting() : global routing already loaded."); AllianceFramework* af = AllianceFramework::get (); if ( nets.empty() ) { forEach ( Net*, net, _cell->getNets() ) { if ( net->getType() == Net::Type::POWER ) continue; if ( net->getType() == Net::Type::GROUND ) continue; if ( net->getType() == Net::Type::CLOCK ) continue; if ( af->isOBSTACLE(net->getName()) ) continue; _routingNets.insert ( *net ); } } else { NetSet::iterator it = nets.begin(); for ( ; it != nets.end() ; it++ ) { if ( ( (*it)->getType() == Net::Type::POWER ) || ( (*it)->getType() == Net::Type::GROUND ) || ( (*it)->getType() == Net::Type::CLOCK ) || ( af->isOBSTACLE((*it)->getName()) ) ) { cerr << Warning("%s is not a routable net, removed from set.",getString(*it).c_str()) << endl; } else _routingNets.insert ( *it ); } } switch ( method ) { case LoadGrByNet: _loadGrByNet(); break; case LoadGrByGCell: default: throw Error ( badMethod , "Katabatic::loadGlobalRouting()" , method , getString(_cell).c_str() ); } _state = StateActive; } void KatabaticEngine::finalizeLayout () { ltrace(90) << "Katabatic::finalizeLayout()" << endl; if ( _state > StateDriving ) return; _state = StateDriving; startMeasures (); _gutKatabatic (); stopMeasures (); printMeasures ( "fin" ); _state = StateGutted; } void KatabaticEngine::_alignate ( Net* net ) { DebugSession::open ( net, 99 ); ltrace(100) << "Katabatic::_alignate ( " << net << " )" << endl; ltracein(99); //cmess2 << " - " << getString(net) << endl; set exploredSegments; vector unexploreds; vector aligneds; forEach ( Component*, icomponent, net->getComponents() ) { Segment* segment = dynamic_cast(*icomponent); if ( segment ) { AutoSegment* seedSegment = Session::lookup ( segment ); if ( seedSegment ) unexploreds.push_back ( seedSegment ); } } sort ( unexploreds.begin(), unexploreds.end(), AutoSegment::CompareCanonical() ); for ( size_t i=0 ; igetSegment()) == exploredSegments.end() ) { ltrace(99) << "New chunk from: " << seedSegment << endl; aligneds.push_back ( seedSegment ); forEach ( AutoSegment*, collapsed, seedSegment->getCollapseds() ) { ltrace(99) << "Aligned: " << *collapsed << endl; aligneds.push_back ( *collapsed ); exploredSegments.insert ( collapsed->getSegment() ); } ltracein(99); sort ( aligneds.begin(), aligneds.end(), AutoSegment::CompareCanonical() ); ltrace(99) << "Seed: " << (void*)aligneds[0]->base() << " " << aligneds[0] << endl; for ( size_t j=1 ; jbase()) << " " << aligneds[j] << endl; } ltrace(159) << "Align on " << aligneds[0] << " " << DbU::getLambda(aligneds[0]->getAxis()) << endl; aligneds[0]->setAxis ( aligneds[0]->getAxis(), Realignate|AxisSet ); aligneds.clear (); ltraceout(99); } } ltraceout(99); DebugSession::close (); } void KatabaticEngine::_canonize ( Net* net ) { DebugSession::open ( net, 99 ); ltrace(100) << "Katabatic::_canonize ( " << net << " )" << endl; ltracein(99); //cmess2 << " - " << getString(net) << endl; set exploredSegments; vector unexploreds; vector aligneds; forEach ( Component*, icomponent, net->getComponents() ) { Segment* segment = dynamic_cast(*icomponent); if ( segment ) { AutoSegment* seedSegment = Session::lookup ( segment ); if ( seedSegment ) unexploreds.push_back ( seedSegment ); } } sort ( unexploreds.begin(), unexploreds.end(), AutoSegment::CompareCanonical() ); for ( size_t i=0 ; igetSegment()) == exploredSegments.end() ) { ltrace(99) << "New chunk from: " << (void*)seedSegment->base() << ":" << seedSegment << endl; aligneds.push_back ( seedSegment ); bool isCanonicalLocal = seedSegment->isLocal(); forEach ( AutoSegment*, collapsed, seedSegment->getCollapseds() ) { ltrace(99) << "Aligned: " << (void*)collapsed->base() << ":" << *collapsed << endl; aligneds.push_back ( *collapsed ); exploredSegments.insert ( collapsed->getSegment() ); if ( collapsed->isGlobal() ) isCanonicalLocal = false; } ltracein(99); sort ( aligneds.begin(), aligneds.end(), AutoSegment::CompareCanonical() ); aligneds[0]->setCanonical ( true ); aligneds[0]->setCanonicalLocal ( isCanonicalLocal ); ltrace(99) << "Canonical: " << (void*)aligneds[0]->base() << ":" << aligneds[0] << endl; for ( size_t j=1 ; jsetCanonical ( false ); ltrace(99) << "Secondary: " << (void*)(aligneds[j]->base()) << ":" << aligneds[j] << endl; } ltrace(159) << "Align on " << aligneds[0] << " " << DbU::getLambda(aligneds[0]->getAxis()) << endl; aligneds[0]->setAxis ( aligneds[0]->getAxis(), Realignate|AxisSet ); aligneds.clear (); ltraceout(99); } } ltraceout(99); DebugSession::close (); } void KatabaticEngine::_computeNetTerminals ( Net* net ) { DebugSession::open ( net, 88 ); ltrace(100) << "Katabatic::_computeNetTerminals ( " << net << " )" << endl; ltracein(99); vector segments; forEach ( Segment*, segment, net->getSegments() ) { AutoSegment* autoSegment = Session::lookup ( *segment ); if ( !autoSegment ) continue; if ( autoSegment->isInvalidated() ) autoSegment->_computeTerminal(); } ltraceout(99); DebugSession::close (); } void KatabaticEngine::_saveNet ( Net* net ) { DebugSession::open ( net, 88 ); ltrace(90) << "Katabatic::_saveNet() " << net << endl; ltracein(90); vector autoContacts; forEach ( Contact*, icontact, net->getContacts() ) { AutoContact* autoContact = Session::lookup ( *icontact ); if ( autoContact ) autoContacts.push_back ( autoContact ); } ltrace(90) << "Breaking/Deleting AutoContacts." << endl; vector::iterator it = autoContacts.begin(); for ( ; it != autoContacts.end() ; it++ ) { ltrace(90) << "Examining " << (void*)*it << " " << (*it) << endl; ltracein(90); ltrace(90) << "In " << (void*)((*it)->getGCell()) << " - " << (*it)->getGCell() << endl; (*it)->breakUp (); // AutoContacts are destroyed through the tool. //(*it)->destroy (); ltraceout(90); } ltrace(90) << "Deleting zero-length segments." << endl; vector nullSegments; set connectedLayers; forEach ( Segment*, segment, net->getSegments() ) { if ( segment->getLength() ) { if ( net->isExternal() ) { NetExternalComponents::setExternal ( *segment ); } continue; } if ( not isTopAndBottomConnected(*segment,connectedLayers) ) { nullSegments.push_back ( *segment ); ltrace(90) << "* Null Length: " << *segment << endl; } } setDestroyBaseSegment ( true ); for ( size_t i = 0 ; i < nullSegments.size() ; i++ ) { Contact* source = dynamic_cast(nullSegments[i]->getSource()); Contact* target = dynamic_cast(nullSegments[i]->getTarget()); if ( source->getAnchor() ) { if ( target->getAnchor() ) { continue; //cerr << Bug("Both source & target are anchored while deleting zero-length segment:\n" // " %s.",getString(nullSegments[i]).c_str()) << endl; } else swap ( source, target ); } ltrace(90) << "Deleting: " << nullSegments[i] << endl; if ( isTopAndBottomConnected(nullSegments[i],connectedLayers) ) { ltrace(90) << "Deletion cancelled, no longer top or bottom connected." << endl; continue; } ltrace(90) << "* Source: " << (void*)source << " " << source << endl; ltrace(90) << "* Target: " << (void*)target << " " << target << endl; const Layer* layer = DataBase::getDB()->getTechnology() ->getViaBetween ( *connectedLayers.begin(), *connectedLayers.rbegin() ); ltrace(90) << *connectedLayers.begin() << " + " << *connectedLayers.rbegin() << endl; ltrace(90) << "* Shrink layer: " << layer << endl; if ( !layer ) { cerr << Error("NULL contact layer while deleting %s." ,getString(nullSegments[i]).c_str()) << endl; continue; } Session::lookup ( nullSegments[i] )->destroy (); vector slaveHooks; Hook* masterHook = source->getBodyHook()->getPreviousMasterHook(); while ( masterHook->getNextHook() != source->getBodyHook() ) { slaveHooks.push_back ( masterHook->getNextHook() ); ltrace(90) << "* detach: " << (void*)masterHook->getNextHook()->getComponent() << " " << masterHook->getNextHook()->getComponent() << endl; masterHook->getNextHook()->detach (); } source->destroy (); masterHook = target->getBodyHook (); for ( size_t j=0 ; j < slaveHooks.size() ; j++ ) { slaveHooks[j]->attach ( masterHook ); } ltrace(90) << (void*)target << " " << target << " setLayer: " << layer << endl; target->setLayer ( layer ); } setDestroyBaseSegment ( false ); ltraceout(90); DebugSession::close (); } void KatabaticEngine::_check ( Net* net ) const { ltrace(200) << "Checking " << net << endl; ltracein(200); forEach ( Segment*, isegment, net->getComponents().getSubSet() ) { AutoSegment* autoSegment = _lookup ( *isegment ); ltrace(200) << autoSegment << endl; if ( autoSegment ) { AutoContact* autoContact = autoSegment->getAutoSource(); ltrace(200) << autoContact << endl; if ( autoContact ) autoContact->checkTopology (); autoContact = autoSegment->getAutoTarget(); ltrace(200) << autoContact << endl; if ( autoContact ) autoContact->checkTopology (); } } ltraceout(200); } void KatabaticEngine::_print ( Net* net ) const { cerr << "Order: Components of " << net << endl; vector segments; forEach ( Segment*, isegment, net->getComponents().getSubSet() ) { AutoSegment* autoSegment = _lookup ( *isegment ); if ( autoSegment ) segments.push_back ( autoSegment ); } sort ( segments.begin(), segments.end(), AutoSegment::CompareCanonical() ); Interval constraints; for ( size_t i=0 ; igetConstraints ( constraints ); cerr << "Order: " << i << " " << segments[i]->isCanonical() << " " << segments[i]->isCollapsed() << " " << setw(6) << DbU::getValueString(segments[i]->getSourceU()) << " " << setw(6) << DbU::getValueString(segments[i]->getLength()) << " " << segments[i]->isGlobal() << " " << segments[i]->isTerminal() << " " << segments[i]->isHorizontal() << " " << setw(6) << DbU::getValueString(segments[i]->getAxis()) << " " << segments[i]->isFixed() << " [" << DbU::getValueString(constraints.getVMin()) << ":" << DbU::getValueString(constraints.getVMax()) << "] " << segments[i] << endl; } } void KatabaticEngine::_print () const { #if defined(CHECK_DETERMINISM) cerr << "Order: Nets components." << endl; #endif vector nets; forEach ( Net*, inet, getCell()->getNets() ) { nets.push_back ( *inet ); } sort ( nets.begin(), nets.end(), NetCompareByName() ); #if defined(CHECK_DETERMINISM) for ( size_t i=0 ; igetName () << " " << _configuration->getRoutingGauge()->getName() << ">"; return ( os.str() ); } Record* KatabaticEngine::_getRecord () const { Record* record = ToolEngine::_getRecord (); record->add ( getSlot ( "_demoMode" , _demoMode ) ); record->add ( getSlot ( "_state" , _state ) ); record->add ( getSlot ( "_configuration" , _configuration ) ); record->add ( getSlot ( "_gcellGrid" , _gcellGrid ) ); record->add ( getSlot ( "_routingNets" , &_routingNets ) ); record->add ( getSlot ( "_autoContactLut" , &_autoContactLut ) ); record->add ( getSlot ( "_autoSegmentLut" , &_autoSegmentLut ) ); return ( record ); } } // End of Katabatic namespace.