Browse Source

Fix crash when importing Eagle schematics with multiple module levels.

Fix annotation when importing Eagle schematic modules.  Apparently Eagle
uses hard coded module name prefixing scheme to generate module symbol
references by prefixing module name separated by a colon.  Even though
this results in references that are incompatible with KiCad, they are
used so that the board and schematic can be synchronized by references
on import.

https://gitlab.com/kicad/code/kicad/-/issues/19718
pcb_db
Wayne Stambaugh 9 months ago
parent
commit
9eaefa3301
  1. 171
      eeschema/sch_io/eagle/sch_io_eagle.cpp
  2. 5
      eeschema/sch_io/eagle/sch_io_eagle.h

171
eeschema/sch_io/eagle/sch_io_eagle.cpp

@ -329,7 +329,6 @@ static void eagleToKicadAlignment( EDA_TEXT* aText, int aEagleAlignment, int aRe
SCH_IO_EAGLE::SCH_IO_EAGLE() : SCH_IO( wxS( "EAGLE" ) ), SCH_IO_EAGLE::SCH_IO_EAGLE() : SCH_IO( wxS( "EAGLE" ) ),
m_rootSheet( nullptr ), m_rootSheet( nullptr ),
m_schematic( nullptr ), m_schematic( nullptr ),
m_module( nullptr ),
m_sheetIndex( 1 ) m_sheetIndex( 1 )
{ {
m_reporter = &WXLOG_REPORTER::GetInstance(); m_reporter = &WXLOG_REPORTER::GetInstance();
@ -389,6 +388,20 @@ SCH_SHEET* SCH_IO_EAGLE::LoadSchematicFile( const wxString& aFileName, SCHEMATIC
wxT( "Can't append to a schematic with no root!" ) ); wxT( "Can't append to a schematic with no root!" ) );
m_rootSheet = &aSchematic->Root(); m_rootSheet = &aSchematic->Root();
// We really should be passing the SCH_SHEET_PATH object to the aAppendToMe attribute
// instead of the SCH_SHEET. The full path is needed to properly generate instance
// data.
SCH_SHEET_LIST hierarchy( m_rootSheet );
for( const SCH_SHEET_PATH& sheetPath : hierarchy )
{
if( sheetPath.Last() == aAppendToMe )
{
m_sheetPath = sheetPath;
break;
}
}
} }
else else
{ {
@ -405,6 +418,10 @@ SCH_SHEET* SCH_IO_EAGLE::LoadSchematicFile( const wxString& aFileName, SCHEMATIC
// Virtual root sheet UUID must be the same as the schematic file UUID. // Virtual root sheet UUID must be the same as the schematic file UUID.
const_cast<KIID&>( m_rootSheet->m_Uuid ) = screen->GetUuid(); const_cast<KIID&>( m_rootSheet->m_Uuid ) = screen->GetUuid();
// There is always at least a root sheet.
m_sheetPath.push_back( m_rootSheet );
m_sheetPath.SetPageNumber( wxT( "1" ) );
} }
SYMBOL_LIB_TABLE* libTable = PROJECT_SCH::SchSymbolLibTable( &m_schematic->Prj() ); SYMBOL_LIB_TABLE* libTable = PROJECT_SCH::SchSymbolLibTable( &m_schematic->Prj() );
@ -666,15 +683,15 @@ void SCH_IO_EAGLE::loadSchematic( const ESCHEMATIC& aSchematic )
for( const auto& [name, epart] : aSchematic.parts ) for( const auto& [name, epart] : aSchematic.parts )
m_partlist[name.Upper()] = epart.get(); m_partlist[name.Upper()] = epart.get();
for( const auto& [modname, emodule] : aSchematic.modules )
for( const auto& [modName, emodule] : aSchematic.modules )
{ {
for( const auto& [name, epart] : emodule->parts )
m_partlist[name.Upper()] = epart.get();
for( const auto& [partName, epart] : emodule->parts )
m_partlist[partName.Upper()] = epart.get();
} }
if( !aSchematic.libraries.empty() ) if( !aSchematic.libraries.empty() )
{ {
for( const auto& [name, elibrary] : aSchematic.libraries )
for( const auto& [libName, elibrary] : aSchematic.libraries )
{ {
EAGLE_LIBRARY* elib = &m_eagleLibs[elibrary->GetName()]; EAGLE_LIBRARY* elib = &m_eagleLibs[elibrary->GetName()];
elib->name = elibrary->GetName(); elib->name = elibrary->GetName();
@ -689,10 +706,6 @@ void SCH_IO_EAGLE::loadSchematic( const ESCHEMATIC& aSchematic )
// local labels will be used for nets found only on that sheet. // local labels will be used for nets found only on that sheet.
countNets( aSchematic ); countNets( aSchematic );
// There is always at least a root sheet.
m_sheetPath.push_back( m_rootSheet );
m_sheetPath.SetPageNumber( wxT( "1" ) );
size_t sheetCount = aSchematic.sheets.size(); size_t sheetCount = aSchematic.sheets.size();
if( sheetCount > 1 ) if( sheetCount > 1 )
@ -727,6 +740,7 @@ void SCH_IO_EAGLE::loadSchematic( const ESCHEMATIC& aSchematic )
wxCHECK2( currentScreen, continue ); wxCHECK2( currentScreen, continue );
sheet->SetParent( m_sheetPath.Last() );
currentScreen->Append( sheet.release() ); currentScreen->Append( sheet.release() );
x += 2; x += 2;
@ -805,7 +819,7 @@ void SCH_IO_EAGLE::loadSheet( const std::unique_ptr<ESHEET>& aSheet )
wxCHECK( sheet && screen, /* void */ ); wxCHECK( sheet && screen, /* void */ );
if( !m_module )
if( m_modules.empty() )
{ {
std::string filename; std::string filename;
wxFileName fn = m_filename; wxFileName fn = m_filename;
@ -824,6 +838,9 @@ void SCH_IO_EAGLE::loadSheet( const std::unique_ptr<ESHEET>& aSheet )
screen->SetFileName( fn.GetFullPath() ); screen->SetFileName( fn.GetFullPath() );
} }
for( const auto& [name, moduleinst] : aSheet->moduleinsts )
loadModuleInstance( moduleinst );
sheet->AutoplaceFields( screen, AUTOPLACE_AUTO ); sheet->AutoplaceFields( screen, AUTOPLACE_AUTO );
if( aSheet->plain ) if( aSheet->plain )
@ -859,19 +876,9 @@ void SCH_IO_EAGLE::loadSheet( const std::unique_ptr<ESHEET>& aSheet )
// Holes and splines currently not handled. Not sure hole has any meaning in scheamtics. // Holes and splines currently not handled. Not sure hole has any meaning in scheamtics.
} }
for( const auto& [name, moduleinst] : aSheet->moduleinsts )
{
SCH_SHEET* modSheet = loadModuleInstance( moduleinst );
screen->Append( modSheet );
}
for( const std::unique_ptr<EINSTANCE>& einstance : aSheet->instances ) for( const std::unique_ptr<EINSTANCE>& einstance : aSheet->instances )
{
if( m_module )
loadInstance( einstance, m_module->parts );
else
loadInstance( einstance, m_eagleDoc->drawing->schematic->parts );
}
loadInstance( einstance, ( m_modules.size() ) ? m_modules.back()->parts
: m_eagleDoc->drawing->schematic->parts );
// Loop through all buses // Loop through all buses
// From the DTD: "Buses receive names which determine which signals they include. // From the DTD: "Buses receive names which determine which signals they include.
@ -956,12 +963,12 @@ void SCH_IO_EAGLE::loadSheet( const std::unique_ptr<ESHEET>& aSheet )
} }
SCH_SHEET* SCH_IO_EAGLE::loadModuleInstance( const std::unique_ptr<EMODULEINST>& aModuleInstance )
void SCH_IO_EAGLE::loadModuleInstance( const std::unique_ptr<EMODULEINST>& aModuleInstance )
{ {
SCH_SHEET* currentSheet = getCurrentSheet(); SCH_SHEET* currentSheet = getCurrentSheet();
SCH_SCREEN* currentScreen = getCurrentScreen(); SCH_SCREEN* currentScreen = getCurrentScreen();
wxCHECK( currentSheet &&currentScreen, nullptr );
wxCHECK( currentSheet &&currentScreen, /* void */ );
m_sheetIndex++; m_sheetIndex++;
@ -977,9 +984,8 @@ SCH_SHEET* SCH_IO_EAGLE::loadModuleInstance( const std::unique_ptr<EMODULEINST>&
aModuleInstance->name, m_filename.GetFullPath() ) ); aModuleInstance->name, m_filename.GetFullPath() ) );
} }
wxFileName fn = m_filename; wxFileName fn = m_filename;
fn.SetName( aModuleInstance->name );
fn.SetName( aModuleInstance->moduleinst );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension ); fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
VECTOR2I portExtWireEndpoint; VECTOR2I portExtWireEndpoint;
@ -993,14 +999,32 @@ SCH_SHEET* SCH_IO_EAGLE::loadModuleInstance( const std::unique_ptr<EMODULEINST>&
std::unique_ptr<SCH_SHEET> newSheet = std::make_unique<SCH_SHEET>( currentSheet, pos, size ); std::unique_ptr<SCH_SHEET> newSheet = std::make_unique<SCH_SHEET>( currentSheet, pos, size );
SCH_SCREEN* newScreen = new SCH_SCREEN( m_schematic );
// The Eagle module for this instance (SCH_SCREEN in KiCad) may have already been loaded.
SCH_SCREEN* newScreen = nullptr;
SCH_SCREENS schFiles( m_rootSheet );
wxCHECK( newSheet && newScreen, nullptr );
for( SCH_SCREEN* schFile = schFiles.GetFirst(); schFile; schFile = schFiles.GetNext() )
{
if( schFile->GetFileName() == fn.GetFullPath() )
{
newScreen = schFile;
break;
}
}
bool isNewSchFile = ( newScreen == nullptr );
if( !newScreen )
{
newScreen = new SCH_SCREEN( m_schematic );
newScreen->SetFileName( fn.GetFullPath() );
}
wxCHECK( newSheet && newScreen, /* void */ );
newSheet->SetScreen( newScreen ); newSheet->SetScreen( newScreen );
newSheet->SetFileName( fn.GetFullName() ); newSheet->SetFileName( fn.GetFullName() );
newSheet->SetName( aModuleInstance->name ); newSheet->SetName( aModuleInstance->name );
newScreen->SetFileName( fn.GetFullPath() );
for( const auto& [portName, port] : it->second->ports ) for( const auto& [portName, port] : it->second->ports )
{ {
@ -1075,67 +1099,53 @@ SCH_SHEET* SCH_IO_EAGLE::loadModuleInstance( const std::unique_ptr<EMODULEINST>&
wxString pageNo = wxString::Format( wxT( "%d" ), m_sheetIndex ); wxString pageNo = wxString::Format( wxT( "%d" ), m_sheetIndex );
newSheet->SetParent( currentSheet );
m_sheetPath.push_back( newSheet.get() ); m_sheetPath.push_back( newSheet.get() );
m_sheetPath.SetPageNumber( pageNo );
currentScreen->Append( newSheet.release() );
m_module = it->second.get();
m_modules.push_back( it->second.get() );
m_moduleInstances.push_back( aModuleInstance.get() );
// It's not clear if Eagle modules can have more than one sheet so this may not be correct.
if( it->second->sheets.size() == 1 )
// Do not reload shared modules that are already loaded.
if( isNewSchFile )
{ {
loadSheet( it->second->sheets.at( 0 ) );
for( const std::unique_ptr<ESHEET>& esheet : it->second->sheets )
loadSheet( esheet );
} }
else else
{ {
int i = 0;
int x = 1;
int y = 1;
// Add instances for shared schematics.
wxString refPrefix;
for( const std::unique_ptr<ESHEET>& esheet : it->second->sheets )
for( const EMODULEINST* emoduleInst : m_moduleInstances )
{ {
pos = VECTOR2I( x * schIUScale.MilsToIU( 1000 ),
y * schIUScale.MilsToIU( 1000 ) );
std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>( newSheet.get(), pos );
SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
sheet->SetScreen( screen );
wxString newFileName = fn.GetName();
wxCHECK2( emoduleInst, continue );
newFileName += wxString::Format( wxS( "_%d" ), i + 1 );
fn.SetName( newFileName );
screen->SetFileName( fn.GetFullPath() );
sheet->SetFileName( fn.GetFullName() );
wxCHECK2( sheet && screen, continue );
wxString subSheetPageNo = wxString::Format( wxT( "%d" ), m_sheetIndex );
refPrefix += emoduleInst->name + wxS( ":" );
}
m_sheetPath.push_back( sheet.get() );
loadSheet( esheet );
SCH_SCREEN* sharedScreen = m_sheetPath.LastScreen();
m_sheetPath.SetPageNumber( subSheetPageNo );
m_sheetPath.pop_back();
if( sharedScreen )
{
for( SCH_ITEM* schItem : sharedScreen->Items().OfType( SCH_SYMBOL_T ) )
{
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( schItem );
newScreen->Append( sheet.release() );
wxCHECK2( symbol && !symbol->GetInstances().empty(), continue );
x += 2;
SCH_SYMBOL_INSTANCE inst = symbol->GetInstances().at( 0 );
wxString newReference = refPrefix + inst.m_Reference.AfterLast( ':' );
if( x > 10 ) // Start next row of sheets.
{
x = 1;
y += 2;
symbol->AddHierarchicalReference( m_sheetPath.Path(), newReference, inst.m_Unit );
} }
m_sheetIndex++;
} }
} }
m_sheetPath.SetPageNumber( pageNo );
m_moduleInstances.pop_back();
m_modules.pop_back();
m_sheetPath.pop_back(); m_sheetPath.pop_back();
m_module = nullptr;
return newSheet.release();
} }
@ -1591,14 +1601,14 @@ SCH_TEXT* SCH_IO_EAGLE::loadLabel( const std::unique_ptr<ELABEL>& aLabel,
VECTOR2I textSize = VECTOR2I( KiROUND( aLabel->size.ToSchUnits() * 0.7 ), VECTOR2I textSize = VECTOR2I( KiROUND( aLabel->size.ToSchUnits() * 0.7 ),
KiROUND( aLabel->size.ToSchUnits() * 0.7 ) ); KiROUND( aLabel->size.ToSchUnits() * 0.7 ) );
if( m_module )
if( m_modules.size() )
{ {
if( m_module->ports.find( aNetName ) != m_module->ports.end() )
if( m_modules.back()->ports.find( aNetName ) != m_modules.back()->ports.end() )
{ {
label = std::make_unique<SCH_HIERLABEL>(); label = std::make_unique<SCH_HIERLABEL>();
label->SetText( escapeName( aNetName ) ); label->SetText( escapeName( aNetName ) );
const auto it = m_module->ports.find( aNetName );
const auto it = m_modules.back()->ports.find( aNetName );
LABEL_SHAPE type; LABEL_SHAPE type;
@ -1958,7 +1968,20 @@ void SCH_IO_EAGLE::loadInstance( const std::unique_ptr<EINSTANCE>& aInstance,
symbol->GetField( REFERENCE_FIELD )->SetVisible( nameAttributeFound ); symbol->GetField( REFERENCE_FIELD )->SetVisible( nameAttributeFound );
} }
symbol->AddHierarchicalReference( m_sheetPath.Path(), reference, unit );
// Eagle has a brain dead module reference scheme where the module names separated by colons
// are prefixed to the symbol references. This will get blown away in KiCad the first time
// any annotation is performed. It is required for the initial synchronization between the
// schematic and the board.
wxString refPrefix;
for( const EMODULEINST* emoduleInst : m_moduleInstances )
{
wxCHECK2( emoduleInst, continue );
refPrefix += emoduleInst->name + wxS( ":" );
}
symbol->AddHierarchicalReference( m_sheetPath.Path(), refPrefix + reference, unit );
// Save the pin positions // Save the pin positions
SYMBOL_LIB_TABLE& schLibTable = *PROJECT_SCH::SchSymbolLibTable( &m_schematic->Prj() ); SYMBOL_LIB_TABLE& schLibTable = *PROJECT_SCH::SchSymbolLibTable( &m_schematic->Prj() );

5
eeschema/sch_io/eagle/sch_io_eagle.h

@ -126,7 +126,7 @@ private:
void loadInstance( const std::unique_ptr<EINSTANCE>& aInstance, void loadInstance( const std::unique_ptr<EINSTANCE>& aInstance,
const std::map<wxString, std::unique_ptr<EPART>>& aParts ); const std::map<wxString, std::unique_ptr<EPART>>& aParts );
SCH_SHEET* loadModuleInstance( const std::unique_ptr<EMODULEINST>& aModuleInstance );
void loadModuleInstance( const std::unique_ptr<EMODULEINST>& aModuleInstance );
EAGLE_LIBRARY* loadLibrary( const ELIBRARY* aLibrary, EAGLE_LIBRARY* aEagleLib ); EAGLE_LIBRARY* loadLibrary( const ELIBRARY* aLibrary, EAGLE_LIBRARY* aEagleLib );
void countNets( const ESCHEMATIC& aSchematic ); void countNets( const ESCHEMATIC& aSchematic );
@ -252,7 +252,8 @@ private:
wxFileName m_filename; wxFileName m_filename;
wxString m_libName; ///< Library name to save symbols wxString m_libName; ///< Library name to save symbols
SCHEMATIC* m_schematic; ///< Passed to Load(), the schematic object being loaded SCHEMATIC* m_schematic; ///< Passed to Load(), the schematic object being loaded
EMODULE* m_module; ///< The current module being loaded or nullptr
std::vector<EMODULE*> m_modules; ///< The current module stack being loaded.
std::vector<EMODULEINST*> m_moduleInstances;
std::map<wxString, const EPART*> m_partlist; std::map<wxString, const EPART*> m_partlist;
std::map<wxString, long long> m_timestamps; std::map<wxString, long long> m_timestamps;

Loading…
Cancel
Save