Browse Source
pcbnew: refactor topology matching algorithm and move it to the connectivity subsystem
jobs
pcbnew: refactor topology matching algorithm and move it to the connectivity subsystem
jobs
5 changed files with 776 additions and 451 deletions
-
3pcbnew/connectivity/CMakeLists.txt
-
455pcbnew/connectivity/topo_match.cpp
-
178pcbnew/connectivity/topo_match.h
-
511pcbnew/tools/multichannel_tool.cpp
-
80pcbnew/tools/multichannel_tool.h
@ -0,0 +1,455 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) Kicad Developers, see change_log.txt for contributors. |
|||
* |
|||
* 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 <cstdio>
|
|||
#include <cstdlib>
|
|||
#include <cmath>
|
|||
#include <string>
|
|||
#include <vector>
|
|||
#include <algorithm>
|
|||
#include <cassert>
|
|||
#include <map>
|
|||
#include <set>
|
|||
#include <cctype>
|
|||
|
|||
#include <footprint.h>
|
|||
#include <wx/string.h>
|
|||
|
|||
#include "topo_match.h"
|
|||
|
|||
namespace TMATCH |
|||
{ |
|||
|
|||
bool PIN::IsIsomorphic( const PIN& b ) const |
|||
{ |
|||
if( m_conns.size() != b.m_conns.size() ) |
|||
{ |
|||
printf("[conns mismatch n1 %d n2 %d c-ref %d c-other %d thispin %s-%s otherpin %s-%s", m_netcode, b.m_netcode, (int) m_conns.size(), (int) b.m_conns.size(), |
|||
m_parent->m_reference.c_str().AsChar(), m_ref.c_str().AsChar(), |
|||
b.m_parent->m_reference.c_str().AsChar(), b.m_ref.c_str().AsChar() ); |
|||
|
|||
for( auto c : m_conns ) |
|||
printf("%s-%s ", c->m_parent->m_reference.c_str().AsChar(), c->m_ref.c_str().AsChar() ); |
|||
|
|||
printf("||"); |
|||
|
|||
for( auto c : b.m_conns ) |
|||
printf("%s-%s ", c->m_parent->m_reference.c_str().AsChar(), c->m_ref.c_str().AsChar() ); |
|||
|
|||
|
|||
printf("] "); |
|||
return false; |
|||
} |
|||
|
|||
if( m_conns.empty() ) |
|||
{ |
|||
printf("[conns empty]"); |
|||
return true; |
|||
} |
|||
|
|||
bool matches[m_conns.size()]; |
|||
|
|||
for( int i = 0; i < m_conns.size(); i++ ) |
|||
matches[i] = false; |
|||
|
|||
//printf("REF: %s\n", format().c_str() );
|
|||
//printf("B : %s\n", b.format().c_str() );
|
|||
|
|||
int nref = 0; |
|||
|
|||
for( auto& cref : m_conns ) |
|||
{ |
|||
//printf("[CREF: %s]", cref->Format().c_str().AsChar() );
|
|||
for( int i = 0; i < m_conns.size(); i++ ) |
|||
{ |
|||
if( b.m_conns[i]->IsTopologicallySimilar( *cref ) ) |
|||
{ |
|||
//printf("[CMATCH: %s]", b.m_conns[i]->Format().c_str().AsChar() );
|
|||
matches[nref] = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
nref++; |
|||
} |
|||
|
|||
bool r = true; |
|||
|
|||
for( int i = 0; i < m_conns.size(); i++ ) |
|||
if( !matches[i] ) |
|||
r = false; |
|||
|
|||
//printf("pin %s vs %s iso=%d\n", format().c_str(), b.format().c_str(), r ? 1: 0 );
|
|||
|
|||
return r; |
|||
} |
|||
|
|||
|
|||
std::vector<COMPONENT*> |
|||
CONNECTION_GRAPH::findMatchingComponents( COMPONENT* aRef, const BACKTRACK_STAGE& partialMatches ) |
|||
{ |
|||
std::vector<COMPONENT*> matches; |
|||
for( auto cmpTarget : m_components ) |
|||
{ |
|||
printf("Check '%s'/'%s' ", aRef->m_reference.c_str().AsChar(), cmpTarget->m_reference.c_str().AsChar() ); |
|||
if( partialMatches.m_locked.find( cmpTarget ) != partialMatches.m_locked.end() ) |
|||
{ |
|||
printf("discard\n"); |
|||
continue; |
|||
} |
|||
|
|||
|
|||
if( aRef->MatchesWith( cmpTarget ) ) |
|||
{ |
|||
printf("match!"); |
|||
//printf("possible match: %s/%s [fp %s/%s]\n", cmpTarget->reference.c_str(), ref->reference.c_str(), cmpTarget->footprintName.c_str(), ref->footprintName.c_str() );
|
|||
|
|||
matches.push_back( cmpTarget ); |
|||
} |
|||
printf("\n"); |
|||
} |
|||
|
|||
return matches; |
|||
} |
|||
|
|||
void COMPONENT::sortPinsByName() |
|||
{ |
|||
std::sort( m_pins.begin(), m_pins.end(), |
|||
[]( PIN* a, PIN* b ) |
|||
{ |
|||
return a->GetReference() < b->GetReference(); |
|||
} ); |
|||
} |
|||
|
|||
void CONNECTION_GRAPH::BuildConnectivity() |
|||
{ |
|||
std::map<int, std::vector<PIN*>> nets; |
|||
|
|||
sortByPinCount(); |
|||
|
|||
for( auto c : m_components ) |
|||
{ |
|||
c->sortPinsByName(); |
|||
|
|||
for( auto p : c->Pins() ) |
|||
{ |
|||
printf("NC %d pin %s\n", p->GetNetCode(), p->m_ref.c_str().AsChar() ); |
|||
if( p->GetNetCode() > 0 ) |
|||
nets[p->GetNetCode()].push_back( p ); |
|||
} |
|||
} |
|||
|
|||
for( auto iter : nets ) |
|||
{ |
|||
printf("net %d: %d connections\n", iter.first, iter.second.size() ); |
|||
for( auto p : iter.second ) |
|||
{ |
|||
for( auto p2 : iter.second ) |
|||
if( p != p2 && !alg::contains( p->m_conns, p2 ) ) |
|||
{ |
|||
p->m_conns.push_back( p2 ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
for( auto c : m_components ) |
|||
for( auto p : c->Pins() ) |
|||
{ |
|||
printf("pin %s: \n", p->m_ref.c_str().AsChar() ); |
|||
|
|||
for( auto c : p->m_conns ) |
|||
printf( "%s ", c->m_ref.c_str().AsChar() ); |
|||
printf("\n"); |
|||
} |
|||
} |
|||
|
|||
CONNECTION_GRAPH::STATUS CONNECTION_GRAPH::FindIsomorphism( CONNECTION_GRAPH* aTarget, |
|||
COMPONENT_MATCHES& aResult ) |
|||
{ |
|||
std::vector<BACKTRACK_STAGE> stack; |
|||
BACKTRACK_STAGE top; |
|||
|
|||
//printf("Ref: %d, tgt: %d\n", m_components.size(), aTarget->m_components.size() );
|
|||
|
|||
if( m_components.empty()|| aTarget->m_components.empty() ) |
|||
return ST_EMPTY; |
|||
|
|||
if( m_components.size() != aTarget->m_components.size() ) |
|||
return ST_COMPONENT_COUNT_MISMATCH; |
|||
|
|||
top.m_ref = m_components.front(); |
|||
top.m_matches = aTarget->findMatchingComponents( top.m_ref, top ); |
|||
|
|||
stack.push_back( top ); |
|||
|
|||
int refIndex = 1; |
|||
bool matchFound = false; |
|||
int nloops = 0; |
|||
while( !stack.empty() ) |
|||
{ |
|||
nloops++; |
|||
auto& current = stack.back(); |
|||
|
|||
if( nloops >= c_ITER_LIMIT ) |
|||
{ |
|||
return ST_ITERATION_COUNT_EXCEEDED; |
|||
} |
|||
|
|||
|
|||
if( current.m_currentMatch >= current.m_matches.size() ) |
|||
{ |
|||
stack.pop_back(); |
|||
continue; |
|||
} |
|||
|
|||
printf("Current '%s' stack %d cm %d/%d locked %d/%d candidate '%s'\n", current.m_ref->m_reference.c_str().AsChar(), (int) stack.size(), current.m_currentMatch, (int) current.m_matches.size(),(int) current.m_locked.size(), (int)m_components.size(), |
|||
current.m_matches[current.m_currentMatch]->m_reference.c_str().AsChar() ); |
|||
|
|||
auto& match = current.m_matches[current.m_currentMatch]; |
|||
|
|||
current.m_currentMatch++; |
|||
current.m_locked[match] = current.m_ref; |
|||
|
|||
|
|||
if( current.m_locked.size() == m_components.size() ) |
|||
{ |
|||
//printf("NLoops: %d\n", nloops);
|
|||
current.m_nloops = nloops; |
|||
|
|||
aResult.clear(); |
|||
|
|||
for( auto iter : current.m_locked ) |
|||
aResult[ iter.second->GetParent() ] = iter.first->GetParent(); |
|||
|
|||
return ST_OK; |
|||
} |
|||
|
|||
|
|||
printf("RI %d cs %d\n", refIndex, (int) m_components.size() ); |
|||
|
|||
if( refIndex >= m_components.size() ) |
|||
break; |
|||
|
|||
//printf("ref '%s', locked %d, stack %d\n", current.locked.size(), stack.size() );
|
|||
|
|||
BACKTRACK_STAGE next( current ); |
|||
next.m_currentMatch = 0; |
|||
next.m_ref = m_components[refIndex++]; |
|||
next.m_matches = aTarget->findMatchingComponents( next.m_ref, next ); |
|||
|
|||
printf("Nxt '%s' matches %d\n", next.m_ref->m_reference.c_str().AsChar(), next.m_matches.size() ); |
|||
printf("m: "); |
|||
for( auto l : next.m_matches ) |
|||
{ |
|||
printf("%s ", l->m_reference.c_str().AsChar() ); |
|||
} |
|||
printf("\n"); |
|||
|
|||
|
|||
// printf(" - matches: %d\n", (int) next.matches.size() );
|
|||
|
|||
if( next.m_matches.empty() ) |
|||
continue; |
|||
|
|||
|
|||
/* printf("LOCKED: ");
|
|||
for( auto l : next.locked ) |
|||
{ |
|||
printf("%s ", l.first->reference.c_str() ); |
|||
} |
|||
printf("\n"); |
|||
|
|||
printf("PUSH L %d\n", (int) next.locked.size() );*/ |
|||
stack.push_back( next ); |
|||
|
|||
//printf("NL %d/%d\n", (int) next.locked.size(), (int) cgRef->components.size() );
|
|||
}; |
|||
|
|||
|
|||
return ST_TOPOLOGY_MISMATCH; |
|||
} |
|||
|
|||
#if 0
|
|||
int main() |
|||
{ |
|||
FILE * f = fopen("connectivity.dump","rb" ); |
|||
auto cgRef = loadCGraph(f); |
|||
auto cgTarget = loadCGraph(f); |
|||
|
|||
cgRef->buildConnectivity(); |
|||
cgTarget->buildConnectivity(); |
|||
|
|||
int attempts = 0; |
|||
int max_loops = 0; |
|||
|
|||
for( ;; ) |
|||
{ |
|||
cgRef->shuffle(); |
|||
cgTarget->shuffle(); |
|||
|
|||
const BacktrackStage latest = cgRef->matchCGraphs( cgTarget ); |
|||
|
|||
if( !latest.locked.size() ) |
|||
{ |
|||
printf("MATCH FAIL\n"); |
|||
break; |
|||
} |
|||
|
|||
//printf("loops: %d\n", latest.nloops );
|
|||
//printf("Locked: %d\n", latest.locked.size() );
|
|||
|
|||
//if (matchFound)
|
|||
//{
|
|||
// for( auto& iter : latest.locked )
|
|||
//{
|
|||
// printf("%-10s : %-10s\n", iter.first->reference.c_str(), iter.second->reference.c_str() );
|
|||
//}
|
|||
|
|||
//}
|
|||
|
|||
if( latest.nloops > max_loops ) |
|||
{ |
|||
max_loops = latest.nloops; |
|||
} |
|||
|
|||
if (attempts % 10000 == 0) |
|||
{ |
|||
printf("attempts: %d maxloops: %d\n", attempts, max_loops ); |
|||
} |
|||
|
|||
attempts++; |
|||
|
|||
} |
|||
|
|||
fclose(f); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
#endif
|
|||
|
|||
COMPONENT::COMPONENT( const wxString& aRef, FOOTPRINT* aParentFp ) : |
|||
m_reference( aRef ), m_parentFootprint( aParentFp ) |
|||
{ |
|||
int i; |
|||
for( i = 0; i < aRef.length(); i++ ) |
|||
{ |
|||
if( std::iswalpha( aRef[i].GetValue() ) ) |
|||
break; |
|||
} |
|||
|
|||
m_prefix = aRef.substr( 0, i ); |
|||
} |
|||
|
|||
bool COMPONENT::IsSameKind( const COMPONENT& b ) const |
|||
{ |
|||
return m_prefix == b.m_prefix && m_parentFootprint->GetFPID() == b.m_parentFootprint->GetFPID(); |
|||
} |
|||
|
|||
void COMPONENT::AddPin( PIN* aPin ) |
|||
{ |
|||
m_pins.push_back( aPin ); |
|||
aPin->SetParent( this ); |
|||
} |
|||
|
|||
bool COMPONENT::MatchesWith( COMPONENT* b ) |
|||
{ |
|||
if( GetPinCount() != b->GetPinCount() ) |
|||
{ |
|||
printf("[cp mismatch]"); |
|||
return false; |
|||
} |
|||
if( m_parentFootprint->GetFPID() != b->m_parentFootprint->GetFPID() ) |
|||
{ |
|||
printf("[fpid mismatch]"); |
|||
return false; |
|||
} |
|||
if( m_prefix != b->m_prefix ) |
|||
{ |
|||
printf("[pre mismatch]"); |
|||
return false; |
|||
} |
|||
|
|||
bool fail = false; |
|||
for( int pin = 0; pin < b->GetPinCount(); pin++ ) |
|||
{ |
|||
if( !b->m_pins[pin]->IsIsomorphic( *m_pins[pin] ) ) |
|||
{ |
|||
printf("[iso fail p%d]", pin ); |
|||
fail = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return !fail; |
|||
} |
|||
|
|||
void CONNECTION_GRAPH::AddFootprint( FOOTPRINT* aFp ) |
|||
{ |
|||
auto cmp = new COMPONENT( aFp->GetReference(), aFp );; |
|||
|
|||
for( auto pad : aFp->Pads() ) |
|||
{ |
|||
//printf("pad %p\n", pad );
|
|||
auto pin = new PIN( ); |
|||
pin->m_netcode = pad->GetNetCode(); |
|||
pin->m_ref = pad->GetNumber(); |
|||
cmp->AddPin( pin ); |
|||
} |
|||
|
|||
m_components.push_back( cmp ); |
|||
} |
|||
|
|||
std::unique_ptr<CONNECTION_GRAPH> CONNECTION_GRAPH::BuildFromFootprintSet( const std::set<FOOTPRINT*>& aFps ) |
|||
{ |
|||
auto cgraph = std::make_unique<CONNECTION_GRAPH>(); |
|||
|
|||
for( auto fp : aFps ) |
|||
{ |
|||
cgraph->AddFootprint( fp ); |
|||
} |
|||
|
|||
cgraph->BuildConnectivity(); |
|||
|
|||
return std::move(cgraph); |
|||
} |
|||
|
|||
CONNECTION_GRAPH::CONNECTION_GRAPH() {} |
|||
CONNECTION_GRAPH::~CONNECTION_GRAPH() |
|||
{ |
|||
for( COMPONENT* fp : m_components ) |
|||
{ |
|||
delete fp; |
|||
} |
|||
} |
|||
|
|||
COMPONENT::~COMPONENT() |
|||
{ |
|||
for( PIN* p : m_pins ) |
|||
{ |
|||
delete p; |
|||
} |
|||
} |
|||
|
|||
|
|||
}; // namespace TMATCH
|
|||
@ -0,0 +1,178 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) Kicad Developers, see change_log.txt for contributors. |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
#ifndef __TOPO_MATCH_H |
|||
#define __TOPO_MATCH_H |
|||
|
|||
#include <vector> |
|||
#include <map> |
|||
|
|||
#include <wx/string.h> |
|||
|
|||
class FOOTPRINT; |
|||
|
|||
namespace TMATCH |
|||
{ |
|||
|
|||
class PIN; |
|||
class CONNECTION_GRAPH; |
|||
|
|||
class COMPONENT |
|||
{ |
|||
friend class PIN; |
|||
friend class CONNECTION_GRAPH; |
|||
|
|||
public: |
|||
COMPONENT( const wxString& aRef, FOOTPRINT* aParentFp ); |
|||
~COMPONENT(); |
|||
|
|||
bool IsSameKind( const COMPONENT& b ) const; |
|||
void AddPin( PIN* p ); |
|||
int GetPinCount() const { return m_pins.size(); } |
|||
bool MatchesWith( COMPONENT* b ); |
|||
std::vector<PIN*>& Pins() { return m_pins; } |
|||
FOOTPRINT* GetParent() const { return m_parentFootprint; } |
|||
|
|||
private: |
|||
void sortPinsByName(); |
|||
|
|||
wxString m_reference; |
|||
wxString m_prefix; |
|||
FOOTPRINT* m_parentFootprint; |
|||
std::vector<PIN*> m_pins; |
|||
}; |
|||
|
|||
class PIN |
|||
{ |
|||
friend class CONNECTION_GRAPH; |
|||
|
|||
public: |
|||
PIN() : m_parent( nullptr ) {} |
|||
~PIN() {} |
|||
|
|||
void SetParent( COMPONENT* parent ) { m_parent = parent; } |
|||
|
|||
const wxString Format() const { return m_parent->m_reference + wxT( "-" ) + m_ref; } |
|||
|
|||
void AddConnection( PIN* pin ) { m_conns.push_back( pin ); } |
|||
|
|||
bool IsTopologicallySimilar( const PIN& b ) const |
|||
{ |
|||
wxASSERT( m_parent != b.m_parent ); |
|||
|
|||
if( !m_parent->IsSameKind( *b.m_parent ) ) |
|||
return false; |
|||
|
|||
//printf("Cmpt '%s'/'%s' %p %p similar=%d\n", ref.c_str(), b.ref.c_str(), parent, b.parent, ref==b.ref ? 1 :0 ); |
|||
|
|||
return m_ref == b.m_ref; |
|||
} |
|||
|
|||
bool IsIsomorphic( const PIN& b ) const; |
|||
|
|||
int GetNetCode() const { return m_netcode; } |
|||
|
|||
const wxString& GetReference() const { return m_ref; } |
|||
|
|||
private: |
|||
|
|||
wxString m_ref; |
|||
int m_netcode; |
|||
COMPONENT* m_parent; |
|||
std::vector<PIN*> m_conns; |
|||
}; |
|||
|
|||
class BACKTRACK_STAGE |
|||
{ |
|||
friend class CONNECTION_GRAPH; |
|||
|
|||
public: |
|||
BACKTRACK_STAGE() |
|||
{ |
|||
m_ref = nullptr; |
|||
m_currentMatch = 0; |
|||
} |
|||
|
|||
BACKTRACK_STAGE( const BACKTRACK_STAGE& other ) |
|||
{ |
|||
m_currentMatch = other.m_currentMatch; |
|||
m_ref = other.m_ref; |
|||
m_matches = other.m_matches; |
|||
m_locked = other.m_locked; |
|||
m_nloops = other.m_nloops; |
|||
} |
|||
|
|||
const std::map<COMPONENT*, COMPONENT*>& GetMatchingComponentPairs() const { return m_locked; } |
|||
|
|||
private: |
|||
COMPONENT* m_ref; |
|||
int m_currentMatch = 0; |
|||
int m_nloops; |
|||
std::vector<COMPONENT*> m_matches; |
|||
std::map<COMPONENT*, COMPONENT*> m_locked; |
|||
}; |
|||
|
|||
typedef std::map<FOOTPRINT*, FOOTPRINT*> COMPONENT_MATCHES; |
|||
|
|||
class CONNECTION_GRAPH |
|||
{ |
|||
public: |
|||
const int c_ITER_LIMIT = 10000; |
|||
|
|||
enum STATUS |
|||
{ |
|||
ST_TOPOLOGY_MISMATCH = -10, |
|||
ST_ITERATION_COUNT_EXCEEDED, |
|||
ST_COMPONENT_COUNT_MISMATCH, |
|||
ST_EMPTY, |
|||
ST_OK = 0 |
|||
}; |
|||
|
|||
CONNECTION_GRAPH(); |
|||
~CONNECTION_GRAPH(); |
|||
|
|||
void BuildConnectivity(); |
|||
void AddFootprint( FOOTPRINT* aFp ); |
|||
STATUS FindIsomorphism( CONNECTION_GRAPH* target, COMPONENT_MATCHES& result ); |
|||
static std::unique_ptr<CONNECTION_GRAPH> BuildFromFootprintSet( const std::set<FOOTPRINT*>& aFps ); |
|||
|
|||
private: |
|||
void sortByPinCount() |
|||
{ |
|||
std::sort( m_components.begin(), m_components.end(), |
|||
[]( COMPONENT* a, COMPONENT* b ) |
|||
{ |
|||
return a->GetPinCount() > b->GetPinCount(); |
|||
} ); |
|||
} |
|||
|
|||
|
|||
std::vector<COMPONENT*> findMatchingComponents( COMPONENT* ref, |
|||
const BACKTRACK_STAGE& partialMatches ); |
|||
|
|||
std::vector<COMPONENT*> m_components; |
|||
}; |
|||
|
|||
}; // namespace TMATCH |
|||
|
|||
#endif |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue