netlist: export/import symbol unit information to footprints

For upcoming gate swapping feature
This commit is contained in:
Mike Williams 2025-09-09 11:30:57 -04:00
parent 1418b03c5a
commit 7e448c404a
6 changed files with 193 additions and 0 deletions

View File

@ -51,3 +51,5 @@ value
version
aliases
alias
unit
units

View File

@ -481,6 +481,80 @@ XNODE* NETLIST_EXPORTER_XML::makeSymbols( unsigned aCtl )
// Output the primary UUID
uuid = symbol->m_Uuid.AsString();
xunits->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, uuid ) );
// Emit unit information (per-unit name and pins) after tstamps
XNODE* xunitInfo;
xcomp->AddChild( xunitInfo = node( wxT( "units" ) ) );
// Emit all units defined by the library symbol, independent of placement
const std::unique_ptr<LIB_SYMBOL>& libSym = symbol->GetLibSymbolRef();
if( libSym )
{
int unitCount = std::max( libSym->GetUnitCount(), 1 );
for( int unitIdx = 1; unitIdx <= unitCount; ++unitIdx )
{
wxString unitName = libSym->GetUnitDisplayName( unitIdx, false );
XNODE* xunit;
xunitInfo->AddChild( xunit = node( wxT( "unit" ) ) );
xunit->AddAttribute( wxT( "name" ), unitName );
XNODE* xpins;
xunit->AddChild( xpins = node( wxT( "pins" ) ) );
// Gather all graphical pins for this unit across body styles
std::vector<SCH_PIN*> pinList = libSym->GetGraphicalPins( unitIdx, 0 );
// Sort by X then Y to establish a stable, spatial order for matching pins
// in the PCB editor when swapping gates
std::sort( pinList.begin(), pinList.end(),
[]( SCH_PIN* a, SCH_PIN* b )
{
auto pa = a->GetPosition();
auto pb = b->GetPosition();
if( pa.x != pb.x )
return pa.x < pb.x;
return pa.y < pb.y;
} );
// Emit pins in this spatial order, deduping by number within the unit only
std::unordered_set<wxString> seen;
for( SCH_PIN* basePin : pinList )
{
bool stackedValid = false;
std::vector<wxString> expandedNums = basePin->GetStackedPinNumbers( &stackedValid );
if( stackedValid && !expandedNums.empty() )
{
for( const wxString& num : expandedNums )
{
if( seen.insert( num ).second )
{
XNODE* xpin;
xpins->AddChild( xpin = node( wxT( "pin" ) ) );
xpin->AddAttribute( wxT( "num" ), num );
}
}
}
else
{
wxString num = basePin->GetShownNumber();
if( seen.insert( num ).second )
{
XNODE* xpin;
xpins->AddChild( xpin = node( wxT( "pin" ) ) );
xpin->AddAttribute( wxT( "num" ), num );
}
}
}
}
}
}
}

View File

@ -750,6 +750,15 @@ public:
void ApplyDefaultSettings( const BOARD& board, bool aStyleFields, bool aStyleText,
bool aStyleShapes );
struct FP_UNIT_INFO
{
wxString m_unitName; // e.g. A
std::vector<wxString> m_pins; // pin numbers in this unit
};
void SetUnitInfo( const std::vector<FP_UNIT_INFO>& aUnits ) { m_unitInfo = aUnits; }
const std::vector<FP_UNIT_INFO>& GetUnitInfo() const { return m_unitInfo; }
bool IsBoardOnly() const { return m_attributes & FP_BOARD_ONLY; }
void SetBoardOnly( bool aIsBoardOnly = true )
{
@ -1180,6 +1189,9 @@ private:
std::unordered_set<wxString> m_transientComponentClassNames;
std::unique_ptr<COMPONENT_CLASS_CACHE_PROXY> m_componentClassCacheProxy;
// Optional unit mapping information for multi-unit symbols
std::vector<FP_UNIT_INFO> m_unitInfo;
};
#endif // FOOTPRINT_H

