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:
Jon Evans 2021-04-04 20:27:22 -04:00
parent 2c05d99d9f
commit c7ecb51f77
8 changed files with 245 additions and 35 deletions

View File

@ -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;
}

View File

@ -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() )
{

View File

@ -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 ) );

View File

@ -32,7 +32,7 @@ MEANDER_PLACER_BASE::MEANDER_PLACER_BASE( ROUTER* aRouter ) :
{
m_world = NULL;
m_currentWidth = 0;
m_padToDieLenth = 0;
m_padToDieLength = 0;
}

View File

@ -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;

View File

@ -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 ) )

View File

@ -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();

View File

@ -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;
};