59 virtual bool Run()
override;
61 virtual const wxString
GetName()
const override {
return wxT(
"length" ); };
70 const std::vector<CONNECTION>& aMatchedConnections );
72 const std::vector<CONNECTION>& aMatchedConnections );
74 const std::vector<CONNECTION>& aMatchedConnections );
77 const std::vector<CONNECTION>& aMatchedConnections );
80 const std::map<wxString, CONNECTION>& aChainAgg );
102 const std::vector<CONNECTION>& aMatchedConnections )
106 bool minViolation =
false;
107 bool maxViolation =
false;
141 if( ( minViolation || maxViolation ) )
147 drcItem->SetErrorDetail(
formatMsg(
_(
"(%s min length %s; actual %s)" ),
155 else if( maxViolation )
157 drcItem->SetErrorDetail(
formatMsg(
_(
"(%s max length %s; actual %s)" ),
166 for(
auto offendingTrack : ent.items )
167 drcItem->AddItem( offendingTrack );
171 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(), ( *ent.items.begin() )->GetLayer() );
177 const std::vector<CONNECTION>& aMatchedConnections )
179 auto checkSkewsImpl = [
this, &aConstraint](
const std::vector<CONNECTION>& connections )
184 double maxLength = 0;
191 if( ent.total > maxLength )
193 maxLength = ent.total;
194 maxNetname = ent.netname;
202 if( ent.totalDelay > maxLength )
204 maxLength = ent.totalDelay;
205 maxNetname = ent.netname;
212 int skew = isTimeDomain ?
KiROUND( ent.totalDelay - maxLength )
213 :
KiROUND( ent.total - maxLength );
215 bool fail_min =
false;
216 bool fail_max =
false;
223 if( fail_min || fail_max )
228 double reportTotal = isTimeDomain ? ent.totalDelay : ent.total;
232 msg.Printf(
_(
"(%s min skew %s; actual %s; target net length %s (from %s); actual %s)" ),
242 msg.Printf(
_(
"(%s max skew %s; actual %s; target net length %s (from %s); actual %s)" ),
251 drcItem->SetErrorDetail( msg );
254 drcItem->SetItems( offendingTrack );
258 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(), ( *ent.items.begin() )->GetLayer() );
266 std::map<int, CONNECTION> netcodeMap;
269 netcodeMap[ent.netcode] = ent;
271 std::vector<std::vector<CONNECTION>> matchedDiffPairs;
273 for(
auto& [netcode, connection] : netcodeMap )
279 int matchedNetcode = matchedNet->
GetNetCode();
281 if( netcodeMap.count( matchedNetcode ) )
283 std::vector<CONNECTION> pair{ connection, netcodeMap[matchedNetcode] };
284 matchedDiffPairs.emplace_back( std::move( pair ) );
285 netcodeMap.erase( matchedNetcode );
291 for(
const std::vector<CONNECTION>& matchedDiffPair : matchedDiffPairs )
292 checkSkewsImpl( matchedDiffPair );
297 checkSkewsImpl( aMatchedConnections );
303 const std::vector<CONNECTION>& aMatchedConnections )
305 for(
const auto& ent : aMatchedConnections )
307 std::shared_ptr<DRC_ITEM> drcItem =
nullptr;
312 wxString msg = wxString::Format(
_(
"(%s max count %d; actual %d)" ),
317 drcItem->SetErrorMessage(
_(
"Too many vias on a connection" ) + wxS(
" " ) + msg );
322 wxString msg = wxString::Format(
_(
"(%s min count %d; actual %d)" ),
327 drcItem->SetErrorMessage(
_(
"Too few vias on a connection" ) + wxS(
" " ) + msg );
333 drcItem->SetItems( offendingTrack );
337 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(), ( *ent.items.begin() )->GetLayer() );
356 if( !aDelayReportMode )
358 if( !
reportPhase(
_(
"Gathering length-constrained connections..." ) ) )
363 std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
365 std::shared_ptr<FROM_TO_CACHE> ftCache =
m_board->GetConnectivity()->GetFromToCache();
369 const size_t progressDelta = 100;
406 std::map< DRC_RULE*, std::vector<CONNECTION> > matches;
408 for(
const auto& [rule, ruleItems] : itemSets )
410 std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
413 netMap[item->GetNetCode()].insert( item );
415 for(
const auto& [netCode, netItems] : netMap )
417 std::vector<LENGTH_DELAY_CALCULATION_ITEM> lengthItems;
418 lengthItems.reserve( netItems.size() );
421 ent.
items = netItems;
437 if( lengthItem.
Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::UNKNOWN )
438 lengthItems.emplace_back( lengthItem );
442 .OptimiseVias =
true, .MergeTracks =
true, .OptimiseTracesInPads =
true, .InferViaInPad =
false
444 LENGTH_DELAY_STATS details = calc->CalculateLengthDetails( lengthItems, opts,
nullptr,
nullptr,
461 ent.
from = ftPath->fromName;
462 ent.
to = ftPath->toName;
466 ent.
from = ent.
to =
_(
"<unconstrained>" );
470 matches[rule].push_back( ent );
474 if( !aDelayReportMode )
476 if( !
reportPhase(
_(
"Checking length constraints..." ) ) )
480 count = matches.size();
482 for( std::pair<
DRC_RULE*
const, std::vector<CONNECTION> > it : matches )
485 auto& matchedConnections = it.second;
490 std::sort( matchedConnections.begin(), matchedConnections.end(),
493 return a.netname < b.netname;
498 REPORT_AUX( wxString::Format( wxT(
"Length-constrained traces for rule '%s':" ),
499 it.first->m_Name ) );
503 REPORT_AUX( wxString::Format( wxT(
" - net: %s, from: %s, to: %s, %d matching items, "
504 "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
508 static_cast<int>( ent.items.size() ),
525 std::map<wxString, CONNECTION> chainAgg;
526 std::optional<DRC_CONSTRAINT> stubConstraint =
528 std::optional<DRC_CONSTRAINT> returnPathConstraint =
531 const bool needsChainAgg =
532 ( netChainLengthConstraint
535 || ( returnPathConstraint
540 for(
const CONNECTION& conn : matchedConnections )
545 wxString chainName = conn.netinfo->GetNetChain();
547 if( chainName.IsEmpty() )
550 auto aggIt = chainAgg.find( chainName );
552 if( aggIt == chainAgg.end() )
556 chainAgg[chainName] = agg;
561 agg.
total += conn.total;
570 agg.
items.insert( conn.items.begin(), conn.items.end() );
575 if( netChainLengthConstraint && netChainLengthConstraint->GetSeverity() !=
RPT_SEVERITY_IGNORE )
577 std::vector<CONNECTION> chainConnections;
578 chainConnections.reserve( chainAgg.size() );
580 for(
auto& [chainName, agg] : chainAgg )
585 if( topo && topo->IsValid() )
590 constraintInput.
total = topo->TrunkLength();
591 constraintInput.
totalDelay = topo->TrunkDelay();
592 constraintInput.
totalRoute = topo->TrunkLength();
603 constraintInput.
total += bridging;
609 chainConnections.push_back( std::move( constraintInput ) );
612 checkLengths( *netChainLengthConstraint, chainConnections );
623 checkSkews( *skewConstraint, matchedConnections );
630 if( returnPathConstraint
632 && !returnPathConstraint->m_ReferenceLayer.IsEmpty() )
646std::shared_ptr<CHAIN_TOPOLOGY>
654 std::set<BOARD_CONNECTED_ITEM*> items;
658 if( t->GetNet() && t->GetNet()->GetNetChain() == aChain )
664 for(
PAD* p : fp->Pads() )
666 if( p->GetNet() && p->GetNet()->GetNetChain() == aChain )
671 auto topo = std::make_shared<CHAIN_TOPOLOGY>(
m_board, aChain, items );
679 const wxString& aRefNet )
681 auto key = std::make_pair( aRefLayer, aRefNet );
691 if( !zone || !zone->IsOnLayer( aRefLayer ) || zone->GetIsRuleArea() )
694 if( !aRefNet.IsEmpty() )
696 wxString zoneNet = zone->GetNetname();
712 else if( zone->Outline() )
719 return &
m_refUnionCache.emplace( key, std::move( refUnion ) ).first->second;
725 const std::vector<CONNECTION>& aMatchedConnections )
727 const bool isTimeDomain =
733 if( !range.HasMax() && !range.HasMin() )
736 auto outOfRange = [&](
double aMeasured )
738 return ( range.HasMax() && aMeasured > range.Max() )
739 || ( range.HasMin() && aMeasured < range.Min() );
745 std::set<wxString> chainNames;
747 for(
const CONNECTION& conn : aMatchedConnections )
749 if( conn.netinfo && !conn.netinfo->GetNetChain().IsEmpty() )
750 chainNames.insert( conn.netinfo->GetNetChain() );
753 std::set<wxString> handledByTopology;
755 for(
const wxString& chainName : chainNames )
759 if( !topoPtr || !topoPtr->IsValid() )
762 handledByTopology.insert( chainName );
766 const double measured = isTimeDomain ? stub.delay : stub.length;
768 if( !outOfRange( measured ) )
771 std::shared_ptr<DRC_ITEM> item =
773 item->SetErrorMessage( wxString::Format(
774 _(
"Stub length (%s) out of range for net chain '%s'." ),
777 item->SetViolatingRule( aRule );
789 for(
const CONNECTION& conn : aMatchedConnections )
796 if( handledByTopology.count( netInfo->
GetNetChain() ) )
808 const double measured = isTimeDomain ? conn.totalDelay
809 :
static_cast<double>( conn.total );
811 if( !outOfRange( measured ) )
814 std::shared_ptr<DRC_ITEM> item =
816 item->SetErrorMessage( wxString::Format(
817 _(
"Stub length (%s) out of range for net chain '%s' on net '%s'." ),
821 item->SetViolatingRule( aRule );
824 item->AddItem( connItem );
829 if( !conn.items.empty() )
831 pos = ( *conn.items.begin() )->GetPosition();
832 layer = ( *conn.items.begin() )->GetLayer();
842 const std::map<wxString, CONNECTION>& aChainAgg )
858 std::vector<BOARD_CONNECTED_ITEM*> items;
863 std::vector<FlaggedRegion>& aFlagged,
866 for(
int o = 0; o < aRegion.OutlineCount(); ++o )
869 fr.poly.AddOutline( aRegion.Outline( o ) );
871 for(
int h = 0; h < aRegion.HoleCount( o ); ++h )
872 fr.poly.AddHole( aRegion.Hole( o, h ) );
874 fr.items.push_back( aItem );
876 aFlagged.push_back( std::move( fr ) );
880 for(
const auto& [chainName, agg] : aChainAgg )
882 std::vector<FlaggedRegion> flagged;
900 flagItem( item, flagged, std::move( itemPoly ) );
915 flagItem( item, flagged, std::move( diff ) );
918 if( flagged.empty() )
925 std::vector<int> parent( flagged.size() );
926 std::iota( parent.begin(), parent.end(), 0 );
928 auto find = [&](
int x )
930 while( parent[x] != x )
932 parent[x] = parent[parent[x]];
939 for(
size_t i = 0; i < flagged.size(); ++i )
941 BOX2I bi = flagged[i].poly.BBox();
944 for(
size_t j = i + 1; j < flagged.size(); ++j )
946 BOX2I bj = flagged[j].poly.BBox();
966 int ri =
find(
static_cast<int>( i ) );
967 int rj =
find(
static_cast<int>( j ) );
974 std::map<int, std::vector<int>> groups;
976 for(
size_t i = 0; i < flagged.size(); ++i )
977 groups[
find(
static_cast<int>( i ) )].push_back(
static_cast<int>( i ) );
979 for(
const auto& [root, members] : groups )
982 std::vector<BOARD_CONNECTED_ITEM*> items;
983 std::set<BOARD_CONNECTED_ITEM*> seen;
986 for(
int idx : members )
992 if( seen.insert( it ).second )
993 items.push_back( it );
997 markerLayer = flagged[idx].layer;
1005 ? items.front()->GetPosition()
1008 std::shared_ptr<DRC_ITEM> drcItem =
1011 wxString msg = wxString::Format(
1012 _(
"Net chain '%s' has no copper return path on reference layer '%s'." ),
1013 chainName, refLayerName );
1015 if( !refNetPattern.IsEmpty() )
1016 msg += wxString::Format(
_(
" (net '%s')" ), refNetPattern );
1018 drcItem->SetErrorMessage( msg );
1019 drcItem->SetViolatingRule( aRule );
1022 drcItem->AddItem( it );
constexpr int ARC_HIGH_DEF
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
Information pertinent to a Pcbnew printed circuit board.
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
constexpr Vec Centre() const
constexpr bool Intersects(const BOX2< Vec > &aRect) const
const MINOPTMAX< int > & GetValue() const
wxString m_ReferenceLayer
bool GetOption(OPTIONS option) const
DRC_RULE * GetParentRule() const
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
std::optional< DRC_CONSTRAINT > FindConstraint(DRC_CONSTRAINT_T aType)
bool runInternal(bool aDelayReportMode=false)
virtual const wxString GetName() const override
std::shared_ptr< CHAIN_TOPOLOGY > chainTopologyFor(const wxString &aChain)
void checkSkews(const DRC_CONSTRAINT &aConstraint, const std::vector< CONNECTION > &aMatchedConnections)
SHAPE_POLY_SET * zoneUnionFor(PCB_LAYER_ID aRefLayer, const wxString &aRefNet)
virtual ~DRC_TEST_PROVIDER_MATCHED_LENGTH()=default
void checkReturnPath(const DRC_CONSTRAINT &aConstraint, DRC_RULE *aRule, const std::map< wxString, CONNECTION > &aChainAgg)
void checkViaCounts(const DRC_CONSTRAINT &aConstraint, const std::vector< CONNECTION > &aMatchedConnections)
DRC_LENGTH_REPORT m_report
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
std::map< wxString, std::shared_ptr< CHAIN_TOPOLOGY > > m_chainTopoCache
DRC_TEST_PROVIDER_MATCHED_LENGTH()
DRC_LENGTH_REPORT::ENTRY CONNECTION
std::map< std::pair< PCB_LAYER_ID, wxString >, SHAPE_POLY_SET > m_refUnionCache
void checkStubLengths(const DRC_CONSTRAINT &aConstraint, DRC_RULE *aRule, const std::vector< CONNECTION > &aMatchedConnections)
void checkLengths(const DRC_CONSTRAINT &aConstraint, const std::vector< CONNECTION > &aMatchedConnections)
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
REPORTER * getLogReporter() const
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
KICAD_T Type() const
Returns the type of object.
Lightweight class which holds a pad, via, or a routed trace outline.
TYPE Type() const
Gets the routing item type.
Class which calculates lengths (and associated routing statistics) in a BOARD context.
LSET is a set of PCB_LAYER_IDs.
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Handle the data for a net.
const wxString & GetNetChain() const
const wxString & GetNetname() const
PAD * GetTerminalPad(int aIndex) const
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
Handle a list of polygons defining a copper zone.
@ ROUND_ALL_CORNERS
All angles are rounded.
@ DRCE_NET_CHAIN_STUB_TOO_LONG
@ DRCE_NET_CHAIN_RETURN_PATH_BREAK
@ DRCE_LENGTH_OUT_OF_RANGE
@ DRCE_VIA_COUNT_OUT_OF_RANGE
@ NET_CHAIN_LENGTH_CONSTRAINT
@ NET_CHAIN_STUB_LENGTH_CONSTRAINT
@ NET_CHAIN_RETURN_PATH_CONSTRAINT
EDA_DATA_TYPE
The type of unit.
PCB_LAYER_ID
A quick note on layer IDs:
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
std::tuple< double, double > BoardChainBridging(const BOARD *aBoard, const wxString &aNetChain)
bool WildCompareString(const wxString &pattern, const wxString &string_to_tst, bool case_sensitive)
Compare a string against wild card (* and ?) pattern using the usual rules.
BOARD_CONNECTED_ITEM * fromItem
BOARD_CONNECTED_ITEM * toItem
std::set< BOARD_CONNECTED_ITEM * > items
int64_t totalPadToDieDelay
Holds length measurement result details and statistics.
Struct to control which optimisations the length calculation code runs on the given path objects.
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_PAD_T
class PAD, a pad in a footprint
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
VECTOR2< int32_t > VECTOR2I