// -*- C++ -*- // // This file is part of the Coriolis Software. // Copyright (c) UPMC 2021-2021, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | // | A n a b a t i c - Routing Toolbox | // | | // | Author : Jean-Paul CHAPUT | // | E-mail : Jean-Paul.Chaput@lip6.fr | // | =============================================================== | // | C++ Module : "./AntennaProtect.cpp" | // +-----------------------------------------------------------------+ #include #include #include #include "hurricane/Bug.h" #include "hurricane/Warning.h" #include "hurricane/DebugSession.h" #include "hurricane/Breakpoint.h" #include "hurricane/Net.h" #include "hurricane/NetExternalComponents.h" #include "hurricane/NetRoutingProperty.h" #include "hurricane/Layer.h" #include "hurricane/RoutingPad.h" #include "hurricane/Pad.h" #include "hurricane/Plug.h" #include "hurricane/Instance.h" #include "hurricane/Vertical.h" #include "hurricane/Horizontal.h" #include "hurricane/Cell.h" #include "crlcore/RoutingGauge.h" #include "etesian/EtesianEngine.h" #include "anabatic/AutoContactTerminal.h" #include "anabatic/AutoSegment.h" #include "anabatic/AnabaticEngine.h" namespace { using namespace std; using namespace CRL; using namespace Hurricane; using namespace Anabatic; using Etesian::EtesianEngine; class CompareGCellByXMin { public: inline bool operator () ( const GCell* lhs, const GCell* rhs ) const; }; inline bool CompareGCellByXMin::operator () ( const GCell* lhs, const GCell* rhs ) const { return lhs->getXMin() < rhs->getXMin(); } // ----------------------------------------------------------------- // Class : "::DiodeCluster". class DiodeCluster { public: static const uint32_t IsDriver; static const uint32_t IsSink; static const uint32_t HasDiode; static const uint32_t IsSegSource; static string toStr ( uint32_t ); public: typedef tuple RPInfos; typedef map RoutingPadInfos; typedef set HorizontalSet; typedef set VerticalSet; typedef map< DbU::Unit, vector > GCellArea; public: DiodeCluster ( RoutingPad*, AnabaticEngine* ); inline bool hasRp ( RoutingPad* ) const; bool hasGCell ( GCell* ) const; inline Net* getTopNet () const; inline RoutingPad* getRefRp () const; inline const RoutingPadInfos& getRoutingPads () const; inline const vector& getDiodes () const; inline DbU::Unit getWL () const; bool needsDiode () const; Box getBoundingBox () const; void merge ( GCell* ); void merge ( RoutingPad* ); void merge ( Segment* ); void mergeHalo ( Segment* ); void forceDiodeOn ( RoutingPad*, Segment*, Flags ); Instance* createDiode ( Etesian::Area*, GCell*, Flags side, uint32_t distance=1 ); const vector& createDiodes ( Etesian::Area* ); private: void _consolidate (); private: AnabaticEngine* _anabatic; bool _sortArea; DbU::Unit _WL; RoutingPadInfos _routingPads; HorizontalSet _horizontals; VerticalSet _verticals; HorizontalSet _horizontalHalo; VerticalSet _verticalHalo; GCellArea _area; vector _diodes; }; const uint32_t DiodeCluster::IsDriver = (1 << 0); const uint32_t DiodeCluster::IsSink = (1 << 1); const uint32_t DiodeCluster::HasDiode = (1 << 2); const uint32_t DiodeCluster::IsSegSource = (1 << 3); string DiodeCluster::toStr ( uint32_t flags ) { string s; s += (flags & IsDriver ) ? 'd' : '-'; s += (flags & IsSink ) ? 's' : '-'; s += (flags & HasDiode ) ? 'D' : '-'; s += (flags & IsSegSource) ? 'S' : '-'; return s; } DiodeCluster::DiodeCluster ( RoutingPad* rp, AnabaticEngine* anabatic ) : _anabatic(anabatic) , _sortArea(true) , _WL(0) , _routingPads() , _horizontals() , _verticals() , _horizontalHalo() , _verticalHalo() , _area() , _diodes() { merge( rp ); } inline Net* DiodeCluster::getTopNet () const { return getRefRp()->getNet(); } inline DbU::Unit DiodeCluster::getWL () const { return _WL; } inline const DiodeCluster::RoutingPadInfos& DiodeCluster::getRoutingPads () const { return _routingPads; } inline const vector& DiodeCluster::getDiodes () const { return _diodes; } bool DiodeCluster::needsDiode () const { for ( auto& item : _routingPads ) { if (std::get<0>(item.second) & IsSink) return true; } return false; } inline bool DiodeCluster::hasRp ( RoutingPad* rp ) const { return (_routingPads.find(rp) != _routingPads.end()); } inline bool DiodeCluster::hasGCell ( GCell* gcell ) const { if (not gcell) return false; auto islice = _area.find( gcell->getYMin()); if (islice == _area.end()) return false; for ( const GCell* igcell : (*islice).second ) if (igcell == gcell) return true; return false; } inline RoutingPad* DiodeCluster::getRefRp () const { if (not _routingPads.empty()) return (*_routingPads.begin()).first; return NULL; } void DiodeCluster::merge ( RoutingPad* rp ) { Plug* rpPlug = dynamic_cast( rp->getPlugOccurrence().getEntity() ); if (rpPlug) { merge( _anabatic->getGCellUnder( rp->getPosition() )); if (rpPlug->getMasterNet()->getDirection() & Net::Direction::DirIn) { _routingPads.insert( make_pair(rp,make_tuple(IsSink,(Segment*)NULL)) ); } else { _routingPads.insert( make_pair(rp,make_tuple(IsDriver,(Segment*)NULL)) ); cdebug_log(147,0) << "| Driver " << rp << endl; } } else { Pin* rpPin = dynamic_cast( rp->getPlugOccurrence().getEntity() ); if (rpPin) { _routingPads.insert( make_pair(rp,make_tuple(IsDriver,(Segment*)NULL)) ); cdebug_log(147,0) << "| Pin (considered driver) " << rp << endl; } } } void DiodeCluster::merge ( Segment* segment ) { _WL += segment->getLength(); GCellsUnder gcells = _anabatic->getGCellsUnder( segment ); if (not gcells->empty()) { _sortArea = true; for ( size_t i=0 ; isize() ; ++i ) { merge( gcells->gcellAt(i) ); } } } void DiodeCluster::merge ( GCell* gcell ) { if (not gcell) return; auto islice = _area.find( gcell->getYMin()); if (islice == _area.end()) islice = _area.insert( make_pair(gcell->getYMin(),vector()) ).first; for ( const GCell* igcell : (*islice).second ) { if (igcell == gcell) return; } (*islice).second.push_back( gcell ); } Box DiodeCluster::getBoundingBox () const { Box bb; for ( auto& item : _routingPads ) bb.merge( item.first->getPosition() ); return bb; } void DiodeCluster::forceDiodeOn ( RoutingPad* rp, Segment* segment, Flags flags ) { cdebug_log(147,0) << "DiodeCluster::forceDiodeOn(): " << endl; cdebug_log(147,0) << " rp=" << rp << endl; cdebug_log(147,0) << " seg=" << segment << endl; if (not rp or not segment) return; for ( auto& item : _routingPads ) { if (item.first == rp) { std::get<0>(item.second) |= HasDiode; if (flags & Flags::Source) std::get<0>(item.second) |= IsSegSource; std::get<1>(item.second) = segment; return; } } cdebug_log(147,0) << "DiodeCluster::forceDiodeOn(): No RP registered." << endl; } Instance* DiodeCluster::createDiode ( Etesian::Area* area, GCell* gcell, Flags side, uint32_t distance ) { cdebug_log(147,0) << "DiodeCluster::createDiode(): from=" << gcell << " distance=" << distance << endl; GCell* neighbor = gcell; for ( uint32_t i=0 ; igetWest(); else if (side.contains(Flags::EastSide )) neighbor = neighbor->getEast(); else if (side.contains(Flags::NorthSide)) neighbor = neighbor->getNorth(); else if (side.contains(Flags::SouthSide)) neighbor = neighbor->getSouth(); } if (not neighbor) return NULL; DbU::Unit xHint = 0; if (side.contains(Flags::WestSide )) xHint = neighbor->getBoundingBox().getXMax(); else if (side.contains(Flags::EastSide )) xHint = neighbor->getBoundingBox().getXMin(); else if (side.contains(Flags::NorthSide)) xHint = neighbor->getBoundingBox().getXCenter(); else if (side.contains(Flags::SouthSide)) xHint = neighbor->getBoundingBox().getXCenter(); Box bb = neighbor->getBoundingBox(); Instance* diode = area->createDiodeUnder( getRefRp(), bb, xHint ); if (diode) { _diodes.push_back( diode ); cdebug_log(147,0) << "> GCell area (neighbor): " << bb << endl; Contact* sourceContact = gcell->breakGoThrough( getTopNet() ); Contact* targetContact = neighbor->hasGContact( getTopNet() ); if (not targetContact) { bool hasGoThrough = (neighbor->hasGoThrough(getTopNet()) != NULL); targetContact = neighbor->breakGoThrough( getTopNet() ); if (not hasGoThrough) { if (side & Flags::Horizontal) { if (sourceContact->getX() > targetContact->getX()) std::swap( sourceContact, targetContact ); Horizontal::create( sourceContact , targetContact , _anabatic->getConfiguration()->getGHorizontalLayer() , bb.getCenter().getY() , _anabatic->getConfiguration()->getGHorizontalPitch() ); } else { if (sourceContact->getY() > targetContact->getY()) std::swap( sourceContact, targetContact ); Vertical::create( sourceContact , targetContact , _anabatic->getConfiguration()->getGVerticalLayer() , bb.getCenter().getX() , _anabatic->getConfiguration()->getGVerticalPitch() ); } } } } return diode; } const vector& DiodeCluster::createDiodes ( Etesian::Area* area ) { if (not needsDiode()) return _diodes; _consolidate(); Instance* diode = NULL; for ( auto& item : _routingPads ) { if (not (std::get<0>(item.second) & HasDiode)) continue; diode = NULL; GCell* gcell = _anabatic->getGCellUnder( item.first->getPosition() ); Horizontal* h = dynamic_cast(std::get<1>(item.second)); if (h) { cdebug_log(147,0) << "> Long segment (forced diode) h=" << h << endl; diode = area->createDiodeUnder( getRefRp() , h->getBoundingBox() , item.first->getPosition().getX() ); if (diode) { _diodes.push_back( diode ); continue; } } auto islice = _area.find( gcell->getYMin()); if (islice != _area.end()) { Box gcellsBb; bool containsRp = false; vector& slice = (*islice).second; for ( size_t i=0 ; i < slice.size() ; ++i ) { if (slice[i] == gcell) containsRp = true; if ((i > 0) and (slice[i-1]->getXMax() < slice[i]->getXMin())) { cdebug_log(147,0) << "> GCell area (forced diode): " << gcellsBb << endl; if (containsRp) { diode = area->createDiodeUnder( getRefRp(), gcellsBb, item.first->getPosition().getX() ); if (diode) { _diodes.push_back( diode ); containsRp = false; break; } } gcellsBb.makeEmpty(); } gcellsBb.merge( slice[i]->getBoundingBox() ); } if (diode) continue; if (containsRp and not gcellsBb.isEmpty()) { cdebug_log(147,0) << "> GCell area (forced diode, end): " << gcellsBb << endl; diode = area->createDiodeUnder( getRefRp(), gcellsBb, item.first->getPosition().getX() ); if (diode) { _diodes.push_back( diode ); continue; } } cdebug_log(147,0) << "> Try along connecting segment" << endl; Horizontal* h = dynamic_cast( std::get<1>(item.second) ); if (h) { if (std::get<0>(item.second) & IsSegSource) { for ( uint32_t i=0 ; i<3 ; ++i ) { if ((diode = createDiode(area,gcell,Flags::EastSide,i))) break; } } else { for ( uint32_t i=0 ; i<3 ; ++i ) { if ((diode = createDiode(area,gcell,Flags::WestSide,i))) break; } } } else { if (std::get<0>(item.second) & IsSegSource) { for ( uint32_t i=0 ; i<3 ; ++i ) { if ((diode = createDiode(area,gcell,Flags::NorthSide,i))) break; } } else { for ( uint32_t i=0 ; i<3 ; ++i ) { if ((diode = createDiode(area,gcell,Flags::SouthSide,i))) break; } } } } } if (diode) return _diodes; for ( auto& item : _area ) { cdebug_log(147,0) << "+ Slice @" << DbU::getValueString(item.first) << endl; Box gcellsBb; vector& slice = item.second; for ( size_t i=0 ; (i < slice.size()) and not diode ; ++i ) { if ((i > 0) and (slice[i-1]->getXMax() < slice[i]->getXMin())) { cdebug_log(147,0) << "> GCell area: " << gcellsBb << endl; diode = area->createDiodeUnder( getRefRp(), gcellsBb, gcellsBb.getXCenter() ); if (diode) { _diodes.push_back( diode ); return _diodes; } gcellsBb.makeEmpty(); } cdebug_log(147,0) << "| Agglomerate [" << i << "]: " << slice[i] << endl; gcellsBb.merge( slice[i]->getBoundingBox() ); } if (not gcellsBb.isEmpty()) { cdebug_log(147,0) << "> GCell area (end): " << gcellsBb << endl; diode = area->createDiodeUnder( getRefRp(), gcellsBb, gcellsBb.getXCenter() ); if (diode) { _diodes.push_back( diode ); return _diodes; } } } for ( auto& item : _area ) { vector& slice = item.second; if (createDiode(area,slice[0 ],Flags::WestSide)) return _diodes; if (createDiode(area,slice[slice.size()-1],Flags::EastSide)) return _diodes; } for ( auto& item : _routingPads ) { GCell* gcell = _anabatic->getGCellUnder( item.first->getPosition() ); if (createDiode(area,gcell,Flags::NorthSide)) return _diodes; if (createDiode(area,gcell,Flags::SouthSide)) return _diodes; } return _diodes; } void DiodeCluster::_consolidate () { if (not _sortArea) return; for ( auto& item : _area ) { std::sort( item.second.begin(), item.second.end(), CompareGCellByXMin() ); } _sortArea = false; } } // Anonymous namespace. namespace Anabatic { using namespace Hurricane; using CRL::ToolEngine; using Etesian::EtesianEngine; void AnabaticEngine::antennaProtect ( Net* net, uint32_t& failed, uint32_t& total ) { DebugSession::open( net, 145, 150 ); cdebug_log(147,1) << "Net \"" << net->getName() << endl; EtesianEngine* etesian = static_cast ( ToolEngine::get( getCell(), EtesianEngine::staticGetName() )); DbU::Unit antennaMaxWL = etesian->getAntennaMaxWL(); vector clusters; set rpsDone; set segmentsDone; for ( RoutingPad* rp : net->getRoutingPads() ) { if (rpsDone.find(rp) != rpsDone.end()) continue; DiodeCluster* cluster = new DiodeCluster ( rp, this ); clusters.push_back( cluster ); vector hooksStack; hooksStack.push_back( rp->getBodyHook() ); while ( not hooksStack.empty() ) { Hook* topHook = hooksStack.back(); RoutingPad* topRp = dynamic_cast( topHook->getComponent() ); RoutingPad* connexRp = NULL; hooksStack.pop_back(); for ( Hook* hook : topHook->getHooks() ) { RoutingPad* gcellRp = dynamic_cast( hook->getComponent() ); if (gcellRp) { if (not connexRp) connexRp = gcellRp; if (rpsDone.find(gcellRp) == rpsDone.end()) cluster->merge( gcellRp ); rpsDone.insert( gcellRp ); } } for ( Hook* hook : topHook->getHooks() ) { Segment* segment = dynamic_cast( hook->getComponent() ); if (segment) { cdebug_log(147,0) << "| " << DbU::getValueString(segment->getLength()) << " " << segment << endl; if (segmentsDone.find(segment) != segmentsDone.end()) continue; segmentsDone.insert( segment ); if (segment->getLength() > antennaMaxWL/2) { Flags flags = (segment->getSourceHook() == hook) ? Flags::Source : Flags::Target; if (not connexRp) connexRp = topRp; cluster->forceDiodeOn( connexRp, segment, flags ); continue; } cluster->merge( segment ); if (dynamic_cast(hook)) { hooksStack.push_back( segment->getTargetHook() ); } else { hooksStack.push_back( segment->getSourceHook() ); } } } } } Cell* diodeCell = etesian->getDiodeCell(); Net* diodeOutput = NULL; for ( Net* net : diodeCell->getNets() ) { if (net->isSupply() or not net->isExternal()) continue; diodeOutput = net; break; } if (clusters.size() > 1) { total += clusters.size(); cdebug_log(147,1) << "Net \"" << net->getName() << " has " << clusters.size() << " diode clusters." << endl; for ( size_t i=0 ; ineedsDiode()) continue; const vector diodes = clusters[i]->createDiodes( etesian->getArea() ); RoutingPad* rp = clusters[i]->getRefRp(); if (not diodes.empty()) { Net* topNet = rp->getNet(); Plug* sinkPlug = dynamic_cast( rp->getPlugOccurrence().getEntity() ); Path path = rp->getOccurrence().getPath().getHeadPath(); if (sinkPlug) { for ( Instance* diode : diodes ) { cdebug_log(147,0) << " Bind diode input:" << endl; cdebug_log(147,0) << " " << diode << " @" << diode ->getTransformation() << endl; cdebug_log(147,0) << " topNet->getCell():" << topNet->getCell() << endl; cdebug_log(147,0) << " " << rp->getOccurrence().getPath() << endl; //Path path = rp->getOccurrence().getPath().getHeadPath(); Plug* diodePlug = diode->getPlug( diodeOutput ); diodePlug->setNet( sinkPlug->getNet() ); RoutingPad* diodeRp = RoutingPad::create( topNet, Occurrence(diodePlug,path), RoutingPad::BiggestArea ); GCell* gcell = getGCellUnder( diodeRp->getPosition() ); if (gcell) { Contact* contact = gcell->breakGoThrough( topNet ); contact->getBodyHook()->merge( diodeRp->getBodyHook() ); } } } else { cerr << Error( "EtesianEngine::antennaProtect(): For %s (rps:%u, clusters:%u)\n" " Cannot get a Plug from %s (?)." , getString(net).c_str() , rpsDone.size() , clusters.size() , getString(clusters[i]->getRefRp()).c_str() ) << endl; } } else { cerr << Error( "EtesianEngine::antennaProtect(): For %s (rps:%u, clusters:%u)\n" " Cannot find a diode nearby %s." , getString(net).c_str() , rpsDone.size() , clusters.size() , getString(clusters[i]->getRefRp()).c_str() ) << endl; failed += 1; } } cdebug_tabw(147,-1); } for ( DiodeCluster* cluster : clusters ) delete cluster; cdebug_tabw(147,-1); DebugSession::close(); } void AnabaticEngine::antennaProtect () { //DebugSession::open( 145, 150 ); if (not ToolEngine::get( getCell(), EtesianEngine::staticGetName() )) { cerr << Warning( "AnabaticEngine::antennaProtect(): No EtesianEngine found, skipped." ) << endl; return; } EtesianEngine* etesian = static_cast ( ToolEngine::get( getCell(), EtesianEngine::staticGetName() )); DbU::Unit segmentMaxWL = etesian->getAntennaMaxWL() / 2; if (not etesian->getDiodeCell()) { cerr << Warning( "AnabaticEngine::antennaProtect(): No diode cell found, skipped." ) << endl; return; } startMeasures(); openSession(); uint32_t failed = 0; uint32_t total = 0; for ( Net* net : getCell()->getNets() ) { if (net->isSupply()) continue; if (net->isClock ()) continue; antennaProtect( net, failed, total ); } cmess2 << Dots::asString ( " - Antenna maximum WL" , DbU::getValueString(etesian->getAntennaMaxWL()) ) << endl; cmess2 << Dots::asString ( " - Segment maximum WL" , DbU::getValueString(segmentMaxWL) ) << endl; cmess2 << Dots::asInt ( " - Total needed diodes", total ) << endl; cmess2 << Dots::asInt ( " - Failed to allocate" , failed ) << endl; cmess2 << Dots::asPercentage( " - Success ratio" , (float)(total-failed)/(float)total ) << endl; stopMeasures(); printMeasures( "antennas" ); // cmess2 << " - Total segments : " << total << endl; // cmess2 << " - Global segments : " << global << endl; // cmess2 << " - Ratio : " // << ((float)global/(float)total)*100.0 << "%." << endl; Session::close(); //DebugSession::close(); Breakpoint::stop( 99, "After diodes insertions." ); } } // Anabatic namespace.