2021-03-23 11:14:39 -05:00
|
|
|
// -*- 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 {
|
2021-04-15 17:05:04 -05:00
|
|
|
public:
|
|
|
|
const uint32_t AS_DRIVER = (1<<0);
|
|
|
|
const uint32_t AS_SINK = (1<<1);
|
2021-03-23 11:14:39 -05:00
|
|
|
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 ();
|
2021-04-15 17:05:04 -05:00
|
|
|
Plug* raddTransPlug ( Net* topNet, Path, uint32_t flags );
|
|
|
|
Net* raddTransNet ( Net* topNet, Path, uint32_t flags );
|
2021-03-23 11:14:39 -05:00
|
|
|
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<RoutingPad*> _rps;
|
|
|
|
vector<Cluster*> _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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-04-15 17:05:04 -05:00
|
|
|
Plug* Cluster::raddTransPlug ( Net* topNet, Path path, uint32_t flags )
|
2021-03-23 11:14:39 -05:00
|
|
|
{
|
|
|
|
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() );
|
|
|
|
}
|
2021-04-15 17:05:04 -05:00
|
|
|
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 );
|
|
|
|
|
2021-03-23 11:14:39 -05:00
|
|
|
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())
|
2021-04-15 17:05:04 -05:00
|
|
|
headPlug = raddTransPlug( masterNet, tailPath, flags );
|
2021-03-23 11:14:39 -05:00
|
|
|
|
|
|
|
return headPlug;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-04-15 17:05:04 -05:00
|
|
|
Net* Cluster::raddTransNet ( Net* topNet, Path path, uint32_t flags )
|
2021-03-23 11:14:39 -05:00
|
|
|
{
|
|
|
|
if (path.isEmpty()) return topNet;
|
2021-04-15 17:05:04 -05:00
|
|
|
return raddTransPlug( topNet, path, flags )->getMasterNet();
|
2021-03-23 11:14:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 );
|
2021-04-15 17:05:04 -05:00
|
|
|
blockNet = raddTransNet( upDriver, inputPath, AS_SINK );
|
2021-03-23 11:14:39 -05:00
|
|
|
}
|
|
|
|
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();
|
|
|
|
|
|
|
|
_driverNet = Net::create( topCell, driverName );
|
2021-04-15 17:05:04 -05:00
|
|
|
_driverNet->setDirection( Net::Direction::OUT );
|
2021-04-16 03:31:49 -05:00
|
|
|
getSubNetNames()->nextSubNet();
|
|
|
|
|
|
|
|
driverName.insert( 0, "cmpt_" );
|
|
|
|
_buffer = Instance::create( cellPnR, driverName, bufferDatas->getCell() );
|
2021-03-23 11:14:39 -05:00
|
|
|
if (topCell == cellPnR) blockNet = _driverNet;
|
|
|
|
else {
|
|
|
|
outputPath = Path( instancePnR );
|
2021-04-15 17:05:04 -05:00
|
|
|
blockNet = raddTransNet( _driverNet, outputPath, AS_DRIVER );
|
2021-03-23 11:14:39 -05:00
|
|
|
}
|
|
|
|
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();
|
2021-04-15 17:05:04 -05:00
|
|
|
Net* deepNet = raddTransNet( _driverNet, plugOcc.getPath(), AS_SINK );
|
2021-03-23 11:14:39 -05:00
|
|
|
if (dynamic_cast<Pin*>(plugOcc.getEntity())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Plug* deepSinkPlug = dynamic_cast<Plug*>( 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<Cluster*> > _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<Cluster*>& clusters : _clustersStack ) {
|
|
|
|
for ( Cluster* cluster : clusters ) {
|
|
|
|
if (dynamic_cast<BufferTree*>(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 );
|
2021-04-15 17:05:04 -05:00
|
|
|
Net* deepDriverNet = raddTransNet( _rootNet, driverRpOcc.getPath(), AS_DRIVER );
|
2021-03-23 11:14:39 -05:00
|
|
|
dynamic_cast<Plug*>( 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<Cluster*>() );
|
|
|
|
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<Pin* >( rpOccurrence.getEntity() );
|
|
|
|
if (pin) {
|
|
|
|
if (not rpPin) {
|
|
|
|
if (dynamic_cast<Pin* >( rpOccurrence.getEntity() )) {
|
|
|
|
rpPin = rp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cdebug_log(123,0) << "Excluded: " << pin << endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Plug* rpPlug = dynamic_cast<Plug*>( 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<Cluster*>() );
|
|
|
|
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<Cluster*>& 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<Net*,uint32_t> > netDatas;
|
|
|
|
for ( Net* net : getCell()->getNets() ) {
|
More generic H-Tree support to accomodate the LS180 PLL internal clock.
The H-Tree support is now allowed for any net, not only the clocks and
not only top-level nets. This allow to better management of the LS180
internal clock signal.
* New: In Cell::flattenNets(Instance*,set<string>,uint64_t) new overload
of the function to allow the user to select nets that will *not*
be flattened. This makes the NoClockFlatten flag effectively obsolete,
we keep it for backward compatibility.
The net names can be of non top level ones. In that case, they must
use the name an HyperNet will get (the Occurrence name). For example:
"instance1.instance2.deep_net_name".
* New: In PyCell, update the wrapper for the new parameter of flattenNets(),
new utility function pyListToStringSet() to translate a Python list into
a C++ set of names.
* New: In EtesianEngine, add support for a list of nets to be excluded
from the flattening procedure. Those excluded nets will also be
excludeds from the Coloquinte nets *and* HFNS synthesis, as they
are likely to be manageds by a H-Tree.
* Change: In AnabaticEngine::_loadGrByNet(), now also skip nets that are
flagged as manually detailed route.
* New: In AnabaticEngine::antennaProtect(), do not try to insert diodes
on nets that are already fixed or detaled route. This replace the
clock exclusion.
* New: In cumulus/plugins.{block,htree,chip}, replace the concept
of clock-tree by the more generic H-Tree. That is, we can ask the P&R
to create H-Tree on any net of the design, not only the ones matcheds
as clock. The net does not even need to be top-level.
This is to manage the PLL internal clock generated by the PLL in
the LS180 chip.
Start to change all reference to "clock" into "H-Tree".
* Bug: In cumulus/plugins.chip.powerplanes.Builder._connectHTree(),
there was an inversion of the H & V routing gauges to compute the
track into which put the H-Tree center to corona edge wiring.
This was causing tracks to be used twice, seen in the ao68000 test
bench.
2021-05-30 17:02:23 -05:00
|
|
|
if (isExcluded(getString(net->getName()))) continue;
|
|
|
|
|
2021-03-23 11:14:39 -05:00
|
|
|
uint32_t rpCount = 0;
|
|
|
|
for ( RoutingPad* rp : net->getRoutingPads() ) {
|
|
|
|
Occurrence rpOcc = rp->getPlugOccurrence();
|
|
|
|
Pin* pin = dynamic_cast<Pin*>( rpOcc.getEntity() );
|
|
|
|
if (pin) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getBlockInstance()) {
|
|
|
|
if (rpOcc.getPath().getHeadInstance() != getBlockInstance()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Plug* rpPlug = dynamic_cast<Plug*>( 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.size() ; ++i ) {
|
|
|
|
if (i) {
|
|
|
|
if ((i%10) == 0) cmess2 << "\n ";
|
|
|
|
else cmess2 << " ";
|
|
|
|
} else {
|
|
|
|
cmess2 << " ";
|
|
|
|
}
|
|
|
|
cmess2 << "[" << std::get<1>( netDatas[i] ) << "]";
|
|
|
|
Net* net = std::get<0>( netDatas[i] );
|
|
|
|
BufferTree tree ( this, net );
|
|
|
|
_bufferCount += tree.build();
|
|
|
|
}
|
|
|
|
cmess2 << endl;
|
|
|
|
// for ( tuple<Net*,uint32_t>& 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);
|