/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 1992-2009 jean-pierre.charras@gipsa-lab.inpg.fr * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 1992-2010 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 */ /*****************************/ /* Net list generation code. */ /*****************************/ #include "fctsys.h" #include #include "gr_basic.h" #include "common.h" #include "confirm.h" #include "kicad_string.h" #include "gestfich.h" #include "appl_wxstruct.h" #include "program.h" #include "general.h" #include "netlist.h" #include "protos.h" #include "class_library.h" #include "class_pin.h" #include "build_version.h" #include /** * @bug - Every place in this file where fprintf() is used and the return * is not checked is a bug. The fprintf() function can fail and * returns a value less than 0 when it does. */ /** * Class UNIQUE_STRINGS * tracks unique wxStrings and is useful in telling if a string * has been seen before. */ class UNIQUE_STRINGS { std::set m_set; ///< set of wxStrings already found public: /** * Function Clear * erases the record. */ void Clear() { m_set.clear(); } /** * Function Lookup * returns true if \a aString already exists in the set, otherwise returns * false and adds \a aString to the set for next time. */ bool Lookup( const wxString& aString ); }; bool UNIQUE_STRINGS::Lookup( const wxString& aString ) { bool ret = ( m_set.find( aString ) != m_set.end() ); if( !ret ) m_set.insert( aString ); return ret; } /** * Class EXPORT_HELP * is a private implementation class used in this source file to keep track * of and recycle datastructures used in the generation of various exported netlist * files. Since it is private it is not in a header file. */ class EXPORT_HELP { NETLIST_OBJECT_LIST m_SortedComponentPinList; /// Used for "multi parts per package" components, avoids processing a lib component more than once. UNIQUE_STRINGS m_ReferencesAlreadyFound; /** * Function sprintPinNetName * formats the net name for \a aPin using \a aNetNameFormat into \a aResult. *

* Net name is: *

    *
  • "?" if pin not connected *
  • "netname" for global net (like gnd, vcc .. *
  • "netname_sheetnumber" for the usual nets *
*/ static void sprintPinNetName( wxString* aResult, const wxString& aNetNameFormat, NETLIST_OBJECT* aPin ); /** * Function findNextComponentAndCreatePinList * finds a "suitable" component from the DrawList and optionally builds * its pin list int m_SortedComponentPinList. The list is sorted by pin num. * A suitable component is a "new" real component (power symbols are not * considered). */ SCH_COMPONENT* findNextComponentAndCreatPinList( EDA_BaseStruct* aItem, SCH_SHEET_PATH* aSheetPath ); public: bool Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& aOutFileName ); /** * Function WriteNetListPCBNEW * generates a net list file (Format 2 improves ORCAD PCB) * = TRUE if with_pcbnew * Format Pcbnew (OrcadPcb2 + reviews and lists of net) * = FALSE if with_pcbnew * Format ORCADPCB2 strict */ bool WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with_pcbnew ); void WriteNetListCADSTAR( WinEDA_SchematicFrame* frame, FILE* f ); void WriteListOfNetsCADSTAR( FILE* f, NETLIST_OBJECT_LIST& aObjectsList ); void WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f, bool use_netnames ); bool WriteGENERICListOfNetsTxt( FILE* f, NETLIST_OBJECT_LIST& aObjectsList ); bool WriteGENERICListOfNets( wxXmlNode* aNode, NETLIST_OBJECT_LIST& aObjectsList ); bool AddPinToComponentPinList( SCH_COMPONENT* Component, SCH_SHEET_PATH* sheet, LIB_PIN* PinEntry ); void FindAllInstancesOfComponent( SCH_COMPONENT* Component, LIB_COMPONENT* aEntry, SCH_SHEET_PATH* Sheet_in ); void EraseDuplicatePins( NETLIST_OBJECT_LIST& aPinList ); }; /** * Function WriteNetListFile * creates the netlist file. Netlist info must be existing * @param aFormat = netlist format (NET_TYPE_PCBNEW ...) * @param aFullFileName = full netlist file name * @param aUse_netnames = bool. if true, use net names from labels in schematic * if false, use net numbers (net codes) * bool aUse_netnames is used only for Spice netlist * @return true if success. */ bool WinEDA_SchematicFrame::WriteNetListFile( int aFormat, const wxString& aFullFileName, bool aUse_netnames ) { bool ret = true; FILE* f = NULL; EXPORT_HELP helper; if( aFormat < NET_TYPE_CUSTOM1 ) { if( ( f = wxFopen( aFullFileName, wxT( "wt" ) ) ) == NULL ) { wxString msg = _( "Failed to create file " ) + aFullFileName; DisplayError( this, msg ); return false; } } wxBusyCursor Busy; switch( aFormat ) { case NET_TYPE_PCBNEW: ret = helper.WriteNetListPCBNEW( this, f, TRUE ); fclose( f ); break; case NET_TYPE_ORCADPCB2: ret = helper.WriteNetListPCBNEW( this, f, FALSE ); fclose( f ); break; case NET_TYPE_CADSTAR: helper.WriteNetListCADSTAR( this, f ); fclose( f ); break; case NET_TYPE_SPICE: helper.WriteNetListPspice( this, f, aUse_netnames ); fclose( f ); break; default: { wxFileName tmpFile = aFullFileName; tmpFile.SetExt( wxT( "tmp" ) ); ret = helper.Write_GENERIC_NetList( this, tmpFile.GetFullPath() ); if( !ret ) break; // Call the external module (plug in ) if( g_NetListerCommandLine.IsEmpty() ) break; wxString commandLine; if( wxIsAbsolutePath( g_NetListerCommandLine ) ) commandLine = g_NetListerCommandLine; else commandLine = FindKicadFile( g_NetListerCommandLine ); // this is the input file to the plugin commandLine += wxT( " " ) + tmpFile.GetFullPath(); // this is the output file to the plugin commandLine += wxT( " " ) + aFullFileName; ProcessExecute( commandLine, wxEXEC_SYNC ); // ::wxRemoveFile( tmpFile.GetFullPath() ); } break; } return ret; } /* * Comparison routine for sorting by pin numbers. */ static bool sortPinsByNum( NETLIST_OBJECT* Pin1, NETLIST_OBJECT* Pin2 ) { // return "lhs < rhs" return RefDesStringCompare( Pin1->GetPinNumText(), Pin2->GetPinNumText() ) < 0; } void EXPORT_HELP::sprintPinNetName( wxString* aResult, const wxString& aNetNameFormat, NETLIST_OBJECT* aPin ) { int netcode = aPin->GetNet(); // Not wxString::Clear(), which would free memory. We want the worst // case wxString memory to grow to avoid reallocation from within the // caller's loop. aResult->Empty(); if( netcode != 0 && aPin->m_FlagOfConnection == PAD_CONNECT ) { NETLIST_OBJECT* netref = aPin->m_NetNameCandidate; if( netref ) *aResult = netref->m_Label; if( !aResult->IsEmpty() ) { // prefix non global label names with the sheet path, to avoid name collisions if( netref->m_Type != NET_PINLABEL && netref->m_Type != NET_GLOBLABEL ) { wxString lnet = *aResult; *aResult = netref->m_SheetList.PathHumanReadable(); // If sheet path is too long, use the time stamp name instead if( aResult->Length() > 32 ) *aResult = netref->m_SheetList.Path(); *aResult += lnet; } } else { aResult->Printf( aNetNameFormat.GetData(), netcode ); } } } SCH_COMPONENT* EXPORT_HELP::findNextComponentAndCreatPinList( EDA_BaseStruct* aItem, SCH_SHEET_PATH* aSheetPath ) { wxString ref; m_SortedComponentPinList.clear(); // continue searching from the middle of a linked list (the draw list) for( ; aItem; aItem = aItem->Next() ) { if( aItem->Type() != TYPE_SCH_COMPONENT ) continue; // found next component SCH_COMPONENT* comp = (SCH_COMPONENT*) aItem; // Power symbols and other components which have the reference starting // with "#" are not included in netlist (pseudo or virtual components) ref = comp->GetRef( aSheetPath ); if( ref[0] == wxChar( '#' ) ) continue; // if( Component->m_FlagControlMulti == 1 ) // continue; /* yes */ // removed because with multiple instances of one schematic // (several sheets pointing to 1 screen), this will be erroneously be // toggled. LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( comp->m_ChipName ); if( !entry ) continue; // If component is a "multi parts per package" type if( entry->GetPartCount() > 1 ) { // test if already visited, and if so skip if( m_ReferencesAlreadyFound.Lookup( ref ) ) continue; // Collect all parts and pins for this first occurance of reference. // This is only done once, it would be too expensive otherwise. FindAllInstancesOfComponent( comp, entry, aSheetPath ); } else // entry->GetPartCount() <= 1 means one part per package { LIB_PIN_LIST pins; // constructed once here entry->GetPins( pins, comp->GetUnitSelection( aSheetPath ), comp->m_Convert ); for( size_t i = 0; i < pins.size(); i++ ) { LIB_PIN* pin = pins[i]; wxASSERT( pin->Type() == COMPONENT_PIN_DRAW_TYPE ); AddPinToComponentPinList( comp, aSheetPath, pin ); } } // Sort pins in m_SortedComponentPinList by pin number sort( m_SortedComponentPinList.begin(), m_SortedComponentPinList.end(), sortPinsByNum ); // Remove duplicate Pins in m_SortedComponentPinList EraseDuplicatePins( m_SortedComponentPinList ); return comp; } return NULL; } /** * Function Node * is a convenience function that creates a new wxXmlNode with an optional textual child. * @param aName is the name to associate with a new node of type wxXML_ELEMENT_NODE. * @param aContent is optional, and if given is the text to include in a child * of the returned node, and has type wxXML_TEXT_NODE. */ static wxXmlNode* Node( const wxString& aName, const wxString& aTextualContent = wxEmptyString ) { wxXmlNode* n = new wxXmlNode( 0, wxXML_ELEMENT_NODE, aName ); if( aTextualContent.Len() > 0 ) // excludes wxEmptyString, the default textual content n->AddChild( new wxXmlNode( 0, wxXML_TEXT_NODE, wxEmptyString, aTextualContent ) ); return n; } /** * Function Write_GENERIC_NetList * creates a generic netlist, now in XML. * @return bool - true if there were no errors, else false. */ bool EXPORT_HELP::Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& aOutFileName ) { #if 1 // output the XML format netlist. wxXmlDocument xdoc; // tree markers or walkers wxXmlNode* xroot; // root node wxXmlNode* xcomps; // start of components // some strings we need many times, but don't want to construct more // than once for performance. These are used within loops so the // enclosing wxString constructor would fire on each loop iteration if // they were in a nested scope. wxString timeStamp; wxString logicalLibName; // these are actually constructor invocations, not assignments as it appears: const wxString sFields = wxT( "fields" ); const wxString sField = wxT( "field" ); const wxString sComponent = wxT( "comp" ); // use "part" ? const wxString sName = wxT( "name" ); const wxString sRef = wxT( "ref" ); const wxString sPins = wxT( "pins" ); const wxString sPin = wxT( "pin" ); const wxString sValue = wxT( "value" ); const wxString sSheetPath = wxT( "sheetpath" ); const wxString sFootprint = wxT( "footprint" ); const wxString sDatasheet = wxT( "datasheet" ); const wxString sTStamp = wxT( "tstamp" ); const wxString sTStamps = wxT( "tstamps" ); const wxString sTSFmt = wxT( "%8.8lX" ); // comp->m_TimeStamp const wxString sLibSource = wxT( "libsource" ); const wxString sLibPart = wxT( "libpart" ); const wxString sLib = wxT( "lib" ); const wxString sPart = wxT( "part" ); const wxString sNames = wxT( "names" ); m_ReferencesAlreadyFound.Clear(); xdoc.SetRoot( xroot = Node( wxT( "netlist" ) ) ); xroot->AddProperty( wxT( "version" ), wxT( "B" ) ); xroot->AddChild( xcomps = Node( wxT( "components" ) ) ); SCH_SHEET_LIST sheetList; // output is xml, so there is no reason to remove spaces from the field values. for( SCH_SHEET_PATH* path = sheetList.GetFirst(); path; path = sheetList.GetNext() ) { for( EDA_BaseStruct* schItem = path->LastDrawList(); schItem; schItem = schItem->Next() ) { SCH_COMPONENT* comp = findNextComponentAndCreatPinList( schItem, path ); if( !comp ) break; // No component left schItem = comp; wxXmlNode* xcomp; // current component being constructed // Output the component's elments in order of expected access frequency. // This may not always look best, but it will allow faster execution // under XSL processing systems which do sequential searching within // an element. xcomps->AddChild( xcomp = Node( sComponent ) ); xcomp->AddProperty( sRef, comp->GetRef( path ) ); xcomp->AddChild( Node( sValue, comp->GetField( VALUE )->m_Text ) ); if( !comp->GetField( FOOTPRINT )->m_Text.IsEmpty() ) xcomp->AddChild( Node( sFootprint, comp->GetField( FOOTPRINT )->m_Text ) ); if( !comp->GetField( DATASHEET )->m_Text.IsEmpty() ) xcomp->AddChild( Node( sDatasheet, comp->GetField( DATASHEET )->m_Text ) ); // Export all user defined fields within the component, // which start at field index MANDATORY_FIELDS. Only output the // container element if there are any s. if( comp->GetFieldCount() > MANDATORY_FIELDS ) { wxXmlNode* xfields; xcomp->AddChild( xfields = Node( sFields ) ); for( int fldNdx = MANDATORY_FIELDS; fldNdx < comp->GetFieldCount(); ++fldNdx ) { SCH_FIELD* f = comp->GetField( fldNdx ); // only output a field if non empty if( !f->m_Text.IsEmpty() ) { wxXmlNode* xfield; xfields->AddChild( xfield = Node( sField, f->m_Text ) ); xfield->AddProperty( sName, f->m_Name ); } } } wxXmlNode* xlibsource; xcomp->AddChild( xlibsource = Node( sLibSource ) ); // "logical" library name, which is in anticipation of a better search // algorithm for parts based on "logical_lib.part" and where logical_lib // is merely the library name minus path and extension. LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( comp->m_ChipName ); if( entry ) xlibsource->AddProperty( sLib, entry->GetLibrary()->GetLogicalName() ); xlibsource->AddProperty( sPart, comp->m_ChipName ); wxXmlNode* xsheetpath; xcomp->AddChild( xsheetpath = Node( sSheetPath ) ); xsheetpath->AddProperty( sTStamps, path->Path() ); xsheetpath->AddProperty( sNames, path->PathHumanReadable() ); timeStamp.Printf( sTSFmt, comp->m_TimeStamp ); xcomp->AddChild( Node( sTStamp, timeStamp ) ); } } // @todo generate the nested s xroot->AddChild( Node( wxT( "libparts" ) ) ); wxXmlNode* xnets; xroot->AddChild( xnets = Node( wxT( "nets" ) ) ); WriteGENERICListOfNets( xnets, g_NetObjectslist ); return xdoc.Save( aOutFileName, 2 /* indent bug, today was ignored by wxXml lib */ ); #else // ouput the well established/old net list format which was not XML. wxString field; wxString footprint; wxString netname; FILE* out; int ret = 0; // OR on each call, test sign bit at very end. if( ( out = wxFopen( aOutFileName, wxT( "wt" ) ) ) == NULL ) { wxString msg = _( "Failed to create file " ) + aOutFileName; DisplayError( frame, msg ); return false; } m_ReferencesAlreadyFound.Clear(); ret |= fprintf( out, "$BeginNetlist\n" ); // Create netlist module section ret |= fprintf( out, "$BeginComponentList\n" ); SCH_SHEET_LIST sheetList; for( SCH_SHEET_PATH* path = sheetList.GetFirst(); path; path = sheetList.GetNext() ) { for( EDA_BaseStruct* schItem = path->LastDrawList(); schItem; schItem = schItem->Next() ) { SCH_COMPONENT* comp = findNextComponentAndCreatPinList( schItem, path ); if( !comp ) break; // No component left schItem = comp; footprint.Empty(); if( !comp->GetField( FOOTPRINT )->IsVoid() ) { footprint = comp->GetField( FOOTPRINT )->m_Text; footprint.Replace( wxT( " " ), wxT( "_" ) ); } ret |= fprintf( out, "\n$BeginComponent\n" ); ret |= fprintf( out, "TimeStamp=%8.8lX\n", comp->m_TimeStamp ); ret |= fprintf( out, "Footprint=%s\n", CONV_TO_UTF8( footprint ) ); field = wxT( "Reference=" ) + comp->GetRef( path ) + wxT( "\n" ); field.Replace( wxT( " " ), wxT( "_" ) ); ret |= fputs( CONV_TO_UTF8( field ), out ); field = comp->GetField( VALUE )->m_Text; field.Replace( wxT( " " ), wxT( "_" ) ); ret |= fprintf( out, "Value=%s\n", CONV_TO_UTF8( field ) ); field = comp->m_ChipName; field.Replace( wxT( " " ), wxT( "_" ) ); ret |= fprintf( out, "Libref=%s\n", CONV_TO_UTF8( field ) ); // Write pin list: ret |= fprintf( out, "$BeginPinList\n" ); for( unsigned ii = 0; ii < m_SortedComponentPinList.size(); ii++ ) { NETLIST_OBJECT* Pin = m_SortedComponentPinList[ii]; if( !Pin ) continue; netname = ReturnPinNetName( Pin, wxT( "$-%.6d" ) ); if( netname.IsEmpty() ) netname = wxT( "?" ); ret |= fprintf( out, "%.4s=%s\n", (char*) &Pin->m_PinNum, CONV_TO_UTF8( netname ) ); } ret |= fprintf( out, "$EndPinList\n" ); ret |= fprintf( out, "$EndComponent\n" ); } } ret |= fprintf( out, "$EndComponentList\n" ); ret |= fprintf( out, "\n$BeginNets\n" ); if( !WriteGENERICListOfNetsTxt( out, g_NetObjectslist ) ) ret = -1; ret |= fprintf( out, "$EndNets\n" ); ret |= fprintf( out, "\n$EndNetlist\n" ); ret |= fclose( out ); return ret >= 0; #endif } /* Routine generation of the netlist file (Format PSPICE) * = TRUE if use_netnames * Nodes are identified by the netname * If the nodes are identified by the netnumber * * All graphics text commentary by a [.-+] PSpice or [.-+] gnucap * Are considered in placing orders in the netlist * [.-] Or PSpice gnucap are beginning * + + Gnucap and PSpice are ultimately NetList */ void EXPORT_HELP::WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f, bool use_netnames ) { char Line[1024]; SCH_SHEET_PATH* sheet; EDA_BaseStruct* DrawList; SCH_COMPONENT* Component; int nbitems; wxString text; wxArrayString SpiceCommandAtBeginFile, SpiceCommandAtEndFile; wxString msg; wxString netName; #define BUFYPOS_LEN 4 wxChar bufnum[BUFYPOS_LEN + 1]; DateAndTime( Line ); fprintf( f, "* %s (Spice format) creation date: %s\n\n", NETLIST_HEAD_STRING, Line ); /* Create text list starting by [.-]pspice , or [.-]gnucap (simulator * commands) and create text list starting by [+]pspice , or [+]gnucap * (simulator commands) */ bufnum[BUFYPOS_LEN] = 0; SCH_SHEET_LIST SheetList; for( sheet = SheetList.GetFirst(); sheet != NULL; sheet = SheetList.GetNext() ) { for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Next() ) { wxChar ident; if( DrawList->Type() != TYPE_SCH_TEXT ) continue; #define DRAWTEXT ( (SCH_TEXT*) DrawList ) text = DRAWTEXT->m_Text; if( text.IsEmpty() ) continue; ident = text.GetChar( 0 ); if( ident != '.' && ident != '-' && ident != '+' ) continue; text.Remove( 0, 1 ); // Remove the first char. text.Remove( 6 ); // text contains 6 char. text.MakeLower(); if( ( text == wxT( "pspice" ) ) || ( text == wxT( "gnucap" ) ) ) { /* Put the Y position as an ascii string, for sort by vertical * position, using usual sort string by alphabetic value */ int ypos = DRAWTEXT->m_Pos.y; for( int ii = 0; ii < BUFYPOS_LEN; ii++ ) { bufnum[BUFYPOS_LEN - 1 - ii] = (ypos & 63) + ' '; ypos >>= 6; } text = DRAWTEXT->m_Text.AfterFirst( ' ' ); // First BUFYPOS_LEN char are the Y position. msg.Printf( wxT( "%s %s" ), bufnum, text.GetData() ); if( ident == '+' ) SpiceCommandAtEndFile.Add( msg ); else SpiceCommandAtBeginFile.Add( msg ); } } } /* Print texts starting by [.-]pspice , ou [.-]gnucap (of course, without * the Y position string)*/ nbitems = SpiceCommandAtBeginFile.GetCount(); if( nbitems ) { SpiceCommandAtBeginFile.Sort(); for( int ii = 0; ii < nbitems; ii++ ) { SpiceCommandAtBeginFile[ii].Remove( 0, BUFYPOS_LEN ); SpiceCommandAtBeginFile[ii].Trim( TRUE ); SpiceCommandAtBeginFile[ii].Trim( FALSE ); fprintf( f, "%s\n", CONV_TO_UTF8( SpiceCommandAtBeginFile[ii] ) ); } } fprintf( f, "\n" ); // Create component list m_ReferencesAlreadyFound.Clear(); for( sheet = SheetList.GetFirst(); sheet != NULL; sheet = SheetList.GetNext() ) { for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Next() ) { DrawList = Component = findNextComponentAndCreatPinList( DrawList, sheet ); if( Component == NULL ) break; fprintf( f, "%s ", CONV_TO_UTF8( Component->GetRef( sheet ) ) ); // Write pin list: for( unsigned ii = 0; ii < m_SortedComponentPinList.size(); ii++ ) { NETLIST_OBJECT* Pin = m_SortedComponentPinList[ii]; if( !Pin ) continue; sprintPinNetName( &netName , wxT( "N-%.6d" ), Pin ); if( netName.IsEmpty() ) netName = wxT( "?" ); if( use_netnames ) fprintf( f, " %s", CONV_TO_UTF8( netName ) ); else // Use number for net names (with net number = 0 for // "GND" { // NetName = "0" is "GND" net for Spice if( netName == wxT( "0" ) || netName == wxT( "GND" ) ) fprintf( f, " 0" ); else fprintf( f, " %d", Pin->GetNet() ); } } fprintf( f, " %s\n", CONV_TO_UTF8( Component->GetField( VALUE )->m_Text ) ); } } m_SortedComponentPinList.clear(); /* Print texts starting by [+]pspice , ou [+]gnucap */ nbitems = SpiceCommandAtEndFile.GetCount(); if( nbitems ) { fprintf( f, "\n" ); SpiceCommandAtEndFile.Sort(); for( int ii = 0; ii < nbitems; ii++ ) { SpiceCommandAtEndFile[ii].Remove( 0, +BUFYPOS_LEN ); SpiceCommandAtEndFile[ii].Trim( TRUE ); SpiceCommandAtEndFile[ii].Trim( FALSE ); fprintf( f, "%s\n", CONV_TO_UTF8( SpiceCommandAtEndFile[ii] ) ); } } fprintf( f, "\n.end\n" ); } bool EXPORT_HELP::WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with_pcbnew ) { wxString field; wxString footprint; char dateBuf[256]; int ret = 0; // zero now, OR in the sign bit on error wxString netName; std::vector cmpList; DateAndTime( dateBuf ); if( with_pcbnew ) ret |= fprintf( f, "# %s created %s\n(\n", NETLIST_HEAD_STRING, dateBuf ); else ret |= fprintf( f, "( { %s created %s }\n", NETLIST_HEAD_STRING, dateBuf ); // Create netlist module section m_ReferencesAlreadyFound.Clear(); SCH_SHEET_LIST sheetList; for( SCH_SHEET_PATH* path = sheetList.GetFirst(); path; path = sheetList.GetNext() ) { for( EDA_BaseStruct* item = path->LastDrawList(); item; item = item->Next() ) { SCH_COMPONENT* comp = findNextComponentAndCreatPinList( item, path ); if( !comp ) break; item = comp; // Get the Component FootprintFilter and put the component in // cmpList if filter is present LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( comp->m_ChipName ); if( entry ) { if( entry->m_FootprintList.GetCount() != 0 ) // Put in list { cmpList.push_back( OBJ_CMP_TO_LIST() ); cmpList.back().m_RootCmp = comp; cmpList.back().SetRef( comp->GetRef( path ) ); } } if( !comp->GetField( FOOTPRINT )->IsVoid() ) { footprint = comp->GetField( FOOTPRINT )->m_Text; footprint.Replace( wxT( " " ), wxT( "_" ) ); } else footprint = wxT( "$noname" ); field = comp->GetRef( path ); ret |= fprintf( f, " ( %s %s", CONV_TO_UTF8( comp->GetPath( path ) ), CONV_TO_UTF8( footprint ) ); ret |= fprintf( f, " %s", CONV_TO_UTF8( field ) ); field = comp->GetField( VALUE )->m_Text; field.Replace( wxT( " " ), wxT( "_" ) ); ret |= fprintf( f, " %s", CONV_TO_UTF8( field ) ); if( with_pcbnew ) // Add the lib name for this component { field = comp->m_ChipName; field.Replace( wxT( " " ), wxT( "_" ) ); ret |= fprintf( f, " {Lib=%s}", CONV_TO_UTF8( field ) ); } ret |= fprintf( f, "\n" ); // Write pin list: for( unsigned ii = 0; ii < m_SortedComponentPinList.size(); ii++ ) { NETLIST_OBJECT* pin = m_SortedComponentPinList[ii]; if( !pin ) continue; sprintPinNetName( &netName, wxT( "N-%.6d" ), pin ); if( netName.IsEmpty() ) netName = wxT( "?" ); netName.Replace( wxT( " " ), wxT( "_" ) ); ret |= fprintf( f, " ( %4.4s %s )\n", (char*) &pin->m_PinNum, CONV_TO_UTF8( netName ) ); } ret |= fprintf( f, " )\n" ); } } ret |= fprintf( f, ")\n*\n" ); m_SortedComponentPinList.clear(); // Write the allowed footprint list for each component if( with_pcbnew && cmpList.size() ) { wxString ref; ret |= fprintf( f, "{ Allowed footprints by component:\n" ); for( unsigned ii = 0; ii < cmpList.size(); ii++ ) { SCH_COMPONENT* comp = cmpList[ii].m_RootCmp; LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( comp->m_ChipName ); ref = cmpList[ii].GetRef(); ref.Replace( wxT( " " ), wxT( "_" ) ); ret |= fprintf( f, "$component %s\n", CONV_TO_UTF8( ref ) ); // Write the footprint list for( unsigned jj = 0; jj < entry->m_FootprintList.GetCount(); jj++ ) { ret |= fprintf( f, " %s\n", CONV_TO_UTF8( entry->m_FootprintList[jj] ) ); } ret |= fprintf( f, "$endlist\n" ); } ret |= fprintf( f, "$endfootprintlist\n}\n" ); } if( with_pcbnew ) { ret |= fprintf( f, "{ Pin List by Nets\n" ); if( !WriteGENERICListOfNetsTxt( f, g_NetObjectslist ) ) ret = -1; ret |= fprintf( f, "}\n" ); ret |= fprintf( f, "#End\n" ); } return ret >= 0; } /* * Add a new pin description in the pin list m_SortedComponentPinList * a pin description is a pointer to the corresponding structure * created by BuildNetList() in the table g_NetObjectslist */ bool EXPORT_HELP::AddPinToComponentPinList( SCH_COMPONENT* aComponent, SCH_SHEET_PATH* aSheetPath, LIB_PIN* aPin ) { // Search the PIN description for Pin in g_NetObjectslist for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ ) { NETLIST_OBJECT* pin = g_NetObjectslist[ii]; if( pin->m_Type != NET_PIN ) continue; if( pin->m_Link != aComponent ) continue; if( pin->m_PinNum != aPin->m_PinNum ) continue; // most expensive test at the end. if( pin->m_SheetList != *aSheetPath ) continue; m_SortedComponentPinList.push_back( pin ); if( m_SortedComponentPinList.size() >= MAXPIN ) { // Log message for Internal error DisplayError( NULL, wxT( "AddPinToComponentPinList err: MAXPIN reached" ) ); } return true; // we're done, we appended. } return false; } /** Function EraseDuplicatePins * Function to remove duplicate Pins in the TabPin pin list * (This is a list of pins found in the whole schematic, for a given * component) * These duplicate pins were put in list because some pins (powers... ) * are found more than one time when we have a multiple parts per package * component * for instance, a 74ls00 has 4 parts, and therefore the VCC pin and GND pin * appears 4 times * in the list. * @param aPinList = a NETLIST_OBJECT_LIST that contains the list of pins for a * given component. * Note: this list *MUST* be sorted by pin number (.m_PinNum member value) */ void EXPORT_HELP::EraseDuplicatePins( NETLIST_OBJECT_LIST& aPinList ) { if( aPinList.size() == 0 ) // Trivial case: component with no pin return; for( unsigned ii = 0; ii < aPinList.size(); ii++ ) { if( aPinList[ii] == NULL ) /* already deleted */ continue; /* Search for duplicated pins * If found, remove duplicates. The priority is to keep connected pins * and remove unconnected * - So this allows (for instance when using multi op amps per package * - to connect only one op amp to power * Because the pin list is sorted by m_PinNum value, duplicated pins * are necessary successive in list */ int idxref = ii; for( unsigned jj = ii + 1; jj < aPinList.size(); jj++ ) { if( aPinList[jj] == NULL ) // Already removed continue; // other pin num end of duplicate list. if( aPinList[idxref]->m_PinNum != aPinList[jj]->m_PinNum ) break; if( aPinList[idxref]->m_FlagOfConnection == PAD_CONNECT ) aPinList[jj] = NULL; else /* the reference pin is not connected: remove this pin if the * other pin is connected */ { if( aPinList[jj]->m_FlagOfConnection == PAD_CONNECT ) { aPinList[idxref] = NULL; idxref = jj; } else // the 2 pins are not connected: remove the tested pin, // and continue ... aPinList[jj] = NULL; } } } } /** * Function FindAllInstancesOfComponent * is used for "multiple parts per package" components. * * Search all instances of Component_in, * Calls AddPinToComponentPinList() to and pins founds to the current * component pin list */ void EXPORT_HELP::FindAllInstancesOfComponent( SCH_COMPONENT* aComponent, LIB_COMPONENT* aEntry, SCH_SHEET_PATH* aSheetPath ) { wxString ref = aComponent->GetRef( aSheetPath ); wxString ref2; SCH_SHEET_LIST sheetList; for( SCH_SHEET_PATH* sheet = sheetList.GetFirst(); sheet; sheet = sheetList.GetNext() ) { for( EDA_BaseStruct* schItem = sheet->LastDrawList(); schItem; schItem = schItem->Next() ) { if( schItem->Type() != TYPE_SCH_COMPONENT ) continue; SCH_COMPONENT* comp2 = (SCH_COMPONENT*) schItem; ref2 = comp2->GetRef( sheet ); if( ref2.CmpNoCase( ref ) != 0 ) continue; if( aEntry == NULL ) continue; for( LIB_PIN* pin = aEntry->GetNextPin(); pin; pin = aEntry->GetNextPin( pin ) ) { wxASSERT( pin->Type() == COMPONENT_PIN_DRAW_TYPE ); if( pin->m_Unit && pin->m_Unit != comp2->GetUnitSelection( aSheetPath ) ) continue; if( pin->m_Convert && pin->m_Convert != comp2->m_Convert ) continue; // A suitable pin is found: add it to the current list AddPinToComponentPinList( comp2, sheet, pin ); } } } } /* Written in the file / net list (ranked by Netcode), and elements that are * connected */ bool EXPORT_HELP::WriteGENERICListOfNetsTxt( FILE* f, NETLIST_OBJECT_LIST& aObjectsList ) { int ret = 0; int netCode; int lastNetCode = -1; int sameNetcodeCount = 0; wxString netName; wxString ref; wxString netcodeName; char firstItemInNet[256]; for( unsigned ii = 0; ii < aObjectsList.size(); ii++ ) { SCH_COMPONENT* comp; // New net found, write net id; if( ( netCode = aObjectsList[ii]->GetNet() ) != lastNetCode ) { sameNetcodeCount = 0; // Items count for this net netName.Empty(); // Find a label (if exists) for this net. NETLIST_OBJECT* netref; netref = aObjectsList[ii]->m_NetNameCandidate; if( netref ) netName = netref->m_Label; netcodeName.Printf( wxT( "Net %d " ), netCode ); netcodeName += wxT( "\"" ); if( !netName.IsEmpty() ) { if( ( netref->m_Type != NET_PINLABEL ) && ( netref->m_Type != NET_GLOBLABEL ) ) { // usual net name, prefix it by the sheet path netcodeName += netref->m_SheetList.PathHumanReadable(); } netcodeName += netName; } netcodeName += wxT( "\"" ); // Add the netname without prefix, in cases we need only the // "short" netname netcodeName += wxT( " \"" ) + netName + wxT( "\"" ); lastNetCode = netCode; } if( aObjectsList[ii]->m_Type != NET_PIN ) continue; comp = (SCH_COMPONENT*) aObjectsList[ii]->m_Link; // Get the reference for the net name and the main parent component ref = comp->GetRef( &aObjectsList[ii]->m_SheetList ); if( ref[0] == wxChar( '#' ) ) continue; // Pseudo component (Like Power symbol) // Print the pin list for this net, use special handling if // 2 or more items are connected: // if first item for this net found, defer printing this connection // until a second item will is found if( ++sameNetcodeCount == 1 ) { snprintf( firstItemInNet, sizeof(firstItemInNet), " %s %.4s\n", CONV_TO_UTF8( ref ), (const char*) &aObjectsList[ii]->m_PinNum ); } // Second item for this net found, print the Net name, and the // first item if( sameNetcodeCount == 2 ) { ret |= fprintf( f, "%s\n", CONV_TO_UTF8( netcodeName ) ); ret |= fputs( firstItemInNet, f ); } if( sameNetcodeCount >= 2 ) ret |= fprintf( f, " %s %.4s\n", CONV_TO_UTF8( ref ), (const char*) &aObjectsList[ii]->m_PinNum ); } return ret >= 0; } /** * Function WriteGENERICListOfNets * saves a netlist in xml format. */ bool EXPORT_HELP::WriteGENERICListOfNets( wxXmlNode* aNode, NETLIST_OBJECT_LIST& aObjectsList ) { wxString netCodeTxt; wxString netName; wxString ref; wxString sNet = wxT( "net" ); wxString sName = wxT( "name" ); wxString sCode = wxT( "code" ); wxString sRef = wxT( "ref" ); wxString sPin = wxT( "pin" ); wxString sNode = wxT( "node" ); wxString sFmtd = wxT( "%d" ); wxXmlNode* xnet = 0; int netCode; int lastNetCode = -1; int sameNetcodeCount = 0; /* output: */ for( unsigned ii = 0; ii < aObjectsList.size(); ii++ ) { SCH_COMPONENT* comp; // New net found, write net id; if( ( netCode = aObjectsList[ii]->GetNet() ) != lastNetCode ) { sameNetcodeCount = 0; // item count for this net netName.Empty(); // Find a label for this net, if it exists. NETLIST_OBJECT* netref = aObjectsList[ii]->m_NetNameCandidate; if( netref ) { if( netref->m_Type != NET_PINLABEL && netref->m_Type != NET_GLOBLABEL ) { // usual net name, prefix it by the sheet path netName = netref->m_SheetList.PathHumanReadable(); } netName += netref->m_Label; } lastNetCode = netCode; } if( aObjectsList[ii]->m_Type != NET_PIN ) continue; comp = (SCH_COMPONENT*) aObjectsList[ii]->m_Link; // Get the reference for the net name and the main parent component ref = comp->GetRef( &aObjectsList[ii]->m_SheetList ); if( ref[0] == wxChar( '#' ) ) continue; if( ++sameNetcodeCount == 1 ) { aNode->AddChild( xnet = Node( sNet ) ); netCodeTxt.Printf( sFmtd, netCode ); xnet->AddProperty( sCode, netCodeTxt ); xnet->AddProperty( sName, netName ); } wxXmlNode* xnode; xnet->AddChild( xnode = Node( sNode ) ); xnode->AddProperty( sRef, ref ); xnode->AddProperty( sPin, LIB_PIN::ReturnPinStringNum( aObjectsList[ii]->m_PinNum ) ); } return true; } /* Generate CADSTAR net list. */ wxString StartLine( wxT( "." ) ); /* Routine generation of the netlist file (CADSTAR Format) * Header: * HEA .. * TIM .. 2004 07 29 16 22 17 * APA .. "Cadstar RINF Output - Version 6.0.2.3" * INCH UNI .. 1000.0 in * FULL TYP .. * * List of components: * .. ADD_COM X1 "CNT D41612 (48pts CONTOUR TM)" * .. ADD_COM U2 "74HCT245D" "74HCT245D" * * Connections: * .. ADD_TER RR2 * 6 "$ 42" * .. B U1 100 * 6 CA * * ADD_TER .. U2 * 6 "$ 59" * .. B * U7 39 * U6 17 * U1 * 122 * * .. ADD_TER P2 * 1 "$ 9" * .. B * T3 1 *U1 * 14 */ void EXPORT_HELP::WriteNetListCADSTAR( WinEDA_SchematicFrame* frame, FILE* f ) { wxString StartCmpDesc = StartLine + wxT( "ADD_COM" ); wxString msg; wxString footprint; char Line[1024]; SCH_SHEET_PATH* sheet; EDA_BaseStruct* DrawList; SCH_COMPONENT* Component; wxString Title = wxGetApp().GetAppName() + wxT( " " ) + GetBuildVersion(); fprintf( f, "%sHEA\n", CONV_TO_UTF8( StartLine ) ); DateAndTime( Line ); fprintf( f, "%sTIM %s\n", CONV_TO_UTF8( StartLine ), Line ); fprintf( f, "%sAPP ", CONV_TO_UTF8( StartLine ) ); fprintf( f, "\"%s\"\n", CONV_TO_UTF8( Title ) ); fprintf( f, "\n" ); // Create netlist module section m_ReferencesAlreadyFound.Clear(); SCH_SHEET_LIST SheetList; for( sheet = SheetList.GetFirst(); sheet != NULL; sheet = SheetList.GetNext() ) { for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Next() ) { DrawList = Component = findNextComponentAndCreatPinList( DrawList, sheet ); if( Component == NULL ) break; /* doing nothing with footprint if( !Component->GetField( FOOTPRINT )->IsVoid() ) { footprint = Component->GetField( FOOTPRINT )->m_Text; footprint.Replace( wxT( " " ), wxT( "_" ) ); } else footprint = wxT( "$noname" ); */ msg = Component->GetRef( sheet ); fprintf( f, "%s ", CONV_TO_UTF8( StartCmpDesc ) ); fprintf( f, "%s", CONV_TO_UTF8( msg ) ); msg = Component->GetField( VALUE )->m_Text; msg.Replace( wxT( " " ), wxT( "_" ) ); fprintf( f, " \"%s\"", CONV_TO_UTF8( msg ) ); fprintf( f, "\n" ); } } fprintf( f, "\n" ); m_SortedComponentPinList.clear(); WriteListOfNetsCADSTAR( f, g_NetObjectslist ); fprintf( f, "\n%sEND\n", CONV_TO_UTF8( StartLine ) ); } /* * Written in the file / net list (ranked by Netcode), and * Pins connected to it * Format: *. ADD_TER RR2 6 "$ 42" *. B U1 100 * 6 CA */ void EXPORT_HELP::WriteListOfNetsCADSTAR( FILE* f, NETLIST_OBJECT_LIST& aObjectsList ) { wxString InitNetDesc = StartLine + wxT( "ADD_TER" ); wxString StartNetDesc = StartLine + wxT( "TER" ); wxString netcodeName, InitNetDescLine; unsigned ii; int print_ter = 0; int NetCode, lastNetCode = -1; SCH_COMPONENT* Cmp; wxString NetName; for( ii = 0; ii < aObjectsList.size(); ii++ ) aObjectsList[ii]->m_Flag = 0; for( ii = 0; ii < g_NetObjectslist.size(); ii++ ) { // Get the NetName of the current net : if( ( NetCode = aObjectsList[ii]->GetNet() ) != lastNetCode ) { NetName.Empty(); NETLIST_OBJECT* netref; netref = aObjectsList[ii]->m_NetNameCandidate; if( netref ) NetName = netref->m_Label; netcodeName = wxT( "\"" ); if( !NetName.IsEmpty() ) { if( ( netref->m_Type != NET_PINLABEL ) && ( netref->m_Type != NET_GLOBLABEL ) ) { // usual net name, prefix it by the sheet path netcodeName += netref->m_SheetList.PathHumanReadable(); } netcodeName += NetName; } else // this net has no name: create a default name $ netcodeName << wxT( "$" ) << NetCode; netcodeName += wxT( "\"" ); lastNetCode = NetCode; print_ter = 0; } if( aObjectsList[ii]->m_Type != NET_PIN ) continue; if( aObjectsList[ii]->m_Flag != 0 ) continue; Cmp = (SCH_COMPONENT*) aObjectsList[ii]->m_Link; wxString refstr = Cmp->GetRef( &(aObjectsList[ii]->m_SheetList) ); if( refstr[0] == '#' ) continue; // Power supply symbols. switch( print_ter ) { case 0: { char buf[5]; wxString str_pinnum; strncpy( buf, (char*) &aObjectsList[ii]->m_PinNum, 4 ); buf[4] = 0; str_pinnum = CONV_FROM_UTF8( buf ); InitNetDescLine.Printf( wxT( "\n%s %s %.4s %s" ), GetChars( InitNetDesc ), GetChars( refstr ), GetChars( str_pinnum ), GetChars( netcodeName ) ); } print_ter++; break; case 1: fprintf( f, "%s\n", CONV_TO_UTF8( InitNetDescLine ) ); fprintf( f, "%s %s %.4s\n", CONV_TO_UTF8( StartNetDesc ), CONV_TO_UTF8( refstr ), (char*) &aObjectsList[ii]->m_PinNum ); print_ter++; break; default: fprintf( f, " %s %.4s\n", CONV_TO_UTF8( refstr ), (char*) &aObjectsList[ii]->m_PinNum ); break; } aObjectsList[ii]->m_Flag = 1; // Search for redundant pins to avoid generation of the same connection // more than once. for( unsigned jj = ii + 1; jj < aObjectsList.size(); jj++ ) { if( aObjectsList[jj]->GetNet() != NetCode ) break; if( aObjectsList[jj]->m_Type != NET_PIN ) continue; SCH_COMPONENT* tstcmp = (SCH_COMPONENT*) aObjectsList[jj]->m_Link; wxString p = Cmp->GetPath( &( aObjectsList[ii]->m_SheetList ) ); wxString tstp = tstcmp->GetPath( &( aObjectsList[jj]->m_SheetList ) ); if( p.Cmp( tstp ) != 0 ) continue; if( aObjectsList[jj]->m_PinNum == aObjectsList[ii]->m_PinNum ) aObjectsList[jj]->m_Flag = 1; } } }