// -*- C++ -*- // // This file is part of the Coriolis Software. // Copyright (c) Sorbonne Université 2014-2022, 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 : "./EtesianEngine.cpp" | // +-----------------------------------------------------------------+ #include #include #include #include "hurricane/configuration/Configuration.h" #include "hurricane/utilities/Dots.h" #include "hurricane/DebugSession.h" #include "hurricane/Bug.h" #include "hurricane/Error.h" #include "hurricane/Warning.h" #include "hurricane/Breakpoint.h" #include "hurricane/DataBase.h" #include "hurricane/Layer.h" #include "hurricane/Net.h" #include "hurricane/Pad.h" #include "hurricane/Pin.h" #include "hurricane/Plug.h" #include "hurricane/Cell.h" #include "hurricane/Occurrence.h" #include "hurricane/Instance.h" #include "hurricane/Vertical.h" #include "hurricane/Horizontal.h" #include "hurricane/RoutingPad.h" #include "hurricane/NetExternalComponents.h" #include "hurricane/UpdateSession.h" #include "hurricane/viewer/CellWidget.h" #include "hurricane/viewer/CellViewer.h" #include "crlcore/Utilities.h" #include "crlcore/Measures.h" #include "crlcore/Histogram.h" #include "crlcore/AllianceFramework.h" #include "etesian/EtesianEngine.h" namespace { using namespace std; using namespace Hurricane; using Etesian::EtesianEngine; using coloquinte::CellOrientation; using coloquinte::CellRowPolarity; Instance* extractInstance ( const RoutingPad* rp ) { return rp->getOccurrence().getPath().getTailInstance(); } string extractInstanceName ( const RoutingPad* rp ) { ostringstream name; Occurrence occurrence = rp->getOccurrence(); name << getString(occurrence.getOwnerCell()->getName()) << ':'; if (not rp->getOccurrence().getPath().getHeadPath().isEmpty()) name << getString(rp->getOccurrence().getPath().getHeadPath().getName()) << ":"; if (rp->getOccurrence().getPath().getTailInstance()) name << "I." << getString(rp->getOccurrence().getPath().getTailInstance()->getName()); else name << "C"; return name.str(); } #if THIS_IS_DISABLED string extractPinName ( const RoutingPad* rp ) { ostringstream name; Occurrence occurrence = rp->getOccurrence(); name << getString(occurrence.getOwnerCell()->getName()) << ':'; if (not rp->getOccurrence().getPath().isEmpty()) name << getString(rp->getOccurrence().getPath().getName()) << ":"; name << "T." << getString(rp->_getEntityAsComponent()->getNet()->getName()); return name.str(); } string extractTerminalName ( const RoutingPad* rp ) { ostringstream name; Occurrence occurrence = rp->getOccurrence(); name << getString(occurrence.getOwnerCell()->getName()) << ':'; if (not rp->getOccurrence().getPath().isEmpty()) name << getString(rp->getOccurrence().getPath().getName()) << ":"; name << "T." << getString(rp->_getEntityAsComponent()->getNet()->getName()); return name.str(); } #endif Point extractRpOffset ( const RoutingPad* rp ) { Cell* masterCell = rp->getOccurrence().getMasterCell(); Component* component = rp->_getEntityAs(); // TODO: verify that it doesn't assume that the orientation is North Box masterBox = masterCell->getAbutmentBox(); Point offset; if (component) { offset.setX( component->getCenter().getX() - masterBox.getXMin() ); offset.setY( component->getCenter().getY() - masterBox.getYMin() ); } else { // Why? offset = masterBox.getCenter(); } return offset; } Transformation toTransformation ( coloquinte::Point position , coloquinte::CellOrientation orientation , Cell* model , DbU::Unit hpitch , DbU::Unit vpitch ) { DbU::Unit tx = position.x * vpitch; DbU::Unit ty = position.y * hpitch; Box cellBox = model->getAbutmentBox(); Transformation::Orientation orient = Transformation::Orientation::ID; switch (orientation) { case CellOrientation::N: orient = Transformation::Orientation::ID; break; case CellOrientation::R180: ty += cellBox.getHeight(); tx += cellBox.getWidth(); orient = Transformation::Orientation::R2; break; case CellOrientation::MX: orient = Transformation::Orientation::MY; ty += cellBox.getHeight(); break; case CellOrientation::MY: orient = Transformation::Orientation::MX; tx += cellBox.getWidth(); break; /* case CellOrientation::R90: orient = Transformation::Orientation::R1; // TODO break; case CellOrientation::R270: orient = Transformation::Orientation::R3; // TODO break; case CellOrientation::MX90: orient = Transformation::Orientation::XR; // TODO break; case CellOrientation::MY90: orient = Transformation::Orientation::YR; // TODO break; */ default: throw Error("Unsupported orientation " + toString(orientation)); } return Transformation( tx, ty, orient ); } uint32_t getOutputSide ( Cell* cell ) { static map cache; auto icache = cache.find( cell ); if (icache != cache.end()) return (*icache).second; Net* output = NULL; for ( Net* net : cell->getNets() ) { if (net->isSupply()) continue; if (net->getDirection() & Net::Direction::DirOut) { output = net; break; } } if (not output) return EtesianEngine::RightSide; Box ab = cell->getAbutmentBox(); DbU::Unit left = ab.getWidth(); DbU::Unit right = ab.getWidth(); for ( Component* comp : NetExternalComponents::get(output) ) { Box bb = comp->getBoundingBox(); DbU::Unit distance = bb.getXMin() - ab.getXMin(); left = std::min( left, distance ); distance = ab.getXMax() - bb.getXMax(); right = std::min( right, distance ); } uint32_t flags = (left < right) ? EtesianEngine::LeftSide : EtesianEngine::RightSide; cache[ cell ] = flags; return flags; } } // Anonymous namespace. namespace Etesian { using std::cout; using std::cerr; using std::endl; using std::setw; using std::left; using std::string; using std::ostream; using std::ofstream; using std::ostringstream; using std::setprecision; using std::vector; using std::pair; using std::make_pair; using std::unordered_map; using Utilities::Dots; using Hurricane::DebugSession; using Hurricane::tab; using Hurricane::ForEachIterator; using Hurricane::Bug; using Hurricane::Error; using Hurricane::Warning; using Hurricane::Breakpoint; using Hurricane::Box; using Hurricane::DataBase; using Hurricane::Layer; using Hurricane::Cell; using Hurricane::Instance; using Hurricane::Pin; using Hurricane::RoutingPad; using Hurricane::Net; using Hurricane::Occurrence; using Hurricane::CellWidget; using CRL::ToolEngine; using CRL::AllianceFramework; using CRL::Catalog; using CRL::addMeasure; using CRL::Measures; using CRL::MeasuresSet; using CRL::CatalogExtension; const char* missingEtesian = "%s :\n\n" " Cell %s do not have any EtesianEngine (or not yet created).\n"; // ------------------------------------------------------------------- // Class : "Etesian::EtesianEngine". Name EtesianEngine::_toolName = "Etesian"; const Name& EtesianEngine::staticGetName () { return _toolName; } EtesianEngine* EtesianEngine::get ( const Cell* cell ) { return static_cast(ToolEngine::get(cell,staticGetName())); } EtesianEngine::EtesianEngine ( Cell* cell ) : Super (cell) , _configuration(new Configuration()) , _block (NULL) , _ySpinSet (false) , _flatDesign (false) , _placeArea (cell->getAbutmentBox()) , _trackAvoids () , _surface (NULL) , _circuit (NULL) , _placementLB (NULL) , _placementUB (NULL) , _instsToIds () , _idsToInsts () , _viewer (NULL) , _diodeCell (NULL) , _feedCells (this) , _bufferCells (this) , _bloatCells (this) , _area (NULL) , _yspinSlice0 (0) , _sliceHeight (0) , _fixedAbHeight(0) , _fixedAbWidth (0) , _diodeCount (0) , _bufferCount (0) , _excludedNets () { } void EtesianEngine::_postCreate () { Super::_postCreate(); // Ugly: Name based detection of ISPD benchmarks. // Maybe base it on whether the imported library is in Bookshelf format or not? if (getString(getCell()->getName()).substr(0,7) == "bigblue") { cmess2 << " o ISPD benchmark <" << getCell()->getName() << ">, no feed cells will be added." << endl; } else { _diodeCell = DataBase::getDB()->getCell( getConfiguration()->getDiodeName() );; if (not _diodeCell) { cerr << Warning( "EtesianEngine::_postCreate() Unable to find \"%s\" diode cell." , getConfiguration()->getDiodeName().c_str() ) << endl; } _bloatCells.select( getConfiguration()->getBloat() ); string bufferName = getConfiguration()->getSpareBufferName();; Cell* buffer = DataBase::getDB()->getCell( bufferName ); if (buffer) { _bufferCells.useBuffer( buffer, 8, 20 ); } else { cerr << Warning( "EtesianEngine::_postCreate() Unable to find \"%s\" spare buffer cell." , bufferName.c_str() ) << endl; } string feedNames = getConfiguration()->getFeedNames(); char separator = ','; if (feedNames.empty()) { cerr << Warning( "EtesianEngine::_postCreate() No feed cells configured.\n" " (please configure \"cfg.etesian.feedNames\")" ) << endl; } while ( not feedNames.empty() ) { size_t cut = feedNames.find( separator ); string feedName; if (cut != string::npos) { feedName = feedNames.substr( 0, cut ); feedNames = feedNames.substr( cut+1 ); } else { feedName = feedNames; feedNames.clear(); } Cell* feed = DataBase::getDB()->getCell( feedName ); if (not feed) feed = AllianceFramework::get()->getCell( feedName, Catalog::State::Views|Catalog::State::Foreign ); if (feed) _feedCells.useFeed( feed ); else cerr << Warning( "EtesianEngine::_postCreate() Unable to find \"%s\" feed cell." , feedName.c_str() ) << endl; } string tieName = getConfiguration()->getTieName(); Cell* tie = DataBase::getDB()->getCell( tieName ); if (not tie) tie = AllianceFramework::get()->getCell( tieName, Catalog::State::Views|Catalog::State::Foreign ); if (tie) _feedCells.useTie( tie ); else cerr << Warning( "EtesianEngine::_postCreate() Unable to find \"%s\" tie cell." , tieName.c_str() ) << endl; } _sliceHeight = getCellGauge()->getSliceHeight(); loadLeafCellLayouts(); } EtesianEngine* EtesianEngine::create ( Cell* cell ) { if (not cell) throw Error( "EtesianEngine::create(): NULL cell argument." ); EtesianEngine* etesian = new EtesianEngine ( cell ); etesian->_postCreate(); return etesian; } void EtesianEngine::_preDestroy () { cdebug_log(120,1) << "EtesianEngine::_preDestroy()" << endl; cmess1 << " o Deleting ToolEngine<" << getName() << "> from Cell <" << getCell()->getName() << ">" << endl; Super::_preDestroy(); cdebug.log(120,-1); } EtesianEngine::~EtesianEngine () { clearColoquinte(); delete _area; delete _configuration; } void EtesianEngine::clearColoquinte () { cmess1 << " o Clearing Coloquinte data-structures on <" << getCell()->getName() << ">" << endl; delete _surface; delete _circuit; delete _placementLB; delete _placementUB; InstancesToIds emptyInstsToIds; _instsToIds.swap( emptyInstsToIds ); vector emptyIdsToInsts; _idsToInsts.swap( emptyIdsToInsts ); _surface = NULL; _circuit = NULL; _placementLB = NULL; _placementUB = NULL; _diodeCount = 0; } const Name& EtesianEngine::getName () const { return _toolName; } const Configuration* EtesianEngine::getConfiguration () const { return _configuration; } Configuration* EtesianEngine::getConfiguration () { return _configuration; } void EtesianEngine::setPlaceArea ( const Box& placeArea ) { Box topAb = getBlockCell()->getAbutmentBox(); if (not topAb.contains(placeArea)) { cerr << Warning( "EtesianEngine::setPlaceArea(): placedArea is not *fully* inside topAb, trucating.\n" " * placedArea=%s\n" " * topAb=%s\n" , getString(placeArea).c_str() , getString(topAb).c_str() ) << endl; _placeArea = topAb.getIntersection( placeArea ); } DbU::Unit sliceHeight = getSliceHeight(); _placeArea = Box( DbU::toCeil ( placeArea.getXMin(), sliceHeight ) , DbU::toCeil ( placeArea.getYMin(), sliceHeight ) , DbU::toFloor( placeArea.getXMax(), sliceHeight ) , DbU::toFloor( placeArea.getYMax(), sliceHeight ) ); size_t bottomSlice = (_placeArea.getYMin() - topAb.getYMin()) / sliceHeight; if (bottomSlice % 2) throw Error( "EtesianEngine::setPlaceArea(): placedArea bottom must be on an even slice relative to topAb.\n" " * placedArea=%s\n" " * topAb=%s\n" " * bottomSlice=%u\n" , getString(placeArea).c_str() , getString(topAb).c_str() , bottomSlice ); } void EtesianEngine::setDefaultAb () { _bloatCells.resetDxSpace(); double spaceMargin = getSpaceMargin(); double aspectRatio = getAspectRatio(); size_t instanceNb = 0; DbU::Unit cellLength = 0; cmess2 << " o Looking through the hierarchy." << endl; vector feedOccurrences; for( Occurrence occurrence : getCell()->getTerminalNetlistInstanceOccurrences() ) { Instance* instance = static_cast(occurrence.getEntity()); Cell* masterCell = instance->getMasterCell(); string instanceName = occurrence.getCompactString(); if (CatalogExtension::isFeed(masterCell)) { cerr << Warning( "Found a feedcell %s in an unplaced design, removing." , instanceName.c_str() ) << endl; feedOccurrences.push_back( occurrence ); continue; } if (masterCell->getAbutmentBox().getHeight() % getSliceHeight() != 0) { cmess2 << " - Using as block: " << occurrence.getCompactString() << "." << endl; cerr << Error( "EtesianEngine::setDefaultAb(): Cell not aligned on the slice height, \"%s\"." , getString(instance->getName()).c_str() ) << endl; } DbU::Unit nbRows = masterCell->getAbutmentBox().getHeight() / getSliceHeight(); cellLength += nbRows * _bloatCells.getAb( occurrence ).getWidth() ; instanceNb += 1; } if (cellLength == 0) { throw Error( "EtesianEngine::setDefaultAb(): Null surface area computed for \"%s\" (are physical views loaded?)." , getString(getCell()->getName()).c_str() ); } if (cellLength < 0) throw Error( "EtesianEngine::setDefaultAb(): Negative surface area computed for \"%s\" (bad bloat profile?)." , getString(getCell()->getName()).c_str() ); DbU::Unit bloatLength = _bloatCells.getDxSpace(); double bloatMargin = ( (double)cellLength / (double)(cellLength - bloatLength) ) - 1.0; DbU::Unit gcellLength = (double)cellLength*(1.0+spaceMargin) / (double)getSliceHeight(); if (gcellLength == 0) { throw Error( "EtesianEngine::setDefaultAb(): Null g-length for \"%s\" (are you using the right gauge?)." , getString(getCell()->getName()).c_str() ); } double rows = 0.0; double columns = 0.0; if (not getFixedAbWidth()) { if (getFixedAbHeight()) rows = getFixedAbHeight() / getSliceHeight(); else rows = std::ceil( sqrt( (double)gcellLength/aspectRatio ) ); columns = std::ceil( (double)gcellLength / rows ); } else { columns = getFixedAbWidth() / getSliceHeight(); rows = std::ceil( (double)gcellLength / columns ); } UpdateSession::open(); for ( auto occurrence : feedOccurrences ) { static_cast(occurrence.getEntity())->destroy(); } DbU::Unit abWidth = columns*getSliceHeight(); DbU::Unit adjust = abWidth % getSliceStep(); if (adjust) abWidth += getSliceStep() - adjust; getCell()->setAbutmentBox( Box( 0 , 0 , abWidth , rows*getSliceHeight() ) ); _placeArea = getCell()->getAbutmentBox(); UpdateSession::close(); //if (_viewer) _viewer->getCellWidget()->fitToContents(); cmess1 << " o Creating abutment box (margin:" << (spaceMargin*100.0) << "% aspect ratio:" << (aspectRatio*100.0) << "% g-length:" << DbU::getValueString(cellLength) << ")" << endl; if (getFixedAbHeight()) cmess1 << " - Fixed AB height: " << DbU::getValueString(getFixedAbHeight()) << endl; if (getFixedAbWidth()) cmess1 << " - Fixed AB width: " << DbU::getValueString(getFixedAbWidth()) << endl; cmess1 << " - Bloat space margin: " << setprecision(4) << (bloatMargin*100.0) << "%." << endl; cmess1 << " - " << getCell()->getAbutmentBox() << endl; cmess1 << " - GCell grid: [" << (int)columns << "x" << (int)rows << "]" << endl; } void EtesianEngine::resetPlacement () { cmess1 << " o Erasing previous placement of <" << getCell()->getName() << ">" << endl; _flatDesign = true; Dots dots ( cmess2, " ", 80, 1000 ); if (not cmess2.enabled()) dots.disable(); Box topAb = getBlockCell()->getAbutmentBox(); Transformation topTransformation; if (getBlockInstance()) topTransformation = getBlockInstance()->getTransformation(); topTransformation.applyOn( topAb ); UpdateSession::open(); for ( Occurrence occurrence : getBlockCell()->getNonTerminalNetlistInstanceOccurrences() ) { Instance* instance = static_cast(occurrence.getEntity()); Cell* masterCell = instance->getMasterCell(); if (not masterCell->getAbutmentBox().isEmpty()) { if ( (instance->getPlacementStatus() != Instance::PlacementStatus::PLACED) and (instance->getPlacementStatus() != Instance::PlacementStatus::FIXED ) ) { throw Error( "EtesianEngine::toColoquinte(): Non-leaf instance \"%s\" of \"%s\" has an abutment box but is *not* placed." , getString(instance ->getName()).c_str() , getString(masterCell->getName()).c_str() ); } else { bool isFullyPlaced = true; for ( Instance* subInstance : masterCell->getInstances() ) { if ( (subInstance->getPlacementStatus() != Instance::PlacementStatus::PLACED) and (subInstance->getPlacementStatus() != Instance::PlacementStatus::FIXED ) ) { isFullyPlaced = false; break; } } if (isFullyPlaced) { masterCell->setTerminalNetlist( true ); } } } if ( masterCell->getAbutmentBox().isEmpty() or ( (masterCell->getAbutmentBox().getHeight() == topAb.getHeight()) and (masterCell->getAbutmentBox().getWidth () == topAb.getWidth ()) ) ) { // Have to check here if the model is fully placed or not. //masterCell->setAbutmentBox( topAb ); //instance->setTransformation( Transformation() ); // (0,0,ID). //instance->setPlacementStatus( Instance::PlacementStatus::PLACED ); occurrence.makeInvalid(); instance->slaveAbutmentBox(); } } vector feedOccurrences; for( Occurrence occurrence : getCell()->getTerminalNetlistInstanceOccurrences(getBlockInstance()) ) { dots.dot(); if ( _flatDesign and not occurrence.getPath().getTailPath().isEmpty()) _flatDesign = true; Instance* instance = static_cast(occurrence.getEntity()); Cell* masterCell = instance->getMasterCell(); string instanceName = occurrence.getCompactString(); if (CatalogExtension::isFeed(masterCell)) feedOccurrences.push_back( occurrence ); } for ( auto occurrence : feedOccurrences ) { Instance* instance = static_cast(occurrence.getEntity()); instance->destroy(); } // if (not getBlockCell()->getAbutmentBox().isEmpty() ) // setFixedAbHeight( getBlockCell()->getAbutmentBox().getHeight() ); getBlockCell()->setAbutmentBox( Box() ); getBlockCell()->resetFlags( Cell::Flags::Placed ); _placeArea.makeEmpty(); UpdateSession::close(); dots.finish( Dots::Reset ); if (_viewer) _viewer->getCellWidget()->refresh(); } size_t EtesianEngine::toColoquinte () { clearColoquinte(); AllianceFramework* af = AllianceFramework::get(); DbU::Unit hpitch = getSliceStep(); DbU::Unit vpitch = getSliceStep(); DbU::Unit sliceHeight = getSliceHeight(); bool isFlexLib = (getGauge()->getName() == "FlexLib"); CRL::Histogram stdCellSizes ( 0.0, 1.0, 2 ); stdCellSizes.setTitle ( "Width" , 0 ); stdCellSizes.setColor ( "green" , 0 ); stdCellSizes.setIndent( " ", 0 ); stdCellSizes.setTitle ( "Bloat" , 1 ); stdCellSizes.setColor ( "red" , 1 ); stdCellSizes.setIndent( " ", 1 ); cmess1 << " o Converting \"" << getCell()->getName() << "\" into Coloquinte." << endl; cmess1 << ::Dots::asString(" - H-pitch" , DbU::getValueString(hpitch)) << endl; cmess1 << ::Dots::asString(" - V-pitch" , DbU::getValueString(vpitch)) << endl; cmess1 << ::Dots::asString(" - Slice height" , DbU::getValueString(sliceHeight)) << endl; if (isFlexLib) cmess1 << ::Dots::asString(" - Using patches for" , "\"FlexLib\"") << endl; cmess2 << " o Looking through the hierarchy." << endl; cmess2 << " - Whole place area: " << getBlockCell()->getAbutmentBox() << "." << endl; cmess2 << " - Sub-place Area: " << _placeArea << "." << endl; DbU::Unit totalLength = (_placeArea.getHeight() / sliceHeight) * _placeArea.getWidth(); DbU::Unit usedLength = 0; DbU::Unit registerLength = 0; Dots dots ( cmess2, " ", 80, 1000 ); if (not cmess2.enabled()) dots.disable(); size_t instancesNb = 0; size_t fixedNb = 0; size_t registerNb = 0; Box topAb = _placeArea; Transformation topTransformation; if (getBlockInstance()) { topTransformation = getBlockInstance()->getTransformation(); topTransformation.applyOn( topAb ); for ( Instance* instance : getCell()->getInstances() ) { if (instance == getBlockInstance()) continue; string masterName = getString( instance->getMasterCell()->getName() ); Box instanceAb = instance->getAbutmentBox(); if (af->isRegister(masterName)) { ++registerNb; registerLength += instanceAb.getWidth(); } if (instance->getPlacementStatus() == Instance::PlacementStatus::FIXED) { if (topAb.intersect(instanceAb)) { ++instancesNb; ++fixedNb; totalLength -= (instanceAb.getHeight() / sliceHeight) * instanceAb.getWidth(); } } if (instance->getPlacementStatus() == Instance::PlacementStatus::PLACED) { cerr << "PLACED " << instance << endl; } } } _surface = new coloquinte::Rectangle( (int)(topAb.getXMin() / hpitch) , (int)(topAb.getXMax() / hpitch) , (int)(topAb.getYMin() / vpitch) , (int)(topAb.getYMax() / vpitch) ); for ( Occurrence occurrence : getCell()->getTerminalNetlistInstanceOccurrences(getBlockInstance()) ) { ++instancesNb; Instance* instance = static_cast(occurrence.getEntity()); Box instanceAb = _bloatCells.getAb( occurrence ); string masterName = getString( instance->getMasterCell()->getName() ); DbU::Unit length = (instanceAb.getHeight() / sliceHeight) * instanceAb.getWidth(); if (af->isRegister(masterName)) { ++registerNb; registerLength += length; } if (instance->getPlacementStatus() == Instance::PlacementStatus::FIXED) { ++fixedNb; totalLength -= length; } else { usedLength += length; if (instance->getPlacementStatus() == Instance::PlacementStatus::PLACED) { cerr << "PLACED " << instance << endl; } } } if (instancesNb <= fixedNb) { cerr << Error( "EtesianEngine::toColoquinte(): \"%s\" has no instance to place, doing nothing." , getString(getCell()->getName()).c_str() ) << endl; return 0; } cmess1 << ::Dots::asPercentage( " - Effective space margin" , (float)(totalLength-usedLength)/(float)totalLength ) << endl; cmess1 << ::Dots::asUInt( " - Number of instances ", instancesNb ) << endl; if (instancesNb) { float ratio = ((float)registerNb / (float)instancesNb) * 100.0; ostringstream os1; os1 << registerNb << " (" << fixed << setprecision(2) << ratio << "%)"; cmess1 << ::Dots::asString ( " - Registers (DFF) ", os1.str() ) << endl; cmess1 << ::Dots::asPercentage( " - Registers (DFF) area " , (float)(registerLength)/(float)totalLength ) << endl; ratio = ((float)_bufferCount / (float)instancesNb) * 100.0; ostringstream os2; os2 << _bufferCount << " (" << fixed << setprecision(2) << ratio << "%)"; cmess1 << ::Dots::asString( " - Buffers ", os2.str() ) << endl; } cout.flush(); // Coloquinte circuit description data-structures. // One dummy fixed instance at the end _circuit = new coloquinte::Circuit( instancesNb+1 ); vector cellX( instancesNb+1 ); vector cellY( instancesNb+1 ); vector orient( instancesNb+1 ); vector cellWidth( instancesNb+1 ); vector cellHeight( instancesNb+1 ); vector cellIsFixed( instancesNb+1 ); vector cellIsObstruction( instancesNb+1 ); vector cellRowPolarity( instancesNb+1, CellRowPolarity::SAME ); cmess1 << " - Building RoutingPads (transhierarchical)" << endl; //getCell()->flattenNets( Cell::Flags::BuildRings|Cell::Flags::NoClockFlatten ); //getCell()->flattenNets( getBlockInstance(), Cell::Flags::NoClockFlatten ); getCell()->flattenNets( NULL, _excludedNets, Cell::Flags::NoClockFlatten ); int instanceId = 0; if (getBlockInstance()) { for ( Instance* instance : getCell()->getInstances() ) { if (instance == getBlockInstance()) continue; if (instance->getPlacementStatus() == Instance::PlacementStatus::FIXED) { Box overlapAb = topAb.getIntersection( instance->getAbutmentBox() ); if (not overlapAb.isEmpty()) { // Upper rounded int xsize = (overlapAb.getWidth () + hpitch - 1) / hpitch; int ysize = (overlapAb.getHeight() + vpitch - 1) / vpitch; // Lower rounded int xpos = overlapAb.getXMin() / hpitch; int ypos = overlapAb.getYMin() / vpitch; cellX[instanceId] = xpos; cellY[instanceId] = ypos; cellWidth[instanceId] = xsize; cellHeight[instanceId] = ysize; cellIsFixed[instanceId] = true; cellIsObstruction[instanceId] = true; _instsToIds.insert( make_pair(instance,instanceId) ); _idsToInsts.push_back( make_tuple(instance,vector()) ); ++instanceId; dots.dot(); } } } } // Compute a bloat factor to be reach 1 - densityVariation density double bloatFactor = std::max(1.0, (1.0 - getDensityVariation()) * totalLength / usedLength); // Limit the maximum size of cells after bloat to avoid placement issues int maxBloatSize = _surface->width() / 8; if (bloatFactor != 1.0) { ostringstream bf; bf << fixed << setprecision(2) << bloatFactor << "%"; cmess1 << ::Dots::asString( " - Coloquinte cell bloat factor ", bf.str() ) << endl; } int rowHeight = (getSliceHeight() + vpitch - 1) / vpitch; for ( Occurrence occurrence : getCell()->getTerminalNetlistInstanceOccurrences(getBlockInstance()) ) { if (instanceId >= instancesNb) { // This will be an error ++instanceId; continue; } _checkNotAFeed(occurrence); Instance* instance = static_cast(occurrence.getEntity()); Cell* masterCell = instance->getMasterCell(); stdCellSizes.addSample( (float)(masterCell->getAbutmentBox().getWidth() / hpitch), 0 ); Box instanceAb = _bloatCells.getAb( occurrence ); stdCellSizes.addSample( (float)(instanceAb.getWidth() / hpitch), 1 ); Transformation instanceTransf = instance->getTransformation(); occurrence.getPath().getTransformation().applyOn( instanceTransf ); instanceTransf.applyOn( instanceAb ); // Upper rounded int xsize = (instanceAb.getWidth () + hpitch - 1) / hpitch; int ysize = (instanceAb.getHeight() + vpitch - 1) / vpitch; // Lower rounded int xpos = instanceAb.getXMin() / hpitch; int ypos = instanceAb.getYMin() / vpitch; string masterName = getString( instance->getMasterCell()->getName() ); if (isFlexLib and not instance->isFixed() and (masterName == "buf_x8")) ++xsize; // Take bloat into account to compute the size xsize = std::max(xsize, std::min(maxBloatSize, (int) (xsize * bloatFactor))); cellX[instanceId] = xpos; cellY[instanceId] = ypos; cellWidth[instanceId] = xsize; cellHeight[instanceId] = ysize; int nbRows = ysize / rowHeight; if (nbRows % 2 != 1) { cellRowPolarity[instanceId] = CellRowPolarity::NW; } if ( not instance->isFixed() and instance->isTerminalNetlist() ) { cellIsFixed[instanceId] = false; cellIsObstruction[instanceId] = false; } else { cellIsFixed[instanceId] = true; cellIsObstruction[instanceId] = true; } _instsToIds.insert( make_pair(instance,instanceId) ); _idsToInsts.push_back( make_tuple(instance,vector()) ); ++instanceId; dots.dot(); } if (instanceId != instancesNb) { throw Error( "EtesianEngine::toColoquinte(): %d leaf instances, but expected %d\n" " maybe a virtual flattening problem." , instanceId, instancesNb ); } // Dummy fixed instance at the end cellX[instanceId] = 0; cellY[instanceId] = 0; cellWidth[instanceId] = 0; cellHeight[instanceId] = 0; cellIsFixed[instanceId] = true; cellIsObstruction[instanceId] = true; dots.finish( Dots::Reset|Dots::FirstDot ); size_t netsNb = 0; for ( Net* net : getCell()->getNets() ) { const char* excludedType = NULL; if (net->getType() == Net::Type::POWER ) excludedType = "POWER"; if (net->getType() == Net::Type::GROUND) excludedType = "GROUND"; if (net->getType() == Net::Type::CLOCK ) excludedType = "CLOCK"; if (isExcluded(getString(net->getName()))) excludedType = "USER_EXCLUDED"; if (excludedType) { cparanoid << Warning( "%s is not a routable net (%s,excluded)." , getString(net).c_str(), excludedType ) << endl; continue; } if (af->isBLOCKAGE(net->getName())) continue; ++netsNb; } _circuit->setCellX(cellX); _circuit->setCellY(cellY); _circuit->setCellOrientation(orient); _circuit->setCellWidth(cellWidth); _circuit->setCellHeight(cellHeight); _circuit->setCellIsFixed(cellIsFixed); _circuit->setCellIsObstruction(cellIsObstruction); _circuit->setCellRowPolarity(cellRowPolarity); cmess1 << " - Converting " << netsNb << " nets" << endl; for ( Net* net : getCell()->getNets() ) { const char* excludedType = NULL; if (net->getType() == Net::Type::POWER ) excludedType = "POWER"; if (net->getType() == Net::Type::GROUND) excludedType = "GROUND"; if (net->getType() == Net::Type::CLOCK ) excludedType = "CLOCK"; if (isExcluded(getString(net->getName()))) excludedType = "USER_EXCLUDED"; if (excludedType) continue; if (af->isBLOCKAGE(net->getName())) continue; dots.dot(); string topCellInstancePin = getString(getCell()->getName()) + ":C"; vector netCells, pinX, pinY; for ( RoutingPad* rp : net->getRoutingPads() ) { Path path = rp->getOccurrence().getPath(); Pin* pin = dynamic_cast( rp->getOccurrence().getEntity() ); if (pin) { if (path.isEmpty()) { Point pt = rp->getCenter(); int xpin = pt.getX() / hpitch; int ypin = pt.getY() / vpitch; // Dummy last instance pinX.push_back(xpin); pinY.push_back(ypin); netCells.push_back(instanceId); } continue; } if (getBlockInstance()) { // For Gabriel Gouvine : if there are multiple blocks (i.e. we have a true // floorplan, there may be RoutingPad that are elsewhere. We should check // that the RP is placed or is inside a define area (the abutment box of // it's own block). No example yet of that case, though. if (path.getHeadInstance() != getBlockInstance()) { cerr << Warning( "EtesianEngine::toColoquinte(): Net %s has a RoutingPad that is not rooted at the placed instance.\n" " * Placed instance: %s\n" " * RoutingPad: %s" , getString(net).c_str() , getString(getBlockInstance()).c_str() , getString(rp->getOccurrence()).c_str() ) << endl; continue; } } Instance* instance = extractInstance ( rp ); Point offset = extractRpOffset ( rp ); int xpin = offset.getX() / hpitch; int ypin = offset.getY() / vpitch; auto iid = _instsToIds.find( instance ); if (iid == _instsToIds.end()) { if (not instance) { string insName = extractInstanceName( rp ); cerr << Error( "Unable to lookup instance \"%s\".", insName.c_str() ) << endl; } } else { pinX.push_back(xpin); pinY.push_back(ypin); netCells.push_back((*iid).second); } } _circuit->addNet(netCells, pinX, pinY); } dots.finish( Dots::Reset ); cmess1 << " - Standard cells widths:" << endl; cmess2 << stdCellSizes.toString(0) << endl; if (_bloatCells.getSelected()->getName() != "disabled") cmess2 << stdCellSizes.toString(1) << endl; _circuit->setupRows(*_surface, rowHeight); _circuit->check(); _placementLB = new coloquinte::PlacementSolution (); _placementUB = new coloquinte::PlacementSolution ( *_placementLB ); return instancesNb-fixedNb; } void EtesianEngine::adjustSliceHeight () { /* * Modify the slice height if it doesn't match the one given by the Alliance Framework. * Useful for Bookshelf benchmarks */ std::set heights; for ( Occurrence occurrence : getCell()->getTerminalNetlistInstanceOccurrences(getBlockInstance()) ) { Instance* instance = static_cast(occurrence.getEntity()); if (instance->getPlacementStatus() != Instance::PlacementStatus::FIXED) { Cell* masterCell = instance->getMasterCell(); heights.emplace(masterCell->getAbutmentBox().getHeight()); } } if (heights.empty() || *heights.begin() <= 0) { cerr << Warning("No appropriate slice height found") << endl; return; } DbU::Unit newSliceHeight = *heights.begin(); for (DbU::Unit h : heights) { if (h % newSliceHeight != 0) { cerr << Warning("Cannot set slice height to %d (does not divide cell height %d).", newSliceHeight, h) << endl; return; } _sliceHeight = newSliceHeight; } } void EtesianEngine::_coloquinteCallback(coloquinte::PlacementStep step) { auto placement = _circuit->solution(); if (step == coloquinte::PlacementStep::LowerBound) { *_placementLB = placement; } else { *_placementUB = placement; } // Graphical update GraphicUpdate conf = getUpdateConf(); if (conf == GraphicUpdate::UpdateAll) { _updatePlacement(&placement); } else if (conf == GraphicUpdate::LowerBound && step == coloquinte::PlacementStep::LowerBound) { _updatePlacement(&placement); } } void EtesianEngine::globalPlace () { coloquinte::ColoquinteParameters params(getPlaceEffort()); coloquinte::PlacementCallback callback =std::bind(&EtesianEngine::_coloquinteCallback, this, std::placeholders::_1); _circuit->placeGlobal(params, callback); *_placementUB = _circuit->solution(); } void EtesianEngine::detailedPlace () { coloquinte::ColoquinteParameters params(getPlaceEffort()); coloquinte::PlacementCallback callback =std::bind(&EtesianEngine::_coloquinteCallback, this, std::placeholders::_1); _circuit->placeDetailed(params, callback); *_placementUB = _circuit->solution(); *_placementLB = *_placementUB; // In case we run other passes _updatePlacement(_placementUB); } void EtesianEngine::loadLeafCellLayouts () { AllianceFramework* af = AllianceFramework::get(); for ( Occurrence occurrence : getCell()->getTerminalNetlistInstanceOccurrences(getBlockInstance()) ) { Instance* instance = static_cast(occurrence.getEntity()); Cell* masterCell = instance->getMasterCell(); af->getCell( getString(masterCell->getName()), Catalog::State::Physical ); } } void EtesianEngine::place () { if (getBlockCell()->isPlaced()) { cerr << Warning( "EtesianEngine::place(): The cell \"%s\" is already placed (aborting)" , getString(getBlockCell()->getName()).c_str() ) << std::endl; return; } getBlockCell()->uniquify(); getConfiguration()->print( getCell() ); adjustSliceHeight(); if ( (getCell() == getBlockCell()) and getCell()->getAbutmentBox().isEmpty() ) { resetPlacement(); setDefaultAb(); } //findYSpin(); if (not toColoquinte()) return; cmess1 << " o Running Coloquinte." << endl; startMeasures(); cmess1 << _circuit->report() << std::endl; cmess1 << " o Global placement." << endl; globalPlace(); cmess1 << " o Detailed Placement." << endl; detailedPlace(); //toHurricane(); //addFeeds(); cmess1 << " o Placement finished." << endl; stopMeasures(); printMeasures(); addMeasure( "placeT", getTimer().getCombTime() ); UpdateSession::open(); for ( Net* net : getCell()->getNets() ) { for ( RoutingPad* rp : net->getComponents().getSubSet() ) { rp->invalidate(); } } getCell()->setFlags( Cell::Flags::Placed ); for ( Occurrence occurrence : getCell()->getNonTerminalNetlistInstanceOccurrences(getBlockInstance()) ) { Instance* instance = static_cast(occurrence.getEntity()); if (instance->getPlacementStatus() == Instance::PlacementStatus::UNPLACED) instance->setPlacementStatus( Instance::PlacementStatus::PLACED ); } UpdateSession::close(); } void EtesianEngine::_updatePlacement ( const coloquinte::PlacementSolution* placement ) { UpdateSession::open(); Transformation topTransformation; if (getBlockInstance()) topTransformation = getBlockInstance()->getTransformation(); topTransformation.invert(); DbU::Unit diodeWidth = (_diodeCell) ? _diodeCell->getAbutmentBox().getWidth() : 0; vector< tuple > diodeInsts; for ( Occurrence occurrence : getCell()->getTerminalNetlistInstanceOccurrences(getBlockInstance()) ) { DbU::Unit hpitch = getSliceStep(); DbU::Unit vpitch = getSliceStep(); Point instancePosition; Instance* instance = static_cast(occurrence.getEntity()); string instanceName = occurrence.getCompactString(); // Remove the enclosing brackets... //instanceName.erase( 0, 1 ); //instanceName.erase( instanceName.size()-1 ); auto iid = _instsToIds.find( instance ); if (iid == _instsToIds.end() ) { cerr << Error( "Unable to lookup instance <%s>.", instanceName.c_str() ) << endl; } else { if (instance->getPlacementStatus() == Instance::PlacementStatus::FIXED) continue; //uint32_t outputSide = getOutputSide( instance->getMasterCell() ); auto place = (*placement)[(*iid).second]; Transformation cellTrans = toTransformation( place.position , place.orientation , instance->getMasterCell() , hpitch , vpitch ); topTransformation.applyOn( cellTrans ); // This is temporary as it's not trans-hierarchic: we ignore the positions // of all the intermediary instances. instance->setTransformation( cellTrans ); instance->setPlacementStatus( Instance::PlacementStatus::PLACED ); } } UpdateSession::close(); if (_viewer) _viewer->getCellWidget()->refresh(); } Instance* EtesianEngine::_createDiode ( Cell* owner ) { if (not _diodeCell) return NULL; return Instance::create( owner, getUniqueDiodeName(), _diodeCell ); } string EtesianEngine::getUniqueDiodeName () { ostringstream os; os << "diode_" << _getNewDiodeId(); return os.str(); } void EtesianEngine::_checkNotAFeed( Occurrence occurrence ) const { Instance* instance = static_cast(occurrence.getEntity()); Cell* masterCell = instance->getMasterCell(); if (CatalogExtension::isFeed(masterCell)) { string feedName = getString( instance->getName() ); if ( (feedName.substr(0,11) != "spare_feed_") or (not instance->isFixed())) { string instanceName = occurrence.getCompactString(); // Remove the enclosing brackets... instanceName.erase( 0, 1 ); instanceName.erase( instanceName.size()-1 ); throw Error( "EtesianEngine::toColoquinte(): Feed instance \"%s\" found." , instanceName.c_str() ); } } } string EtesianEngine::_getTypeName () const { return "Etesian::EtesianEngine"; } string EtesianEngine::_getString () const { ostringstream os; os << "<" << "EtesianEngine " << getCell()->getName () << ">"; return os.str(); } Record* EtesianEngine::_getRecord () const { Record* record = Super::_getRecord (); if (record) { record->add( getSlot( "_configuration", _configuration ) ); record->add( getSlot( "_area" , _area ) ); record->add( getSlot( "_diodeCount" , _diodeCount ) ); } return record; } } // Etesian namespace.