Update PNS length tuning algorithm to match the new BOARD algorithm
Now tracks inside pads are optimized to give a more accurate length. Also, the start/end of the assembled line is used to identify the right pad joints for pad-to-die length calculation. Fixes https://gitlab.com/kicad/code/kicad/-/issues/4204 Fixes https://gitlab.com/kicad/code/kicad/-/issues/1765 Fixes https://gitlab.com/kicad/code/kicad/-/issues/6877
This commit is contained in:
parent
2c05d99d9f
commit
c7ecb51f77
|
@ -1665,8 +1665,9 @@ std::tuple<int, double, double> BOARD::GetTrackLength( const TRACK& aTrack ) con
|
|||
double length = 0.0;
|
||||
double package_length = 0.0;
|
||||
|
||||
constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
|
||||
constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
|
||||
auto connectivity = GetBoard()->GetConnectivity();
|
||||
BOARD_STACKUP& stackup = GetDesignSettings().GetStackupDescriptor();
|
||||
|
||||
for( BOARD_CONNECTED_ITEM* item : connectivity->GetConnectedItems(
|
||||
static_cast<const BOARD_CONNECTED_ITEM*>( &aTrack ), types ) )
|
||||
|
@ -1677,8 +1678,7 @@ std::tuple<int, double, double> BOARD::GetTrackLength( const TRACK& aTrack ) con
|
|||
{
|
||||
if( track->Type() == PCB_VIA_T )
|
||||
{
|
||||
VIA* via = static_cast<VIA*>( track );
|
||||
BOARD_STACKUP& stackup = GetDesignSettings().GetStackupDescriptor();
|
||||
VIA* via = static_cast<VIA*>( track );
|
||||
length += stackup.GetLayerDistance( via->TopLayer(), via->BottomLayer() );
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "pns_dp_meander_placer.h"
|
||||
#include "pns_diff_pair.h"
|
||||
#include "pns_router.h"
|
||||
#include "pns_solid.h"
|
||||
|
||||
namespace PNS {
|
||||
|
||||
|
@ -99,12 +100,30 @@ bool DP_MEANDER_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
|
|||
!m_originPair.NLine().SegmentCount() )
|
||||
return false;
|
||||
|
||||
m_tunedPathP = topo.AssembleTrivialPath( m_originPair.PLine().GetLink( 0 ) );
|
||||
m_tunedPathN = topo.AssembleTrivialPath( m_originPair.NLine().GetLink( 0 ) );
|
||||
SOLID* padA = nullptr;
|
||||
SOLID* padB = nullptr;
|
||||
|
||||
m_padToDieP = GetTotalPadToDieLength( m_originPair.PLine() );
|
||||
m_padToDieN = GetTotalPadToDieLength( m_originPair.NLine() );
|
||||
m_padToDieLenth = std::max( m_padToDieP, m_padToDieN );
|
||||
m_tunedPathP = topo.AssembleTuningPath( m_originPair.PLine().GetLink( 0 ), &padA, &padB );
|
||||
|
||||
m_padToDieP = 0;
|
||||
|
||||
if( padA )
|
||||
m_padToDieP += padA->GetPadToDie();
|
||||
|
||||
if( padB )
|
||||
m_padToDieP += padB->GetPadToDie();
|
||||
|
||||
m_tunedPathN = topo.AssembleTuningPath( m_originPair.NLine().GetLink( 0 ), &padA, &padB );
|
||||
|
||||
m_padToDieN = 0;
|
||||
|
||||
if( padA )
|
||||
m_padToDieN += padA->GetPadToDie();
|
||||
|
||||
if( padB )
|
||||
m_padToDieN += padB->GetPadToDie();
|
||||
|
||||
m_padToDieLength = std::max( m_padToDieP, m_padToDieN );
|
||||
|
||||
m_world->Remove( m_originPair.PLine() );
|
||||
m_world->Remove( m_originPair.NLine() );
|
||||
|
@ -122,8 +141,8 @@ void DP_MEANDER_PLACER::release()
|
|||
|
||||
long long int DP_MEANDER_PLACER::origPathLength() const
|
||||
{
|
||||
long long int totalP = m_padToDieLenth;
|
||||
long long int totalN = m_padToDieLenth;
|
||||
long long int totalP = m_padToDieLength;
|
||||
long long int totalN = m_padToDieLength;
|
||||
|
||||
for( const ITEM* item : m_tunedPathP.CItems() )
|
||||
{
|
||||
|
|
|
@ -72,10 +72,19 @@ bool MEANDER_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
|
|||
m_world = Router()->GetWorld()->Branch();
|
||||
m_originLine = m_world->AssembleLine( m_initialSegment );
|
||||
|
||||
m_padToDieLenth = GetTotalPadToDieLength( m_originLine );
|
||||
SOLID* padA = nullptr;
|
||||
SOLID* padB = nullptr;
|
||||
|
||||
TOPOLOGY topo( m_world );
|
||||
m_tunedPath = topo.AssembleTrivialPath( m_initialSegment );
|
||||
m_tunedPath = topo.AssembleTuningPath( m_initialSegment, &padA, &padB );
|
||||
|
||||
m_padToDieLength = 0;
|
||||
|
||||
if( padA )
|
||||
m_padToDieLength += padA->GetPadToDie();
|
||||
|
||||
if( padB )
|
||||
m_padToDieLength += padB->GetPadToDie();
|
||||
|
||||
m_world->Remove( m_originLine );
|
||||
|
||||
|
@ -88,7 +97,7 @@ bool MEANDER_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
|
|||
|
||||
long long int MEANDER_PLACER::origPathLength() const
|
||||
{
|
||||
long long int total = m_padToDieLenth;
|
||||
long long int total = m_padToDieLength;
|
||||
|
||||
for( int idx = 0; idx < m_tunedPath.Size(); idx++ )
|
||||
{
|
||||
|
@ -237,7 +246,6 @@ bool MEANDER_PLACER::CommitPlacement()
|
|||
}
|
||||
|
||||
|
||||
|
||||
bool MEANDER_PLACER::CheckFit( MEANDER_SHAPE* aShape )
|
||||
{
|
||||
LINE l( m_originLine, aShape->CLine( 0 ) );
|
||||
|
|
|
@ -32,7 +32,7 @@ MEANDER_PLACER_BASE::MEANDER_PLACER_BASE( ROUTER* aRouter ) :
|
|||
{
|
||||
m_world = NULL;
|
||||
m_currentWidth = 0;
|
||||
m_padToDieLenth = 0;
|
||||
m_padToDieLength = 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ protected:
|
|||
NODE* m_world;
|
||||
|
||||
///< Total length added by pad to die size.
|
||||
int m_padToDieLenth;
|
||||
int m_padToDieLength;
|
||||
|
||||
///< Width of the meandered trace(s).
|
||||
int m_currentWidth;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "pns_itemset.h"
|
||||
#include "pns_topology.h"
|
||||
#include "pns_meander_skew_placer.h"
|
||||
#include "pns_solid.h"
|
||||
|
||||
#include "pns_router.h"
|
||||
#include "pns_debug_decorator.h"
|
||||
|
@ -79,24 +80,42 @@ bool MEANDER_SKEW_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
|
|||
!m_originPair.NLine().SegmentCount() )
|
||||
return false;
|
||||
|
||||
m_tunedPathP = topo.AssembleTrivialPath( m_originPair.PLine().GetLink( 0 ) );
|
||||
m_tunedPathN = topo.AssembleTrivialPath( m_originPair.NLine().GetLink( 0 ) );
|
||||
SOLID* padA = nullptr;
|
||||
SOLID* padB = nullptr;
|
||||
|
||||
m_tunedPathP = topo.AssembleTuningPath( m_originPair.PLine().GetLink( 0 ), &padA, &padB );
|
||||
|
||||
m_padToDieP = 0;
|
||||
|
||||
if( padA )
|
||||
m_padToDieP += padA->GetPadToDie();
|
||||
|
||||
if( padB )
|
||||
m_padToDieP += padB->GetPadToDie();
|
||||
|
||||
m_tunedPathN = topo.AssembleTuningPath( m_originPair.NLine().GetLink( 0 ), &padA, &padB );
|
||||
|
||||
m_padToDieN = 0;
|
||||
|
||||
if( padA )
|
||||
m_padToDieN += padA->GetPadToDie();
|
||||
|
||||
if( padB )
|
||||
m_padToDieN += padB->GetPadToDie();
|
||||
|
||||
m_world->Remove( m_originLine );
|
||||
|
||||
m_currentWidth = m_originLine.Width();
|
||||
m_currentEnd = VECTOR2I( 0, 0 );
|
||||
m_padToDieN = GetTotalPadToDieLength( m_originPair.NLine() );
|
||||
m_padToDieP = GetTotalPadToDieLength( m_originPair.PLine() );
|
||||
|
||||
if ( m_originPair.PLine().Net() == m_originLine.Net() )
|
||||
{
|
||||
m_padToDieLenth = m_padToDieN;
|
||||
m_padToDieLength = m_padToDieN;
|
||||
m_coupledLength = itemsetLength( m_tunedPathN );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_padToDieLenth = m_padToDieP;
|
||||
m_padToDieLength = m_padToDieP;
|
||||
m_coupledLength = itemsetLength( m_tunedPathP );
|
||||
}
|
||||
|
||||
|
@ -112,7 +131,7 @@ long long int MEANDER_SKEW_PLACER::origPathLength() const
|
|||
|
||||
long long int MEANDER_SKEW_PLACER::itemsetLength( const ITEM_SET& aSet ) const
|
||||
{
|
||||
long long int total = m_padToDieLenth;
|
||||
long long int total = m_padToDieLength;
|
||||
for( const ITEM* item : aSet.CItems() )
|
||||
{
|
||||
if( const LINE* l = dyn_cast<const LINE*>( item ) )
|
||||
|
|
|
@ -177,14 +177,14 @@ ITEM* TOPOLOGY::NearestUnconnectedItem( JOINT* aStart, int* aAnchor, int aKindMa
|
|||
}
|
||||
|
||||
|
||||
bool TOPOLOGY::followTrivialPath( LINE* aLine, bool aLeft, ITEM_SET& aSet, std::set<ITEM*>& aVisited )
|
||||
bool TOPOLOGY::followTrivialPath( LINE* aLine, bool aLeft, ITEM_SET& aSet,
|
||||
std::set<ITEM*>& aVisited, JOINT** aTerminalJoint )
|
||||
{
|
||||
assert( aLine->IsLinked() );
|
||||
|
||||
VECTOR2I anchor = aLeft ? aLine->CPoint( 0 ) : aLine->CPoint( -1 );
|
||||
LINKED_ITEM* last =
|
||||
aLeft ? aLine->Links().front() : aLine->Links().back();
|
||||
JOINT* jt = m_world->FindJoint( anchor, aLine );
|
||||
VECTOR2I anchor = aLeft ? aLine->CPoint( 0 ) : aLine->CPoint( -1 );
|
||||
LINKED_ITEM* last = aLeft ? aLine->Links().front() : aLine->Links().back();
|
||||
JOINT* jt = m_world->FindJoint( anchor, aLine );
|
||||
|
||||
assert( jt != NULL );
|
||||
|
||||
|
@ -204,7 +204,12 @@ bool TOPOLOGY::followTrivialPath( LINE* aLine, bool aLeft, ITEM_SET& aSet, std::
|
|||
}
|
||||
|
||||
if( !next_seg )
|
||||
{
|
||||
if( aTerminalJoint )
|
||||
*aTerminalJoint = jt;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LINE l = m_world->AssembleLine( next_seg );
|
||||
|
||||
|
@ -230,21 +235,25 @@ bool TOPOLOGY::followTrivialPath( LINE* aLine, bool aLeft, ITEM_SET& aSet, std::
|
|||
aSet.Add( l );
|
||||
}
|
||||
|
||||
return followTrivialPath( &l, aLeft, aSet, aVisited );
|
||||
return followTrivialPath( &l, aLeft, aSet, aVisited, aTerminalJoint );
|
||||
}
|
||||
|
||||
if( aTerminalJoint )
|
||||
*aTerminalJoint = jt;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const ITEM_SET TOPOLOGY::AssembleTrivialPath( ITEM* aStart )
|
||||
const ITEM_SET TOPOLOGY::AssembleTrivialPath( ITEM* aStart,
|
||||
std::pair<JOINT*, JOINT*>* aTerminalJoints )
|
||||
{
|
||||
ITEM_SET path;
|
||||
std::set<ITEM*> visited;
|
||||
SEGMENT* seg;
|
||||
VIA* via;
|
||||
|
||||
seg = dyn_cast<SEGMENT*> (aStart);
|
||||
seg = dyn_cast<SEGMENT*>( aStart );
|
||||
|
||||
if(!seg && (via = dyn_cast<VIA*>( aStart ) ) )
|
||||
{
|
||||
|
@ -265,13 +274,144 @@ const ITEM_SET TOPOLOGY::AssembleTrivialPath( ITEM* aStart )
|
|||
|
||||
path.Add( l );
|
||||
|
||||
followTrivialPath( &l, false, path, visited );
|
||||
followTrivialPath( &l, true, path, visited );
|
||||
JOINT* jointA = nullptr;
|
||||
JOINT* jointB = nullptr;
|
||||
|
||||
followTrivialPath( &l, false, path, visited, &jointA );
|
||||
followTrivialPath( &l, true, path, visited, &jointB );
|
||||
|
||||
if( aTerminalJoints )
|
||||
{
|
||||
wxASSERT( jointA && jointB );
|
||||
*aTerminalJoints = std::make_pair( jointA, jointB );
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
const ITEM_SET TOPOLOGY::AssembleTuningPath( ITEM* aStart, SOLID** aStartPad, SOLID** aEndPad )
|
||||
{
|
||||
std::pair<JOINT*, JOINT*> joints;
|
||||
ITEM_SET initialPath = AssembleTrivialPath( aStart, &joints );
|
||||
|
||||
PAD* padA = nullptr;
|
||||
PAD* padB = nullptr;
|
||||
|
||||
for( ITEM* item : joints.first->LinkList() )
|
||||
{
|
||||
if( item->OfKind( ITEM::SOLID_T ) )
|
||||
{
|
||||
BOARD_ITEM* bi = static_cast<SOLID*>( item )->Parent();
|
||||
|
||||
if( bi->Type() == PCB_PAD_T )
|
||||
{
|
||||
padA = static_cast<PAD*>( bi );
|
||||
|
||||
if( aStartPad )
|
||||
*aStartPad = static_cast<SOLID*>( item );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for( ITEM* item : joints.second->LinkList() )
|
||||
{
|
||||
if( item->OfKind( ITEM::SOLID_T ) )
|
||||
{
|
||||
BOARD_ITEM* bi = static_cast<SOLID*>( item )->Parent();
|
||||
|
||||
if( bi->Type() == PCB_PAD_T )
|
||||
{
|
||||
padB = static_cast<PAD*>( bi );
|
||||
|
||||
if( aEndPad )
|
||||
*aEndPad = static_cast<SOLID*>( item );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto clipLineToPad =
|
||||
[]( SHAPE_LINE_CHAIN& aLine, PAD* aPad, bool aForward = true )
|
||||
{
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& shape = aPad->GetEffectivePolygon();
|
||||
|
||||
int start = aForward ? 0 : aLine.PointCount() - 1;
|
||||
int delta = aForward ? 1 : -1;
|
||||
|
||||
// Skip the "first" (or last) vertex, we already know it's contained in the pad
|
||||
int clip = start;
|
||||
|
||||
for( int vertex = start + delta;
|
||||
aForward ? vertex < aLine.PointCount() : vertex >= 0;
|
||||
vertex += delta )
|
||||
{
|
||||
SEG seg( aLine.GetPoint( vertex ), aLine.GetPoint( vertex - delta ) );
|
||||
|
||||
bool containsA = shape->Contains( seg.A );
|
||||
bool containsB = shape->Contains( seg.B );
|
||||
|
||||
if( containsA && containsB )
|
||||
{
|
||||
// Whole segment is inside: clip out this segment
|
||||
clip = vertex;
|
||||
}
|
||||
else if( containsB &&
|
||||
( aForward ? vertex < aLine.PointCount() - 1 : vertex > 0 ) )
|
||||
{
|
||||
// Only one point inside: Find the intersection
|
||||
VECTOR2I loc;
|
||||
|
||||
if( shape->Collide( seg, 0, nullptr, &loc ) )
|
||||
{
|
||||
aLine.Replace( vertex - delta, vertex - delta, loc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !aForward && clip < start )
|
||||
aLine.Remove( clip + 1, start );
|
||||
else if( clip > start )
|
||||
aLine.Remove( start, clip - 1 );
|
||||
|
||||
// Now connect the dots
|
||||
aLine.Insert( aForward ? 0 : aLine.PointCount(), aPad->GetPosition() );
|
||||
};
|
||||
|
||||
auto processPad =
|
||||
[&]( PAD* aPad )
|
||||
{
|
||||
const std::shared_ptr<SHAPE_POLY_SET>& shape = aPad->GetEffectivePolygon();
|
||||
|
||||
for( int idx = 0; idx < initialPath.Size(); idx++ )
|
||||
{
|
||||
if( initialPath[idx]->Kind() != ITEM::LINE_T )
|
||||
continue;
|
||||
|
||||
LINE* line = static_cast<LINE*>( initialPath[idx] );
|
||||
|
||||
SHAPE_LINE_CHAIN& slc = line->Line();
|
||||
|
||||
if( shape->Contains( slc.CPoint( 0 ) ) )
|
||||
clipLineToPad( slc, aPad, true );
|
||||
else if( shape->Contains( slc.CPoint( -1 ) ) )
|
||||
clipLineToPad( slc, aPad, false );
|
||||
}
|
||||
};
|
||||
|
||||
if( padA )
|
||||
processPad( padA );
|
||||
|
||||
if( padB )
|
||||
processPad( padB );
|
||||
|
||||
return initialPath;
|
||||
}
|
||||
|
||||
|
||||
const ITEM_SET TOPOLOGY::ConnectedItems( JOINT* aStart, int aKindMask )
|
||||
{
|
||||
return ITEM_SET();
|
||||
|
|
|
@ -55,7 +55,30 @@ public:
|
|||
const ITEM_SET ConnectedItems( ITEM* aStart, int aKindMask = ITEM::ANY_T );
|
||||
int64_t ShortestConnectionLength( ITEM* aFrom, ITEM* aTo );
|
||||
|
||||
const ITEM_SET AssembleTrivialPath( ITEM* aStart );
|
||||
/**
|
||||
* Assembles a trivial path between two joints given a starting item
|
||||
* @param aStart is the item to assemble from
|
||||
* @param aTerminalJoints will be filled with the start and end points of the assembled path
|
||||
* @return a set of items in the path
|
||||
*/
|
||||
const ITEM_SET AssembleTrivialPath( ITEM* aStart,
|
||||
std::pair<JOINT*, JOINT*>* aTerminalJoints = nullptr );
|
||||
|
||||
/**
|
||||
* Like AssembleTrivialPath, but follows the track length algorithm, which discards segments
|
||||
* that are fully inside pads, and truncates segments that cross into a pad (adding a straight-
|
||||
* line segment from the intersection to the pad anchor).
|
||||
*
|
||||
* NOTE: When changing this, sync with BOARD::GetTrackLength()
|
||||
*
|
||||
* @param aStart is the item to assemble a path from
|
||||
* @param aStartPad will be filled with the starting pad of the path, if found
|
||||
* @param aEndPad will be filled with the ending pad of the path, if found
|
||||
* @return an item set containing all the items in the path
|
||||
*/
|
||||
const ITEM_SET AssembleTuningPath( ITEM* aStart, SOLID** aStartPad = nullptr,
|
||||
SOLID** aEndPad = nullptr );
|
||||
|
||||
const DIFF_PAIR AssembleDiffPair( SEGMENT* aStart );
|
||||
|
||||
bool AssembleDiffPair( ITEM* aStart, DIFF_PAIR& aPair );
|
||||
|
@ -63,7 +86,8 @@ public:
|
|||
const std::set<ITEM*> AssembleCluster( ITEM* aStart, int aLayer );
|
||||
|
||||
private:
|
||||
bool followTrivialPath( LINE* aLine, bool aLeft, ITEM_SET& aSet, std::set<ITEM*>& aVisited );
|
||||
bool followTrivialPath( LINE* aLine, bool aLeft, ITEM_SET& aSet, std::set<ITEM*>& aVisited,
|
||||
JOINT** aTerminalJoint = nullptr );
|
||||
|
||||
NODE *m_world;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue