Fix incremental connectivity

The connectivity routine would consider symbols, overwriting unused
subgraphs for pins that were not in the change list.  This is resolved
by updating the full connectivity to only use pins in the graph since
symbols are not connected independently.

In the process of adding QA tests for this change, additional issues
with the schematic QA were discovered.  Specifically, we were not
properly setting the root sheet UUID.  This was partially masked by a
const_cast setting of the RefDes in sch_symbol when called the RefDes
getter.  This exposed the fact that our QA ERC numbers did not match the
schematic editor stand alone ERC numbers.  So the test value for one
check needed to be updated

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17528

(cherry picked from commit a9f35ba42e)
This commit is contained in:
Seth Hillbrand 2024-04-10 16:40:43 -07:00
parent 07b5fd342a
commit 55c864ec45
14 changed files with 898 additions and 87 deletions

View File

@ -87,6 +87,8 @@ void CONNECTION_SUBGRAPH::RemoveItem( SCH_ITEM* aItem )
bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCheckMultipleDrivers ) bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCheckMultipleDrivers )
{ {
std::lock_guard lock( m_driver_mutex );
auto candidate_cmp = [&]( SCH_ITEM* a, SCH_ITEM* b ) -> bool auto candidate_cmp = [&]( SCH_ITEM* a, SCH_ITEM* b ) -> bool
{ {
// meet irreflexive requirements of std::sort // meet irreflexive requirements of std::sort
@ -631,6 +633,21 @@ void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnco
items.push_back( item ); items.push_back( item );
dirty_items.insert( item ); dirty_items.insert( item );
} }
// If the symbol isn't dirty, look at the pins
// TODO: remove symbols from connectivity graph and only use pins
else if( item->Type() == SCH_SYMBOL_T )
{
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
{
if( pin->IsConnectivityDirty() )
{
items.push_back( pin );
dirty_items.insert( pin );
}
}
}
// Ensure the hierarchy info stored in the SCH_SCREEN (such as symbol units) reflects // Ensure the hierarchy info stored in the SCH_SCREEN (such as symbol units) reflects
// the current SCH_SHEET_PATH // the current SCH_SHEET_PATH
@ -710,37 +727,76 @@ std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> CONNECTION_GRAPH::ExtractAffected
aSubgraph->getAllConnectedItems( retvals, subgraphs ); aSubgraph->getAllConnectedItems( retvals, subgraphs );
}; };
auto extract_element = [&]( SCH_ITEM* aItem )
{
CONNECTION_SUBGRAPH* item_sg = GetSubgraphForItem( aItem );
if( !item_sg )
{
wxLogTrace( ConnTrace, wxT( "Item %s not found in connection graph" ), aItem->GetTypeDesc() );
return;
}
if( !item_sg->ResolveDrivers( true ) )
{
wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld (%p) has no driver" ),
aItem->GetTypeDesc(), item_sg->m_code, item_sg );
}
std::vector<CONNECTION_SUBGRAPH*> sg_to_scan = GetAllSubgraphs( item_sg->GetNetName() );
if( sg_to_scan.empty() )
{
wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld with net %s has no neighbors" ),
aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName() );
sg_to_scan.push_back( item_sg );
}
wxLogTrace( ConnTrace,
wxT( "Removing all item %s connections from subgraph %ld with net %s: Found "
"%zu subgraphs" ),
aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName(),
sg_to_scan.size() );
for( CONNECTION_SUBGRAPH* sg : sg_to_scan )
{
traverse_subgraph( sg );
for( auto& bus_it : sg->m_bus_neighbors )
{
for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
traverse_subgraph( bus_sg );
}
for( auto& bus_it : sg->m_bus_parents )
{
for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
traverse_subgraph( bus_sg );
}
}
alg::delete_matching( m_items, aItem );
};
for( SCH_ITEM* item : aItems ) for( SCH_ITEM* item : aItems )
{ {
auto it = m_item_to_subgraph_map.find( item ); if( item->Type() == SCH_SHEET_T )
if( it == m_item_to_subgraph_map.end() )
continue;
CONNECTION_SUBGRAPH* sg = it->second;
traverse_subgraph( sg );
for( auto& bus_it : sg->m_bus_neighbors )
{ {
for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second ) SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
traverse_subgraph( bus_sg );
for( SCH_SHEET_PIN* pin : sheet->GetPins() )
extract_element( pin );
} }
else if ( item->Type() == SCH_SYMBOL_T )
for( auto& bus_it : sg->m_bus_parents )
{
for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
traverse_subgraph( bus_sg );
}
alg::delete_matching( m_items, item );
if( item->Type() == SCH_SYMBOL_T )
{ {
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item ); SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
for( SCH_PIN* pin : symbol->GetPins( &sg->m_sheet ) ) for( SCH_PIN* pin : symbol->GetPins( &m_schematic->CurrentSheet() ) )
alg::delete_matching( m_items, pin ); extract_element( pin );
}
else
{
extract_element( item );
} }
} }
@ -770,6 +826,7 @@ void CONNECTION_GRAPH::RemoveItem( SCH_ITEM* aItem )
void CONNECTION_GRAPH::removeSubgraphs( std::set<CONNECTION_SUBGRAPH*>& aSubgraphs ) void CONNECTION_GRAPH::removeSubgraphs( std::set<CONNECTION_SUBGRAPH*>& aSubgraphs )
{ {
wxLogTrace( ConnTrace, wxT( "Removing %zu subgraphs" ), aSubgraphs.size() );
std::sort( m_driver_subgraphs.begin(), m_driver_subgraphs.end() ); std::sort( m_driver_subgraphs.begin(), m_driver_subgraphs.end() );
std::sort( m_subgraphs.begin(), m_subgraphs.end() ); std::sort( m_subgraphs.begin(), m_subgraphs.end() );
std::set<int> codes_to_remove; std::set<int> codes_to_remove;
@ -928,12 +985,30 @@ void CONNECTION_GRAPH::removeSubgraphs( std::set<CONNECTION_SUBGRAPH*>& aSubgrap
void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet, void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet,
const std::vector<SCH_ITEM*>& aItemList ) const std::vector<SCH_ITEM*>& aItemList )
{ {
wxLogTrace( wxT( "Updating connectivity for sheet %s with %zu items" ),
aSheet.Last()->GetFileName(), aItemList.size() );
std::map<VECTOR2I, std::vector<SCH_ITEM*>> connection_map; std::map<VECTOR2I, std::vector<SCH_ITEM*>> connection_map;
auto updatePin = [&]( SCH_PIN* aPin, SCH_CONNECTION* aConn )
{
aConn->SetType( CONNECTION_TYPE::NET );
// because calling the first time is not thread-safe
wxString name = aPin->GetDefaultNetName( aSheet );
aPin->ClearConnectedItems( aSheet );
// power symbol pins need to be post-processed later
if( aPin->IsGlobalPower() )
{
aConn->SetName( name );
m_global_power_pins.emplace_back( std::make_pair( aSheet, aPin ) );
}
};
for( SCH_ITEM* item : aItemList ) for( SCH_ITEM* item : aItemList )
{ {
std::vector<VECTOR2I> points = item->GetConnectionPoints(); std::vector<VECTOR2I> points = item->GetConnectionPoints();
item->ConnectedItems( aSheet ).clear(); item->ClearConnectedItems( aSheet );
if( item->Type() == SCH_SHEET_T ) if( item->Type() == SCH_SHEET_T )
{ {
@ -941,7 +1016,7 @@ void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet,
{ {
pin->InitializeConnection( aSheet, this ); pin->InitializeConnection( aSheet, this );
pin->ConnectedItems( aSheet ).clear(); pin->ClearConnectedItems( aSheet );
connection_map[ pin->GetTextPos() ].push_back( pin ); connection_map[ pin->GetTextPos() ].push_back( pin );
m_items.emplace_back( pin ); m_items.emplace_back( pin );
@ -953,23 +1028,10 @@ void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet,
for( SCH_PIN* pin : symbol->GetPins( &aSheet ) ) for( SCH_PIN* pin : symbol->GetPins( &aSheet ) )
{ {
SCH_CONNECTION* conn = pin->InitializeConnection( aSheet, this );
VECTOR2I pos = pin->GetPosition();
// because calling the first time is not thread-safe
wxString name = pin->GetDefaultNetName( aSheet );
pin->ConnectedItems( aSheet ).clear();
// power symbol pins need to be post-processed later
if( pin->IsGlobalPower() )
{
conn->SetName( name );
m_global_power_pins.emplace_back( std::make_pair( aSheet, pin ) );
}
connection_map[ pos ].push_back( pin );
m_items.emplace_back( pin ); m_items.emplace_back( pin );
SCH_CONNECTION* conn = pin->InitializeConnection( aSheet, this );
updatePin( pin, conn );
connection_map[ pin->GetPosition() ].push_back( pin );
} }
} }
else else
@ -994,7 +1056,10 @@ void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet,
break; break;
case SCH_PIN_T: case SCH_PIN_T:
conn->SetType( CONNECTION_TYPE::NET ); if( points.empty() )
points = { static_cast<SCH_PIN*>( item )->GetPosition() };
updatePin( static_cast<SCH_PIN*>( item ), conn );
break; break;
case SCH_BUS_WIRE_ENTRY_T: case SCH_BUS_WIRE_ENTRY_T:
@ -1075,9 +1140,6 @@ void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet,
connected_item->SetLayer( busLine ? LAYER_BUS_JUNCTION : LAYER_JUNCTION ); connected_item->SetLayer( busLine ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
} }
SCH_ITEM_SET& connected_set = connected_item->ConnectedItems( aSheet );
connected_set.reserve( connection_vec.size() );
for( SCH_ITEM* test_item : connection_vec ) for( SCH_ITEM* test_item : connection_vec )
{ {
bool bus_connection_ok = true; bool bus_connection_ok = true;
@ -1111,7 +1173,7 @@ void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet,
test_item->ConnectionPropagatesTo( connected_item ) && test_item->ConnectionPropagatesTo( connected_item ) &&
bus_connection_ok ) bus_connection_ok )
{ {
connected_set.push_back( test_item ); connected_item->AddConnectionTo( aSheet, test_item );
} }
} }
@ -1213,7 +1275,7 @@ void CONNECTION_GRAPH::buildItemSubGraphs()
connected_conn->SetSubgraphCode( subgraph->m_code ); connected_conn->SetSubgraphCode( subgraph->m_code );
m_item_to_subgraph_map[connected_item] = subgraph; m_item_to_subgraph_map[connected_item] = subgraph;
subgraph->AddItem( connected_item ); subgraph->AddItem( connected_item );
SCH_ITEM_SET& citemset = connected_item->ConnectedItems( sheet ); const SCH_ITEM_VEC& citemset = connected_item->ConnectedItems( sheet );
for( SCH_ITEM* citem : citemset ) for( SCH_ITEM* citem : citemset )
{ {
@ -1248,6 +1310,8 @@ void CONNECTION_GRAPH::resolveAllDrivers()
return candidate->m_dirty; return candidate->m_dirty;
} ); } );
wxLogTrace( ConnTrace, wxT( "Resolving drivers for %zu subgraphs" ), dirty_graphs.size() );
std::vector<std::future<size_t>> returns( dirty_graphs.size() ); std::vector<std::future<size_t>> returns( dirty_graphs.size() );
auto update_lambda = []( CONNECTION_SUBGRAPH* subgraph ) -> size_t auto update_lambda = []( CONNECTION_SUBGRAPH* subgraph ) -> size_t
@ -1378,7 +1442,7 @@ void CONNECTION_GRAPH::generateBusAliasMembers()
for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs ) for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
{ {
SCH_ITEM_SET vec = subgraph->GetAllBusLabels(); SCH_ITEM_VEC vec = subgraph->GetAllBusLabels();
for( SCH_ITEM* item : vec ) for( SCH_ITEM* item : vec )
{ {
@ -1556,14 +1620,20 @@ void CONNECTION_GRAPH::processSubGraphs()
if( !subgraph->m_strong_driver ) if( !subgraph->m_strong_driver )
{ {
std::vector<CONNECTION_SUBGRAPH*>* vec = &m_net_name_to_subgraphs_map.at( name ); std::vector<CONNECTION_SUBGRAPH*> vec_empty;
std::vector<CONNECTION_SUBGRAPH*>* vec = &vec_empty;
if( m_net_name_to_subgraphs_map.count( name ) )
vec = &m_net_name_to_subgraphs_map.at( name );
// If we are a unique bus vector, check if we aren't actually unique because of another // If we are a unique bus vector, check if we aren't actually unique because of another
// subgraph with a similar bus vector // subgraph with a similar bus vector
if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS ) if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
{ {
wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" ); wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" );
vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
if( m_net_name_to_subgraphs_map.count( prefixOnly ) )
vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
} }
if( vec->size() > 1 ) if( vec->size() > 1 )
@ -2831,8 +2901,8 @@ CONNECTION_SUBGRAPH* CONNECTION_GRAPH::GetSubgraphForItem( SCH_ITEM* aItem )
} }
const std::vector<CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetAllSubgraphs( const std::vector<CONNECTION_SUBGRAPH*>
const wxString& aNetName ) const CONNECTION_GRAPH::GetAllSubgraphs( const wxString& aNetName ) const
{ {
std::vector<CONNECTION_SUBGRAPH*> subgraphs; std::vector<CONNECTION_SUBGRAPH*> subgraphs;

View File

@ -301,6 +301,8 @@ private:
/// A cache of connections that are part of this subgraph but that don't have /// A cache of connections that are part of this subgraph but that don't have
/// an owning element (i.e. bus members) /// an owning element (i.e. bus members)
std::set<SCH_CONNECTION*> m_bus_element_connections; std::set<SCH_CONNECTION*> m_bus_element_connections;
std::mutex m_driver_mutex;
}; };
struct NET_NAME_CODE_CACHE_KEY struct NET_NAME_CODE_CACHE_KEY

View File

@ -267,6 +267,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
// Make ${SHEETNAME} work on the root sheet until we properly support // Make ${SHEETNAME} work on the root sheet until we properly support
// naming the root sheet // naming the root sheet
Schematic().Root().SetName( _( "Root" ) ); Schematic().Root().SetName( _( "Root" ) );
wxLogDebug( "Loaded schematic with root sheet UUID %s", Schematic().Root().m_Uuid.AsString() );
} }
if( !pi->GetError().IsEmpty() ) if( !pi->GetError().IsEmpty() )

View File

@ -1744,12 +1744,16 @@ void SCH_EDIT_FRAME::RecalculateConnections( SCH_COMMIT* aCommit, SCH_CLEANUP_FL
{ {
for( SCH_ITEM* item : GetScreen()->Items().Overlapping( pt ) ) for( SCH_ITEM* item : GetScreen()->Items().Overlapping( pt ) )
{ {
// Leave this check in place. Overlapping items are not necessarily connectable.
if( !item->IsConnectable() )
continue;
if( item->Type() == SCH_LINE_T ) if( item->Type() == SCH_LINE_T )
{ {
if( item->HitTest( pt ) ) if( item->HitTest( pt ) )
changed_items.insert( item ); changed_items.insert( item );
} }
else if( item->Type() == SCH_SYMBOL_T ) else if( item->Type() == SCH_SYMBOL_T && item->IsConnected( pt ) )
{ {
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item ); SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
std::vector<SCH_PIN*> pins = symbol->GetPins(); std::vector<SCH_PIN*> pins = symbol->GetPins();
@ -1767,7 +1771,6 @@ void SCH_EDIT_FRAME::RecalculateConnections( SCH_COMMIT* aCommit, SCH_CLEANUP_FL
} }
else else
{ {
// Non-connectable objects have already been pruned.
if( item->IsConnected( pt ) ) if( item->IsConnected( pt ) )
changed_items.insert( item ); changed_items.insert( item );
} }
@ -1786,21 +1789,7 @@ void SCH_EDIT_FRAME::RecalculateConnections( SCH_COMMIT* aCommit, SCH_CLEANUP_FL
for( auto&[ path, item ] : all_items ) for( auto&[ path, item ] : all_items )
{ {
wxCHECK2( item, continue ); wxCHECK2( item, continue );
item->SetConnectivityDirty();
switch( item->Type() )
{
case SCH_FIELD_T:
case SCH_PIN_T:
{
SCH_ITEM* parent = static_cast<SCH_ITEM*>( item->GetParent() );
wxCHECK2( parent, continue );
parent->SetConnectivityDirty();
break;
}
default:
item->SetConnectivityDirty();
}
} }
new_graph.Recalculate( list, false, &changeHandler ); new_graph.Recalculate( list, false, &changeHandler );

View File

@ -194,7 +194,16 @@ std::shared_ptr<NETCLASS> SCH_ITEM::GetEffectiveNetClass( const SCH_SHEET_PATH*
} }
SCH_ITEM_SET& SCH_ITEM::ConnectedItems( const SCH_SHEET_PATH& aSheet ) void SCH_ITEM::ClearConnectedItems( const SCH_SHEET_PATH& aSheet )
{
auto it = m_connected_items.find( aSheet );
if( it != m_connected_items.end() )
it->second.clear();
}
const SCH_ITEM_VEC& SCH_ITEM::ConnectedItems( const SCH_SHEET_PATH& aSheet )
{ {
return m_connected_items[ aSheet ]; return m_connected_items[ aSheet ];
} }
@ -202,13 +211,17 @@ SCH_ITEM_SET& SCH_ITEM::ConnectedItems( const SCH_SHEET_PATH& aSheet )
void SCH_ITEM::AddConnectionTo( const SCH_SHEET_PATH& aSheet, SCH_ITEM* aItem ) void SCH_ITEM::AddConnectionTo( const SCH_SHEET_PATH& aSheet, SCH_ITEM* aItem )
{ {
SCH_ITEM_SET& set = m_connected_items[ aSheet ]; SCH_ITEM_VEC& vec = m_connected_items[ aSheet ];
// The vector elements are small, so reserve 1k at a time to prevent re-allocations // The vector elements are small, so reserve 1k at a time to prevent re-allocations
if( set.size() == set.capacity() ) if( vec.size() == vec.capacity() )
set.reserve( set.size() + 4096 ); vec.reserve( vec.size() + 4096 );
set.emplace_back( aItem ); // Add item to the correct place in the sorted vector if it is not already there
auto it = std::lower_bound( vec.begin(), vec.end(), aItem );
if( it == vec.end() || *it != aItem )
vec.insert( it, aItem );
} }

View File

@ -151,7 +151,7 @@ public:
std::vector<DANGLING_END_ITEM>& aItemListByPos ); std::vector<DANGLING_END_ITEM>& aItemListByPos );
}; };
typedef std::vector<SCH_ITEM*> SCH_ITEM_SET; typedef std::vector<SCH_ITEM*> SCH_ITEM_VEC;
/** /**
@ -429,13 +429,18 @@ public:
/** /**
* Retrieve the set of items connected to this item on the given sheet. * Retrieve the set of items connected to this item on the given sheet.
*/ */
SCH_ITEM_SET& ConnectedItems( const SCH_SHEET_PATH& aPath ); const SCH_ITEM_VEC& ConnectedItems( const SCH_SHEET_PATH& aPath );
/** /**
* Add a connection link between this item and another. * Add a connection link between this item and another.
*/ */
void AddConnectionTo( const SCH_SHEET_PATH& aPath, SCH_ITEM* aItem ); void AddConnectionTo( const SCH_SHEET_PATH& aPath, SCH_ITEM* aItem );
/**
* Clear all connections to this item.
*/
void ClearConnectedItems( const SCH_SHEET_PATH& aPath );
/** /**
* Create a new connection object associated with this object. * Create a new connection object associated with this object.
* *
@ -563,7 +568,7 @@ protected:
// to store a initial pos of the item or mouse cursor // to store a initial pos of the item or mouse cursor
/// Store pointers to other items that are connected to this one, per sheet. /// Store pointers to other items that are connected to this one, per sheet.
std::map<SCH_SHEET_PATH, SCH_ITEM_SET, SHEET_PATH_CMP> m_connected_items; std::map<SCH_SHEET_PATH, SCH_ITEM_VEC, SHEET_PATH_CMP> m_connected_items;
/// Store connectivity information, per sheet. /// Store connectivity information, per sheet.
std::unordered_map<SCH_SHEET_PATH, SCH_CONNECTION*> m_connection_map; std::unordered_map<SCH_SHEET_PATH, SCH_CONNECTION*> m_connection_map;

View File

@ -274,7 +274,7 @@ bool SCH_LABEL_BASE::IsType( const std::vector<KICAD_T>& aScanTypes ) const
if( m_connected_items.find( Schematic()->CurrentSheet() ) == m_connected_items.end() ) if( m_connected_items.find( Schematic()->CurrentSheet() ) == m_connected_items.end() )
return false; return false;
const SCH_ITEM_SET& item_set = m_connected_items.at( Schematic()->CurrentSheet() ); const SCH_ITEM_VEC& item_set = m_connected_items.at( Schematic()->CurrentSheet() );
for( KICAD_T scanType : aScanTypes ) for( KICAD_T scanType : aScanTypes )
{ {

View File

@ -770,7 +770,6 @@ const wxString SCH_SYMBOL::GetRef( const SCH_SHEET_PATH* sheet, bool aIncludeUni
// the same symbol references, but perhaps this is best. // the same symbol references, but perhaps this is best.
if( ref.IsEmpty() && !GetField( REFERENCE_FIELD )->GetText().IsEmpty() ) if( ref.IsEmpty() && !GetField( REFERENCE_FIELD )->GetText().IsEmpty() )
{ {
const_cast<SCH_SYMBOL*>( this )->SetRef( sheet, GetField( REFERENCE_FIELD )->GetText() );
ref = GetField( REFERENCE_FIELD )->GetText(); ref = GetField( REFERENCE_FIELD )->GetText();
} }

View File

@ -0,0 +1,478 @@
(kicad_sch
(version 20231120)
(generator "eeschema")
(generator_version "8.0")
(uuid "5e4124ac-877c-4d6f-a16b-822110ac68a1")
(paper "A4")
(lib_symbols
(symbol "Connector:TestPoint"
(pin_numbers hide)
(pin_names
(offset 0.762) hide)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "TP"
(at 0 6.858 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Value" "TestPoint"
(at 0 5.08 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" ""
(at 5.08 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 5.08 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "test point"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_keywords" "test point tp"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_fp_filters" "Pin* Test*"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "TestPoint_0_1"
(circle
(center 0 3.302)
(radius 0.762)
(stroke
(width 0)
(type default)
)
(fill
(type none)
)
)
)
(symbol "TestPoint_1_1"
(pin passive line
(at 0 0 90)
(length 2.54)
(name "1"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
)
(symbol "power:GND"
(power)
(pin_numbers hide)
(pin_names
(offset 0) hide)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "#PWR"
(at 0 -6.35 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "GND"
(at 0 -3.81 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Power symbol creates a global label with name \"GND\" , ground"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_keywords" "global power"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "GND_0_1"
(polyline
(pts
(xy 0 0) (xy 0 -1.27) (xy 1.27 -1.27) (xy 0 -2.54) (xy -1.27 -1.27) (xy 0 -1.27)
)
(stroke
(width 0)
(type default)
)
(fill
(type none)
)
)
)
(symbol "GND_1_1"
(pin power_in line
(at 0 0 270)
(length 0)
(name "~"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
)
)
(symbol
(lib_id "Connector:TestPoint")
(at 82.55 88.9 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(fields_autoplaced yes)
(uuid "86891145-73fb-48da-aca6-492a4fb35190")
(property "Reference" "TP1"
(at 85.09 84.328 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Value" "TestPoint"
(at 85.09 86.868 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Footprint" ""
(at 87.63 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 87.63 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "test point"
(at 82.55 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "2bf2be02-e7d9-4e42-8e57-404647a392eb")
)
(instances
(project ""
(path "/5e4124ac-877c-4d6f-a16b-822110ac68a1"
(reference "TP1")
(unit 1)
)
)
)
)
(symbol
(lib_id "power:GND")
(at 82.55 88.9 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(fields_autoplaced yes)
(uuid "b9e595cb-65cf-4cf6-b2cb-772d3688abf0")
(property "Reference" "#PWR01"
(at 82.55 95.25 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "GND"
(at 82.55 93.98 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" ""
(at 82.55 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" ""
(at 82.55 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Power symbol creates a global label with name \"GND\" , ground"
(at 82.55 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "e434aab5-d764-4bd8-a761-696fb56ace19")
)
(instances
(project ""
(path "/5e4124ac-877c-4d6f-a16b-822110ac68a1"
(reference "#PWR01")
(unit 1)
)
)
)
)
(symbol
(lib_id "Connector:TestPoint")
(at 99.06 88.9 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(fields_autoplaced yes)
(uuid "d4520136-e0ab-467d-b271-26033a078cb6")
(property "Reference" "TP2"
(at 101.6 84.328 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Value" "TestPoint"
(at 101.6 86.868 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Footprint" ""
(at 104.14 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 104.14 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "test point"
(at 99.06 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "2bf2be02-e7d9-4e42-8e57-404647a392eb")
)
(instances
(project ""
(path "/5e4124ac-877c-4d6f-a16b-822110ac68a1"
(reference "TP2")
(unit 1)
)
)
)
)
(symbol
(lib_id "power:GND")
(at 99.06 88.9 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(fields_autoplaced yes)
(uuid "fa30dd75-0d78-423e-a304-836357e80e2f")
(property "Reference" "#PWR02"
(at 99.06 95.25 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "GND"
(at 99.06 93.98 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" ""
(at 99.06 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" ""
(at 99.06 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Power symbol creates a global label with name \"GND\" , ground"
(at 99.06 88.9 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "e434aab5-d764-4bd8-a761-696fb56ace19")
)
(instances
(project ""
(path "/5e4124ac-877c-4d6f-a16b-822110ac68a1"
(reference "#PWR02")
(unit 1)
)
)
)
)
(sheet_instances
(path "/"
(page "1")
)
)
)

View File

@ -24,6 +24,13 @@
"track_widths": [], "track_widths": [],
"via_dimensions": [] "via_dimensions": []
}, },
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_presets": [], "layer_presets": [],
"viewports": [] "viewports": []
}, },
@ -212,15 +219,23 @@
"bus_label_syntax": "error", "bus_label_syntax": "error",
"bus_to_bus_conflict": "error", "bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error", "bus_to_net_conflict": "error",
"conflicting_netclasses": "error",
"different_unit_footprint": "error", "different_unit_footprint": "error",
"different_unit_net": "error", "different_unit_net": "error",
"duplicate_reference": "error", "duplicate_reference": "error",
"duplicate_sheet_names": "error", "duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error", "extra_units": "error",
"footprint_link_issues": "warning",
"global_label_dangling": "warning", "global_label_dangling": "warning",
"hier_label_mismatch": "error", "hier_label_mismatch": "error",
"label_dangling": "error", "label_dangling": "error",
"lib_symbol_issues": "warning", "lib_symbol_issues": "warning",
"lib_symbol_mismatch": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning", "multiple_net_names": "warning",
"net_not_bus_member": "warning", "net_not_bus_member": "warning",
"no_connect_connected": "warning", "no_connect_connected": "warning",
@ -230,6 +245,8 @@
"pin_to_pin": "warning", "pin_to_pin": "warning",
"power_pin_not_driven": "error", "power_pin_not_driven": "error",
"similar_labels": "warning", "similar_labels": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "ignore",
"unannotated": "error", "unannotated": "error",
"unit_value_mismatch": "error", "unit_value_mismatch": "error",
"unresolved_variable": "error", "unresolved_variable": "error",
@ -241,13 +258,13 @@
"pinned_symbol_libs": [] "pinned_symbol_libs": []
}, },
"meta": { "meta": {
"filename": "sim2_V6.kicad_pro", "filename": "issue13591.kicad_pro",
"version": 1 "version": 1
}, },
"net_settings": { "net_settings": {
"classes": [ "classes": [
{ {
"bus_width": 12.0, "bus_width": 12,
"clearance": 0.2, "clearance": 0.2,
"diff_pair_gap": 0.25, "diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25, "diff_pair_via_gap": 0.25,
@ -261,14 +278,17 @@
"track_width": 0.25, "track_width": 0.25,
"via_diameter": 0.8, "via_diameter": 0.8,
"via_drill": 0.4, "via_drill": 0.4,
"wire_width": 6.0 "wire_width": 6
} }
], ],
"meta": { "meta": {
"version": 2 "version": 3
}, },
"net_colors": null, "net_colors": null,
"netclass_assignments": null, "netclass_assignments": {
"Net-(D1-A)": "",
"Net-(R1-Pad1)": ""
},
"netclass_patterns": [] "netclass_patterns": []
}, },
"pcbnew": { "pcbnew": {
@ -276,14 +296,75 @@
"gencad": "", "gencad": "",
"idf": "", "idf": "",
"netlist": "", "netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "", "specctra_dsn": "",
"step": "", "step": "",
"svg": "",
"vrml": "" "vrml": ""
}, },
"page_layout_descr_file": "" "page_layout_descr_file": ""
}, },
"schematic": { "schematic": {
"annotate_start_num": 0, "annotate_start_num": 0,
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"name": "Grouped By Value",
"sort_asc": true,
"sort_field": "Reference"
},
"connection_grid_size": 50.0,
"drawing": { "drawing": {
"dashed_lines_dash_length_ratio": 12.0, "dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0, "dashed_lines_gap_length_ratio": 3.0,
@ -297,6 +378,11 @@
"intersheets_ref_suffix": "", "intersheets_ref_suffix": "",
"junction_size_choice": 3, "junction_size_choice": 3,
"label_size_ratio": 0.375, "label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0, "pin_symbol_size": 25.0,
"text_offset_ratio": 0.15 "text_offset_ratio": 0.15
}, },
@ -318,8 +404,11 @@
"page_layout_descr_file": "", "page_layout_descr_file": "",
"plot_directory": "", "plot_directory": "",
"spice_adjust_passive_values": false, "spice_adjust_passive_values": false,
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"", "spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false, "spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false, "spice_save_all_voltages": false,
"subpart_first_id": 65, "subpart_first_id": 65,
"subpart_id_separator": 0 "subpart_id_separator": 0

View File

@ -51,7 +51,7 @@ void LoadSheetSchematicContents( const std::string& fileName, SCH_SHEET* sheet )
wxASSERT( fileStream.is_open() ); wxASSERT( fileStream.is_open() );
STDISTREAM_LINE_READER reader; STDISTREAM_LINE_READER reader;
reader.SetStream( fileStream ); reader.SetStream( fileStream );
SCH_IO_KICAD_SEXPR_PARSER parser( &reader ); SCH_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, 0, sheet );
parser.ParseSchematic( sheet ); parser.ParseSchematic( sheet );
} }

View File

@ -61,6 +61,7 @@ set( QA_EESCHEMA_SRCS
test_lib_part.cpp test_lib_part.cpp
test_netlist_exporter_kicad.cpp test_netlist_exporter_kicad.cpp
test_ee_item.cpp test_ee_item.cpp
test_incremental_netlister.cpp
test_legacy_power_symbols.cpp test_legacy_power_symbols.cpp
test_pin_numbers.cpp test_pin_numbers.cpp
test_sch_netclass.cpp test_sch_netclass.cpp

View File

@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_CASE( ERCHierarchicalSchematics, ERC_REGRESSION_TEST_FIXTURE
{ {
{ "issue10926_1", 3 }, { "issue10926_1", 3 },
{ "issue12814", 0 }, { "issue12814", 0 },
{ "ERC_dynamic_power_symbol_test", 5 } { "ERC_dynamic_power_symbol_test", 2 }
}; };
for( const std::pair<wxString, int>& test : tests ) for( const std::pair<wxString, int>& test : tests )

View File

@ -0,0 +1,164 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.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 3
* 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:
* https://www.gnu.org/licenses/gpl-3.0.en.html
* or you may search the http://www.gnu.org website for the version 32 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <schematic_utils/schematic_file_util.h>
#include <connection_graph.h>
#include <schematic.h>
#include <sch_sheet.h>
#include <sch_screen.h>
#include <settings/settings_manager.h>
#include <locale_io.h>
struct INCREMENTAL_NETLIST_TEST_FIXTURE
{
INCREMENTAL_NETLIST_TEST_FIXTURE() :
m_settingsManager( true /* headless */ )
{ }
SETTINGS_MANAGER m_settingsManager;
std::unique_ptr<SCHEMATIC> m_schematic;
};
BOOST_FIXTURE_TEST_CASE( RemoveAddItems, INCREMENTAL_NETLIST_TEST_FIXTURE )
{
LOCALE_IO dummy;
// Check for Errors when using global labels
std::vector<wxString> tests = {// "incremental_test",
// "issue10430",
// "issue10926_1",
// "issue11926",
// "issue12505",
// "issue12814",
// "issue13112",
// "issue13162",
// "issue13212",
// "issue13431",
// "issue13591",
// "issue16223",
// "issue6588",
"issue7203"};//,
// "issue9367"};
for( const wxString& test : tests )
{
KI_TEST::LoadSchematic( m_settingsManager, test, m_schematic );
SCH_SHEET_LIST sheets = m_schematic->GetSheets();
for( const SCH_SHEET_PATH& path : sheets )
{
for( size_t ii = 0; ii < path.size(); ++ii )
{
const SCH_SHEET* sheet = path.GetSheet( ii );
const SCH_SCREEN* screen = sheet->GetScreen();
std::vector<SCH_ITEM*> items;
for( SCH_ITEM* item : screen->Items() )
{
if( !item->IsConnectable() )
{
continue;
}
if( item->Type() == SCH_SYMBOL_T )
{
for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( item )->GetPins() )
{
items.push_back( pin );
}
}
else
{
items.push_back( item );
}
}
for( SCH_ITEM* item : items )
{
for( SCH_ITEM* check_item : items )
{
auto& conn_items = check_item->ConnectedItems( path );
auto conn = check_item->Connection();
std::string netname = conn ? conn->GetNetName().ToStdString() : "NoNet";
int subgraph = conn ? conn->SubgraphCode() : -1;
BOOST_TEST_MESSAGE( test.ToStdString() << ": Item "
<< check_item->GetFriendlyName().ToStdString()
<< " in net " << netname << " subgraph " << subgraph
<< " has " << conn_items.size() << " connections" );
}
SCH_CONNECTION* connection = item->Connection();
if( !connection )
continue;
wxString netname = connection->GetNetName();
if( !item->IsConnectable() )
continue;
SCH_ITEM_VEC prev_items = item->ConnectedItems( path );
std::sort( prev_items.begin(), prev_items.end() );
alg::remove_duplicates( prev_items );
std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> all_items =
m_schematic->ConnectionGraph()->ExtractAffectedItems( { item } );
all_items.insert( { path, item } );
BOOST_TEST_MESSAGE( test.ToStdString() << ": Item "
<< item->GetFriendlyName().ToStdString()
<< " in net " << netname.ToStdString()
<< " has " << all_items.size() << " affected items" );
CONNECTION_GRAPH new_graph( m_schematic.get() );
new_graph.SetLastCodes( m_schematic->ConnectionGraph() );
for( auto&[ path, item ] : all_items )
{
wxCHECK2( item, continue );
item->SetConnectivityDirty();
}
new_graph.Recalculate( sheets, false );
m_schematic->ConnectionGraph()->Merge( new_graph );
SCH_ITEM_VEC curr_items = item->ConnectedItems( path );
std::sort( curr_items.begin(), curr_items.end() );
alg::remove_duplicates( curr_items );
BOOST_CHECK_MESSAGE( prev_items == curr_items,
test.ToStdString() << ": Item "
<< item->GetFriendlyName().ToStdString()
<< " in net " << netname.ToStdString()
<< " changed from " << prev_items.size()
<< " to " << curr_items.size() << " Location:" << item->GetPosition().x << "," << item->GetPosition().y );
}
}
}
}
}