View File

@ -332,6 +332,8 @@ void KICAD_NETLIST_PARSER::parseComponent()
bool duplicatePinsAreJumpers = false;
std::vector<std::set<wxString>> jumperPinGroups;
std::vector<COMPONENT::UNIT_INFO> parsedUnits;
// The token comp was read, so the next data is (ref P1)
while( (token = NextTok() ) != T_RIGHT )
{
@ -496,6 +498,87 @@ void KICAD_NETLIST_PARSER::parseComponent()
break;
case T_units:
{
// Parse a section like:
// (units (unit (ref "U1A") (name "A") (pins (pin "1") (pin "2"))))
while( ( token = NextTok() ) != T_RIGHT )
{
if( token == T_LEFT )
token = NextTok();
if( token == T_unit )
{
COMPONENT::UNIT_INFO info;
while( ( token = NextTok() ) != T_RIGHT )
{
if( token == T_LEFT )
token = NextTok();
switch( token )
{
case T_name:
NeedSYMBOLorNUMBER();
info.m_unitName = From_UTF8( CurText() );
NeedRIGHT();
break;
case T_pins:
while( ( token = NextTok() ) != T_RIGHT )
{
if( token == T_LEFT )
token = NextTok();
if( token == T_pin )
{
wxString pinNum;
// Parse pins in attribute style: (pin (num "1"))
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
token = NextTok();
if( token == T_num )
{
NeedSYMBOLorNUMBER();
pinNum = From_UTF8( CurText() );
NeedRIGHT();
}
else
{
// ignore other subfields of pin
// leave bare tokens untouched; they are not supported in this context
}
}
if( !pinNum.IsEmpty() )
info.m_pins.emplace_back( pinNum );
}
else
{
skipCurrent();
}
}
break;
default:
skipCurrent();
break;
}
}
parsedUnits.push_back( info );
}
else
{
skipCurrent();
}
}
break;
}
case T_component_classes:
while( ( token = NextTok() ) != T_RIGHT )
{
@ -583,6 +666,7 @@ void KICAD_NETLIST_PARSER::parseComponent()
component->SetDuplicatePadNumbersAreJumpers( duplicatePinsAreJumpers );
std::ranges::copy( jumperPinGroups, std::inserter( component->JumperPadGroups(),
component->JumperPadGroups().end() ) );
component->SetUnitInfo( parsedUnits );
m_netlist->AddComponent( component );
}

View File

@ -56,6 +56,14 @@ void COMPONENT::SetFootprint( FOOTPRINT* aFootprint )
aFootprint->SetValue( m_value );
aFootprint->SetFPID( m_fpid );
aFootprint->SetPath( path );
// Copy over unit info
std::vector<FOOTPRINT::FP_UNIT_INFO> fpUnits;
for( const UNIT_INFO& u : m_units )
fpUnits.push_back( { u.m_unitName, u.m_pins } );
aFootprint->SetUnitInfo( fpUnits );
}

View File

@ -206,6 +206,16 @@ public:
NETLIST_GROUP* GetGroup() const { return m_group; }
void SetGroup( NETLIST_GROUP* aGroup ) { m_group = aGroup; }
// Unit info for multi-unit symbols
struct UNIT_INFO
{
wxString m_unitName; // e.g. A
std::vector<wxString> m_pins; // pin numbers in this unit
};
void SetUnitInfo( const std::vector<UNIT_INFO>& aUnits ) { m_units = aUnits; }
const std::vector<UNIT_INFO>& GetUnitInfo() const { return m_units; }
private:
std::vector<COMPONENT_NET> m_nets; ///< list of nets shared by the component pins
@ -260,6 +270,9 @@ private:
NETLIST_GROUP* m_group;
static COMPONENT_NET m_emptyNet;
// Unit information parsed from the netlist (optional)
std::vector<UNIT_INFO> m_units;
};