drc: initial version of matched trace lengths test provider

This commit is contained in:
Tomasz Wlostowski 2020-09-23 23:49:34 +02:00
parent 2bacfe8202
commit 2258c861c9
1 changed files with 542 additions and 0 deletions

View File

@ -0,0 +1,542 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2020 KiCad Developers.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <common.h>
#include <class_board.h>
#include <class_drawsegment.h>
#include <class_pad.h>
#include <drc/drc_engine.h>
#include <drc/drc_item.h>
#include <drc/drc_rule.h>
#include <drc/drc_test_provider.h>
#include <connectivity/connectivity_data.h>
#include <connectivity/connectivity_algo.h>
#include <pcb_expr_evaluator.h>
/*
Single-ended matched length test.
Errors generated:
- DRCE_SIGNAL_LENGTH
*/
namespace test {
class DRC_TEST_PROVIDER_MATCHED_LENGTH : public DRC_TEST_PROVIDER
{
public:
DRC_TEST_PROVIDER_MATCHED_LENGTH ()
{
}
virtual ~DRC_TEST_PROVIDER_MATCHED_LENGTH()
{
}
virtual bool Run() override;
virtual const wxString GetName() const override
{
return "length";
};
virtual const wxString GetDescription() const override
{
return "Tests matched track lengths.";
}
virtual int GetNumPhases() const override
{
return 1;
}
virtual std::set<DRC_CONSTRAINT_TYPE_T> GetConstraintTypes() const override;
bool checkFromToPath( BOARD_CONNECTED_ITEM* aItem, const wxString& aFrom, const wxString& aTo );
private:
struct FROM_TO_ENDPOINT
{
wxString name;
D_PAD* parent;
};
struct FROM_TO_PATH
{
int net;
D_PAD *from;
D_PAD *to;
wxString fromName, toName;
wxString fromWildcard, toWildcard;
bool isUnique;
std::set<BOARD_CONNECTED_ITEM*> pathItems;
};
int cacheFromToPaths( const wxString& aFrom, const wxString& aTo );
void buildEndpointList();
std::vector<FROM_TO_ENDPOINT> m_ftEndpoints;
std::vector<FROM_TO_PATH> m_ftPaths;
BOARD* m_board;
int m_largestClearance;
};
};
bool exprFromTo( LIBEVAL::CONTEXT* aCtx, void* self )
{
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
LIBEVAL::VALUE* result = aCtx->AllocValue();
LIBEVAL::VALUE* argTo = aCtx->Pop();
LIBEVAL::VALUE* argFrom = aCtx->Pop();
result->Set(0.0);
aCtx->Push( result );
if(!item)
return false;
auto drcEngine = item->GetBoard()->GetDesignSettings().m_DRCEngine;
assert ( drcEngine );
auto lengthTestProvider = static_cast<test::DRC_TEST_PROVIDER_MATCHED_LENGTH*>( drcEngine->GetTestProvider("length") );
if( lengthTestProvider->checkFromToPath( static_cast<BOARD_CONNECTED_ITEM*>( item ),
argFrom->AsString(),
argTo->AsString() ) )
{
result->Set(1.0);
}
return true;
}
void test::DRC_TEST_PROVIDER_MATCHED_LENGTH::buildEndpointList( )
{
m_ftEndpoints.clear();
for( auto mod : m_board->Modules() )
{
for( auto pad : mod->Pads() )
{
FROM_TO_ENDPOINT ent;
ent.name = mod->GetReference() + "-" + pad->GetName();
ent.parent = pad;
m_ftEndpoints.push_back( ent );
ent.name = mod->GetReference();
ent.parent = pad;
m_ftEndpoints.push_back( ent );
}
}
}
enum PATH_STATUS {
PS_OK = 0,
PS_MULTIPLE_PATHS = -1,
PS_NO_PATH = -2
};
static bool isVertexVisited( CN_ITEM* v, const std::vector<CN_ITEM*>& path )
{
for( auto u : path )
{
if ( u == v )
return true;
}
return false;
}
static PATH_STATUS uniquePathBetweenNodes( CN_ITEM* u, CN_ITEM* v, std::vector<CN_ITEM*>& outPath )
{
using Path = std::vector<CN_ITEM*>;
std::deque<Path> Q;
Path p;
int pathFound = false;
p.push_back( u );
Q.push_back( p );
while( Q.size() )
{
Path path = Q.front();
Q.pop_front();
CN_ITEM *last = path.back();
if( last == v )
{
outPath = path;
if( pathFound )
return PS_MULTIPLE_PATHS;
pathFound = true;
}
for( auto ci : last->ConnectedItems() )
{
bool vertexVisited = isVertexVisited(ci, path);
for( auto &p : Q )
if (isVertexVisited(ci, p))
{
vertexVisited = true;
break;
}
if (!vertexVisited) {
Path newpath(path);
newpath.push_back(ci);
Q.push_back(newpath);
}
}
}
return pathFound ? PS_OK : PS_NO_PATH;
};
int test::DRC_TEST_PROVIDER_MATCHED_LENGTH::cacheFromToPaths( const wxString& aFrom, const wxString& aTo )
{
std::vector<FROM_TO_PATH> paths;
auto connectivity = m_board->GetConnectivity();
auto cnAlgo = connectivity->GetConnectivityAlgo();
for( auto& endpoint : m_ftEndpoints )
{
if( WildCompareString( aFrom, endpoint.name, false ) )
{
FROM_TO_PATH p;
p.net = endpoint.parent->GetNetCode();
p.from = endpoint.parent;
p.to = nullptr;
paths.push_back(p);
}
}
for( auto &path : paths )
{
int count = 0;
auto netName = path.from->GetNetname();
wxString fromName = path.from->GetParent()->GetReference() + "-" + path.from->GetName();
const KICAD_T onlyRouting[] = { PCB_PAD_T, PCB_ARC_T, PCB_VIA_T, PCB_TRACE_T, EOT };
auto padCandidates = connectivity->GetConnectedItems( path.from, onlyRouting );
D_PAD* toPad = nullptr;
for( auto pitem : padCandidates )
{
if( pitem == path.from )
continue;
if( pitem->Type() != PCB_PAD_T )
continue;
D_PAD *pad = static_cast<D_PAD*>( pitem );
wxString toName = pad->GetParent()->GetReference() + "-" + pad->GetName();
for ( auto& endpoint : m_ftEndpoints )
{
if( pad == endpoint.parent )
{
if( WildCompareString( aTo, endpoint.name, false ) )
{
//printf("match from-to: %s -> %s [net %s] p %p\n", (const char *)fromName, (const char *) toName, (const char *) netName, endpoint.parent );
count++;
toPad = endpoint.parent;
path.to = toPad;
path.fromName = fromName;
path.toName = toName;
path.fromWildcard = aFrom;
path.toWildcard = aTo;
if( count >= 2 )
{
//printf("Multiple targets found, aborting...\n");
path.to = nullptr;
}
}
}
}
}
}
int newPaths = 0;
for( auto &path : paths )
{
if( !path.from || !path.to )
continue;
CN_ITEM *cnFrom = cnAlgo->ItemEntry( path.from ).GetItems().front();
CN_ITEM *cnTo = cnAlgo->ItemEntry( path.to ).GetItems().front();
CN_ITEM::CONNECTED_ITEMS upath;
auto result = uniquePathBetweenNodes( cnFrom, cnTo, upath );
if( result == PS_OK )
path.isUnique = true;
else
path.isUnique = false;
reportAux( wxString::Format( _("Check path: %s -> %s (net %s)"), path.fromName, path.toName, cnFrom->Parent()->GetNetname() ) );
if( result == PS_NO_PATH )
continue;
for( auto item : upath )
{
path.pathItems.insert( item->Parent() );
}
m_ftPaths.push_back(path);
newPaths++;
}
reportAux( _("Cached %d paths\n"), newPaths );
return newPaths;
}
bool test::DRC_TEST_PROVIDER_MATCHED_LENGTH::checkFromToPath( BOARD_CONNECTED_ITEM* aItem, const wxString& aFrom, const wxString& aTo )
{
int nFromTosFound = 0;
//printf("Check %d cached paths [%p]\n", m_ftPaths.size(), aItem );
for( int attempt = 0; attempt < 2; attempt++ )
{
// item already belongs to path
for( auto& ftPath : m_ftPaths )
{
if( aFrom == ftPath.fromWildcard &&
aTo == ftPath.toWildcard )
{
nFromTosFound++;
if( ftPath.pathItems.count( aItem ) )
{
// printf("Found cached path for %p [%s->%s]\n", aItem, (const char *)ftPath.fromName, (const char *) ftPath.toName );
return true;
}
}
}
if( !nFromTosFound )
cacheFromToPaths( aFrom, aTo );
else
return false;
}
return false;
}
bool test::DRC_TEST_PROVIDER_MATCHED_LENGTH::Run()
{
m_board = m_drcEngine->GetBoard();
reportPhase(( "Gathering length-constrained connections..." ));
typedef std::vector<BOARD_CONNECTED_ITEM*> CITEMS;
std::map<DRC_RULE*, CITEMS> itemSets;
auto evaluateLengthConstraints =
[&]( BOARD_ITEM *item ) -> bool
{
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_LENGTH, item );
if( constraint.IsNull() )
return true;
auto citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
itemSets[ constraint.GetParentRule() ].push_back( citem );
return true;
};
buildEndpointList( );
forEachGeometryItem( { PCB_TRACE_T, PCB_VIA_T, PCB_ARC_T },
LSET::AllCuMask(), evaluateLengthConstraints );
struct LENGTH_ENTRY {
int netcode;
wxString netname;
CITEMS items;
int viaCount;
int totalRoute;
int totalVia;
int totalPadToDie;
int total;
};
typedef std::vector<LENGTH_ENTRY> LENGTH_ENTRIES;
std::map<DRC_RULE*, LENGTH_ENTRIES> matches;
for( auto it : itemSets )
{
std::map<int, CITEMS> netMap;
for( auto citem : it.second )
netMap[ citem->GetNetCode() ].push_back( citem );
for( auto nitem : netMap )
{
LENGTH_ENTRY ent;
ent.items = nitem.second;
ent.netcode = nitem.first;
ent.netname = m_board->GetNetInfo().GetNetItem( ent.netcode )->GetNetname();
ent.viaCount = 0;
ent.totalRoute = 0;
ent.totalVia = 0;
ent.totalPadToDie = 0;
for( auto citem : nitem.second )
{
if ( auto bi = dyn_cast<VIA*>( citem ) )
{
ent.viaCount++;
ent.totalVia += 0; // fixme: via thru distance
}
else if ( auto bi = dyn_cast<TRACK*>(citem ))
{
ent.totalRoute += bi->GetLength();
}
}
ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
matches[ it.first ].push_back(ent);
}
}
for( auto it : matches )
{
DRC_RULE *rule = it.first;
auto& matchedConnections = it.second;
std::sort( matchedConnections.begin(), matchedConnections.end(),
[] ( const LENGTH_ENTRY&a, const LENGTH_ENTRY&b ) -> int
{
return a.netname < b.netname;
}
);
reportAux( wxString::Format( _("Length-constrained traces for rule '%s':"), it.first->m_Name ) );
for( const auto& ent : matchedConnections )
{
reportAux(wxString::Format(
" - net %s: %d matching items, total: %s (tracks: %s, vias: %s, pad-to-die: %s), vias: %d",
ent.netname,
(int) ent.items.size(),
MessageTextFromValue( userUnits(), ent.total, true ),
MessageTextFromValue( userUnits(), ent.totalRoute, true ),
MessageTextFromValue( userUnits(), ent.totalVia, true ),
MessageTextFromValue( userUnits(), ent.totalPadToDie, true ),
ent.viaCount
) );
}
OPT<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( DRC_CONSTRAINT_TYPE_LENGTH );
if( lengthConstraint )
{
for( const auto& ent : matchedConnections )
{
bool minViolation = false;
bool maxViolation = false;
int minLen, maxLen;
if( lengthConstraint->GetValue().HasMin() && ent.total < lengthConstraint->GetValue().Min() )
{
//printf("Min violation %d %d\n", ent.total, lengthConstraint->GetValue().Min() );
minViolation = true;
minLen = lengthConstraint->GetValue().Min();
}
else if( lengthConstraint->GetValue().HasMax() && ent.total > lengthConstraint->GetValue().Max() )
{
//printf("Max violation %d %d\n", ent.total, lengthConstraint->GetValue().Min() );
maxViolation = true;
maxLen = lengthConstraint->GetValue().Max();
}
if( (minViolation || maxViolation) )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LENGTH_OUT_OF_RANGE );
wxString msg = drcItem->GetErrorText() + " (" + lengthConstraint->GetParentRule()->m_Name + " ";
if( minViolation )
msg += wxString::Format( _("minimum length: %s; actual: %s)" ),
MessageTextFromValue( userUnits(), minLen, true ),
MessageTextFromValue( userUnits(), ent.total, true ) );
else if( maxViolation )
msg += wxString::Format( _("maximum length: %s; actual: %s)" ),
MessageTextFromValue( userUnits(), maxLen, true ),
MessageTextFromValue( userUnits(), ent.total, true ) );
drcItem->SetErrorMessage( msg );
// drcItem->SetItems( aRefItem->parent, aTestItem->parent );
drcItem->SetViolatingRule( lengthConstraint->GetParentRule() );
reportViolation( drcItem, wxPoint(0, 0) ); //aRefItem->parent->GetPosition() );
}
}
}
}
reportRuleStatistics();
return true;
}
std::set<DRC_CONSTRAINT_TYPE_T> test::DRC_TEST_PROVIDER_MATCHED_LENGTH::GetConstraintTypes() const
{
return { DRC_CONSTRAINT_TYPE_LENGTH, DRC_CONSTRAINT_TYPE_SKEW };
}
namespace detail
{
static DRC_REGISTER_TEST_PROVIDER<test::DRC_TEST_PROVIDER_MATCHED_LENGTH> dummy;
}