/** * @file pcbnew/netlist.cpp */ /* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2011 Jean-Pierre Charras. * Copyright (C) 1992-2011 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 */ /* * Functions to read a netlist: * - Load new footprints and initialize net info * - Test for missing or extra footprints * - Recalculate full connectivity info * * Important remark: * When reading a netlist, Pcbnew must identify existing footprints (link * between existing footprints an components in netlist) * This identification can be made from 2 fields: * - The reference (U2, R5 ..): this is the normal mode * - The Time Stamp : useful after a full schematic * reannotation because references can be changed for the component linked to its footprint. * So when reading a netlist, ReadPcbNetlist() can use references or time stamps * to identify footprints on board and the corresponding component in schematic. * If we want to fully reannotate a schematic this sequence must be used * 1 - SAVE your board !!! * 2 - Create and read the netlist (to ensure all info is correct, mainly * references and time stamp) * 3 - Reannotate the schematic (references will be changed, but not time stamps ) * 4 - Recreate and read the new netlist using the Time Stamp identification * (that reinit the new references) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * Function OpenNetlistFile * used to open a netlist file */ static FILE* OpenNetlistFile( const wxString& aFullFileName ) { if( aFullFileName.IsEmpty() ) return NULL; // No filename: exit FILE* file = wxFopen( aFullFileName, wxT( "rt" ) ); if( file == NULL ) { wxString msg; msg.Printf( _( "Netlist file %s not found" ), GetChars( aFullFileName ) ); wxMessageBox( msg ); } return file; } /** * Function ReadPcbNetlist * Update footprints (load missing footprints and delete on request extra * footprints) * Update connectivity info ( Net Name list ) * Update Reference, value and "TIME STAMP" * @param aNetlistFullFilename = netlist file name (*.net) * @param aCmpFullFileName = cmp/footprint list file name (*.cmp) if not found, * @param aMessageWindow = a wxTextCtrl to print messages (can be NULL). * @param aChangeFootprint = true to change existing footprints * when the netlist gives a different footprint. * false to keep existing footprints * @param aDeleteBadTracks - true to erase erroneous tracks after updating connectivity info. * @param aDeleteExtraFootprints - true to remove unlocked footprints found on board but not * in netlist. * @param aSelect_By_Timestamp - true to use schematic timestamps instead of schematic references * to identify footprints on board * (Must be used after a full reannotation in schematic). * @return true if Ok */ bool PCB_EDIT_FRAME::ReadPcbNetlist( const wxString& aNetlistFullFilename, const wxString& aCmpFullFileName, wxTextCtrl* aMessageWindow, bool aChangeFootprint, bool aDeleteBadTracks, bool aDeleteExtraFootprints, bool aSelect_By_Timestamp ) { FILE* netfile = OpenNetlistFile( aNetlistFullFilename ); if( !netfile ) return false; SetLastNetListRead( aNetlistFullFilename ); bool useCmpfile = ! aCmpFullFileName.IsEmpty() && wxFileExists( aCmpFullFileName ); if( aMessageWindow ) { wxString msg; msg.Printf( _( "Reading Netlist \"%s\"" ), GetChars( aNetlistFullFilename ) ); aMessageWindow->AppendText( msg + wxT( "\n" ) ); if( useCmpfile ) { msg.Printf( _( "Using component/footprint link file \"%s\"" ), GetChars( aCmpFullFileName ) ); aMessageWindow->AppendText( msg + wxT( "\n" ) ); } if( aSelect_By_Timestamp ) { msg.Printf( _( "Using time stamp selection" ), GetChars( aCmpFullFileName ) ); aMessageWindow->AppendText( msg + wxT( "\n" ) ); } } // Clear undo and redo lists to avoid inconsistencies between lists GetScreen()->ClearUndoRedoList(); OnModify(); // Clear flags and pointers to avoid inconsistencies GetBoard()->m_Status_Pcb = 0; SetCurItem( NULL ); wxBusyCursor dummy; // Shows an hourglass while calculating NETLIST_READER netList_Reader( this, aMessageWindow ); netList_Reader.m_UseTimeStamp = aSelect_By_Timestamp; netList_Reader.m_ChangeFootprints = aChangeFootprint; netList_Reader.m_UseCmpFile = useCmpfile; netList_Reader.SetFilesnames( aNetlistFullFilename, aCmpFullFileName ); netList_Reader.ReadNetList( netfile ); // Delete footprints not found in netlist: if( aDeleteExtraFootprints ) { if( IsOK( NULL, _( "Ok to delete not locked footprints not found in netlist?" ) ) ) netList_Reader.RemoveExtraFootprints(); } // Rebuild the board connectivity: Compile_Ratsnest( NULL, true ); if( aDeleteBadTracks && GetBoard()->m_Track ) { // Remove erroneous tracks if( RemoveMisConnectedTracks() ) Compile_Ratsnest( NULL, true ); } GetBoard()->DisplayInfo( this ); m_canvas->Refresh(); return true; } /* * Function ReadNetList * The main function to detect the netlist format,and run the right netlist reader * aFile = the already opened file (will be closed by the netlist reader) */ bool NETLIST_READER::ReadNetList( FILE* aFile ) { // Try to determine the netlist type: bool new_fmt = true; std::string header( "(export" ); for( unsigned ii = 0; ii < header.size(); ii++ ) { int data; do { data = fgetc( aFile ); } while ( ( data == ' ' ) &&( EOF != data ) ) ; if( (int)header[ii] == data ) continue; new_fmt = false; break; } rewind( aFile ); bool success; if( new_fmt ) success = ReadKicadNetList( aFile ); else success = ReadOldFmtdNetList( aFile ); return success; } bool NETLIST_READER::InitializeModules() { if( m_UseCmpFile ) // Try to get footprint name from .cmp file { readModuleComponentLinkfile(); } for( unsigned ii = 0; ii < m_modulesInNetlist.size(); ii++ ) { MODULE_INFO* currmod_info = m_modulesInNetlist[ii]; // Test if module is already loaded. wxString * idMod = m_UseTimeStamp? &currmod_info->m_TimeStamp : &currmod_info->m_Reference; MODULE* module = FindModule( *idMod ); if( module == NULL ) // not existing, load it { m_newModulesList.push_back( currmod_info ); } } bool success = loadNewModules(); // Update modules fields for( unsigned ii = 0; ii < m_modulesInNetlist.size(); ii++ ) { MODULE_INFO* currmod_info = m_modulesInNetlist[ii]; // Test if module is already loaded. wxString * idMod = m_UseTimeStamp? &currmod_info->m_TimeStamp : &currmod_info->m_Reference; MODULE* module = FindModule( *idMod ); if( module ) { // Update current module ( reference, value and "Time Stamp") module->m_Reference->m_Text = currmod_info->m_Reference; module->m_Value->m_Text = currmod_info->m_Value; module->SetPath( currmod_info->m_TimeStamp ); } else // not existing { } } // clear pads netnames for( MODULE* module = m_pcbframe->GetBoard()->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->m_Pads; pad; pad = pad->Next() ) pad->SetNetname( wxEmptyString ); } return success; } void NETLIST_READER::TestFootprintsMatchingAndExchange() { for( MODULE* module = m_pcbframe->GetBoard()->m_Modules; module; module = module->Next() ) { // Search for the corresponding module info MODULE_INFO * mod_info = NULL; for( unsigned ii = 0; ii < m_modulesInNetlist.size(); ii++ ) { MODULE_INFO * candidate = m_modulesInNetlist[ii]; // Test if mod_info matches the current module: if( candidate->m_Reference.CmpNoCase( module->GetReference() ) == 0 ) { mod_info = candidate; break; } } if( mod_info == NULL ) // not found in netlist continue; if( module->GetLibRef().CmpNoCase( mod_info->m_Footprint ) != 0 ) { if( m_ChangeFootprints ) // footprint exchange allowed. { MODULE* newModule = m_pcbframe->GetModuleLibrary( wxEmptyString, mod_info->m_Footprint, false ); if( newModule ) { // Change old module to the new module (and delete the old one) m_pcbframe->Exchange_Module( module, newModule, NULL ); module = newModule; } else if( m_messageWindow ) { wxString msg; msg.Printf( _( "Component \"%s\": module [%s] not found\n" ), GetChars( mod_info->m_Reference ), GetChars( mod_info->m_Footprint ) ); m_messageWindow->AppendText( msg ); } } else if( m_messageWindow ) { wxString msg; msg.Printf( _( "Component \"%s\": Mismatch! module is [%s] and netlist said [%s]\n" ), GetChars( mod_info->m_Reference ), GetChars( module->GetLibRef() ), GetChars( mod_info->m_Footprint ) ); m_messageWindow->AppendText( msg ); } } } } /** * Function SetPadNetName * Update a pad netname * @param aModule = module reference * @param aPadname = pad name (pad num) * @param aNetname = new net name of the pad * @return a pointer to the pad or NULL if the pad is not found */ D_PAD* NETLIST_READER::SetPadNetName( const wxString & aModule, const wxString & aPadname, const wxString & aNetname ) { MODULE* module = m_pcbframe->GetBoard()->FindModuleByReference( aModule ); if( module ) { D_PAD * pad = module->FindPadByName( aPadname ); if( pad ) { pad->SetNetname( aNetname ); return pad; } if( m_messageWindow ) { wxString msg; msg.Printf( _( "Module [%s]: Pad [%s] not found" ), GetChars( aModule ), GetChars( aPadname ) ); m_messageWindow->AppendText( msg + wxT( "\n" ) ); } } return NULL; } /* function RemoveExtraFootprints * Remove (delete) not locked footprints found on board, but not in netlist */ void NETLIST_READER::RemoveExtraFootprints() { MODULE* nextModule; MODULE* module = m_pcbframe->GetBoard()->m_Modules; for( ; module != NULL; module = nextModule ) { unsigned ii; nextModule = module->Next(); if( module->m_ModuleStatus & MODULE_is_LOCKED ) continue; for( ii = 0; ii < m_modulesInNetlist.size(); ii++ ) { MODULE_INFO* mod_info = m_modulesInNetlist[ii]; if( module->m_Reference->m_Text.CmpNoCase( mod_info->m_Reference ) == 0 ) break; // Module is found in net list. } if( ii == m_modulesInNetlist.size() ) // Module not found in netlist. module->DeleteStructure(); } } /* Search for a module id the modules existing in the current BOARD. * aId is a key to identify the module to find: * The reference or the full time stamp, according to m_UseTimeStamp * Returns the module is found, NULL otherwise. */ MODULE* NETLIST_READER::FindModule( const wxString& aId ) { MODULE* module = m_pcbframe->GetBoard()->m_Modules; for( ; module != NULL; module = module->Next() ) { if( m_UseTimeStamp ) // identification by time stamp { if( aId.CmpNoCase( module->m_Path ) == 0 ) return module; } else // identification by Reference { if( aId.CmpNoCase( module->m_Reference->m_Text ) == 0 ) return module; } } return NULL; } /** * build and shows a list of existing modules on board * The user can select a module from this list * @return a pointer to the selected module or NULL */ MODULE* PCB_EDIT_FRAME::ListAndSelectModuleName( void ) { MODULE* Module; if( GetBoard()->m_Modules == NULL ) { DisplayError( this, _( "No Modules" ) ); return 0; } wxArrayString listnames; Module = (MODULE*) GetBoard()->m_Modules; for( ; Module != NULL; Module = (MODULE*) Module->Next() ) listnames.Add( Module->m_Reference->m_Text ); EDA_LIST_DIALOG dlg( this, _( "Components" ), listnames, wxEmptyString ); if( dlg.ShowModal() != wxID_OK ) return NULL; wxString ref = dlg.GetTextSelection(); Module = (MODULE*) GetBoard()->m_Modules; for( ; Module != NULL; Module = Module->Next() ) { if( Module->m_Reference->m_Text == ref ) break; } return Module; } /* * Function Test_Duplicate_Missing_And_Extra_Footprints * Build a list of duplicate, missing and extra footprints * from the current board and a netlist netlist : * Shows 3 lists: * 1 - duplicate footprints on board * 2 - missing footprints (found in netlist but not on board) * 3 - footprints not in netlist but on board * @param aNetlistFullFilename = the full filename netlist */ void PCB_EDIT_FRAME::Test_Duplicate_Missing_And_Extra_Footprints( const wxString& aNetlistFullFilename ) { int nberr = 0; wxArrayString list; // The list of messages to display if( GetBoard()->m_Modules == NULL ) { DisplayInfoMessage( this, _( "No modules" ) ); return; } FILE* netfile = OpenNetlistFile( aNetlistFullFilename ); if( !netfile ) return; SetLastNetListRead( aNetlistFullFilename ); // Build the list of references of the net list modules. NETLIST_READER netList_Reader( this ); netList_Reader.SetFilesnames( aNetlistFullFilename, wxEmptyString ); netList_Reader.BuildModuleListOnly( true ); if( ! netList_Reader.ReadNetList( netfile ) ) return; // error std::vector & moduleInfoList = netList_Reader.GetModuleInfoList(); if( moduleInfoList.size() == 0 ) { wxMessageBox( _( "No modules in NetList" ) ); return; } // Search for duplicate footprints. list.Add( _( "Duplicates" ) ); MODULE* module = GetBoard()->m_Modules; for( ; module != NULL; module = module->Next() ) { MODULE* altmodule = module->Next(); for( ; altmodule != NULL; altmodule = altmodule->Next() ) { if( module->m_Reference->m_Text.CmpNoCase( altmodule->m_Reference->m_Text ) == 0 ) { if( module->m_Reference->m_Text.IsEmpty() ) list.Add( wxT("") ); else list.Add( module->m_Reference->m_Text ); nberr++; break; } } } // Search for missing modules on board. list.Add( _( "Missing:" ) ); for( unsigned ii = 0; ii < moduleInfoList.size(); ii++ ) { MODULE_INFO* mod_info = moduleInfoList[ii]; module = GetBoard()->FindModuleByReference( mod_info->m_Reference ); if( module == NULL ) // Module missing, not found in board { list.Add( mod_info->m_Reference ); nberr++; } } // Search for modules found on board but not in net list. list.Add( _( "Not in Netlist:" ) ); module = GetBoard()->m_Modules; for( ; module != NULL; module = module->Next() ) { unsigned ii; for( ii = 0; ii < moduleInfoList.size(); ii++ ) { MODULE_INFO* mod_info = moduleInfoList[ii]; if( module->m_Reference->m_Text.CmpNoCase( mod_info->m_Reference ) == 0 ) break; // Module is in net list. } if( ii == moduleInfoList.size() ) // Module not found in netlist { list.Add( module->m_Reference->m_Text ); nberr++; } } wxSingleChoiceDialog dlg( this, wxEmptyString, _( "Check Modules" ), list, NULL, wxCHOICEDLG_STYLE & ~wxCANCEL ); dlg.ShowModal(); } /* * function readModuleComponentLinkfile * read the *.cmp file ( filename in m_cmplistFullName ) * giving the equivalence Footprint_names / components * to find the footprint name corresponding to aCmpIdent * return true if the file can be read * * Sample file: * * Cmp-Mod V01 Genere by Pcbnew 29/10/2003-13: 11:6 * * BeginCmp * TimeStamp = /32307DE2/AA450F67; * Reference = C1; * ValeurCmp = 47uF; * IdModule = CP6; * EndCmp * */ bool NETLIST_READER::readModuleComponentLinkfile() { wxString refcurrcmp; // Stores value read from line like Reference = BUS1; wxString timestamp; // Stores value read from line like TimeStamp = /32307DE2/AA450F67; wxString footprint; // Stores value read from line like IdModule = CP6; FILE* cmpFile = wxFopen( m_cmplistFullName, wxT( "rt" ) ); if( cmpFile == NULL ) { wxString msg; msg.Printf( _( "File <%s> not found, use Netlist for footprints selection" ), GetChars( m_cmplistFullName ) ); if( m_messageWindow ) m_messageWindow->AppendText( msg ); return false; } // netlineReader dtor will close cmpFile FILE_LINE_READER netlineReader( cmpFile, m_cmplistFullName ); wxString buffer; wxString value; while( netlineReader.ReadLine() ) { buffer = FROM_UTF8( netlineReader.Line() ); if( ! buffer.StartsWith( wxT("BeginCmp") ) ) continue; // Begin component description. refcurrcmp.Empty(); footprint.Empty(); timestamp.Empty(); while( netlineReader.ReadLine() ) { buffer = FROM_UTF8( netlineReader.Line() ); if( buffer.StartsWith( wxT("EndCmp") ) ) break; // store string value, stored between '=' and ';' delimiters. value = buffer.AfterFirst( '=' ); value = value.BeforeLast( ';'); value.Trim(true); value.Trim(false); if( buffer.StartsWith( wxT("Reference") ) ) { refcurrcmp = value; continue; } if( buffer.StartsWith( wxT("IdModule =" ) ) ) { footprint = value; continue; } if( buffer.StartsWith( wxT("TimeStamp =" ) ) ) { timestamp = value; continue; } } // Find the corresponding item in module info list: for( unsigned ii = 0; ii < m_modulesInNetlist.size(); ii++ ) { MODULE_INFO * mod_info = m_modulesInNetlist[ii]; if( m_UseTimeStamp ) // Use schematic timestamp to locate the footprint { if( mod_info->m_TimeStamp.CmpNoCase( timestamp ) == 0 && !timestamp.IsEmpty() ) { // Found if( !footprint.IsEmpty() ) mod_info->m_Footprint = footprint; break; } } else // Use schematic reference to locate the footprint { if( mod_info->m_Reference.CmpNoCase( refcurrcmp ) == 0 ) // Found! { if( !footprint.IsEmpty() ) mod_info->m_Footprint = footprint; break; } } } } return true; } /* Function to sort the footprint list, used by loadNewModules. * the given list is sorted by name */ static bool SortByLibName( MODULE_INFO* ref, MODULE_INFO* cmp ) { int ii = ref->m_Footprint.CmpNoCase( cmp->m_Footprint ); return ii > 0; } /* Load new modules from library. * If a new module is already loaded it is duplicated, which avoids multiple * unnecessary disk or net access to read libraries. * return false if a footprint is not found, true if OK */ bool NETLIST_READER::loadNewModules() { MODULE_INFO* ref, * cmp; MODULE* Module = NULL; wxPoint ModuleBestPosition; BOARD* pcb = m_pcbframe->GetBoard(); bool success = true; if( m_newModulesList.size() == 0 ) return true; sort( m_newModulesList.begin(), m_newModulesList.end(), SortByLibName ); // Calculate the footprint "best" position: EDA_RECT bbbox = pcb->ComputeBoundingBox( true ); if( bbbox.GetWidth() || bbbox.GetHeight() ) { ModuleBestPosition = bbbox.GetEnd(); ModuleBestPosition.y += 5000; } ref = cmp = m_newModulesList[0]; for( unsigned ii = 0; ii < m_newModulesList.size(); ii++ ) { cmp = m_newModulesList[ii]; if( (ii == 0) || ( ref->m_Footprint != cmp->m_Footprint) ) { // New footprint : must be loaded from a library Module = m_pcbframe->GetModuleLibrary( wxEmptyString, cmp->m_Footprint, false ); ref = cmp; if( Module == NULL ) { success = false; if( m_messageWindow ) { wxString msg; msg.Printf( _( "Component [%s]: footprint <%s> not found" ), GetChars( cmp->m_Reference ), GetChars( cmp->m_Footprint ) ); msg += wxT("\n"); m_messageWindow->AppendText( msg ); } continue; } Module->SetPosition( ModuleBestPosition ); /* Update schematic links : reference "Time Stamp" and schematic * hierarchical path */ Module->m_Reference->m_Text = cmp->m_Reference; Module->SetTimeStamp( GetNewTimeStamp() ); Module->SetPath( cmp->m_TimeStamp ); } else { // Footprint already loaded from a library, duplicate it (faster) if( Module == NULL ) continue; // Module does not exist in library. MODULE* newmodule = new MODULE( *Module ); newmodule->SetParent( pcb ); pcb->Add( newmodule, ADD_APPEND ); Module = newmodule; Module->m_Reference->m_Text = cmp->m_Reference; Module->SetTimeStamp( GetNewTimeStamp() ); Module->SetPath( cmp->m_TimeStamp ); } } return success; }