router: Various bugfixes in length tuning meandering

- When fine-tuning, keep adjust to the original baseline length
- Enforce a minimum amplitude based on settings, DP gap and track width
- Use binary search to fine-tune amplitude if the length is not trivial
- Account for inequalities in balancing
- Don't add the meander if adding it with min. amplitude will make the
  tuning worse
- Keep a distance after meandering end to prevent overlaps in DP

Fixes https://gitlab.com/kicad/code/kicad/issues/9267
Fixes https://gitlab.com/kicad/code/kicad/issues/1776
This commit is contained in:
Alex 2022-12-04 07:52:21 +03:00 committed by dsa-t
parent 0562eaa7b1
commit 90ad4dcc05
3 changed files with 245 additions and 72 deletions

View File

@ -2,7 +2,7 @@
* KiRouter - a push-and-(sometimes-)shove PCB router
*
* Copyright (C) 2013-2014 CERN
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software: you can redistribute it and/or modify it
@ -127,6 +127,7 @@ void MEANDERED_LINE::MeanderSegment( const SEG& aBase, bool aSide, int aBaseInde
{
m.Fit( MT_TURN, aBase, m_last, side );
AddMeander( new MEANDER_SHAPE( m ) );
side = !side;
started = true;
}
else
@ -136,8 +137,6 @@ void MEANDERED_LINE::MeanderSegment( const SEG& aBase, bool aSide, int aBaseInde
AddMeander( new MEANDER_SHAPE( m ) );
turning = false;
}
side = !side;
}
}
else if( !singleSided && started )
@ -188,12 +187,33 @@ void MEANDERED_LINE::MeanderSegment( const SEG& aBase, bool aSide, int aBaseInde
}
int MEANDER_SHAPE::MinAmplitude() const
{
int minAmplitude = Settings().m_minAmplitude;
// DP meanders don't really support smaller amplitudes
minAmplitude = std::max( minAmplitude, std::abs( m_baselineOffset ) * 2 );
// The path length won't be correct with very small arcs
if( m_placer->MeanderSettings().m_cornerStyle == MEANDER_STYLE_ROUND )
minAmplitude = std::max( minAmplitude, m_width + std::abs( m_baselineOffset ) * 2 );
return minAmplitude;
}
int MEANDER_SHAPE::cornerRadius() const
{
// TODO: fix diff-pair meandering so we can use non-100% radii
int rPercent = m_dual ? 100 : Settings().m_cornerRadiusPercentage;
return (int64_t) spacing() * rPercent / 200;
int optCr = (int64_t) spacing() * rPercent / 200;
int minCr = std::abs( m_baselineOffset );
int maxCr = std::min( m_amplitude / 2, spacing() / 2 );
int cr = std::clamp( optCr, minCr, maxCr );
return cr;
}
@ -316,12 +336,14 @@ void MEANDER_SHAPE::uShape( int aSides, int aCorner, int aTop )
SHAPE_LINE_CHAIN MEANDER_SHAPE::genMeanderShape( const VECTOR2D& aP, const VECTOR2D& aDir,
bool aSide, MEANDER_TYPE aType, int aAmpl,
bool aSide, MEANDER_TYPE aType,
int aBaselineOffset )
{
int cr = cornerRadius();
int offset = aBaselineOffset;
int spc = spacing();
int amplitude = m_amplitude;
int targetBaseLen = m_targetBaseLen;
if( aSide )
offset *= -1;
@ -329,9 +351,9 @@ SHAPE_LINE_CHAIN MEANDER_SHAPE::genMeanderShape( const VECTOR2D& aP, const VECTO
VECTOR2D dir_u_b( aDir.Resize( offset ) );
VECTOR2D dir_v_b( dir_u_b.Perpendicular() );
if( 2 * cr > aAmpl )
if( 2 * cr > amplitude )
{
cr = aAmpl / 2;
cr = amplitude / 2;
}
if( 2 * cr > spc )
@ -339,8 +361,19 @@ SHAPE_LINE_CHAIN MEANDER_SHAPE::genMeanderShape( const VECTOR2D& aP, const VECTO
cr = spc / 2;
}
if( cr - offset < 0 )
{
cr = offset;
}
m_meanCornerRadius = cr;
int sCorner = cr - offset;
int uCorner = cr + offset;
int startSide = amplitude - 2 * cr + std::abs( offset );
int turnSide = amplitude - cr;
int top = spc - 2 * cr;
SHAPE_LINE_CHAIN lc;
start( &lc, aP + dir_v_b, aDir );
@ -354,45 +387,62 @@ SHAPE_LINE_CHAIN MEANDER_SHAPE::genMeanderShape( const VECTOR2D& aP, const VECTO
}
case MT_START:
{
miter( cr - offset, false );
uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr );
forward( std::min( cr - offset, cr + offset ) );
if( targetBaseLen )
top = std::max( top, targetBaseLen - sCorner - uCorner * 2 + offset );
miter( sCorner, false );
uShape( startSide, uCorner, top );
forward( std::min( sCorner, uCorner ) );
forward( std::abs( offset ) );
break;
}
case MT_FINISH:
{
if( targetBaseLen )
top = std::max( top, targetBaseLen - cr - spc );
start( &lc, aP - dir_u_b, aDir );
turn( -ANGLE_90 );
forward( std::min( cr - offset, cr + offset ) );
forward( std::min( sCorner, uCorner ) );
forward( std::abs( offset ) );
uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr );
miter( cr - offset, false );
uShape( startSide, uCorner, top );
miter( sCorner, false );
if( targetBaseLen >= spc + cr )
lc.Append( aP + dir_v_b + aDir.Resize( targetBaseLen ) );
else
lc.Append( aP + dir_v_b + aDir.Resize( 2 * spc - cr ) );
break;
}
case MT_TURN:
{
if( targetBaseLen )
top = std::max( top, targetBaseLen - uCorner * 2 + offset * 2 );
start( &lc, aP - dir_u_b, aDir );
turn( -ANGLE_90 );
forward( std::abs( offset ) );
uShape( aAmpl - cr, cr + offset, spc - 2 * cr );
uShape( turnSide, uCorner, top );
forward( std::abs( offset ) );
break;
}
case MT_SINGLE:
{
miter( cr - offset, false );
uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr );
miter( cr - offset, false );
if( targetBaseLen )
top = std::max( top, ( targetBaseLen - sCorner * 2 - uCorner * 2 ) / 2 );
miter( sCorner, false );
uShape( startSide, uCorner, top );
miter( sCorner, false );
lc.Append( aP + dir_v_b + aDir.Resize( 2 * spc ) );
break;
}
default:
break;
default: break;
}
if( aSide )
@ -490,34 +540,27 @@ bool MEANDER_SHAPE::Fit( MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP
}
}
int minAmpl = st.m_minAmplitude;
int maxAmpl = st.m_maxAmplitude;
if( m_dual )
{
minAmpl = std::max( minAmpl, 2 * std::abs( m_baselineOffset ) );
maxAmpl = std::max( maxAmpl, 2 * std::abs( m_baselineOffset ) );
}
int minAmpl = MinAmplitude();
int maxAmpl = std::max( st.m_maxAmplitude, minAmpl );
for( int ampl = maxAmpl; ampl >= minAmpl; ampl -= st.m_step )
{
m_amplitude = ampl;
if( m_dual )
{
m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl,
m_baselineOffset );
m_shapes[1] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl,
-m_baselineOffset );
m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, m_baselineOffset );
m_shapes[1] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, -m_baselineOffset );
}
else
{
m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, 0 );
m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, 0 );
}
m_type = aType;
m_baseSeg = aSeg;
m_p0 = aP;
m_side = aSide;
m_amplitude = ampl;
updateBaseSegment();
@ -531,12 +574,12 @@ bool MEANDER_SHAPE::Fit( MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP
void MEANDER_SHAPE::Recalculate()
{
m_shapes[0] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, m_amplitude,
m_shapes[0] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type,
m_dual ? m_baselineOffset : 0 );
if( m_dual )
m_shapes[1] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type,
m_amplitude, -m_baselineOffset );
-m_baselineOffset );
updateBaseSegment();
}
@ -560,11 +603,12 @@ void MEANDER_SHAPE::MakeEmpty()
VECTOR2I dir = m_clippedBaseSeg.B - m_clippedBaseSeg.A;
m_type = MT_EMPTY;
m_amplitude = 0;
m_shapes[0] = genMeanderShape( m_p0, dir, m_side, m_type, 0, m_dual ? m_baselineOffset : 0 );
m_shapes[0] = genMeanderShape( m_p0, dir, m_side, m_type, m_dual ? m_baselineOffset : 0 );
if( m_dual )
m_shapes[1] = genMeanderShape( m_p0, dir, m_side, m_type, 0, -m_baselineOffset );
m_shapes[1] = genMeanderShape( m_p0, dir, m_side, m_type, -m_baselineOffset );
}
@ -654,12 +698,23 @@ int MEANDER_SHAPE::BaselineLength() const
}
int MEANDER_SHAPE::MaxTunableLength() const
long long int MEANDER_SHAPE::CurrentLength() const
{
return CLine( 0 ).Length();
}
long long int MEANDER_SHAPE::MinTunableLength() const
{
MEANDER_SHAPE copy = *this;
copy.SetTargetBaselineLength( BaselineLength() );
copy.Resize( copy.MinAmplitude() );
return copy.CurrentLength();
}
void MEANDER_SHAPE::updateBaseSegment( )
{
if( m_dual )

View File

@ -2,7 +2,7 @@
* KiRouter - a push-and-(sometimes-)shove PCB router
*
* Copyright (C) 2013-2015 CERN
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
@ -129,6 +129,7 @@ public:
// Do not leave uninitialized members, and keep static analyzer quiet:
m_type = MT_SINGLE;
m_amplitude = 0;
m_targetBaseLen = 0;
m_side = false;
m_baseIndex = 0;
m_currentTarget = nullptr;
@ -273,7 +274,17 @@ public:
/**
* @return the length of the fitted line chain.
*/
int MaxTunableLength() const;
long long int CurrentLength() const;
/**
* @return the minumum tunable length according to settings.
*/
long long int MinTunableLength() const;
/**
* @return the minumum possible amplitude according to settings.
*/
int MinAmplitude() const;
/**
* @return the current meandering settings.
@ -299,6 +310,14 @@ public:
m_baselineOffset = aOffset;
}
/**
* Sets the target length of the baseline. When resizing, the meander will try to
* fit the baseline length into the specified value.
*
* @param aLength the minimum baseline length.
*/
void SetTargetBaselineLength( int aLength ) { m_targetBaseLen = aLength; }
private:
friend class MEANDERED_LINE;
@ -322,7 +341,7 @@ private:
///< Produce a meander shape of given type.
SHAPE_LINE_CHAIN genMeanderShape( const VECTOR2D& aP, const VECTOR2D& aDir, bool aSide,
MEANDER_TYPE aType, int aAmpl, int aBaselineOffset = 0 );
MEANDER_TYPE aType, int aBaselineOffset = 0 );
///< Recalculate the clipped baseline after the parameters of the meander have been changed.
void updateBaseSegment();
@ -354,6 +373,9 @@ private:
///< Average radius of meander corners (for correction of DP meanders).
int m_meanCornerRadius;
///< Minimum length of the base segment to target when resizing.
int m_targetBaseLen;
///< First point of the meandered line.
VECTOR2I m_p0;

