From 1dafcbc882a6b5b2eee0e9627c54f656e487eda0 Mon Sep 17 00:00:00 2001 From: Jean-Paul Chaput Date: Sun, 6 Mar 2016 12:37:30 +0100 Subject: [PATCH] UTurn management in Katabatic & Kite. * New: in Katabatic, in AutoSegment, recognize segments that are U-turn. That is, based on two turn Contacts and going both top or bottom. * New: In Kite, manage one pitch U-turn by making them diseapear from the tracks. And make them same-metal when saving. Add a pack stage to try to compact U-turn. --- katabatic/src/AutoSegment.cpp | 46 +- katabatic/src/katabatic/AutoSegment.h | 4 +- kite/src/DataNegociate.cpp | 30 +- kite/src/NegociateWindow.cpp | 61 +- kite/src/RoutingEvent.cpp | 29 +- kite/src/Session.cpp | 8 +- kite/src/TrackCost.cpp | 15 +- kite/src/TrackElement.cpp | 3 +- kite/src/TrackSegment.cpp | 1 + kite/src/TrackSegment.cpp.OK | 898 ++++++++++++++++++++++++++ kite/src/kite/DataNegociate.h | 5 +- kite/src/kite/NegociateWindow.h | 5 +- kite/src/kite/RoutingEvent.h | 11 +- kite/src/kite/TrackElement.h | 3 +- kite/src/kite/TrackSegment.h | 3 +- 15 files changed, 1085 insertions(+), 37 deletions(-) create mode 100644 kite/src/TrackSegment.cpp.OK diff --git a/katabatic/src/AutoSegment.cpp b/katabatic/src/AutoSegment.cpp index cb8787b2..54c44a47 100644 --- a/katabatic/src/AutoSegment.cpp +++ b/katabatic/src/AutoSegment.cpp @@ -1,7 +1,7 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -1154,15 +1154,57 @@ namespace Katabatic { } - bool AutoSegment::canReduce () const + bool AutoSegment::isUTurn () const { + if (isGlobal()) return false; + + AutoContact* source = getAutoSource(); + AutoContact* target = getAutoTarget(); + + cerr << "AutoSegment::isUTurn():" << endl; + + if (not source->isTurn() or not target->isTurn()) return false; + + cerr << " Turn connected" << endl; + + AutoSegment* perpandicular = source->getPerpandicular( this ); + bool onPSourceSource = (perpandicular->getAutoSource() == source); + + perpandicular = target->getPerpandicular( this ); + bool onPTargetSource = (perpandicular->getAutoSource() == target); + + cerr << " PSource:" << onPSourceSource << " PTarget:" << onPTargetSource << endl; + + return not (onPSourceSource xor onPTargetSource); + } + + + bool AutoSegment::isReduceCandidate () const + { + if (isGlobal()) return false; + if (not isSpinTopOrBottom()) return false; + if (_reduceds) return false; + AutoContact* source = getAutoSource(); AutoContact* target = getAutoTarget(); if (not source->isTurn() or not target->isTurn()) return false; + + return true; + } + + + bool AutoSegment::canReduce () const + { + if (isGlobal()) return false; if (not isSpinTopOrBottom()) return false; if (_reduceds) return false; + AutoContact* source = getAutoSource(); + AutoContact* target = getAutoTarget(); + + if (not source->isTurn() or not target->isTurn()) return false; + unsigned int perpandicularDepth = getDepth(); if (isSpinBottom()) --perpandicularDepth; else if (isSpinTop()) { diff --git a/katabatic/src/katabatic/AutoSegment.h b/katabatic/src/katabatic/AutoSegment.h index f48043eb..a8ac253c 100644 --- a/katabatic/src/katabatic/AutoSegment.h +++ b/katabatic/src/katabatic/AutoSegment.h @@ -1,7 +1,7 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -187,6 +187,8 @@ namespace Katabatic { inline bool isUnsetAxis () const; inline bool isSlackened () const; inline bool isUserDefined () const; + bool isReduceCandidate () const; + bool isUTurn () const; virtual bool _canSlacken () const = 0; bool canReduce () const; bool mustRaise () const; diff --git a/kite/src/DataNegociate.cpp b/kite/src/DataNegociate.cpp index eaf34f56..5b55e2d4 100644 --- a/kite/src/DataNegociate.cpp +++ b/kite/src/DataNegociate.cpp @@ -1,7 +1,7 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -59,6 +59,7 @@ namespace Kite { , _attractors () , _perpandiculars () , _perpandicularFree(false) + , _reduceRanges { Interval(), Interval() } { } @@ -73,6 +74,10 @@ namespace Kite { if ( _attractors[i] > axis ) attraction += _attractors[i] - axis; else attraction += axis - _attractors[i]; } + for ( size_t i=0 ; i<2 ; ++i ) { + if (_reduceRanges[i].isEmpty()) continue; + if (_reduceRanges[i].contains(axis)) attraction -= 2*_trackSegment->getPitch(); + } return attraction; } @@ -85,10 +90,14 @@ namespace Kite { ltrace(148) << "DataNegociate::update() - " << _trackSegment << endl; ltracein(148); + size_t reduceCandidates = 0; + DbU::Unit pitch = _trackSegment->getPitch(); vector collapseds; vector perpandiculars; map attractorSpins; + _reduceRanges[0].makeEmpty(); + _reduceRanges[1].makeEmpty(); _perpandiculars.clear(); AutoSegment::getTopologicalInfos( _trackSegment->base() , collapseds @@ -127,7 +136,9 @@ namespace Kite { if (RoutingEvent::getStage() == RoutingEvent::Repair) perpandicular->base()->setFlagsOnAligneds( Katabatic::SegUnbound ); - interval.inflate( DbU::fromLambda(-0.5) ); + //cerr << "perpandicular:" << perpandicular << endl; + //cerr << " " << interval << endl; + //interval.inflate( DbU::fromLambda(-0.5) ); ltrace(148) << "| perpandicular: " << perpandiculars[i] << endl; ltrace(148) << "| canonical: " << perpandicular << endl; @@ -144,6 +155,7 @@ namespace Kite { ltrace(148) << "Not in any track " << perpandicular << endl; } +#if 0 if (interval.isPonctual()) { ltrace(148) << "Punctual attractor @" << DbU::getValueString(interval.getVMin()) << endl; _attractors.push_back( interval.getVMin() ); @@ -177,6 +189,20 @@ namespace Kite { ltrace(148) << "Right attractor @" << DbU::getValueString(interval.getVMax()) << endl; } + if (perpandicular->base()->isReduceCandidate()) { + if (reduceCandidates < 2) { + if (interval.getVMin()+DbU::fromLambda(0.5) == _trackSegment->getAxis()) { + _reduceRanges[reduceCandidates] = Interval( interval.getVMax()-pitch + , interval.getVMax()+pitch ); + } else if (interval.getVMax()-DbU::fromLambda(0.5) == _trackSegment->getAxis()) { + _reduceRanges[reduceCandidates] = Interval( interval.getVMin()-pitch + , interval.getVMin()+pitch ); + } + ++reduceCandidates; + } + } +#endif + ltraceout(148); } if ( not _trackSegment->isTerminal() and (_perpandiculars.size() < 2) ) diff --git a/kite/src/NegociateWindow.cpp b/kite/src/NegociateWindow.cpp index 99191c1c..45bcc51a 100644 --- a/kite/src/NegociateWindow.cpp +++ b/kite/src/NegociateWindow.cpp @@ -1,7 +1,7 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -375,6 +375,58 @@ namespace Kite { } + void NegociateWindow::_pack ( size_t& count, bool last ) + { + unsigned long limit = _kite->getEventsLimit(); + unsigned int pushStage = RoutingEvent::getStage(); + RoutingEvent::setStage( RoutingEvent::Pack ); + + RoutingEventQueue packQueue; + //for ( size_t i = (count > 600) ? count-600 : 0 + // ; (i<_eventHistory.size()-(last ? 0 : 100)) and not isInterrupted() ; i++ ) { + for ( size_t i=0 ; i<_eventHistory.size() ; ++i ) { + RoutingEvent* event = _eventHistory.getNth(i); + + if ( event and not event->isCloned() ) { + cerr << "Cloned:" << event->isCloned() + << " UTurn:" << event->getSegment()->isUTurn() << " " << event->getSegment() << endl; + } + + if ( event and not event->isCloned() and event->getSegment()->isUTurn() ) { + event->reschedule( packQueue, 0 ); + } + } + packQueue.commit(); + + while ( not packQueue.empty() and not isInterrupted() ) { + RoutingEvent* event = packQueue.pop(); + + if (tty::enabled()) { + cmess2 << " " + << setfill(' ') << tty::reset << tty::cr; + cmess2.flush(); + } else { + cmess2 << " getEventLevel() << ":" << event->getPriority() << "> " + << event->getSegment() + << endl; + cmess2.flush(); + } + + event->process( packQueue, _eventHistory, _eventLoop ); + + if (RoutingEvent::getProcesseds() >= limit) setInterrupt( true ); + } + // Count will be wrong! + + RoutingEvent::setStage( pushStage ); + } + + size_t NegociateWindow::_negociate () { ltrace(500) << "Deter| NegociateWindow::_negociate()" << endl; @@ -412,10 +464,15 @@ namespace Kite { } event->process( _eventQueue, _eventHistory, _eventLoop ); - count++; + + //if (count and not (count % 500)) { + // _pack( count, false ); + //} + if (RoutingEvent::getProcesseds() >= limit) setInterrupt( true ); } + //_pack( count, true ); if (count and cmess2.enabled() and tty::enabled()) cmess1 << endl; ltrace(500) << "Deter| Repair Stage" << endl; diff --git a/kite/src/RoutingEvent.cpp b/kite/src/RoutingEvent.cpp index 9cf6540d..fd4904e3 100644 --- a/kite/src/RoutingEvent.cpp +++ b/kite/src/RoutingEvent.cpp @@ -1,14 +1,14 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | // | K i t e - D e t a i l e d R o u t e r | // | | // | Author : Jean-Paul CHAPUT | -// | E-mail : Jean-Paul.Chaput@asim.lip6.fr | +// | E-mail : Jean-Paul.Chaput@lip6.fr | // | =============================================================== | // | C++ Module : "./RoutingEvent.cpp" | // +-----------------------------------------------------------------+ @@ -341,6 +341,8 @@ namespace Kite { if (getStage() == Repair) { fork->setMode( RoutingEvent::Repair ); _segment->getDataNegociate()->setState( DataNegociate::Repair ); + } else if (getStage() == RoutingEvent::Pack) { + fork->setMode( RoutingEvent::Pack ); } queue.repush( fork ); @@ -415,12 +417,13 @@ namespace Kite { //_preCheck( _segment ); _eventLevel = 0; - history.push( this ); + if (_mode != Pack) history.push( this ); if ( isProcessed() or isDisabled() ) { ltrace(200) << "Already processed or disabled." << endl; } else { setProcessed(); + setTimeStamp( _processeds ); switch ( _mode ) { case Negociate: _processNegociate( queue, history ); break; @@ -529,10 +532,7 @@ namespace Kite { { ltrace(200) << "* Mode:Pack." << endl; - if (_segment->getTrack() != NULL) { - ltrace(200) << "* Cancel: already in Track." << endl; - return; - } + if (not _segment->isUTurn()) return; SegmentFsm fsm ( this, queue, history ); if (fsm.getState() == SegmentFsm::MissingData ) return; @@ -543,15 +543,14 @@ namespace Kite { ltrace(200) << "| " << fsm.getCost(i) << endl; ltraceout(200); - if (fsm.getCosts().size() and fsm.getCost(0).isFree()) { - ltrace(200) << "Insert in free space." << endl; - Session::addInsertEvent( _segment, fsm.getCost(0).getTrack() ); + if ( _segment->getTrack() + and fsm.getCosts().size() + and fsm.getCost(0).isFree() + and (fsm.getCost(0).getTrack() != _segment->getTrack()) ) { + + cerr << "_processPack(): move to " << fsm.getCost(0).getTrack() << endl; + Session::addMoveEvent( _segment, fsm.getCost(0).getTrack() ); fsm.setState( SegmentFsm::SelfInserted ); - } else { - ltrace(200) << "Pack failed." << endl; - _mode = Negociate; - fsm.addAction( _segment, SegmentAction::SelfInsert ); - fsm.doActions(); } } diff --git a/kite/src/Session.cpp b/kite/src/Session.cpp index 90dd21c7..639f4c8e 100644 --- a/kite/src/Session.cpp +++ b/kite/src/Session.cpp @@ -1,7 +1,7 @@ // -*- mode: C++; explicit-buffer-name: "Session.cpp" -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -320,12 +320,12 @@ namespace Kite { void Session::_addMoveEvent ( TrackElement* segment, Track* track ) { if (not segment->getTrack()) { - cerr << Bug( " Kite::Session::addMoveEvent() : %s has no target Track." + cerr << Bug( " Kite::Session::addMoveEvent() : %s is not yet in a track." , getString(segment).c_str() ) << endl; - return; + } else { + _addRemoveEvent( segment ); } - _addRemoveEvent( segment ); _addInsertEvent( segment, track ); } diff --git a/kite/src/TrackCost.cpp b/kite/src/TrackCost.cpp index 5a7de899..be81dcee 100644 --- a/kite/src/TrackCost.cpp +++ b/kite/src/TrackCost.cpp @@ -1,7 +1,7 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -166,6 +166,19 @@ namespace Kite { if ( lhs._delta > rhs._delta ) return false; } +#if 0 + DbU::Unit lhsMixedWeight = 0.5*lhs._deltaPerpand; + DbU::Unit rhsMixedWeight = 0.5*rhs._deltaPerpand; + + if ( not (_flags & TrackCost::IgnoreAxisWeight) ) { + lhsMixedWeight += lhsMixedWeight; + rhsMixedWeight += rhsMixedWeight; + } + + if (lhsMixedWeight < rhsMixedWeight) return true; + if (lhsMixedWeight > rhsMixedWeight) return false; +#endif + if ( not (_flags & TrackCost::IgnoreAxisWeight) ) { if ( lhs._axisWeight < rhs._axisWeight ) return true; if ( lhs._axisWeight > rhs._axisWeight ) return false; diff --git a/kite/src/TrackElement.cpp b/kite/src/TrackElement.cpp index 05ac0ef4..13721409 100644 --- a/kite/src/TrackElement.cpp +++ b/kite/src/TrackElement.cpp @@ -1,7 +1,7 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -150,6 +150,7 @@ namespace Kite { bool TrackElement::isSlackened () const { return false; } bool TrackElement::isDogleg () const { return false; } bool TrackElement::isReduced () const { return false; } + bool TrackElement::isUTurn () const { return false; } bool TrackElement::isUserDefined () const { return false; } // Predicates. bool TrackElement::canSlacken () const { return false; } diff --git a/kite/src/TrackSegment.cpp b/kite/src/TrackSegment.cpp index ee0fec35..011b0066 100644 --- a/kite/src/TrackSegment.cpp +++ b/kite/src/TrackSegment.cpp @@ -161,6 +161,7 @@ namespace Kite { bool TrackSegment::isDogleg () const { return _base->isDogleg(); } bool TrackSegment::isReduced () const { return _base->isReduced(); } bool TrackSegment::isUserDefined () const { return _base->isUserDefined(); } + bool TrackSegment::isUTurn () const { return _base->isUTurn(); } // Predicates. // Accessors. unsigned long TrackSegment::getId () const { return _base->getId(); } diff --git a/kite/src/TrackSegment.cpp.OK b/kite/src/TrackSegment.cpp.OK new file mode 100644 index 00000000..ee0fec35 --- /dev/null +++ b/kite/src/TrackSegment.cpp.OK @@ -0,0 +1,898 @@ +// -*- C++ -*- +// +// This file is part of the Coriolis Software. +// Copyright (c) UPMC 2008-2016, All Rights Reserved +// +// +-----------------------------------------------------------------+ +// | C O R I O L I S | +// | K i t e - D e t a i l e d R o u t e r | +// | | +// | Author : Jean-Paul CHAPUT | +// | E-mail : Jean-Paul.Chaput@asim.lip6.fr | +// | =============================================================== | +// | C++ Module : "./TrackSegment.cpp" | +// +-----------------------------------------------------------------+ + + +#include +#include +#include "hurricane/Bug.h" +#include "hurricane/Warning.h" +#include "hurricane/BasicLayer.h" +#include "hurricane/Net.h" +#include "hurricane/Name.h" +#include "hurricane/RoutingPad.h" +#include "katabatic/AutoContact.h" +#include "katabatic/GCell.h" +#include "crlcore/RoutingGauge.h" +#include "kite/DataNegociate.h" +#include "kite/TrackSegment.h" +#include "kite/Track.h" +#include "kite/Session.h" +#include "kite/RoutingEvent.h" +#include "kite/NegociateWindow.h" +#include "kite/KiteEngine.h" + + +namespace Kite { + + using namespace std; + using Hurricane::inltrace; + using Hurricane::ltracein; + using Hurricane::ltraceout; + using Hurricane::tab; + using Hurricane::ForEachIterator; + using Hurricane::Bug; + using Hurricane::Error; + using Hurricane::BasicLayer; + using Hurricane::Net; + using Hurricane::Name; + using Hurricane::RoutingPad; + using Katabatic::SegSlackened; + using Katabatic::KbPropagate; + +// ------------------------------------------------------------------- +// Class : "TrackSegment". + + size_t TrackSegment::_allocateds = 0; + + + size_t TrackSegment::getAllocateds () + { return _allocateds; } + + + TrackSegment::TrackSegment ( AutoSegment* segment, Track* track ) + : TrackElement (track) + , _base (segment) + , _freedomDegree(0) + , _ppitch (0) + , _data (NULL) + , _dogLegLevel (0) + { + ltrace(99) << "CTOR TrackSegment " << (void*)this << ":" << this << endl; + ltrace(99) << " over " << (void*)segment << ":" << segment << endl; + + setFlags( TElemCreated|TElemLocked ); + if (segment) { + _data = new DataNegociate( this ); + _base->getCanonical( _sourceU, _targetU ); + updateFreedomDegree(); + updatePPitch(); + } + + ++_allocateds; + } + + + void TrackSegment::_postCreate () + { + TrackElement::_postCreate(); + base()->addObserver( getObserver() ); + } + + + TrackSegment::~TrackSegment () + { + if (_data) delete _data; + --_allocateds; + } + + + void TrackSegment::_preDestroy () + { + ltrace(90) << "TrackSegment::_preDestroy() - " << (void*)this + << " [" << (void*)_base << ", " + << (void*)(_base?_base->base():NULL) << "]" << endl; + + DbU::Unit length = base()->getLength(); + if ( (length > 0) and (length < getPPitch()) ) { + BasicLayer* layer = getLayer()->getBasicLayers().getFirst(); + DbU::Unit width = base()->getWidth(); + Contact* source = base()->getAutoSource()->base(); + Contact* target = base()->getAutoTarget()->base(); + if (isHorizontal()) { + width = std::max( width, source->getBoundingBox(layer).getHeight() ); + width = std::max( width, target->getBoundingBox(layer).getHeight() ); + } else { + width = std::max( width, source->getBoundingBox(layer).getWidth() ); + width = std::max( width, target->getBoundingBox(layer).getWidth() ); + } + base()->base()->setWidth( width ); + } + + base()->removeObserver( getObserver() ); + TrackElement::_preDestroy(); + } + + + TrackElement* TrackSegment::create ( AutoSegment* segment, Track* track, bool& created ) + { + created = false; + + TrackElement* trackElement = Session::lookup( segment->base() ); + if (not trackElement) { + TrackSegment* trackSegment = new TrackSegment( segment, track ); + trackSegment->_postCreate(); + created = true; + + trackSegment->invalidate(); + + ltrace(200) << "TrackSegment::create(): " << trackSegment << endl; + trackElement = trackSegment; + } + + return trackElement; + } + + +// Formerly Inline Functions. +// Wrappeds. + AutoSegment* TrackSegment::base () const { return _base; } + bool TrackSegment::isFixed () const { return _base->isFixed(); } + bool TrackSegment::isHorizontal () const { return _base->isHorizontal(); } + bool TrackSegment::isVertical () const { return _base->isVertical(); } + bool TrackSegment::isLocal () const { return not _base->isWeakGlobal() and not _base->isGlobal(); } + bool TrackSegment::isGlobal () const { return _base->isWeakGlobal() or _base->isGlobal(); } + bool TrackSegment::isBipoint () const { return _base->isBipoint(); } + bool TrackSegment::isTerminal () const { return _base->isTerminal(); } + bool TrackSegment::isStrongTerminal ( unsigned int flags ) const { return _base->isStrongTerminal(flags); } + bool TrackSegment::isStrap () const { return _base->isStrap(); } + bool TrackSegment::isSlackened () const { return _base->isSlackened(); } + bool TrackSegment::isDogleg () const { return _base->isDogleg(); } + bool TrackSegment::isReduced () const { return _base->isReduced(); } + bool TrackSegment::isUserDefined () const { return _base->isUserDefined(); } +// Predicates. +// Accessors. + unsigned long TrackSegment::getId () const { return _base->getId(); } + unsigned int TrackSegment::getDirection () const { return _base->getDirection(); } + Net* TrackSegment::getNet () const { return _base->getNet(); } + const Layer* TrackSegment::getLayer () const { return _base->getLayer(); } + DbU::Unit TrackSegment::getPitch () const { return _base->getPitch(); } + DbU::Unit TrackSegment::getPPitch () const { return _ppitch; } + DbU::Unit TrackSegment::getAxis () const { return _base->getAxis(); } + unsigned long TrackSegment::getFreedomDegree () const { return _freedomDegree; } + unsigned int TrackSegment::getDoglegLevel () const { return _dogLegLevel; } + Interval TrackSegment::getSourceConstraints () const { return _base->getSourceConstraints(); } + Interval TrackSegment::getTargetConstraints () const { return _base->getTargetConstraints(); } + TrackElement* TrackSegment::getCanonical ( Interval& i ) { return Session::lookup( _base->getCanonical(i)->base() ); } + TrackElements TrackSegment::getPerpandiculars () { return new TrackElements_Perpandiculars(this); } +// Mutators. + void TrackSegment::invalidate () { setFlags( TElemInvalidated ); _base->invalidate(); } + + + DataNegociate* TrackSegment::getDataNegociate ( unsigned int flags ) const + { + if (flags & KtDataSelf) return _data; + + TrackElement* parent = getParent(); + return (parent) ? parent->getDataNegociate() : NULL; + } + + + TrackElement* TrackSegment::getNext () const + { + size_t dummy = _index; + return _track->getNext( dummy, getNet() ); + } + + + TrackElement* TrackSegment::getPrevious () const + { + size_t dummy = _index; + return _track->getPrevious( dummy, getNet() ); + } + + + TrackElement* TrackSegment::getParent () const + { + AutoSegment* baseParent = base()->getParent(); + if (not baseParent) return NULL; + + TrackElement* element = Session::lookup( baseParent ); + return element; + } + + + Interval TrackSegment::getFreeInterval () const + { + if (not _track) return Interval(false); + + size_t begin = _index; + size_t end = _index; + + return _track->expandFreeInterval( begin, end, Track::InsideElement, getNet() ); + } + + + size_t TrackSegment::getGCells ( Katabatic::GCellVector& gcells ) const + { + Katabatic::GCellVector().swap( gcells ); + + Katabatic::GCell* sourceGCell = base()->getAutoSource()->getGCell(); + Katabatic::GCell* targetGCell = base()->getAutoTarget()->getGCell(); + + ltrace(148) << "getGCells(): sourceGCell: " << sourceGCell << endl; + ltrace(148) << "getGCells(): targetGCell: " << targetGCell << endl; + + for( AutoSegment* segment : base()->getAligneds() ) { + ltrace(148) << "| " << segment << endl; + + Katabatic::GCell* gcell = segment->getAutoSource()->getGCell(); + if (gcell->getIndex() < sourceGCell->getIndex()) { + sourceGCell = gcell; + ltrace(148) << "getGCells(): new sourceGCell: " << sourceGCell << endl; + } + + gcell = segment->getAutoTarget()->getGCell(); + if (gcell->getIndex() > targetGCell->getIndex()) { + targetGCell = gcell; + ltrace(148) << "getGCells(): new targetGCell: " << targetGCell << endl; + } + } + + if (not sourceGCell or not targetGCell) return 0; + if (not sourceGCell) { gcells.push_back( targetGCell ); return 1; } + if (not targetGCell) { gcells.push_back( sourceGCell ); return 1; } + + if (isHorizontal()) { + gcells.push_back( sourceGCell ); + while ( sourceGCell != targetGCell ) { + sourceGCell = sourceGCell->getRight(); + if (not sourceGCell) break; + + gcells.push_back( sourceGCell ); + } + } else { + gcells.push_back( sourceGCell ); + while ( sourceGCell != targetGCell ) { + sourceGCell = sourceGCell->getUp(); + if (not sourceGCell) break; + + gcells.push_back( sourceGCell ); + } + } + + return gcells.size(); + } + + + size_t TrackSegment::getPerpandicularsBound ( set& bounds ) + { + bounds.clear (); + + set baseBounds; + set::iterator ibase; + _base->getPerpandicularsBound( baseBounds ); + + for ( ibase=baseBounds.begin() ; ibase!=baseBounds.end() ; ++ibase ) { + TrackElement* segment = Session::lookup( *ibase ); + if (segment) + bounds.insert( segment ); + } + + return bounds.size(); + } + + + void TrackSegment::setDoglegLevel ( unsigned int level ) + { + if (level > 15) { + cerr << Bug("%s has reached maximum dog leg count (15)." + ,_getString().c_str()) << endl; + level = 15; + } + _dogLegLevel = level; + } + + + void TrackSegment::updateFreedomDegree () + { _freedomDegree = _base->getSlack(); } + + + void TrackSegment::updatePPitch () + { + _ppitch = _base->getPPitch(); + //cerr << "Update P/Pitch (" << DbU::toLambda(getPPitch()) << ") on " << this << endl; + } + + + void TrackSegment::setTrack ( Track* track ) + { TrackElement::setTrack( track ); } + + + void TrackSegment::detach () + { + ltrace(200) << "TrackSegment::detach() - " << endl; + + setTrack( NULL ); + setIndex( (size_t)-1 ); + setFlags( TElemLocked ); + } + + + void TrackSegment::revalidate () + { + unsetFlags( TElemCreated ); + ltrace(148) << "revalidate() - " << this << endl; + + _base->getCanonical( _sourceU, _targetU ); + + if (_track) Session::addSortEvent( _track, true ); + unsetFlags( TElemInvalidated ); + } + + + void TrackSegment::setAxis ( DbU::Unit axis, unsigned int flags ) + { + _base->setAxis( axis, flags ); + invalidate(); + } + + + void TrackSegment::swapTrack ( TrackElement* other ) + { + if (not other) return; + + ltrace(200) << "TrackSegment::swapTrack()" << endl; + + size_t thisIndex = getIndex (); + Track* thisTrack = getTrack (); + size_t otherIndex = other->getIndex (); + Track* otherTrack = other->getTrack (); + + if (_track and otherTrack and (_track != otherTrack)) { + cerr << Error("TrackSegment::swapTrack() - swapping TrackSegments from different tracks.") << endl; + } + + setTrack( NULL ); + other->setTrack( NULL ); + + other->setTrack( thisTrack ); + other->setIndex( thisIndex ); + if (thisTrack) thisTrack->setSegment( other, thisIndex ); + + setTrack( otherTrack ); + setIndex( otherIndex ); + if (_track) _track->setSegment( this, _index ); + +#if defined(CHECK_DATABASE_DISABLED) + if (_track) _track->_check(); + else if (other->getTrack()) other->getTrack()->_check(); +#endif + + RoutingEvent* thisEvent = getDataNegociate(KtDataSelf)->getRoutingEvent(); + RoutingEvent* otherEvent = other->getDataNegociate()->getRoutingEvent(); + + if (thisEvent ) thisEvent ->setSegment( other ); + if (otherEvent) otherEvent->setSegment( this ); + + ltrace(200) << "| this: " << this << endl; + ltrace(200) << "| other: " << other << endl; + } + + + void TrackSegment::reschedule ( unsigned int level ) + { + ltrace(200) << "TrackSegment::reschedule() - " << this << endl; + ltracein(200); + + if (not _data or not _data->hasRoutingEvent()) + Session::getNegociateWindow()->addRoutingEvent( this, level ); + else { + if (_track != NULL) + Session::addRemoveEvent( this ); + Session::getNegociateWindow()->rescheduleEvent( _data->getRoutingEvent(), level ); + } + + ltraceout(200); + } + + + float TrackSegment::getMaxUnderDensity ( unsigned int flags ) const + { return _base->getMaxUnderDensity( flags ); } + + + bool TrackSegment::canPivotUp ( float reserve ) const + { return _base->canPivotUp(reserve); } + + + bool TrackSegment::canPivotDown ( float reserve ) const + { return _base->canPivotDown( reserve ); } + + + bool TrackSegment::canMoveUp ( float reserve, unsigned int flags ) const + { return _base->canMoveUp( reserve, flags ); } + + + bool TrackSegment::canSlacken () const + { + ltrace(200) << "TrackSegment::canSlacken() doglegLevel:" << getDoglegLevel() << endl; + return (not isSlackened() and (getDoglegLevel() <= 3)) ? _base->canSlacken(KbPropagate) : false; + } + + bool TrackSegment::slacken ( unsigned int flags ) + { + ltrace(200) << "TrackSegment::slacken()" << endl; + + bool success = false; + + if (not isSlackened()) { + TrackElement* perpandicular = NULL; + TrackElement* parallel = NULL; + + ltracein(200); + + success = base()->slacken( flags|KbPropagate ); + _postDoglegs( perpandicular, parallel ); + + ltraceout(200); + return success; + } else + cerr << Bug("TrackSegment::slacken(): NULL base or already slackened.") << endl; + + return success; + } + + + bool TrackSegment::moveUp ( unsigned int flags ) + { + bool success = false; + + ltrace(200) << "TrackSegment::moveUp() " << flags << endl; + ltracein(200); + + success = base()->moveUp( flags ); + if (success) { + TrackElement* perpandicular = NULL; + TrackElement* parallel = NULL; + + Session::revalidateTopology(); + _postDoglegs( perpandicular, parallel ); + } + + ltraceout(200); + + return success; + } + + + bool TrackSegment::moveDown ( unsigned int flags ) + { + bool success = false; + + ltrace(200) << "TrackSegment::moveDown() " << flags << endl; + ltracein(200); + + success = base()->moveDown( flags ); + if (success) { + TrackElement* perpandicular = NULL; + TrackElement* parallel = NULL; + + Session::revalidateTopology(); + _postDoglegs( perpandicular, parallel ); + } + + ltraceout(200); + + return success; + } + + + bool TrackSegment::moveAside ( unsigned int flags ) + { + bool success = true; + + ltrace(200) << "TrackSegment::moveAside() - " + << ((flags&KtMoveToLeft )?"left" :"") + << ((flags&KtMoveToRight)?"rigth":"") << endl; + ltracein(200); + if (flags & KtMoveToLeft ) base()->moveULeft (); + if (flags & KtMoveToRight) base()->moveURight(); + ltraceout(200); + + return success; + } + + + TrackElement* TrackSegment::getSourceDogleg () + { + if (not hasSourceDogleg()) return NULL; + + unsigned int direction = perpandicularTo( getDirection() ); + TrackElement* dogleg = NULL; + for( Segment* segment : base()->getAutoSource()->getSlaveComponents().getSubSet() ) { + dogleg = Session::lookup( segment ); + if (dogleg and (dogleg->getDirection() == direction)) { + ltrace(200) << "Source dogleg: " << dogleg << endl; + return dogleg; + } + } + return NULL; + } + + + TrackElement* TrackSegment::getTargetDogleg () + { + if (not hasSourceDogleg()) return NULL; + + unsigned int direction = perpandicularTo( getDirection() ); + TrackElement* dogleg = NULL; + for( Segment* segment : base()->getAutoTarget()->getSlaveComponents().getSubSet() ) { + dogleg = Session::lookup( segment ); + if (dogleg and (dogleg->getDirection() == direction)) { + ltrace(200) << "Target dogleg: " << dogleg << endl; + return dogleg; + } + } + return NULL; + } + + + bool TrackSegment::canDogleg () + { + ltrace(200) << "TrackSegment::canDogleg()" << endl; + + if (not isLocal()) { + ltrace(200) << "Failed: is not local." << endl; + return false; + } + + if (isFixed()) { + ltrace(200) << "Failed: is fixed." << endl; + return false; + } + + if (isRouted()) { + ltrace(200) << "Failed: belongs to an already routed net." << endl; + return false; + } + + if (isSlackened()) { + ltrace(200) << "Failed: is local & slackened." << endl; + return false; + } + + if (hasSourceDogleg() or hasTargetDogleg()) { + ltrace(200) << "Failed: already has source or target dogleg." << endl; + return false; + } + + if (getDoglegLevel() > 3) { + ltrace(200) << "Failed: maximum dogleg level reached (4)." << endl; + return false; + } + + return true; + } + + + bool TrackSegment::canDogleg ( Katabatic::GCell* doglegGCell, unsigned int flags ) + { + ltrace(200) << "TrackSegment::canDogleg(GCell*) " << doglegGCell << endl; + ltracein(200); + + if (doglegGCell->isUnderIoPad()) { + ltrace(200) << "false: Cannot dogleg in a GCell under an I/O Pad." << endl; + ltraceout(200); + return false; + } + + if (isFixed()) { + ltrace(200) << "false: Cannot dogleg a fixed segment." << endl; + ltraceout(200); + return false; + } + + if (isRouted()) { + ltrace(200) << "false: Cannot dogleg a segment belonging to an already routed net." << endl; + ltraceout(200); + return false; + } + + if (isLocal()) { + if (hasSourceDogleg() or hasTargetDogleg()) { + ltrace(200) << "false: Cannot dogleg again a local segment." << endl; + ltraceout(200); + return false; + } + if (isSlackened()) { + ltrace(200) << "false: Cannot dogleg a local slackened segment." << endl; + ltraceout(200); + return false; + } + } + + if (getDoglegLevel() > 3) { + ltrace(200) << "Failed: maximum dogleg level reached (4)." << endl; + ltraceout(200); + return false; + } + + vector gcells; + getGCells( gcells ); + + ltrace(190) << "Source: " << *gcells.begin () << endl; + ltrace(190) << "Target: " << *gcells.rbegin() << endl; + + bool isGCellInside = false; + for ( size_t igcell=0 ; igcell 3) { + ltrace(200) << "Failed: maximum dogleg level reached (4)." << endl; + return false; + } + + return _base->canDogleg(interval); + } + + + TrackElement* TrackSegment::makeDogleg () + { + Katabatic::AutoContact* source = _base->getAutoSource(); + Katabatic::AutoContact* target = _base->getAutoTarget(); + Katabatic::GCell* gcell = _base->getAutoSource()->getGCell(); + + TrackElement* dogleg = NULL; + TrackElement* parallel = NULL; + makeDogleg( gcell, dogleg, parallel ); + + if (dogleg) { + if (source->isTerminal() xor target->isTerminal()) { + if (target->isTerminal()) + source = target; + + DbU::Unit axis = (_base->isHorizontal()) ? source->getX() : source->getY(); + + ltrace(200) << "Setting dogleg axis @" << DbU::getValueString(axis) << endl; + dogleg->setAxis( axis ); + } + } + return dogleg; + } + + + TrackElement* TrackSegment::makeDogleg ( Katabatic::GCell* dogLegGCell + , TrackElement*& perpandicular + , TrackElement*& parallel + ) + { + ltrace(200) << "TrackSegment::makeDogleg(GCell*)" << endl; + ltrace(200) << "Break in: " << dogLegGCell << endl; + + base()->makeDogleg( dogLegGCell ); + _postDoglegs( perpandicular, parallel ); + + return perpandicular; + } + + + TrackElement* TrackSegment::makeDogleg ( Interval interval, unsigned int& flags ) + { + TrackElement* perpandicular = NULL; + TrackElement* parallel = NULL; + + ltrace(200) << "TrackSegment::makeDogleg(Interval)" << endl; + flags = base()->makeDogleg( interval ); + _postDoglegs( perpandicular, parallel ); + + return perpandicular; + } + + + void TrackSegment::_postDoglegs ( TrackElement*& perpandicular, TrackElement*& parallel ) + { + ltrace(200) << "TrackSegment::_postDoglegs()" << endl; + ltracein(200); + + unsigned int doglegLevel = 0; + const vector& doglegs = Session::getDoglegs(); + vector segments; + + if (not doglegs.empty()) { + if (doglegs.size()%3 != 0) + cerr << Error( "Session::_postDoglegs(): Number of created segments incoherent with pure doglegs (%u)." + , doglegs.size() ) << endl; + + for ( size_t i=0 ; icreateTrackSegment(doglegs[i],0) ); + segments[i+0]->setFlags( TElemTargetDogleg ); + segments[i+0]->getDataNegociate()->resetRipupCount(); + //segments[i+0]->getDataNegociate()->resetStateCount(); + segments[i+0]->getDataNegociate()->setState( DataNegociate::RipupPerpandiculars ); + doglegLevel = segments[i+0]->getDoglegLevel(); + segments[i+0]->setDoglegLevel( doglegLevel + (segments[i]->isLocal()?1:0) ); + + ltrace(200) << "Looking up new perpand: " << doglegs[i+1] << endl; + segments.push_back( Session::getNegociateWindow()->createTrackSegment(doglegs[i+1],0) ); + segments[i+1]->setFlags( TElemSourceDogleg|TElemTargetDogleg ); + segments[i+1]->setDoglegLevel( doglegLevel + 1 ); + + ltrace(200) << "Looking up new parallel: " << doglegs[i+2] << endl; + segments.push_back( Session::getNegociateWindow()->createTrackSegment(doglegs[i+2],0) ); + segments[i+2]->setFlags( TElemSourceDogleg ); + segments[i+2]->getDataNegociate()->resetStateCount(); + segments[i+2]->getDataNegociate()->setState( segments[i+0]->getDataNegociate()->getState() ); + segments[i+2]->setDoglegLevel( doglegLevel + (segments[i]->isLocal()?1:0) ); + + segments[i+0]->getDataNegociate()->setChildSegment( segments[i+2] ); + + perpandicular = segments[i+1]; + parallel = segments[i+2]; + } + + // TO CHECK + // If the original TrackElement was inserted in a Track, must check + // if the new bit takes it's place or not. + //if ( getGCell() != originalGCell ) swapTrack ( segments[2] ); + + for ( size_t i=0 ; ireschedule ( ((i%3==1) ? 0 : 1) ); + const char* segPart = "Unknown"; + switch ( i%3 ) { + case 0: segPart = "original "; break; + case 1: segPart = "perpand "; break; + case 2: segPart = "new paral"; break; + } + ltrace(200) << "[" << (i/3) << ":" << i << "] " << segPart << ": " + << segments[i] << endl; + } + } else { + reschedule( 1 ); + } + + ltraceout(200); + + Session::doglegReset(); + } + + + bool TrackSegment::_check () const + { + if (not base()) return true; + + bool coherency = true; + + if (not base()->isCanonical()) { + cerr << "[CHECK] " << this << " supporting AutoSegment is not canonical." << endl; + coherency = false; + } + + DbU::Unit min; + DbU::Unit max; + base()->checkPositions(); + base()->getCanonical( min, max ); + if (getSourceU() != min) { + cerr << "[CHECK] " << this << " has bad source position " + << "cache:" << DbU::getValueString(getSourceU()) << " vs. " + << "canon:" << DbU::getValueString(min) << "." << endl; + coherency = false; + } + if (getTargetU() != max) { + cerr << "[CHECK] " << this << " has bad target position " + << "cache:" << DbU::getValueString(getTargetU()) << " vs. " + << "canon:" << DbU::getValueString(max) << "." << endl; + coherency = false; + } + + return coherency; + } + + + string TrackSegment::_getTypeName () const + { return "TrackSegment"; } + + + string TrackSegment::_getString () const + { + string s1 = _base->_getString(); + string s2 = " [" + DbU::getValueString(_sourceU) + + ":" + DbU::getValueString(_targetU) + "]" + + " " + DbU::getValueString(_targetU-_sourceU) + + " " + getString(_dogLegLevel) + + " [" + ((_track) ? getString(_index) : "npos") + "] " + + ((isRouted() ) ? "R" : "-") + + ((isSlackened() ) ? "S" : "-") + + ((_track ) ? "T" : "-") + + ((canRipple() ) ? "r" : "-") + + ((hasSourceDogleg()) ? "s" : "-") + + ((hasTargetDogleg()) ? "t" : "-"); + + s1.insert( s1.size()-1, s2 ); + + return s1; + } + + + Record* TrackSegment::_getRecord () const + { + Record* record = TrackElement::_getRecord(); + record->add( getSlot( "_base", _base ) ); + return record; + } + + +} // Kite namespace. diff --git a/kite/src/kite/DataNegociate.h b/kite/src/kite/DataNegociate.h index 742ad2be..6ba12a4f 100644 --- a/kite/src/kite/DataNegociate.h +++ b/kite/src/kite/DataNegociate.h @@ -1,14 +1,14 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | // | K i t e - D e t a i l e d R o u t e r | // | | // | Author : Jean-Paul CHAPUT | -// | E-mail : Jean-Paul.Chaput@asim.lip6.fr | +// | E-mail : Jean-Paul.Chaput@lip6.fr | // | =============================================================== | // | C++ Header : "./kite/DataNegociate.h" | // +-----------------------------------------------------------------+ @@ -108,6 +108,7 @@ namespace Kite { vector _attractors; vector _perpandiculars; Interval _perpandicularFree; + Interval _reduceRanges[2]; private: DataNegociate ( const DataNegociate& ); DataNegociate& operator= ( const DataNegociate& ); diff --git a/kite/src/kite/NegociateWindow.h b/kite/src/kite/NegociateWindow.h index 0014d854..3fdb0395 100644 --- a/kite/src/kite/NegociateWindow.h +++ b/kite/src/kite/NegociateWindow.h @@ -1,14 +1,14 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | // | K i t e - D e t a i l e d R o u t e r | // | | // | Author : Jean-Paul CHAPUT | -// | E-mail : Jean-Paul.Chaput@asim.lip6.fr | +// | E-mail : Jean-Paul.Chaput@lip6.fr | // | =============================================================== | // | C++ Header : "./kite/NegociateWindow.h" | // +-----------------------------------------------------------------+ @@ -116,6 +116,7 @@ namespace Kite { void run ( unsigned int flags ); void printStatistics () const; void _createRouting ( Katabatic::GCell* ); + void _pack ( size_t& count, bool last ); size_t _negociate (); Hurricane::Record* _getRecord () const; std::string _getString () const; diff --git a/kite/src/kite/RoutingEvent.h b/kite/src/kite/RoutingEvent.h index 5850f048..15a29b5e 100644 --- a/kite/src/kite/RoutingEvent.h +++ b/kite/src/kite/RoutingEvent.h @@ -1,7 +1,7 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -75,7 +75,7 @@ namespace Kite { DbU::Unit _sourceU; Net* _net; unsigned long _id; - friend class Compare; + friend class Compare; }; public: @@ -115,6 +115,7 @@ namespace Kite { inline bool isRipedByLocal () const; inline bool isOverConstrained () const; inline unsigned int getId () const; + inline unsigned int getTimeStamp () const; inline bool getMode () const; inline bool canMinimize () const; unsigned int getState () const; @@ -142,6 +143,7 @@ namespace Kite { RoutingEvent* reschedule ( RoutingEventQueue&, unsigned int eventLevel ); void setMode ( unsigned int ); void setState ( unsigned int ); + inline void setTimeStamp ( unsigned int ); inline void setProcessed ( bool state=true ); inline void setDisabled ( bool state=true ); inline void setMinimized ( bool state=true ); @@ -178,6 +180,7 @@ namespace Kite { bool _forceToHint; bool _ripedByLocal; unsigned int _id; + unsigned int _timeStamp; TrackElement* _segment; DataNegociate* _dataNegociate; DbU::Unit _axisHistory; @@ -205,8 +208,9 @@ namespace Kite { inline bool RoutingEvent::isRipedByLocal () const { return _ripedByLocal; } inline bool RoutingEvent::isOverConstrained () const { return _overConstrained; } inline unsigned int RoutingEvent::getId () const { return _id; } + inline unsigned int RoutingEvent::getTimeStamp () const { return _timeStamp; } inline bool RoutingEvent::getMode () const { return _mode; } - inline bool RoutingEvent::canMinimize () const { return !_minimized; } + inline bool RoutingEvent::canMinimize () const { return not _minimized; } inline const RoutingEvent::Key& RoutingEvent::getKey () const { return _key; } inline TrackElement* RoutingEvent::getSegment () const { return _segment; } inline const vector& RoutingEvent::getPerpandiculars () const { return _dataNegociate->getPerpandiculars(); } @@ -223,6 +227,7 @@ namespace Kite { inline unsigned int RoutingEvent::getTracksNb () const { return _tracksNb; } inline unsigned int RoutingEvent::getTracksFree () const { return _tracksFree; } inline unsigned int RoutingEvent::getInsertState () const { return _insertState; } + inline void RoutingEvent::setTimeStamp ( unsigned int stamp ) { _timeStamp = stamp; } inline void RoutingEvent::setProcessed ( bool state ) { _processed = state; } inline void RoutingEvent::setDisabled ( bool state ) { _disabled = state; } inline void RoutingEvent::setMinimized ( bool state ) { _minimized = state; } diff --git a/kite/src/kite/TrackElement.h b/kite/src/kite/TrackElement.h index dd7695e4..7b2a68ff 100644 --- a/kite/src/kite/TrackElement.h +++ b/kite/src/kite/TrackElement.h @@ -1,7 +1,7 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -117,6 +117,7 @@ namespace Kite { virtual bool isSlackened () const; virtual bool isDogleg () const; virtual bool isReduced () const; + virtual bool isUTurn () const; virtual bool isUserDefined () const; // Predicates. inline bool isCreated () const; diff --git a/kite/src/kite/TrackSegment.h b/kite/src/kite/TrackSegment.h index a79524c7..120ff35e 100644 --- a/kite/src/kite/TrackSegment.h +++ b/kite/src/kite/TrackSegment.h @@ -1,7 +1,7 @@ // -*- C++ -*- // // This file is part of the Coriolis Software. -// Copyright (c) UPMC 2008-2015, All Rights Reserved +// Copyright (c) UPMC 2008-2016, All Rights Reserved // // +-----------------------------------------------------------------+ // | C O R I O L I S | @@ -68,6 +68,7 @@ namespace Kite { virtual bool isSlackened () const; virtual bool isDogleg () const; virtual bool isReduced () const; + virtual bool isUTurn () const; virtual bool isUserDefined () const; // Predicates. virtual bool canDogleg ();