// -*- 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 | // | E t e s i a n - A n a l y t i c P l a c e r | // | | // | Author : Jean-Paul CHAPUT | // | E-mail : Jean-Paul.Chaput@lip6.fr | // | =============================================================== | // | C++ Module : "./HFNS.cpp" | // +-----------------------------------------------------------------+ #include "hurricane/Error.h" #include "hurricane/Warning.h" #include "hurricane/DebugSession.h" #include "hurricane/DataBase.h" #include "hurricane/UpdateSession.h" #include "hurricane/DeepNet.h" #include "hurricane/Pin.h" #include "hurricane/Plug.h" #include "hurricane/RoutingPad.h" #include "hurricane/Path.h" #include "hurricane/Library.h" #include "hurricane/viewer/CellWidget.h" #include "hurricane/viewer/CellViewer.h" #include "crlcore/AllianceFramework.h" #include "crlcore/ToolBox.h" #include "etesian/EtesianEngine.h" namespace Etesian { using namespace std; using Hurricane::tab; using Hurricane::Warning; using Hurricane::Error; using Hurricane::Path; using Hurricane::Transformation; using Hurricane::DataBase; using Hurricane::Library; using Hurricane::Go; using Hurricane::DeepNet; using Hurricane::Pin; using Hurricane::Plug; using Hurricane::RoutingPad; using Hurricane::UpdateSession; using CRL::AllianceFramework; using CRL::CatalogExtension; using CRL::getTransformation; using CRL::SubNetNames; using Etesian::EtesianEngine; // ------------------------------------------------------------------- // Class : "::Cluster". class Cluster { public: const uint32_t AS_DRIVER = (1<<0); const uint32_t AS_SINK = (1<<1); public: Cluster ( EtesianEngine* ); virtual ~Cluster (); inline size_t getSize () const; inline EtesianEngine* getEtesian () const; virtual Cluster* getParent () const; virtual SubNetNames* getSubNetNames (); inline void setParent ( Cluster* ); bool merge ( RoutingPad* ); bool merge ( Cluster* ); inline void swapRps ( Cluster* ); inline Net* getInputNet (); inline Net* getOutputNet (); Plug* raddTransPlug ( Net* topNet, Path, uint32_t flags ); Net* raddTransNet ( Net* topNet, Path, uint32_t flags ); Plug* getPlugByNet ( Instance* instance, Net* cellNet ); void createInput ( Net* ); void createOutput (); virtual void splitNet (); virtual string _getTypeName () const; virtual string _getString () const; private: EtesianEngine* _etesian; vector _rps; vector _clusters; Cluster* _parent; Instance* _buffer; Net* _driverNet; }; Cluster::Cluster ( EtesianEngine* etesian ) : _etesian (etesian) , _rps () , _clusters () , _parent (NULL) , _buffer (NULL) , _driverNet(NULL) { } Cluster::~Cluster () { } inline EtesianEngine* Cluster::getEtesian () const { return _etesian; } inline size_t Cluster::getSize () const { return _rps.size() + _clusters.size(); } Cluster* Cluster::getParent () const { return _parent; } SubNetNames* Cluster::getSubNetNames () { return _parent->getSubNetNames(); } inline void Cluster::setParent ( Cluster* parent ) { _parent = parent; } inline void Cluster::swapRps ( Cluster* other ) { _rps.swap( other->_rps ); } bool Cluster::merge ( RoutingPad* newRp ) { for ( RoutingPad* rp : _rps ) { if (rp == newRp) return false; } _rps.push_back( newRp ); return true; } bool Cluster::merge ( Cluster* child ) { for ( Cluster* cluster : _clusters ) { if (cluster == child) return false; } _clusters.push_back( child ); child->setParent( this ); return true; } Plug* Cluster::getPlugByNet ( Instance* instance, Net* cellNet ) { for ( Plug* plug : instance->getPlugs() ) { if (plug->getNet() == cellNet) return plug; } return NULL; } Plug* Cluster::raddTransPlug ( Net* topNet, Path path, uint32_t flags ) { if (path.isEmpty()) return NULL; if (topNet->getCell() != path.getOwnerCell() ) { throw Error( "Cluster::raddTransPlug(): \"topNet\" and \"path\" must belong to the same cell.\n" " * \"topNet\" is owned by %s\n" " * \"path\" is owned by %s" , getString(topNet->getCell()).c_str() , getString(path.getOwnerCell()).c_str() ); } Path tailPath = path.getTailPath(); Instance* headInstance = path.getHeadInstance(); Plug* headPlug = getPlugByNet( headInstance, topNet ); Net* masterNet = NULL; if (not headPlug) { Cell* masterCell = headInstance->getMasterCell(); masterNet = masterCell->getNet( topNet->getName() ); if (not masterNet) { masterNet = Net::create( masterCell, topNet->getName() ); masterNet->setType ( topNet->getType() ); } uint32_t direction = masterNet->getDirection(); if (flags & AS_DRIVER) direction |= Net::Direction::OUT; if (flags & AS_SINK ) direction |= Net::Direction::IN; masterNet->setDirection( (Net::Direction::Code)direction ); masterNet->setExternal( true ); headPlug = headInstance->getPlug( masterNet ); if (not headPlug) throw Error( "Cluster::raddTransPlug(): Plug not created for \"%s\" on instance \"%s\" of \"%s\"" , getString(topNet->getName()).c_str() , getString(headInstance->getName()).c_str() , getString(masterCell->getName()).c_str() ); headPlug->setNet( topNet ); } else { masterNet = headPlug->getMasterNet(); } if (not tailPath.isEmpty()) headPlug = raddTransPlug( masterNet, tailPath, flags ); return headPlug; } Net* Cluster::raddTransNet ( Net* topNet, Path path, uint32_t flags ) { if (path.isEmpty()) return topNet; return raddTransPlug( topNet, path, flags )->getMasterNet(); } void Cluster::createInput ( Net* upDriver ) { Cell* topCell = _etesian->getCell(); Cell* cellPnR = _etesian->getBlockCell(); Instance* instancePnR = _etesian->getBlockInstance(); BufferDatas* bufferDatas = _etesian->getBufferCells().getBiggestBuffer(); Net* blockNet = upDriver; Path inputPath = Path(); if (topCell != cellPnR) { inputPath = Path( instancePnR ); blockNet = raddTransNet( upDriver, inputPath, AS_SINK ); } Plug* inputPlug = bufferDatas->getInput( _buffer ); inputPlug->setNet( blockNet ); RoutingPad::create( upDriver, Occurrence(inputPlug,inputPath), RoutingPad::BiggestArea ); } void Cluster::createOutput () { Cell* topCell = _etesian->getCell(); Cell* cellPnR = _etesian->getBlockCell(); Instance* instancePnR = _etesian->getBlockInstance(); BufferDatas* bufferDatas = _etesian->getBufferCells().getBiggestBuffer(); string driverName = getSubNetNames()->getSubNetName(); Net* blockNet = NULL; Path outputPath = Path(); driverName.insert( 0, "cmpt_" ); _buffer = Instance::create( cellPnR, driverName, bufferDatas->getCell() ); getSubNetNames()->nextSubNet(); _driverNet = Net::create( topCell, driverName ); _driverNet->setDirection( Net::Direction::OUT ); if (topCell == cellPnR) blockNet = _driverNet; else { outputPath = Path( instancePnR ); blockNet = raddTransNet( _driverNet, outputPath, AS_DRIVER ); } Plug* outputPlug = bufferDatas->getOutput( _buffer ); outputPlug->setNet( blockNet ); RoutingPad::create( _driverNet, Occurrence(outputPlug,outputPath), RoutingPad::BiggestArea ); } void Cluster::splitNet () { createOutput(); for ( Cluster* cluster : _clusters ) { cluster->createInput( _driverNet ); } for ( RoutingPad* rp : _rps ) { Occurrence plugOcc = rp->getPlugOccurrence(); Net* deepNet = raddTransNet( _driverNet, plugOcc.getPath(), AS_SINK ); if (dynamic_cast(plugOcc.getEntity())) { continue; } Plug* deepSinkPlug = dynamic_cast( plugOcc.getEntity() ); deepSinkPlug->setNet( deepNet ); rp->destroy(); RoutingPad::create( _driverNet, plugOcc, RoutingPad::BiggestArea ); } } string Cluster::_getTypeName () const { return "Cluster"; } string Cluster::_getString () const { string s = "<" + _getTypeName() + " "; s += getString(_rps.size()) + "+" + getString(_clusters.size()) + ">"; return s; } // ------------------------------------------------------------------- // Class : "::BufferTree". class BufferTree : public Cluster { public: BufferTree ( EtesianEngine*, Net* ); virtual ~BufferTree (); virtual Cluster* getParent () const; virtual SubNetNames* getSubNetNames (); virtual void splitNet (); void rpartition (); uint32_t build (); string _getTypeName () const; private: SubNetNames _subNetNames; bool _isDeepNet; Net* _rootNet; RoutingPad* _rpDriver; vector< vector > _clustersStack; }; BufferTree::BufferTree ( EtesianEngine* etesian, Net* rootNet ) : Cluster(etesian) , _subNetNames () , _isDeepNet (true) , _rootNet (rootNet) , _rpDriver (NULL) , _clustersStack() { _subNetNames.match( getString(rootNet->getName()) ); cdebug_log(123,0) << "BufferTree CTOR _clustersStack.size()=" << _clustersStack.size() << endl; } BufferTree::~BufferTree () { for ( vector& clusters : _clustersStack ) { for ( Cluster* cluster : clusters ) { if (dynamic_cast(cluster)) continue; delete cluster; } } } Cluster* BufferTree::getParent () const { return NULL; } SubNetNames* BufferTree::getSubNetNames () { return &_subNetNames; } void BufferTree::splitNet () { if (not _rpDriver) throw Error( "BufferTree::splitNet(): Missing driver on %s.", getString(_rootNet).c_str() ); Cluster::splitNet(); if (_isDeepNet) { Cell* topCell = getEtesian()->getCell(); Name topNetName = _rootNet->getName(); Occurrence driverRpOcc = _rpDriver->getPlugOccurrence(); _rootNet->destroy(); _rootNet = Net::create( topCell, topNetName ); Net* deepDriverNet = raddTransNet( _rootNet, driverRpOcc.getPath(), AS_DRIVER ); dynamic_cast( driverRpOcc.getEntity() )->setNet( deepDriverNet ); RoutingPad::create( _rootNet, driverRpOcc, RoutingPad::BiggestArea ); } createInput( _rootNet ); } void BufferTree::rpartition () { cdebug_log(123,1) << "BufferTree::rpartition()" << endl; _clustersStack.push_back( vector() ); cdebug_log(123,0) << "_clustersStack.size()=" << _clustersStack.size() << endl; _clustersStack.back().push_back( new Cluster(getEtesian()) ); cdebug_log(123,0) << "_clustersStack[0].size()=" << _clustersStack.back().size() << endl; BufferDatas* bufferDatas = getEtesian()->getBufferCells().getBiggestBuffer(); RoutingPad* rpPin = NULL; for ( RoutingPad* rp : _rootNet->getRoutingPads() ) { Occurrence rpOccurrence = rp->getPlugOccurrence(); if (rpOccurrence.getPath().isEmpty()) _isDeepNet = false; Pin* pin = dynamic_cast( rpOccurrence.getEntity() ); if (pin) { if (not rpPin) { if (dynamic_cast( rpOccurrence.getEntity() )) { rpPin = rp; } } cdebug_log(123,0) << "Excluded: " << pin << endl; continue; } Plug* rpPlug = dynamic_cast( rpOccurrence.getEntity() ); Net* masterNet = rpPlug->getMasterNet(); if (masterNet->getDirection() & Net::Direction::DirIn) { if(_clustersStack[0].back()->getSize() >= bufferDatas->getMaxSinks()) { _clustersStack[0].push_back( new Cluster(getEtesian()) ); cdebug_log(123,0) << "_clustersStack[0].size()=" << _clustersStack[0].size() << endl; } cdebug_log(123,0) << "merge: " << _clustersStack[0].back()->getSize() << " " << rp << endl; _clustersStack[0].back()->merge( rp ); } else { _rpDriver = rp; } } if (rpPin) { if (not _rpDriver) { _rpDriver = rpPin; } else { _clustersStack[0].back()->merge( rpPin ); } } if (_clustersStack[0].size() == 1) { _clustersStack[0].back()->swapRps( this ); _clustersStack[0].clear(); _clustersStack[0].push_back( this ); cdebug_log(123,0) << "One cluster special case." << endl; cdebug_tabw(123,-1); return; } size_t depth = 0; while ( _clustersStack[depth].size() > 1) { cdebug_log(123,0) << "New depth " << (depth+1) << endl; _clustersStack.push_back( vector() ); if (_clustersStack[depth].size() <= bufferDatas->getMaxSinks()) { _clustersStack[depth+1].push_back( this ); cdebug_log(123,0) << "TOP cluster" << endl; } else _clustersStack[depth+1].push_back( new Cluster(getEtesian()) ); cdebug_log(123,0) << "CLUSTER _clustersStack[" << depth << "].size()=" << _clustersStack[depth].size() << endl; for ( Cluster* cluster : _clustersStack[depth] ) { if(_clustersStack[depth+1].back()->getSize() >= bufferDatas->getMaxSinks()) { _clustersStack[depth+1].push_back( new Cluster(getEtesian()) ); cdebug_log(123,0) << "_clustersStack[" << (depth+1) << "].size()=" << _clustersStack[depth+1].size() << endl; } cdebug_log(123,0) << "merge: " << _clustersStack[depth+1].back()->getSize() << " " << cluster << endl; _clustersStack[depth+1].back()->merge( cluster ); } ++depth; } cdebug_tabw(123,-1); } uint32_t BufferTree::build () { uint32_t bufferCount = 0; rpartition(); for ( vector& clusters : _clustersStack ) { for ( Cluster* cluster : clusters ) { cluster->splitNet(); ++bufferCount; } } return bufferCount; } string BufferTree::_getTypeName () const { return "BufferTree"; } uint32_t EtesianEngine::doHFNS () { cmess2 << " - High Fanout Net Synthesis (HFNS)." << endl; startMeasures(); BufferDatas* bufferDatas = getBufferCells().getBiggestBuffer(); vector< tuple > netDatas; for ( Net* net : getCell()->getNets() ) { uint32_t rpCount = 0; for ( RoutingPad* rp : net->getRoutingPads() ) { Occurrence rpOcc = rp->getPlugOccurrence(); Pin* pin = dynamic_cast( rpOcc.getEntity() ); if (pin) { continue; } if (getBlockInstance()) { if (rpOcc.getPath().getHeadInstance() != getBlockInstance()) { continue; } } Plug* rpPlug = dynamic_cast( rpOcc.getEntity() ); Net* masterNet = rpPlug->getMasterNet(); if (masterNet->getDirection() & Net::Direction::DirIn) { ++rpCount; } } if (rpCount > bufferDatas->getMaxSinks()) { netDatas.push_back( make_tuple(net,rpCount) ); } } UpdateSession::open(); Go::disableAutoMaterialization(); _bufferCount = 0; for ( size_t i=0 ; i( netDatas[i] ) << "]"; Net* net = std::get<0>( netDatas[i] ); BufferTree tree ( this, net ); _bufferCount += tree.build(); } cmess2 << endl; // for ( tuple& netData : netDatas ) { // Net* net = std::get<0>(netData); // cmess2 << " Net \"" << net->getName() << "\" has " << std::get<1>(netData) << " sinks." << endl; // BufferTree tree ( this, net ); // _bufferCount += tree.build(); // } Go::enableAutoMaterialization(); UpdateSession::close(); stopMeasures(); printMeasures(); cmess2 << " - Total added buffers " << _bufferCount << endl; return _bufferCount; } } // Etesian namespace. GETSTRING_POINTER_SUPPORT(Etesian::Cluster); GETSTRING_POINTER_SUPPORT(Etesian::BufferTree);