View File

@ -2,7 +2,7 @@
* KiRouter - a push-and-(sometimes-)shove PCB router
*
* Copyright (C) 2013-2015 CERN
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software: you can redistribute it and/or modify it
@ -27,6 +27,8 @@
namespace PNS {
const int LENGTH_TARGET_TOLERANCE = 20;
MEANDER_PLACER_BASE::MEANDER_PLACER_BASE( ROUTER* aRouter ) :
PLACEMENT_ALGO( aRouter )
{
@ -131,32 +133,112 @@ void MEANDER_PLACER_BASE::cutTunedLine( const SHAPE_LINE_CHAIN& aOrigin, const V
}
int findAmplitudeBinarySearch( MEANDER_SHAPE& aCopy, int targetLength, int minAmp, int maxAmp )
{
if( minAmp == maxAmp )
return maxAmp;
aCopy.Resize( minAmp );
int minLen = aCopy.CurrentLength();
aCopy.Resize( maxAmp );
int maxLen = aCopy.CurrentLength();
if( minLen > targetLength )
return 0;
if( maxLen < targetLength )
return 0;
int minError = minLen - targetLength;
int maxError = maxLen - targetLength;
if( std::abs( minError ) < LENGTH_TARGET_TOLERANCE
|| std::abs( maxError ) < LENGTH_TARGET_TOLERANCE )
{
return std::abs( minError ) < std::abs( maxError ) ? minAmp : maxAmp;
}
else
{
int left =
findAmplitudeBinarySearch( aCopy, targetLength, minAmp, ( minAmp + maxAmp ) / 2 );
if( left )
return left;
int right =
findAmplitudeBinarySearch( aCopy, targetLength, ( minAmp + maxAmp ) / 2, maxAmp );
if( right )
return right;
}
return 0;
}
int findAmplitudeForLength( MEANDER_SHAPE* m, int targetLength, int minAmp, int maxAmp )
{
MEANDER_SHAPE copy = *m;
// Try to keep the same baseline length
copy.SetTargetBaselineLength( m->BaselineLength() );
long long initialGuess = m->Amplitude() - ( m->CurrentLength() - targetLength ) / 2;
if( initialGuess >= minAmp && initialGuess <= maxAmp )
{
copy.Resize( minAmp );
if( std::abs( copy.CurrentLength() - targetLength ) < LENGTH_TARGET_TOLERANCE )
return initialGuess;
}
// The length is non-trivial, use binary search
return findAmplitudeBinarySearch( copy, targetLength, minAmp, maxAmp );
}
void MEANDER_PLACER_BASE::tuneLineLength( MEANDERED_LINE& aTuned, long long int aElongation )
{
long long int remaining = aElongation;
bool finished = false;
long long int maxElongation = 0;
long long int minElongation = 0;
bool finished = false;
for( MEANDER_SHAPE* m : aTuned.Meanders() )
{
if( m->Type() != MT_CORNER && m->Type() != MT_ARC )
{
if( remaining >= 0 )
remaining -= m->MaxTunableLength() - m->BaselineLength();
MEANDER_SHAPE end = *m;
MEANDER_TYPE endType;
if( remaining < 0 )
if( m->Type() == MT_START || m->Type() == MT_SINGLE )
endType = MT_SINGLE;
else
endType = MT_FINISH;
end.SetType( endType );
end.Recalculate();
long long int maxEndElongation = end.CurrentLength() - end.BaselineLength();
if( maxElongation + maxEndElongation > aElongation )
{
if( !finished )
{
MEANDER_TYPE newType;
if( m->Type() == MT_START || m->Type() == MT_SINGLE )
newType = MT_SINGLE;
else
newType = MT_FINISH;
m->SetType( newType );
m->SetType( endType );
m->Recalculate();
if( endType == MT_SINGLE )
{
// Check if we need to fit this meander
long long int endMinElongation =
( m->MinTunableLength() - m->BaselineLength() );
if( minElongation + endMinElongation >= aElongation )
m->MakeEmpty();
}
finished = true;
}
else
@ -164,38 +246,52 @@ void MEANDER_PLACER_BASE::tuneLineLength( MEANDERED_LINE& aTuned, long long int
m->MakeEmpty();
}
}
maxElongation += m->CurrentLength() - m->BaselineLength();
minElongation += m->MinTunableLength() - m->BaselineLength();
}
}
remaining = aElongation;
int meanderCount = 0;
long long int remainingElongation = aElongation;
int meanderCount = 0;
for( MEANDER_SHAPE* m : aTuned.Meanders() )
{
if( m->Type() != MT_CORNER && m->Type() != MT_ARC && m->Type() != MT_EMPTY )
{
if(remaining >= 0)
{
remaining -= m->MaxTunableLength() - m->BaselineLength();
meanderCount ++;
}
remainingElongation -= m->CurrentLength() - m->BaselineLength();
meanderCount++;
}
}
long long int balance = 0;
long long int lenReductionLeft = -remainingElongation;
int meandersLeft = meanderCount;
if( meanderCount )
balance = -remaining / meanderCount;
if( lenReductionLeft < 0 || !meandersLeft )
return;
if( balance >= 0 )
for( MEANDER_SHAPE* m : aTuned.Meanders() )
{
for( MEANDER_SHAPE* m : aTuned.Meanders() )
if( m->Type() != MT_CORNER && m->Type() != MT_ARC && m->Type() != MT_EMPTY )
{
if( m->Type() != MT_CORNER && m->Type() != MT_ARC && m->Type() != MT_EMPTY )
{
m->Resize( std::max( m->Amplitude() - balance / 2,
(long long int) m_settings.m_minAmplitude ) );
}
long long int lenReductionHere = lenReductionLeft / meandersLeft;
long long int initialLen = m->CurrentLength();
int minAmpl = m->MinAmplitude();
int amp = findAmplitudeForLength( m, initialLen - lenReductionHere, minAmpl,
m->Amplitude() );
if( amp < minAmpl )
amp = minAmpl;
m->SetTargetBaselineLength( m->BaselineLength() );
m->Resize( amp );
lenReductionLeft -= initialLen - m->CurrentLength();
meandersLeft--;
if( !meandersLeft )
break;
}
}
}