1883 lines
59 KiB
C++
1883 lines
59 KiB
C++
|
|
// -*- 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 : "./AutoSegment.cpp" |
|
|
// | *************************************************************** |
|
|
// | U p d a t e s |
|
|
// | |
|
|
// x-----------------------------------------------------------------x
|
|
|
|
|
|
#include "hurricane/Warning.h"
|
|
#include "hurricane/Bug.h"
|
|
#include "hurricane/DataBase.h"
|
|
#include "hurricane/Technology.h"
|
|
#include "hurricane/Horizontal.h"
|
|
#include "hurricane/Vertical.h"
|
|
#include "crlcore/RoutingGauge.h"
|
|
|
|
#include "katabatic/Session.h"
|
|
#include "katabatic/AutoContact.h"
|
|
#include "katabatic/AutoSegment.h"
|
|
#include "katabatic/AutoHorizontal.h"
|
|
#include "katabatic/AutoVertical.h"
|
|
#include "katabatic/GCell.h"
|
|
#include "katabatic/KatabaticEngine.h"
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
using namespace std;
|
|
using namespace CRL;
|
|
using namespace Hurricane;
|
|
using namespace Katabatic;
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// Local Variables.
|
|
|
|
|
|
const char* badAutoSegmentAnchor =
|
|
"AutoSegment::create() :\n\n"
|
|
" Source and/or target anchor is NOT an <AutoContact> (internal error).\n"
|
|
" Source: %s, Target: %s";
|
|
|
|
const char* dupAutoSegmentAnchor =
|
|
"AutoSegment::create() :\n\n"
|
|
" Source and Target anchor are the same : %s (internal error).";
|
|
|
|
const char* badSegment =
|
|
"Katabatic::AutoSegment::create () :\n\n"
|
|
" Segment between %s and %s\n"
|
|
" is neither horizontal nor vertical .\n";
|
|
|
|
const char* badSegmentSource =
|
|
"Katabatic::AutoSegment::create () :\n\n"
|
|
" Source anchor of segment %s is not a Contact\n"
|
|
" (%s)\n";
|
|
|
|
const char* badSegmentTarget =
|
|
"Katabatic::AutoSegment::create () :\n\n"
|
|
" Source anchor of segment %s is not a Contact\n"
|
|
" (%s)\n";
|
|
|
|
const char* mismatchSegmentSource =
|
|
"Katabatic::AutoSegment::create () :\n\n"
|
|
" Source anchor of segment %s is already an AutoContact\n"
|
|
" (%s)\n";
|
|
|
|
const char* mismatchSegmentTarget =
|
|
"Katabatic::AutoSegment::create () :\n\n"
|
|
" Target anchor of segment %s is already an AutoContact\n"
|
|
" (%s)\n";
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// Local Functions.
|
|
|
|
|
|
bool getTerminalInterval ( AutoSegment* autoSegment
|
|
, AutoContact* fromContact
|
|
, bool isHorizontal
|
|
, DbU::Unit& min
|
|
, DbU::Unit& max
|
|
)
|
|
{
|
|
AutoContact* terminalContact = NULL;
|
|
|
|
if ( !fromContact ) {
|
|
bool found = getTerminalInterval ( autoSegment
|
|
, autoSegment->getAutoSource()
|
|
, autoSegment->isHorizontal()
|
|
, min
|
|
, max );
|
|
if ( !found )
|
|
found = getTerminalInterval ( autoSegment
|
|
, autoSegment->getAutoTarget()
|
|
, autoSegment->isHorizontal()
|
|
, min
|
|
, max );
|
|
|
|
//if ( !found )
|
|
// cerr << "[ERROR] Cannot find terminal of " << autoSegment << "." << endl;
|
|
|
|
return found;
|
|
} else {
|
|
if ( autoSegment->isGlobal() ) return false;
|
|
|
|
ltrace(88) << "Examining " << autoSegment << " " << fromContact << endl;
|
|
|
|
if ( autoSegment->getSource() == autoSegment->getTarget() ) {
|
|
cerr << Error("Source & Target are the same :\n"
|
|
" %s\n %s"
|
|
,getString(autoSegment).c_str()
|
|
,getString(autoSegment->getSource()).c_str()) << endl;
|
|
}
|
|
|
|
terminalContact = autoSegment->getAutoSource();
|
|
if ( terminalContact == fromContact ) {
|
|
terminalContact = autoSegment->getAutoTarget();
|
|
}
|
|
|
|
if ( !terminalContact->isTerminal() ) {
|
|
AutoSegment* segment = NULL;
|
|
size_t segmentCount = 0;
|
|
forEach ( Component*, icomponent, terminalContact->getSlaveComponents() ) {
|
|
if ( *icomponent == autoSegment->getSegment() ) continue;
|
|
|
|
Segment* connex = dynamic_cast<Segment*>(*icomponent);
|
|
if ( !connex ) continue;
|
|
|
|
segment = Session::lookup ( connex );
|
|
if ( !segment || !segment->isTerminal() ) continue;
|
|
|
|
segmentCount++;
|
|
}
|
|
|
|
if ( segmentCount == 1 ) {
|
|
return getTerminalInterval ( segment, terminalContact, isHorizontal, min, max );
|
|
|
|
return false;
|
|
}
|
|
} else {
|
|
ltrace(88) << "Terminal is " << terminalContact << endl;
|
|
|
|
Box constraintBox = terminalContact->getConstraintBox();
|
|
if ( isHorizontal ) {
|
|
min = constraintBox.getXMin ();
|
|
max = constraintBox.getXMax ();
|
|
} else {
|
|
min = constraintBox.getYMin ();
|
|
max = constraintBox.getYMax ();
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// Class : "AttractorsMap".
|
|
|
|
|
|
class AttractorsMap {
|
|
// Constructor.
|
|
public:
|
|
inline AttractorsMap ();
|
|
inline size_t getAttractorsCount () const;
|
|
DbU::Unit getLowerMedian () const;
|
|
DbU::Unit getUpperMedian () const;
|
|
void addAttractor ( DbU::Unit position );
|
|
protected:
|
|
map<DbU::Unit,size_t> _attractors;
|
|
size_t _attractorsCount;
|
|
};
|
|
|
|
|
|
inline AttractorsMap::AttractorsMap ()
|
|
: _attractors(), _attractorsCount(0)
|
|
{ }
|
|
|
|
|
|
inline size_t AttractorsMap::getAttractorsCount () const
|
|
{
|
|
return _attractorsCount;
|
|
}
|
|
|
|
|
|
void AttractorsMap::addAttractor ( DbU::Unit position )
|
|
{
|
|
_attractors[position]++;
|
|
_attractorsCount++;
|
|
|
|
ltrace(88) << "add Attractor @" << DbU::getLambda(position)
|
|
<< " [" << _attractors[position] << "]" << endl;
|
|
}
|
|
|
|
|
|
DbU::Unit AttractorsMap::getLowerMedian () const
|
|
{
|
|
size_t median = (_attractorsCount/2) + (_attractorsCount%2);
|
|
size_t lower = 0;
|
|
|
|
map<DbU::Unit,size_t>::const_iterator it = _attractors.begin ();
|
|
for ( ; it != _attractors.end() ; it++ ) {
|
|
lower += it->second;
|
|
if ( lower >= median ) break;
|
|
}
|
|
|
|
return it->first;
|
|
}
|
|
|
|
|
|
DbU::Unit AttractorsMap::getUpperMedian () const
|
|
{
|
|
size_t median = _attractorsCount / 2;
|
|
size_t upper = 0;
|
|
|
|
map<DbU::Unit,size_t>::const_iterator it = _attractors.begin ();
|
|
for ( ; it != _attractors.end() ; it++ ) {
|
|
upper += it->second;
|
|
if ( upper > median ) break;
|
|
}
|
|
|
|
return it->first;
|
|
}
|
|
|
|
|
|
} // End of local namespace.
|
|
|
|
|
|
namespace Katabatic {
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
// Class : "Katabatic::AutoSegment::CompareCanonical".
|
|
|
|
|
|
bool AutoSegment::CompareCanonical::operator() ( const AutoSegment* lhs, const AutoSegment* rhs ) const
|
|
{
|
|
if ( lhs->isCanonical () xor rhs->isCanonical () ) return lhs->isCanonical();
|
|
if ( lhs->isCollapsed () xor rhs->isCollapsed () ) return rhs->isCollapsed();
|
|
if ( lhs->isSlackenStrap() xor rhs->isSlackenStrap() ) return lhs->isSlackenStrap();
|
|
|
|
if ( lhs->getSourceU() < rhs->getSourceU() ) return true;
|
|
if ( lhs->getSourceU() > rhs->getSourceU() ) return false;
|
|
|
|
if ( lhs->getLength() > rhs->getLength() ) return true;
|
|
if ( lhs->getLength() < rhs->getLength() ) return false;
|
|
|
|
if ( lhs->isGlobal () xor rhs->isGlobal () ) return lhs->isGlobal();
|
|
if ( lhs->isTerminal () xor rhs->isTerminal () ) return rhs->isTerminal();
|
|
if ( lhs->isHorizontal() xor rhs->isHorizontal() ) return lhs->isHorizontal();
|
|
|
|
if ( lhs->getAxis() < rhs->getAxis() ) return true;
|
|
if ( lhs->getAxis() > rhs->getAxis() ) return false;
|
|
|
|
if ( lhs->isFixed() xor rhs->isFixed() ) return lhs->isFixed();
|
|
|
|
return lhs->getId() < rhs->getId();
|
|
}
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
// Class : "Katabatic::AutoSegment::CompareByDepthLength".
|
|
|
|
|
|
bool AutoSegment::CompareByDepthLength::operator() ( AutoSegment* lhs, AutoSegment* rhs ) const
|
|
{
|
|
if ( Session::getRoutingGauge()->getLayerDepth(lhs->getLayer())
|
|
< Session::getRoutingGauge()->getLayerDepth(rhs->getLayer()) )
|
|
return true;
|
|
|
|
if ( Session::getRoutingGauge()->getLayerDepth(lhs->getLayer())
|
|
> Session::getRoutingGauge()->getLayerDepth(rhs->getLayer()) )
|
|
return false;
|
|
|
|
return AutoSegment::CompareCanonical() ( lhs, rhs );
|
|
}
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
// Class : "Katabatic::AutoSegment".
|
|
|
|
|
|
size_t AutoSegment::_allocateds = 0;
|
|
size_t AutoSegment::_globalsCount = 0;
|
|
unsigned long AutoSegment::_maxId = 0;
|
|
|
|
|
|
DbU::Unit AutoSegment::getX () const
|
|
{
|
|
return getSegment()->getX();
|
|
}
|
|
|
|
|
|
DbU::Unit AutoSegment::getY () const
|
|
{
|
|
return getSegment()->getY();
|
|
}
|
|
|
|
|
|
AutoContact* AutoSegment::getOppositeAnchor ( AutoContact* anchor ) const
|
|
{
|
|
return Session::lookup(static_cast<Contact*>(getOppositeAnchor(anchor->getContact())));
|
|
}
|
|
|
|
|
|
Interval& AutoSegment::getOptimal ( Interval& i ) const
|
|
{
|
|
i.getVMin() = getOptimalMin();
|
|
i.getVMax() = getOptimalMax();
|
|
return i;
|
|
}
|
|
|
|
|
|
bool AutoSegment::checkInvalidated () const
|
|
{
|
|
if ( isInvalidated() )
|
|
cerr << Error("%s is invalidated.",getString(this).c_str()) << endl;
|
|
|
|
return !isInvalidated();
|
|
}
|
|
|
|
|
|
void AutoSegment::invalidate ()
|
|
{
|
|
if ( Session::doDestroyTool() ) return;
|
|
|
|
_invalidate ();
|
|
forEach ( AutoSegment*, isegment, getCollapseds() )
|
|
isegment->_invalidate ();
|
|
}
|
|
|
|
|
|
void AutoSegment::_invalidate ()
|
|
{
|
|
if ( !isInvalidated() ) {
|
|
ltrace(110) << "AutoSegment::_invalidate() " << this << endl;
|
|
|
|
setInvalidated ( true );
|
|
Session::invalidate ( this );
|
|
}
|
|
}
|
|
|
|
|
|
void AutoSegment::revalidate ()
|
|
{
|
|
ltrace(110) << "AutoSegment::revalidate() " << this << endl;
|
|
ltracein(110);
|
|
|
|
setPositions ();
|
|
setInvalidated ( false );
|
|
|
|
ltraceout(110);
|
|
}
|
|
|
|
|
|
DbU::Unit AutoSegment::getSlack () const
|
|
{
|
|
DbU::Unit constraintMin;
|
|
DbU::Unit constraintMax;
|
|
|
|
getConstraints ( constraintMin, constraintMax );
|
|
|
|
return constraintMax - constraintMin;
|
|
}
|
|
|
|
|
|
DbU::Unit AutoSegment::getCost ( DbU::Unit axis ) const
|
|
{
|
|
DbU::Unit optimal = getOptimalMin();
|
|
if ( axis < optimal )
|
|
return optimal - axis;
|
|
|
|
optimal = getOptimalMax();
|
|
if ( axis > optimal )
|
|
return axis - optimal;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
AutoSegment* AutoSegment::getCanonical ( DbU::Unit& min, DbU::Unit& max )
|
|
{
|
|
min = getSourcePosition ();
|
|
max = getTargetPosition ();
|
|
|
|
if ( max < min ) swap ( min, max );
|
|
|
|
AutoSegment* canonical = this;
|
|
size_t canonicals = isCanonical();
|
|
size_t aligneds = 1;
|
|
DbU::Unit collapsedMin;
|
|
DbU::Unit collapsedMax;
|
|
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
if ( isegment->isCanonical() ) {
|
|
canonical = *isegment;
|
|
canonicals++;
|
|
}
|
|
|
|
collapsedMin = isegment->getSourcePosition();
|
|
collapsedMax = isegment->getTargetPosition();
|
|
if ( collapsedMax < collapsedMin ) swap ( collapsedMin, collapsedMax );
|
|
if ( collapsedMin < min ) min = collapsedMin;
|
|
if ( collapsedMax > max ) max = collapsedMax;
|
|
|
|
aligneds++;
|
|
}
|
|
|
|
if ( (canonicals > 1) || ( !canonicals && (aligneds > 2) ) ) {
|
|
cerr << Bug("AutoSegment::getCanonical(): %p:%s"
|
|
"\n Bad canonization: %d canonicals out of %d collapseds."
|
|
, base(), _getString().c_str(), canonicals, aligneds ) << endl;
|
|
|
|
int count = 0;
|
|
cerr << " " << count++ << ": " << this << endl;
|
|
forEach ( AutoSegment*, isegment, getCollapseds() )
|
|
cerr << " " << count++ << ": " << *isegment << endl;
|
|
}
|
|
|
|
return canonical;
|
|
}
|
|
|
|
|
|
AutoSegments AutoSegment::getOnSourceContact ( unsigned int direction )
|
|
{
|
|
return AutoSegments_OnContact
|
|
( this, getSource() ).getSubSet ( AutoSegments_InDirection(direction) );
|
|
}
|
|
|
|
|
|
AutoSegments AutoSegment::getOnTargetContact ( unsigned int direction )
|
|
{
|
|
return AutoSegments_OnContact
|
|
( this, getTarget() ).getSubSet ( AutoSegments_InDirection(direction) );
|
|
}
|
|
|
|
|
|
AutoSegments AutoSegment::getCollapseds ( bool withPerpand )
|
|
{
|
|
return AutoSegments_Collapsed ( this, withPerpand );
|
|
}
|
|
|
|
|
|
AutoSegments AutoSegment::getCollapsedPerpandiculars ()
|
|
{
|
|
return AutoSegments_CollapsedPerpandicular ( this );
|
|
}
|
|
|
|
|
|
bool AutoSegment::isCanonicalStrap () const
|
|
{
|
|
if ( not isStrap() ) return false;
|
|
forEach ( AutoSegment*, isegment, const_cast<AutoSegment*>(this)->getCollapseds() ) {
|
|
if ( not isegment->isStrap() ) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool AutoSegment::collapse ()
|
|
{
|
|
if ( _isGlobal ) {
|
|
cerr << Error("Global %s cannot be collapsed.",getString(this).c_str()) << endl;
|
|
return false;
|
|
}
|
|
if ( _isCollapsed ) return true;
|
|
|
|
_isCollapsed = true;
|
|
|
|
unsigned int direction = (_isHorizontal) ? Constant::Vertical : Constant::Horizontal;
|
|
forEach ( AutoSegment*, isegment, AutoSegments_AnchoredBySource(getAutoSource(),direction) ) {
|
|
isegment->setCanonical ( false );
|
|
}
|
|
forEach ( AutoSegment*, isegment, AutoSegments_AnchoredBySource(getAutoTarget(),direction) ) {
|
|
isegment->setCanonical ( false );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool AutoSegment::expand ()
|
|
{
|
|
if ( _isGlobal ) {
|
|
cerr << Warning("Global %s already uncollapsed.",getString(this).c_str()) << endl;
|
|
return false;
|
|
}
|
|
if ( !_isCollapsed ) {
|
|
cerr << Warning("Local %s already uncollapsed.",getString(this).c_str()) << endl;
|
|
return true;
|
|
}
|
|
|
|
_isCollapsed = false;
|
|
|
|
canonize ();
|
|
|
|
unsigned int direction = (_isHorizontal) ? Constant::Vertical : Constant::Horizontal;
|
|
forEach ( AutoSegment*, segment, getOnSourceContact(direction) ) {
|
|
segment->canonize ();
|
|
}
|
|
forEach ( AutoSegment*, segment, getOnTargetContact(direction) ) {
|
|
segment->canonize ();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool AutoSegment::toConstraintAxis ( set<AutoSegment*>* processeds )
|
|
{
|
|
if ( processeds && (processeds->find(this) != processeds->end()) ) return false;
|
|
|
|
DbU::Unit constraintMin;
|
|
DbU::Unit constraintMax;
|
|
|
|
getConstraints ( constraintMin, constraintMax );
|
|
|
|
// Empty constraint interval: ignore.
|
|
if ( constraintMin > constraintMax ) return false;
|
|
|
|
if ( allowOutsideGCell() ) {
|
|
// Ugly: hard-wired value of the track spacing.
|
|
constraintMin -= DbU::lambda(5.0) * 8;
|
|
constraintMax += DbU::lambda(5.0) * 8;
|
|
}
|
|
|
|
if ( getAxis() < constraintMin ) {
|
|
setAxis ( constraintMin, Realignate, processeds );
|
|
return true;
|
|
}
|
|
|
|
if ( getAxis() > constraintMax ) {
|
|
setAxis ( constraintMax, Realignate, processeds );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool AutoSegment::toOptimalAxis ( set<AutoSegment*>* processeds )
|
|
{
|
|
if ( processeds && (processeds->find(this) != processeds->end()) ) return false;
|
|
|
|
DbU::Unit constraintMin;
|
|
DbU::Unit constraintMax;
|
|
|
|
getConstraints ( constraintMin, constraintMax );
|
|
|
|
DbU::Unit optimalMin = max ( min(getOptimalMin(),constraintMax), constraintMin );
|
|
DbU::Unit optimalMax = min ( max(getOptimalMax(),constraintMin), constraintMax );
|
|
|
|
if ( getAxis() < optimalMin ) {
|
|
#if defined(CHECK_DETERMINISM)
|
|
cerr << "Order: toOptimalMin "
|
|
<< DbU::getValueString(optimalMin) << " ["
|
|
<< DbU::getValueString(optimalMin) << ":"
|
|
<< DbU::getValueString(optimalMax) << "] ["
|
|
<< DbU::getValueString(constraintMin) << ":"
|
|
<< DbU::getValueString(constraintMax) << "] "
|
|
<< this << endl;
|
|
#endif
|
|
setAxis ( optimalMin, Realignate|AxisSet, processeds );
|
|
return true;
|
|
}
|
|
|
|
if ( getAxis() > optimalMax ) {
|
|
#if defined(CHECK_DETERMINISM)
|
|
cerr << "Order: toOptimalMax "
|
|
<< DbU::getValueString(optimalMin) << " ["
|
|
<< DbU::getValueString(optimalMin) << ":"
|
|
<< DbU::getValueString(optimalMax) << "] ["
|
|
<< DbU::getValueString(constraintMin) << ":"
|
|
<< DbU::getValueString(constraintMax) << "] "
|
|
<< this << endl;
|
|
#endif
|
|
setAxis ( optimalMax, Realignate|AxisSet, processeds );
|
|
return true;
|
|
}
|
|
|
|
#if defined(CHECK_DETERMINISM)
|
|
cerr << "Order: in optimal position "
|
|
<< DbU::getValueString(optimalMin) << " ["
|
|
<< DbU::getValueString(optimalMin) << ":"
|
|
<< DbU::getValueString(optimalMax) << "] ["
|
|
<< DbU::getValueString(constraintMin) << ":"
|
|
<< DbU::getValueString(constraintMax) << "] "
|
|
<< this << endl;
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void AutoSegment::setAxis ( DbU::Unit axis, unsigned int flags, set<AutoSegment*>* processeds )
|
|
{
|
|
if ( processeds and (processeds->find(this) != processeds->end()) ) return;
|
|
|
|
if ( ( axis != getAxis() ) and isFixed() ) {
|
|
cerr << Error("AutoSegment::setAxis(): Cannot move a fixed segment.\n"
|
|
" (on: %s)",_getString().c_str()) << endl;
|
|
}
|
|
|
|
if ( _isUnsetAxis and (flags & AxisSet) ) {
|
|
ltrace(200) << "setAxis() - AxisSet flag raised " << this << endl;
|
|
_isUnsetAxis = false;
|
|
}
|
|
if ( ( axis == getAxis() ) and not (flags & Realignate) ) return;
|
|
|
|
ltrace(200) << "setAxis() @"
|
|
<< ((_isHorizontal)?"Y ":"X ") << DbU::getLambda(getAxis())
|
|
<< " to " << DbU::getLambda(axis) << " on " << this << endl;
|
|
ltracein(80);
|
|
|
|
alignate ( axis );
|
|
if ( processeds ) processeds->insert ( this );
|
|
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
isegment->alignate ( getAxis() );
|
|
if ( flags & AxisSet ) isegment->_isUnsetAxis = false;
|
|
if ( processeds ) processeds->insert ( *isegment );
|
|
}
|
|
|
|
ltraceout(80);
|
|
}
|
|
|
|
|
|
void AutoSegment::computeOptimal ( set<AutoSegment*>* processeds )
|
|
{
|
|
ltrace(89) << "computeOptimal() - " << this << endl;
|
|
ltracein(89);
|
|
|
|
if ( processeds && (processeds->find(this) != processeds->end()) ) { ltraceout(89); return; }
|
|
|
|
if ( _isCollapsed ) {
|
|
_optimalMin = 0;
|
|
setOptimalMax ( (_isHorizontal) ? _gcell->getBoundingBox().getYMax()
|
|
: _gcell->getBoundingBox().getXMax() );
|
|
ltraceout(89);
|
|
return;
|
|
}
|
|
|
|
DbU::Unit minGCell = getOrigin();
|
|
DbU::Unit maxGCell = getExtremity();
|
|
DbU::Unit terminalMin;
|
|
DbU::Unit terminalMax;
|
|
AttractorsMap attractors;
|
|
|
|
AutoContact* anchor = getAutoSource();
|
|
if ( anchor->isTerminal() ) {
|
|
Box constraintBox = anchor->getConstraintBox();
|
|
if ( isHorizontal() ) {
|
|
terminalMin = constraintBox.getYMin ();
|
|
terminalMax = constraintBox.getYMax ();
|
|
} else {
|
|
terminalMin = constraintBox.getXMin ();
|
|
terminalMax = constraintBox.getXMax ();
|
|
}
|
|
|
|
attractors.addAttractor ( terminalMin );
|
|
if ( terminalMin != terminalMax )
|
|
attractors.addAttractor ( terminalMax );
|
|
}
|
|
|
|
anchor = getAutoTarget();
|
|
if ( anchor->isTerminal() ) {
|
|
Box constraintBox = anchor->getConstraintBox();
|
|
if ( isHorizontal() ) {
|
|
terminalMin = constraintBox.getYMin ();
|
|
terminalMax = constraintBox.getYMax ();
|
|
} else {
|
|
terminalMin = constraintBox.getXMin ();
|
|
terminalMax = constraintBox.getXMax ();
|
|
}
|
|
|
|
attractors.addAttractor ( terminalMin );
|
|
if ( terminalMin != terminalMax )
|
|
attractors.addAttractor ( terminalMax );
|
|
}
|
|
|
|
forEach ( AutoSegment*, autoSegment, getCollapsedPerpandiculars() ) {
|
|
ltrace(89) << "Perpandicular " << *autoSegment << endl;
|
|
ltracein(89);
|
|
if ( autoSegment->isLocal() ) {
|
|
if ( !autoSegment->isTerminal() ) { ltraceout(89); continue; }
|
|
|
|
DbU::Unit terminalMin;
|
|
DbU::Unit terminalMax;
|
|
|
|
if ( getTerminalInterval ( *autoSegment
|
|
, NULL
|
|
, isHorizontal()
|
|
, terminalMin
|
|
, terminalMax ) ) {
|
|
attractors.addAttractor ( terminalMin );
|
|
if ( terminalMin != terminalMax )
|
|
attractors.addAttractor ( terminalMax );
|
|
}
|
|
} else {
|
|
bool isMin = true;
|
|
if ( isHorizontal()
|
|
&& ( autoSegment->getAutoSource()->getGCell()->getRow() == _gcell->getRow() ) )
|
|
isMin = false;
|
|
if ( isVertical()
|
|
&& ( autoSegment->getAutoSource()->getGCell()->getColumn() == _gcell->getColumn() ) )
|
|
isMin = false;
|
|
attractors.addAttractor ( (isMin) ? minGCell : maxGCell );
|
|
}
|
|
ltraceout(89);
|
|
}
|
|
|
|
DbU::Unit optimalMin;
|
|
DbU::Unit optimalMax;
|
|
DbU::Unit constraintMin;
|
|
DbU::Unit constraintMax;
|
|
getConstraints ( constraintMin, constraintMax );
|
|
|
|
if ( attractors.getAttractorsCount() ) {
|
|
ltrace(89) << "Lower Median " << DbU::getLambda(attractors.getLowerMedian()) << endl;
|
|
ltrace(89) << "Upper Median " << DbU::getLambda(attractors.getUpperMedian()) << endl;
|
|
|
|
optimalMin = attractors.getLowerMedian();
|
|
optimalMax = attractors.getUpperMedian();
|
|
} else {
|
|
optimalMin = 0;
|
|
optimalMax = (_isHorizontal) ? _gcell->getBoundingBox().getYMax()
|
|
: _gcell->getBoundingBox().getXMax();
|
|
}
|
|
|
|
setInBound ( constraintMin, constraintMax, optimalMin );
|
|
setInBound ( constraintMin, constraintMax, optimalMax );
|
|
|
|
if ( processeds ) processeds->insert ( this );
|
|
setOptimalMin ( optimalMin );
|
|
setOptimalMax ( optimalMax );
|
|
forEach ( AutoSegment*, autoSegment, getCollapseds() ) {
|
|
if ( processeds ) processeds->insert ( *autoSegment );
|
|
autoSegment->setOptimalMin ( optimalMin );
|
|
autoSegment->setOptimalMax ( optimalMax );
|
|
}
|
|
|
|
ltraceout(89);
|
|
}
|
|
|
|
|
|
AutoSegment* AutoSegment::canonize ()
|
|
{
|
|
ltrace(159) << "canonize() - " << this << endl;
|
|
|
|
if ( isCanonical() ) {
|
|
ltrace(159) << "* " << this << " canonical" << endl;
|
|
return this;
|
|
}
|
|
|
|
AutoSegment* canonical = this;
|
|
bool hasCanonical = false;
|
|
bool isCanonicalLocal = true;
|
|
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
if ( isegment->isGlobal() ) isCanonicalLocal = false;
|
|
|
|
if ( isegment->isCanonical() ) {
|
|
ltrace(159) << "* " << *isegment << " canonical" << endl;
|
|
//return *isegment;
|
|
canonical = *isegment;
|
|
hasCanonical = true;
|
|
break;
|
|
}
|
|
|
|
if ( !hasCanonical ) {
|
|
if ( CompareCanonical()(*isegment,canonical) )
|
|
canonical = *isegment;
|
|
}
|
|
}
|
|
|
|
canonical->setCanonical ( true );
|
|
canonical->setCanonicalLocal ( isCanonicalLocal );
|
|
|
|
// ltrace: 159
|
|
if ( isCanonical() ) cerr << "* " << this << " canonical" << endl;
|
|
else cerr << "* " << this << endl;
|
|
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
if ( isegment->isCanonical() ) cerr << "| " << *isegment << " canonical" << endl;
|
|
else cerr << "| " << *isegment << endl;
|
|
}
|
|
|
|
return canonical;
|
|
}
|
|
|
|
|
|
AutoSegment::AutoSegment ( Segment* segment
|
|
, bool isHorizontal
|
|
, int type
|
|
, bool terminal
|
|
, bool collapsed
|
|
)
|
|
: _isUnsetAxis (true)
|
|
, _invalidated (false)
|
|
, _isHorizontal (isHorizontal)
|
|
, _isTerminal (terminal)
|
|
, _isCollapsed (collapsed)
|
|
, _isCanonical (false)
|
|
, _isFixed (false)
|
|
, _strap (false)
|
|
, _layerChange (false)
|
|
, _slackened (false)
|
|
, _slackenStrap (false)
|
|
, _allowOutsideGCell(false)
|
|
, _id (_maxId++)
|
|
, _optimalMin (0)
|
|
, _userConstraints (false)
|
|
{
|
|
//cerr << "AutoSegment::AutoSegment() - <id:" << _id << ">" << endl;
|
|
#if defined(CHECK_DETERMINISM)
|
|
cerr << "Order: AutoSegment::AutoSegment() - <id:" << _id << ">" << endl;
|
|
#endif
|
|
AutoContact* source = Session::lookup(dynamic_cast<Contact*>(segment->getSource()));
|
|
AutoContact* target = Session::lookup(dynamic_cast<Contact*>(segment->getTarget()));
|
|
|
|
_allocateds++;
|
|
|
|
_gcell = source->getGCell();
|
|
setOptimalMax ( (_isHorizontal) ? _gcell->getBoundingBox().getYMax()
|
|
: _gcell->getBoundingBox().getXMax() );
|
|
|
|
switch ( type ) {
|
|
case AutoSegment::Global: _isGlobal = true; break;
|
|
case AutoSegment::Local : _isGlobal = false; break;
|
|
case AutoSegment::Guess :
|
|
_isGlobal = ( source->getGCell() != target->getGCell() );
|
|
break;
|
|
}
|
|
_globalsCount += (_isGlobal) ? 1 : 0;
|
|
_isCanonicalLocal = not _isGlobal;
|
|
|
|
_computeTerminal ( segment );
|
|
//if ( source->isTerminal() or target->isTerminal() ) _isTerminal = true;
|
|
|
|
//if ( source->isTerminal()
|
|
// and target->isTerminal()
|
|
// /*and (segment->getLength() < DbU::lambda(25.0))*/
|
|
// and (source->getGCell() == target->getGCell()) ) {
|
|
// _strap = true;
|
|
//}
|
|
|
|
source->setInvalidatedTopology ( true );
|
|
}
|
|
|
|
|
|
void AutoSegment::_preCreate ( Component* source, Component* target )
|
|
{
|
|
AutoContact* acSource = Session::lookup(dynamic_cast<Contact*>(source));
|
|
AutoContact* acTarget = Session::lookup(dynamic_cast<Contact*>(target));
|
|
|
|
_preCreate ( acSource, acTarget );
|
|
}
|
|
|
|
|
|
void AutoSegment::_preCreate ( AutoContact* source, AutoContact* target )
|
|
{
|
|
if ( !source || !target )
|
|
throw Error ( badAutoSegmentAnchor
|
|
, ((source)?getString(source).c_str():"NULL")
|
|
, ((target)?getString(target).c_str():"NULL")
|
|
);
|
|
|
|
if ( source == target )
|
|
throw Error ( dupAutoSegmentAnchor, getString(source).c_str() );
|
|
}
|
|
|
|
|
|
void AutoSegment::_postCreate ()
|
|
{
|
|
Session::invalidate ( getNet() );
|
|
Session::link ( this );
|
|
invalidate ();
|
|
}
|
|
|
|
|
|
void AutoSegment::_preDestroy ()
|
|
{
|
|
ltrace(200) << "AutoSegment::_preDestroy() - " << (void*)this << endl;
|
|
ltracein(90);
|
|
|
|
Session::unlink ( this );
|
|
ltraceout(90);
|
|
}
|
|
|
|
|
|
AutoSegment::~AutoSegment ()
|
|
{
|
|
_allocateds--;
|
|
if ( _isGlobal and (_globalsCount > 0) ) _globalsCount--;
|
|
}
|
|
|
|
|
|
void AutoSegment::_computeTerminal ( Segment* segment )
|
|
{
|
|
AutoContact* source = Session::lookup(dynamic_cast<Contact*>(segment->getSource()));
|
|
AutoContact* target = Session::lookup(dynamic_cast<Contact*>(segment->getTarget()));
|
|
|
|
if ( source->isTerminal() or target->isTerminal() ) _isTerminal = true;
|
|
}
|
|
|
|
|
|
size_t AutoSegment::getAlignedContacts ( map<AutoContact*,int>& innerContacts )
|
|
{
|
|
map<AutoContact*,int>::iterator icontact;
|
|
|
|
innerContacts.clear ();
|
|
innerContacts.insert ( make_pair(getAutoSource(),0x1) );
|
|
innerContacts.insert ( make_pair(getAutoTarget(),0x4) );
|
|
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
if ( (icontact = innerContacts.find(isegment->getAutoSource())) != innerContacts.end() ) {
|
|
if ( icontact->second & 0x1 ) icontact->second |= 0x2;
|
|
else icontact->second |= 0x1;
|
|
} else
|
|
innerContacts.insert ( make_pair(getAutoSource(),0x1) );
|
|
|
|
if ( (icontact = innerContacts.find(isegment->getAutoTarget())) != innerContacts.end() ) {
|
|
if ( icontact->second & 0x4 ) icontact->second |= 0x8;
|
|
else icontact->second |= 0x4;
|
|
} else
|
|
innerContacts.insert ( make_pair(getAutoTarget(),0x4) );
|
|
}
|
|
|
|
return innerContacts.size();
|
|
}
|
|
|
|
|
|
size_t AutoSegment::getPerpandicularsBound ( set<AutoSegment*>& bounds )
|
|
{
|
|
map<AutoContact*,int> contacts;
|
|
map<AutoContact*,int>::iterator icontact;
|
|
|
|
getAlignedContacts ( contacts );
|
|
|
|
for ( icontact=contacts.begin() ; icontact != contacts.end() ; icontact++ ) {
|
|
if ( (icontact->second == 0x1) or (icontact->second == 0x4) ) {
|
|
forEach ( Segment*, isegment, icontact->first->getSlaveComponents().getSubSet<Segment*>() ) {
|
|
AutoSegment* autoSegment = Session::lookup ( *isegment );
|
|
if ( !autoSegment ) continue;
|
|
if ( autoSegment->getDirection() == getDirection() ) continue;
|
|
|
|
bounds.insert ( autoSegment );
|
|
}
|
|
}
|
|
}
|
|
|
|
return bounds.size();
|
|
}
|
|
|
|
|
|
Interval AutoSegment::getMinSpanU ()
|
|
{
|
|
map<AutoContact*,int> contacts;
|
|
map<AutoContact*,int>::iterator icontact;
|
|
|
|
getAlignedContacts ( contacts );
|
|
|
|
DbU::Unit spanMin = DbU::Min;
|
|
DbU::Unit spanMax = DbU::Max;
|
|
Interval constraints;
|
|
unsigned int direction = Constant::perpandicular(getDirection());
|
|
|
|
for ( icontact=contacts.begin() ; icontact != contacts.end() ; icontact++ ) {
|
|
constraints = icontact->first->getUConstraints ( direction );
|
|
if ( icontact->second == 0x1 ) {
|
|
spanMin = max ( spanMin, constraints.getVMax() );
|
|
}
|
|
if ( icontact->second == 0x4 ) {
|
|
spanMax = min ( spanMax, constraints.getVMin() );
|
|
}
|
|
}
|
|
|
|
return Interval(spanMin,spanMax);
|
|
}
|
|
|
|
|
|
void AutoSegment::setAllowOutsideGCell ( bool state, bool propagate )
|
|
{
|
|
if ( allowOutsideGCell() ) return;
|
|
|
|
_setAllowOutsideGCell ( state );
|
|
|
|
if ( propagate ) {
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
isegment->_setAllowOutsideGCell ( state );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void AutoSegment::_setAllowOutsideGCell ( bool state )
|
|
{
|
|
ltrace(200) << "_setAllowOutsideGCell() - " << this << endl;
|
|
_allowOutsideGCell = state;
|
|
}
|
|
|
|
|
|
bool AutoSegment::canGoOutsideGCell () const
|
|
{
|
|
ltracein(200);
|
|
|
|
bool goOutsideGCell = getAutoSource()->canGoOutsideGCell(this);
|
|
goOutsideGCell = goOutsideGCell and getAutoTarget()->canGoOutsideGCell(this);
|
|
|
|
if ( !goOutsideGCell ) {
|
|
ltraceout(200);
|
|
return false;
|
|
}
|
|
|
|
GCell* sourceGCell = getAutoSource()->getGCell();
|
|
GCell* leftGCell = NULL;
|
|
GCell* rightGCell = NULL;
|
|
Interval uside;
|
|
bool goLeft = false;
|
|
bool goRight = false;
|
|
|
|
if ( isHorizontal() ) {
|
|
uside = sourceGCell->getUSide ( Constant::Vertical );
|
|
leftGCell = sourceGCell->getDown();
|
|
rightGCell = sourceGCell->getUp ();
|
|
} else {
|
|
uside = sourceGCell->getUSide ( Constant::Horizontal );
|
|
leftGCell = sourceGCell->getLeft ();
|
|
rightGCell = sourceGCell->getRight();
|
|
}
|
|
|
|
DbU::Unit constraintMin;
|
|
DbU::Unit constraintMax;
|
|
getConstraints ( constraintMin, constraintMax );
|
|
|
|
if ( leftGCell && (uside.getVMin() >= constraintMin) ) {
|
|
ltrace(200) << "Can go Left." << endl;
|
|
goLeft = true;
|
|
}
|
|
// Ugly: Must use the right compensator for VMax.
|
|
if ( rightGCell && (uside.getVMax() <= constraintMax)+DbU::lambda(1.0) ) {
|
|
ltrace(200) << "Can go Right." << endl;
|
|
goRight = true;
|
|
}
|
|
|
|
goOutsideGCell = goOutsideGCell and (goRight or goLeft);
|
|
|
|
// Override.
|
|
//goOutsideGCell = !isGlobal() && !isTerminal();
|
|
|
|
ltrace(200) << "AutoSegment::canGoOutsideGCell() - " << goOutsideGCell << endl;
|
|
ltraceout(200);
|
|
|
|
return goOutsideGCell;
|
|
}
|
|
|
|
|
|
bool AutoSegment::canDesalignate ()
|
|
{
|
|
ltrace(200) << "AutoSegment::canDesalignate()" << endl;
|
|
|
|
map<AutoContact*,int> innerContacts;
|
|
map<AutoContact*,int>::iterator icontact;
|
|
|
|
getAlignedContacts ( innerContacts );
|
|
|
|
for ( icontact=innerContacts.begin() ; icontact != innerContacts.end() ; icontact++ ) {
|
|
ltrace(200) << "| " << "flags:" << icontact->second
|
|
<< " " << (void*)icontact->first->base() << ":" << icontact->first << endl;
|
|
if ( (icontact->second & 0x5 ) && canDesalignate(icontact->first) ) return true;
|
|
//if ( (icontact->second & 0x3 ) && canDesalignate(icontact->first) ) return true;
|
|
//if ( (icontact->second & 0x12) && canDesalignate(icontact->first) ) return true;
|
|
}
|
|
ltrace(200) << "No AutoContact suitable for desalignment." << endl;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void AutoSegment::desalignate ()
|
|
{
|
|
map<AutoContact*,int> innerContacts;
|
|
map<AutoContact*,int>::iterator icontact;
|
|
vector<AutoSegment*> segments;
|
|
|
|
// Ugly. Must fusion with the inner contact loop.
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
segments.push_back ( *isegment );
|
|
}
|
|
|
|
invalidate ();
|
|
getAlignedContacts ( innerContacts );
|
|
|
|
for ( icontact=innerContacts.begin() ; icontact != innerContacts.end() ; icontact++ ) {
|
|
desalignate ( icontact->first );
|
|
//if ( icontact->second & 0x3 ) desalignate ( icontact->first );
|
|
//if ( icontact->second & 0x3 ) desalignate ( icontact->first );
|
|
//if ( icontact->second & 0x12) desalignate ( icontact->first );
|
|
}
|
|
|
|
Session::invalidate ( getNet() );
|
|
Session::revalidateTopology ();
|
|
}
|
|
|
|
|
|
void AutoSegment::changeDepth ( unsigned int depth, bool propagate, bool standAlone )
|
|
{
|
|
invalidate ();
|
|
Session::invalidate ( getNet() );
|
|
Session::setInvalidateMask ( Session::NetSplitContacts );
|
|
|
|
_changeDepth ( depth, true );
|
|
|
|
if ( propagate ) {
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
isegment->_changeDepth ( depth, true );
|
|
}
|
|
}
|
|
|
|
if ( standAlone ) Session::revalidateTopology();
|
|
}
|
|
|
|
|
|
void AutoSegment::_changeDepth ( unsigned int depth, bool withNeighbors )
|
|
{
|
|
ltrace(200) << "_changeDepth() - " << this << endl;
|
|
ltracein(200);
|
|
|
|
const Layer* layer0 = Session::getRoutingGauge()->getRoutingLayer(depth);
|
|
if ( getLayer() != layer0 ) {
|
|
setLayer ( layer0 );
|
|
|
|
getAutoSource()->invalidate();
|
|
getAutoTarget()->invalidate();
|
|
}
|
|
|
|
if ( !withNeighbors ) {
|
|
ltraceout(200);
|
|
return;
|
|
}
|
|
|
|
forEach ( Component*, icomponent, getAutoSource()->getSlaveComponents() ) {
|
|
if ( *icomponent == base() ) continue;
|
|
|
|
Segment* segment = dynamic_cast<Segment*>(*icomponent);
|
|
if ( !segment ) continue;
|
|
|
|
AutoSegment* autoSegment = Session::lookup ( segment );
|
|
if ( !autoSegment ) continue;
|
|
if ( autoSegment->isGlobal () ) continue;
|
|
if ( autoSegment->isTerminal() ) continue;
|
|
|
|
if ( !( autoSegment->isHorizontal() xor isHorizontal() ) ) {
|
|
autoSegment->_changeDepth ( depth, false );
|
|
} else {
|
|
autoSegment->_changeDepth ( depth-1, false );
|
|
}
|
|
}
|
|
|
|
forEach ( Component*, icomponent, getAutoTarget()->getSlaveComponents() ) {
|
|
if ( *icomponent == base() ) continue;
|
|
|
|
Segment* segment = dynamic_cast<Segment*>(*icomponent);
|
|
if ( !segment ) continue;
|
|
|
|
AutoSegment* autoSegment = Session::lookup ( segment );
|
|
if ( !autoSegment ) continue;
|
|
if ( autoSegment->isGlobal () ) continue;
|
|
if ( autoSegment->isTerminal() ) continue;
|
|
|
|
if ( !( autoSegment->isHorizontal() xor isHorizontal() ) ) {
|
|
autoSegment->_changeDepth ( depth, false );
|
|
} else {
|
|
autoSegment->_changeDepth ( depth-1, false );
|
|
}
|
|
}
|
|
|
|
vector<GCell*> gcells;
|
|
getGCells ( gcells );
|
|
for ( size_t i=0 ; i<gcells.size() ; i++ ) {
|
|
gcells[i]->invalidate ();
|
|
}
|
|
|
|
ltraceout(200);
|
|
}
|
|
|
|
|
|
bool AutoSegment::canSlacken ( bool propagate )
|
|
{
|
|
ltrace(200) << "AutoSegment::canSlacken()" << endl;
|
|
|
|
if ( !isGlobal() && !propagate ) return false;
|
|
|
|
if ( _canSlacken() ) return true;
|
|
if ( propagate ) {
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
if ( isegment->_canSlacken() ) return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void AutoSegment::slacken ( bool propagate )
|
|
{
|
|
invalidate ();
|
|
|
|
set<AutoSegment*,AutoSegment::CompareId> collapseds;
|
|
collapseds.insert ( this );
|
|
|
|
if ( propagate ) {
|
|
forEach ( AutoSegment*, isegment, getCollapseds() )
|
|
collapseds.insert ( *isegment );
|
|
}
|
|
|
|
set<AutoSegment*>::iterator isegment = collapseds.begin();
|
|
for ( ; isegment != collapseds.end() ; isegment++ )
|
|
(*isegment)->_slacken ();
|
|
|
|
Session::invalidate ( getNet() );
|
|
Session::revalidateTopology ();
|
|
}
|
|
|
|
|
|
bool AutoSegment::canPivotUp ( bool propagate, float reserve )
|
|
{
|
|
ltrace(200) << "AutoSegment::canPivotUp()" << endl;
|
|
|
|
//if ( isTerminal() ) return false;
|
|
|
|
size_t depth = Session::getRoutingGauge()->getLayerDepth(getLayer());
|
|
if ( depth+2 >= Session::getRoutingGauge()->getDepth() ) return false;
|
|
|
|
vector<GCell*> gcells;
|
|
getGCells ( gcells );
|
|
for ( size_t i=0 ; i<gcells.size() ; i++ ) {
|
|
if ( !gcells[i]->hasFreeTrack(depth,reserve) ) return false;
|
|
}
|
|
|
|
ltrace(200) << getAutoSource() << endl;
|
|
ltrace(200) << getAutoTarget() << endl;
|
|
ltrace(200) << "min depths, Segment:" << depth
|
|
<< " S:" << getAutoSource()->getMinDepth()
|
|
<< " T:" << getAutoTarget()->getMinDepth() << endl;
|
|
|
|
if ( getAutoSource()->getMinDepth() < depth ) return false;
|
|
if ( getAutoTarget()->getMinDepth() < depth ) return false;
|
|
if ( not propagate ) {
|
|
ltrace(200) << "AutoSegment::canPivotUp() - true [no propagate]" << endl;
|
|
return true;
|
|
}
|
|
|
|
if ( propagate ) {
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
isegment->getGCells ( gcells );
|
|
for ( size_t i=0 ; i<gcells.size() ; i++ ) {
|
|
if ( !gcells[i]->hasFreeTrack(depth,reserve) ) return false;
|
|
}
|
|
if ( isegment->getAutoSource()->getMinDepth() < depth ) return false;
|
|
if ( isegment->getAutoTarget()->getMinDepth() < depth ) return false;
|
|
}
|
|
}
|
|
|
|
ltrace(200) << "AutoSegment::canPivotUp() - true [propagate]" << endl;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool AutoSegment::canMoveUp ( bool propagate, float reserve )
|
|
{
|
|
ltrace(200) << "AutoSegment::canMoveUp()" << endl;
|
|
|
|
if ( isLayerChange() ) return false;
|
|
if ( isTerminal() and isLocal() ) return false;
|
|
|
|
size_t depth = Session::getRoutingGauge()->getLayerDepth(getLayer()) + 2;
|
|
if ( depth >= Session::getConfiguration()->getAllowedDepth() ) return false;
|
|
|
|
vector<GCell*> gcells;
|
|
getGCells ( gcells );
|
|
for ( size_t i=0 ; i<gcells.size() ; i++ ) {
|
|
if ( not gcells[i]->hasFreeTrack(depth,reserve) ) return false;
|
|
}
|
|
|
|
if ( isLocal() and not propagate ) {
|
|
if ( not getAutoSource()->canMoveUp(this) ) return false;
|
|
if ( not getAutoTarget()->canMoveUp(this) ) return false;
|
|
return true;
|
|
}
|
|
|
|
bool hasGlobalSegment = false;
|
|
size_t collapseds = 0;
|
|
if ( propagate ) {
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
collapseds++;
|
|
if ( isegment->isGlobal() ) hasGlobalSegment = true;
|
|
|
|
isegment->getGCells ( gcells );
|
|
for ( size_t i=0 ; i<gcells.size() ; i++ ) {
|
|
if ( not gcells[i]->hasFreeTrack(depth,reserve) ) {
|
|
ltrace(200) << "Not enough free track in " << gcells[i] << endl;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool AutoSegment::moveUp ( bool propagate )
|
|
{
|
|
if ( !canMoveUp(propagate) ) return false;
|
|
|
|
changeDepth ( Session::getRoutingGauge()->getLayerDepth(getLayer()) + 2, propagate );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool AutoSegment::canDogLeg ( Interval interval )
|
|
{
|
|
ltrace(200) << "AutoSegment::canDogLeg(Interval) " << interval << endl;
|
|
|
|
size_t leftDogleg = 0;
|
|
size_t rightDogleg = 0;
|
|
if ( getSpanU().contains(interval.getVMin()) ) leftDogleg++;
|
|
if ( getSpanU().contains(interval.getVMax()) ) rightDogleg++;
|
|
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
if ( isegment->getSpanU().contains(interval.getVMin()) ) {
|
|
if ( isegment->isFixed() ) return false;
|
|
leftDogleg++;
|
|
}
|
|
if ( isegment->getSpanU().contains(interval.getVMax()) ) {
|
|
if ( isegment->isFixed() ) return false;
|
|
rightDogleg++;
|
|
}
|
|
}
|
|
|
|
if ( (leftDogleg == 1) and (rightDogleg <= 1) ) return true;
|
|
if ( (leftDogleg <= 1) and (rightDogleg == 1) ) return true;
|
|
|
|
ltrace(200) << "leftCount:" << leftDogleg << " rightCount:" << rightDogleg << endl;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void AutoSegment::makeDogLeg ( Interval interval, bool upLayer, bool& leftDogleg )
|
|
{
|
|
ltrace(200) << "AutoSegment::makeDogLeg(Interval)" << endl;
|
|
|
|
size_t leftDoglegCount = 0;
|
|
size_t rightDoglegCount = 0;
|
|
AutoSegment* leftCandidate = NULL;
|
|
AutoSegment* rightCandidate = NULL;
|
|
|
|
if ( getSpanU().contains(interval.getVMin()) ) { leftCandidate = this; leftDoglegCount++; }
|
|
if ( getSpanU().contains(interval.getVMax()) ) { rightCandidate = this; rightDoglegCount++; }
|
|
|
|
forEach ( AutoSegment*, isegment, getCollapseds() ) {
|
|
if ( isegment->getSpanU().contains(interval.getVMin()) ) { leftCandidate = *isegment; leftDoglegCount++; }
|
|
if ( isegment->getSpanU().contains(interval.getVMax()) ) { rightCandidate = *isegment; rightDoglegCount++; }
|
|
}
|
|
|
|
leftDogleg = true;
|
|
if ( (leftDoglegCount != 1) and (rightDoglegCount != 1) ) return;
|
|
if ( !leftDoglegCount ) {
|
|
leftDogleg = false;
|
|
leftCandidate = rightCandidate;
|
|
rightCandidate = NULL;
|
|
}
|
|
|
|
if ( leftCandidate && rightCandidate ) {
|
|
ltrace(200) << "Left Constraint: " << leftCandidate->getSourceConstraints(true) << endl;
|
|
ltrace(200) << "Right Constraint: " << rightCandidate->getTargetConstraints(true) << endl;
|
|
|
|
if ( leftCandidate ->getTargetConstraints(true).getSize()
|
|
< rightCandidate->getSourceConstraints(true).getSize() ) {
|
|
leftCandidate = rightCandidate;
|
|
leftDogleg = false;
|
|
}
|
|
} else {
|
|
if ( !leftCandidate ) {
|
|
leftCandidate = rightCandidate;
|
|
leftDogleg = false;
|
|
}
|
|
}
|
|
|
|
if ( leftCandidate ) {
|
|
leftCandidate->_makeDogLeg ( getAutoSource()->getGCell(), upLayer );
|
|
|
|
const vector<AutoSegment*>& dogLegs = Session::getDogLegs();
|
|
if ( dogLegs.size() >= 2 ) {
|
|
DbU::Unit axis;
|
|
if ( leftDogleg )
|
|
axis = interval.getVMin() - DbU::lambda(5.0); // Ugly: Hard-wired track spacing.
|
|
else
|
|
axis = interval.getVMax() + DbU::lambda(5.0); // Ugly: Hard-wired track spacing.
|
|
|
|
ltrace(200) << "AutoSegment::makeDogLeg(): @" << DbU::getValueString(axis) << endl;
|
|
dogLegs[1]->setAxis ( axis );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void AutoSegment::makeDogLeg ( GCell* dogLegGCell, bool upLayer )
|
|
{
|
|
ltrace(160) << "AutoSegment::makeDogLeg(GCell*)" << endl;
|
|
ltracein(160);
|
|
|
|
if ( isFixed() ) {
|
|
cerr << Error("AutoSegment::makeDogLeg(): Cannot make a dog leg on a fixed segment.\n"
|
|
" (on: %s)",_getString().c_str()) << endl;
|
|
return;
|
|
}
|
|
|
|
invalidate ();
|
|
|
|
if ( dogLegGCell->getUSide(getDirection()).intersect(getSpanU()) ) {
|
|
ltrace(159) << "Dogleg in " << this << endl;
|
|
_makeDogLeg ( dogLegGCell, upLayer );
|
|
//Session::revalidate ( getNet() );
|
|
} else {
|
|
ltrace(159) << "Looking in aligneds." << endl;
|
|
forEach ( AutoSegment*, aligned, getCollapseds() ) {
|
|
ltrace(159) << "| Try in " << *aligned << endl;
|
|
if ( dogLegGCell->getUSide(getDirection()).intersect(aligned->getSpanU()) ) {
|
|
ltrace(159) << "Dogleg in " << *aligned << endl;
|
|
aligned->_makeDogLeg ( dogLegGCell, upLayer );
|
|
//Session::revalidate ( getNet() );
|
|
ltraceout(160);
|
|
return;
|
|
}
|
|
}
|
|
cerr << Bug("Cannot make a dogleg in %s at %s"
|
|
,_getString().c_str(), getString(dogLegGCell).c_str()) << endl;
|
|
}
|
|
ltraceout(160);
|
|
}
|
|
|
|
|
|
bool AutoSegment::_check () const
|
|
{
|
|
bool coherency = true;
|
|
|
|
coherency = coherency && checkInvalidated();
|
|
coherency = coherency && checkPositions();
|
|
coherency = coherency && checkConstraints();
|
|
|
|
return coherency;
|
|
}
|
|
|
|
|
|
string AutoSegment::_getString () const
|
|
{
|
|
string s = getSegment()->_getString();
|
|
s.insert ( 1, "id: " );
|
|
s.insert ( 4, getString(_id) );
|
|
s.insert ( s.size()-1, (_isFixed )?" F":" -" );
|
|
s.insert ( s.size()-1, (_strap )? "S": "-" );
|
|
s.insert ( s.size()-1, (_isCanonical)? "C": "-" );
|
|
s.insert ( s.size()-1, (_isCollapsed)? "c": "-" );
|
|
s.insert ( s.size()-1, (_isGlobal) ? "g": "-" );
|
|
s.insert ( s.size()-1, (_isTerminal) ? "t": "-" );
|
|
s.insert ( s.size()-1, (_slackened) ? "S": "-" );
|
|
s.insert ( s.size()-1, (_invalidated)? "i": "-" );
|
|
return s;
|
|
}
|
|
|
|
|
|
Record* AutoSegment::_getRecord () const
|
|
{
|
|
Record* record = getSegment()->_getRecord ();
|
|
record->add ( getSlot ( "_gcell" , _gcell ) );
|
|
record->add ( getSlot ( "_isHorizontal" , &_isHorizontal ) );
|
|
record->add ( getSlot ( "_isFixed" , &_isFixed ) );
|
|
record->add ( getSlot ( "_strap" , &_strap ) );
|
|
record->add ( getSlot ( "_layerChange" , &_layerChange ) );
|
|
record->add ( getSlot ( "_isCanonical" , &_isCanonical ) );
|
|
record->add ( getSlot ( "_isCollapsed" , &_isCollapsed ) );
|
|
record->add ( getSlot ( "_isGlobal" , &_isGlobal ) );
|
|
record->add ( getSlot ( "_isTerminal" , &_isTerminal ) );
|
|
record->add ( getSlot ( "_slackened" , &_slackened ) );
|
|
record->add ( getSlot ( "_invalidated" , &_invalidated ) );
|
|
record->add ( getSlot ( "_sourcePosition", &_sourcePosition ) );
|
|
record->add ( getSlot ( "_targetPosition", &_targetPosition ) );
|
|
return record;
|
|
}
|
|
|
|
|
|
AutoSegment* AutoSegment::create ( AutoContact* source
|
|
, AutoContact* target
|
|
, Segment* hurricaneSegment
|
|
)
|
|
{
|
|
static Layer* verticalLayer = DataBase::getDB()->getTechnology()->getLayer ( "METAL3" );
|
|
static Layer* horizontalLayer = DataBase::getDB()->getTechnology()->getLayer ( "METAL2" );
|
|
|
|
AutoSegment* segment;
|
|
Horizontal* horizontal;
|
|
Vertical* vertical;
|
|
|
|
Contact* contact = dynamic_cast<Contact*>(hurricaneSegment->getSource());
|
|
AutoContact* autoContact = Session::lookup(contact);
|
|
if ( contact == NULL ) {
|
|
throw Error ( badSegmentSource, getString(hurricaneSegment).c_str() );
|
|
if ( autoContact and ( autoContact != source ) )
|
|
throw Error ( mismatchSegmentSource
|
|
, getString(hurricaneSegment).c_str()
|
|
, getString(contact).c_str() );
|
|
}
|
|
|
|
contact = dynamic_cast<Contact*>(hurricaneSegment->getTarget());
|
|
autoContact = Session::lookup(contact);
|
|
if ( contact == NULL ) {
|
|
throw Error ( badSegmentTarget, getString(hurricaneSegment).c_str() );
|
|
if ( autoContact and ( autoContact != target ) )
|
|
throw Error ( mismatchSegmentTarget
|
|
, getString(hurricaneSegment).c_str()
|
|
, getString(contact).c_str() );
|
|
}
|
|
|
|
Hook* hook = hurricaneSegment->getSourceHook();
|
|
hook->detach ();
|
|
hook->attach ( source->getBodyHook() );
|
|
|
|
hook = hurricaneSegment->getTargetHook();
|
|
hook->detach ();
|
|
hook->attach ( target->getBodyHook() );
|
|
|
|
if ( (horizontal = dynamic_cast<Horizontal*>(hurricaneSegment) ) ) {
|
|
if ( horizontal->getLayer() != horizontalLayer ) {
|
|
if ( !Session::getKatabatic()->isGMetal(horizontal->getLayer()) )
|
|
cerr << Warning("Segment %s forced to %s."
|
|
,getString(horizontal).c_str()
|
|
,getString(horizontalLayer).c_str()) << endl;
|
|
horizontal->setLayer ( horizontalLayer );
|
|
}
|
|
|
|
segment = AutoHorizontal::create ( horizontal
|
|
, AutoSegment::Global
|
|
, false
|
|
, false
|
|
);
|
|
} else if ( (vertical = dynamic_cast<Vertical*>(hurricaneSegment)) ) {
|
|
if ( vertical->getLayer() != verticalLayer ) {
|
|
if ( !Session::getKatabatic()->isGMetal(vertical->getLayer()) )
|
|
cerr << Warning("Segment %s forced to %s."
|
|
,getString(vertical).c_str()
|
|
,getString(verticalLayer).c_str()) << endl;
|
|
vertical->setLayer ( verticalLayer );
|
|
}
|
|
|
|
segment = AutoVertical::create ( vertical
|
|
, AutoSegment::Global
|
|
, false
|
|
, false
|
|
);
|
|
} else {
|
|
throw Error ( badSegment, getString(source).c_str(), getString(target).c_str() );
|
|
}
|
|
|
|
ltrace(99) << "Creating " << segment << endl;
|
|
|
|
return segment;
|
|
}
|
|
|
|
|
|
AutoSegment* AutoSegment::create ( AutoContact* source
|
|
, AutoContact* target
|
|
, unsigned int dir
|
|
, int type
|
|
, bool terminal
|
|
, bool collapsed
|
|
)
|
|
{
|
|
//static Layer* verticalLayer = DataBase::getDB()->getTechnology()->getLayer ( "METAL3" );
|
|
//static Layer* horizontalLayer = DataBase::getDB()->getTechnology()->getLayer ( "METAL2" );
|
|
|
|
static const Layer* horizontalLayer = Session::getRoutingLayer ( 1 );
|
|
static const Layer* verticalLayer = Session::getRoutingLayer ( 2 );
|
|
|
|
GCell* gcell;
|
|
GCell* end;
|
|
AutoSegment* segment;
|
|
|
|
if ( dir & Constant::Horizontal ) {
|
|
segment = AutoHorizontal::create ( source
|
|
, target
|
|
, horizontalLayer
|
|
, source->getY()
|
|
, DbU::lambda(2.0)
|
|
, type
|
|
, terminal
|
|
, collapsed
|
|
);
|
|
if ( type == AutoSegment::Global ) {
|
|
if ( source->getGCell()->getX() < target->getGCell()->getX() ) {
|
|
gcell = source->getGCell()->getRight();
|
|
end = target->getGCell();
|
|
} else {
|
|
gcell = target->getGCell()->getRight();
|
|
end = source->getGCell();
|
|
}
|
|
for ( ; gcell != end ; gcell = gcell->getRight() ) {
|
|
if ( !gcell ) {
|
|
cerr << Error("AutoSegment::create() : NULL GCell.") << endl;
|
|
break;
|
|
}
|
|
gcell->addHSegment ( segment );
|
|
}
|
|
}
|
|
} else if ( dir & Constant::Vertical ) {
|
|
segment = AutoVertical::create ( source
|
|
, target
|
|
, verticalLayer
|
|
, source->getX()
|
|
, DbU::lambda(2.0)
|
|
, type
|
|
, terminal
|
|
, collapsed
|
|
);
|
|
if ( type == AutoSegment::Global ) {
|
|
if ( source->getGCell()->getY() < target->getGCell()->getY() ) {
|
|
gcell = source->getGCell()->getUp();
|
|
end = target->getGCell();
|
|
} else {
|
|
gcell = target->getGCell()->getUp();
|
|
end = source->getGCell();
|
|
}
|
|
for ( ; gcell != end ; gcell = gcell->getUp() ) {
|
|
if ( !gcell ) {
|
|
cerr << Error("AutoSegment::create() : NULL GCell.") << endl;
|
|
break;
|
|
}
|
|
gcell->addVSegment ( segment );
|
|
}
|
|
}
|
|
} else
|
|
throw Error ( badSegment, getString(source).c_str(), getString(target).c_str() );
|
|
|
|
ltrace(99) << "create() " << segment << endl;
|
|
|
|
return segment;
|
|
}
|
|
|
|
|
|
void AutoSegment::destroy ()
|
|
{
|
|
_preDestroy ();
|
|
delete this;
|
|
}
|
|
|
|
|
|
bool AutoSegment::isTopologicalBound ( AutoSegment* seed
|
|
, bool superior
|
|
, bool isHorizontal )
|
|
{
|
|
ltrace(80) << "isTopologicalBound() - " << seed << endl;
|
|
ltracein(80);
|
|
|
|
set<AutoContact*> exploreds;
|
|
vector<AutoContact*> stack;
|
|
DbU::Unit axis;
|
|
|
|
if ( superior ) axis = seed->getTargetU();
|
|
else axis = seed->getSourceU();
|
|
|
|
ltrace(80) << "check for bound " << DbU::getValueString(axis) << endl;
|
|
|
|
exploreds.insert ( seed->getAutoSource() );
|
|
exploreds.insert ( seed->getAutoTarget() );
|
|
|
|
if ( seed->getLength() ) {
|
|
if ( superior ) stack.push_back ( seed->getAutoTarget() );
|
|
else stack.push_back ( seed->getAutoSource() );
|
|
} else {
|
|
stack.push_back ( seed->getAutoTarget() );
|
|
stack.push_back ( seed->getAutoSource() );
|
|
}
|
|
|
|
while ( !stack.empty() ) {
|
|
AutoContact* currentContact = stack.back();
|
|
stack.pop_back ();
|
|
|
|
ltrace(80) << "Exploring: " << (void*)currentContact
|
|
<< " " << currentContact << endl;
|
|
|
|
exploreds.insert ( currentContact );
|
|
|
|
if ( currentContact->getAnchor() ) { ltraceout(80); return true; }
|
|
|
|
forEach ( Component*, component, currentContact->getSlaveComponents() ) {
|
|
Segment* segment = dynamic_cast<Segment*>(*component);
|
|
if ( !segment ) continue;
|
|
|
|
AutoSegment* autoSegment = Session::lookup ( segment );
|
|
if ( !autoSegment ) continue;
|
|
|
|
if ( !autoSegment->getLength() ) {
|
|
AutoContact* contact = autoSegment->getAutoSource();
|
|
if ( contact && ( contact != currentContact ) ) {
|
|
if ( exploreds.find(contact) == exploreds.end() )
|
|
stack.push_back ( contact );
|
|
}
|
|
|
|
contact = autoSegment->getAutoTarget();
|
|
if ( contact && ( contact != currentContact ) ) {
|
|
if ( exploreds.find(contact) == exploreds.end() )
|
|
stack.push_back ( contact );
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( autoSegment->isHorizontal() != isHorizontal ) continue;
|
|
|
|
ltrace(80) << "| " << autoSegment << endl;
|
|
|
|
if ( superior ) {
|
|
if ( autoSegment->getTargetU() > axis ) { ltraceout(80); return true; }
|
|
} else {
|
|
if ( autoSegment->getSourceU() < axis ) { ltraceout(80); return true; }
|
|
}
|
|
}
|
|
}
|
|
|
|
ltraceout(80);
|
|
return false;
|
|
}
|
|
|
|
|
|
unsigned int AutoSegment::getPerpandicularState ( AutoContact* contact
|
|
, AutoSegment* source
|
|
, AutoSegment* current
|
|
, bool isHorizontalMaster
|
|
, const Layer* masterLayer )
|
|
{
|
|
unsigned int state = 0;
|
|
|
|
bool sourcePerpandicular = arePerpandiculars ( isHorizontalMaster, source );
|
|
bool currentPerpandicular = arePerpandiculars ( isHorizontalMaster, current );
|
|
bool contactAlignate
|
|
= (contact->isHAlignate() and current->isHorizontal() and isHorizontalMaster)
|
|
or (contact->isVAlignate() and !current->isHorizontal() and !isHorizontalMaster);
|
|
|
|
if ( not currentPerpandicular and masterLayer and (masterLayer != current->getLayer()) )
|
|
state |= ParallelAndLayerChange;
|
|
|
|
if ( currentPerpandicular and !current->isCollapsed() )
|
|
state |= PerpandicularAny;
|
|
|
|
if ( sourcePerpandicular ) {
|
|
// Source segment is perpandicular to master.
|
|
if ( currentPerpandicular and !current->isCollapsed() )
|
|
state |= PerpandicularIndirect;
|
|
} else {
|
|
// Source segment is parallel to master.
|
|
if ( not (currentPerpandicular and current->isCollapsed()) and not contactAlignate ) {
|
|
// Current segment is parallel OR expanded.
|
|
state |= ParallelOrExpanded;
|
|
}
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
void AutoSegment::getTopologicalInfos ( AutoSegment* seed
|
|
, vector<AutoSegment*>& collapseds
|
|
, vector<AutoSegment*>& perpandiculars
|
|
, DbU::Unit& leftBound
|
|
, DbU::Unit& rightBound
|
|
)
|
|
{
|
|
ltrace(80) << "getTopologicalInfos() - " << seed << endl;
|
|
|
|
leftBound = DbU::Max;
|
|
rightBound = DbU::Min;
|
|
|
|
AutoSegmentStack stack;
|
|
|
|
stack.push ( seed->getAutoSource(), seed );
|
|
stack.push ( seed->getAutoTarget(), seed );
|
|
|
|
while ( !stack.isEmpty() ) {
|
|
AutoContact* sourceContact = stack.getAutoContact ();
|
|
AutoSegment* sourceSegment = stack.getAutoSegment ();
|
|
|
|
stack.pop ();
|
|
|
|
DbU::Unit constraint;
|
|
|
|
if ( seed->isHorizontal() ) constraint = sourceContact->getCBXMax();
|
|
else constraint = sourceContact->getCBYMax();
|
|
if ( constraint < leftBound ) leftBound = constraint;
|
|
|
|
if ( seed->isHorizontal() ) constraint = sourceContact->getCBXMin();
|
|
else constraint = sourceContact->getCBYMin();
|
|
if ( constraint > rightBound ) rightBound = constraint;
|
|
|
|
forEach ( Component*, component, sourceContact->getSlaveComponents() ) {
|
|
Segment* segment = dynamic_cast<Segment*>(*component);
|
|
if ( ( !segment ) || ( segment == sourceSegment->getSegment() ) ) continue;
|
|
|
|
AutoSegment* currentSegment = Session::lookup ( segment );
|
|
if ( !currentSegment ) {
|
|
cerr << Error("Can't lookup <AutoSegment> for %s.",getString(segment).c_str()) << endl;
|
|
continue;
|
|
}
|
|
|
|
unsigned int state = getPerpandicularState ( sourceContact
|
|
, sourceSegment
|
|
, currentSegment
|
|
, seed );
|
|
if ( state & PerpandicularAny ) {
|
|
ltrace(79) << "Perpandicular: " << currentSegment << endl;
|
|
perpandiculars.push_back ( currentSegment );
|
|
}
|
|
if ( state & (PerpandicularIndirect
|
|
|ParallelOrExpanded
|
|
|ParallelAndLayerChange ) ) {
|
|
ltrace(79) << "Reject: " << currentSegment << endl;
|
|
continue;
|
|
}
|
|
|
|
if ( !areAligneds(currentSegment,seed) ) {
|
|
collapseds.push_back ( currentSegment );
|
|
ltrace(79) << "collapsed: " << currentSegment << endl;
|
|
}
|
|
|
|
Component* opposite = segment->getOppositeAnchor ( sourceContact->getContact() );
|
|
AutoContact* targetContact = Session::lookup(static_cast<Contact*>(opposite));
|
|
if ( targetContact ) stack.push ( targetContact, currentSegment );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int AutoSegment::getTerminalCount ( AutoSegment* seed, vector<AutoSegment*>& collapseds )
|
|
{
|
|
ltrace(80) << "getTerminalCount() - " << seed << " (+collapseds)" << endl;
|
|
|
|
int count = 0;
|
|
for ( size_t i=0 ; i < collapseds.size() ; i++ ) {
|
|
if ( collapseds[i]->isTerminal() )
|
|
count++;
|
|
}
|
|
if ( seed->getAutoSource()->isTerminal() ) count++;
|
|
if ( seed->getAutoTarget()->isTerminal() ) count++;
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
} // End of Katabatic namespace.
|