528 lines
15 KiB
C++
528 lines
15 KiB
C++
/**
|
|
* @file pcbnew/netlist_reader_kicad.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
|
|
*/
|
|
|
|
#include <wx/wx.h>
|
|
#include <netlist_lexer.h> // netlist_lexer is common to Eeschema and Pcbnew
|
|
#include <netlist_reader.h>
|
|
|
|
using namespace NL_T;
|
|
|
|
/**
|
|
* Class PCB_PLOT_PARAMS_PARSER
|
|
* is the parser class for PCB_PLOT_PARAMS.
|
|
*/
|
|
class NETLIST_READER_KICAD_PARSER : public NETLIST_LEXER
|
|
{
|
|
private:
|
|
T token;
|
|
NETLIST_READER * netlist_reader;
|
|
|
|
public:
|
|
NETLIST_READER_KICAD_PARSER( FILE_LINE_READER* aReader, NETLIST_READER *aNetlistReader );
|
|
|
|
/**
|
|
* Function Parse
|
|
* parse the full netlist
|
|
*/
|
|
void Parse( BOARD * aBrd ) throw( IO_ERROR, PARSE_ERROR );
|
|
|
|
/**
|
|
* Function ParseComp
|
|
* parse the comp description like
|
|
* (comp (ref P1)
|
|
* (value DB25FEMELLE)
|
|
* (footprint DB25FC)
|
|
* (libsource (lib conn) (part DB25))
|
|
* (sheetpath (names /) (tstamps /))
|
|
* (tstamp 3256759C))
|
|
*/
|
|
COMPONENT_INFO* ParseComp() throw( IO_ERROR, PARSE_ERROR );
|
|
|
|
|
|
/**
|
|
* Function ParseKicadLibpartList
|
|
* Read the section "libparts" like:
|
|
* (libparts
|
|
* (libpart (lib device) (part C)
|
|
* (description "Condensateur non polarise")
|
|
* (footprints
|
|
* (fp SM*)
|
|
* (fp C?)
|
|
* (fp C1-1))
|
|
* (fields
|
|
* (field (name Reference) C)
|
|
* (field (name Value) C))
|
|
* (pins
|
|
* (pin (num 1) (name ~) (type passive))
|
|
* (pin (num 2) (name ~) (type passive))))
|
|
*
|
|
* And add the strings giving the footprint filter (subsection footprints)
|
|
* of the corresponding module info
|
|
* <p>This section is used by CvPcb, and is not useful in Pcbnew,
|
|
* therefore it it not always read </p>
|
|
*/
|
|
void ParseKicadLibpartList() throw( IO_ERROR, PARSE_ERROR );
|
|
|
|
/**
|
|
* Function ParseNet
|
|
* Parses a section like
|
|
* (net (code 20) (name /PC-A0)
|
|
* (node (ref BUS1) (pin 62))
|
|
* (node (ref U3) (pin 3))
|
|
* (node (ref U9) (pin M6)))
|
|
*
|
|
* and set the corresponfings pads netnames
|
|
*/
|
|
void ParseNet( BOARD * aBrd ) throw( IO_ERROR, PARSE_ERROR );
|
|
|
|
/**
|
|
* Function SkipCurrent
|
|
* Skip the current token level, i.e
|
|
* search for the RIGHT parenthesis which closes the current description
|
|
*/
|
|
void SkipCurrent() throw( IO_ERROR, PARSE_ERROR );
|
|
|
|
// Useful for debug only:
|
|
const char* getTokenName( T aTok )
|
|
{
|
|
return NETLIST_LEXER::TokenName( aTok );
|
|
}
|
|
};
|
|
|
|
|
|
bool NETLIST_READER::ReadKicadNetList( FILE* aFile )
|
|
{
|
|
BOARD * brd = m_pcbframe ? m_pcbframe->GetBoard() : NULL;
|
|
|
|
// netlineReader dtor will close aFile
|
|
FILE_LINE_READER netlineReader( aFile, m_netlistFullName );
|
|
NETLIST_READER_KICAD_PARSER netlist_parser( &netlineReader, this );
|
|
|
|
try
|
|
{
|
|
netlist_parser.Parse( brd );
|
|
}
|
|
catch( IO_ERROR& ioe )
|
|
{
|
|
ioe.errorText += '\n';
|
|
ioe.errorText += _("Netlist error.");
|
|
|
|
wxMessageBox( ioe.errorText );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// NETLIST_READER_KICAD_PARSER
|
|
NETLIST_READER_KICAD_PARSER::NETLIST_READER_KICAD_PARSER( FILE_LINE_READER* aReader,
|
|
NETLIST_READER *aNetlistReader ) :
|
|
NETLIST_LEXER( aReader )
|
|
{
|
|
netlist_reader = aNetlistReader;
|
|
}
|
|
|
|
/**
|
|
* Function SkipCurrent
|
|
* Skip the current token level, i.e
|
|
* search for the RIGHT parenthesis which closes the current description
|
|
*/
|
|
void NETLIST_READER_KICAD_PARSER::SkipCurrent() throw( IO_ERROR, PARSE_ERROR )
|
|
{
|
|
int curr_level = 0;
|
|
while( ( token = NextTok() ) != T_EOF )
|
|
{
|
|
if( token == T_LEFT )
|
|
curr_level--;
|
|
if( token == T_RIGHT )
|
|
{
|
|
curr_level++;
|
|
if( curr_level > 0 )
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void NETLIST_READER_KICAD_PARSER::Parse( BOARD * aBrd )
|
|
throw( IO_ERROR, PARSE_ERROR )
|
|
{
|
|
int plevel = 0; // the count of ')' to read and end of file,
|
|
// after parsing all sections
|
|
|
|
while( ( token = NextTok() ) != T_EOF )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
|
|
switch( token )
|
|
{
|
|
case T_export: // The netlist starts here.
|
|
// nothing to do here,
|
|
// just increment the count of ')' to read and end of file
|
|
plevel++;
|
|
break;
|
|
|
|
case T_version: // The netlist starts here.
|
|
// version id not yet used: read it but does not use it
|
|
NextTok();
|
|
NeedRIGHT();
|
|
break;
|
|
|
|
case T_components: // The section comp starts here.
|
|
while( ( token = NextTok() ) != T_RIGHT )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
if( token == T_comp ) // A comp section if found. Read it
|
|
{
|
|
COMPONENT_INFO* cmp_info = ParseComp();
|
|
netlist_reader->AddModuleInfo( cmp_info );
|
|
}
|
|
}
|
|
if( netlist_reader->BuildModuleListOnlyOpt() )
|
|
return; // at this point, the module list is read and built.
|
|
// Load new footprints
|
|
netlist_reader->InitializeModules();
|
|
netlist_reader->TestFootprintsMatchingAndExchange();
|
|
break;
|
|
|
|
case T_nets: // The section nets starts here.
|
|
while( ( token = NextTok() ) != T_RIGHT )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
if( token == T_net )
|
|
{
|
|
// A net section if found. Read it
|
|
ParseNet( aBrd );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case T_libparts: // The section libparts starts here.
|
|
if( netlist_reader->ReadLibpartSectionOpt() )
|
|
{
|
|
while( ( token = NextTok() ) != T_RIGHT )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
if( token == T_libpart )
|
|
{
|
|
// A libpart section if found. Read it
|
|
ParseKicadLibpartList();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
SkipCurrent();
|
|
break;
|
|
|
|
case T_libraries: // The section libraries starts here.
|
|
// List of libraries in use.
|
|
// Not used here, just skip it
|
|
SkipCurrent();
|
|
break;
|
|
|
|
case T_design: // The section design starts here.
|
|
// Not used (mainly thet are comments), just skip it
|
|
SkipCurrent();
|
|
break;
|
|
|
|
case T_RIGHT: // The closing parenthesis of the file.
|
|
// Not used (mainly thet are comments), just skip it
|
|
plevel--;
|
|
break;
|
|
|
|
default:
|
|
SkipCurrent();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( plevel != 0 )
|
|
{
|
|
wxLogDebug(wxT("NETLIST_READER_KICAD_PARSER::Parse(): bad parenthesis count (count = %d"),
|
|
plevel );
|
|
}
|
|
}
|
|
|
|
void NETLIST_READER_KICAD_PARSER::ParseNet( BOARD * aBrd )
|
|
throw( IO_ERROR, PARSE_ERROR )
|
|
{
|
|
/* Parses a section like
|
|
* (net (code 20) (name /PC-A0)
|
|
* (node (ref BUS1) (pin 62))
|
|
* (node (ref U3) (pin 3))
|
|
* (node (ref U9) (pin M6)))
|
|
*/
|
|
|
|
wxString code;
|
|
wxString name;
|
|
wxString cmpref;
|
|
wxString pin;
|
|
int nodecount = 0;
|
|
std::vector<D_PAD*> padList;
|
|
|
|
// The token net was read, so the next data is (code <number>)
|
|
while( (token = NextTok()) != T_RIGHT )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
switch( token )
|
|
{
|
|
case T_code:
|
|
NeedSYMBOLorNUMBER();
|
|
code = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
break;
|
|
|
|
case T_name:
|
|
NeedSYMBOLorNUMBER();
|
|
name = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
if( name.IsEmpty() ) // Give a dummy net name like N-000109
|
|
name = wxT("N-00000") + code;
|
|
break;
|
|
|
|
case T_node:
|
|
while( (token = NextTok()) != T_RIGHT )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
switch( token )
|
|
{
|
|
case T_ref:
|
|
NeedSYMBOLorNUMBER();
|
|
cmpref = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
break;
|
|
|
|
case T_pin:
|
|
NeedSYMBOLorNUMBER();
|
|
pin = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
break;
|
|
|
|
default:
|
|
SkipCurrent();
|
|
break;
|
|
}
|
|
}
|
|
netlist_reader->SetPadsNetName( cmpref, pin, name, padList );
|
|
nodecount++;
|
|
break;
|
|
|
|
default:
|
|
SkipCurrent();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// When there is only one item in net, clear pad netname
|
|
// Remember one can have more than one pad in list, because a footprint
|
|
// can have many pads with the same pad name
|
|
if( nodecount < 2 )
|
|
for( unsigned ii = 0; ii < padList.size(); ii++ )
|
|
padList[ii]->SetNetname( wxEmptyString );
|
|
}
|
|
|
|
|
|
COMPONENT_INFO* NETLIST_READER_KICAD_PARSER::ParseComp()
|
|
throw( IO_ERROR, PARSE_ERROR )
|
|
{
|
|
/* Parses a section like
|
|
* (comp (ref P1)
|
|
* (value DB25FEMELLE)
|
|
* (footprint DB25FC)
|
|
* (libsource (lib conn) (part DB25))
|
|
* (sheetpath (names /) (tstamps /))
|
|
* (tstamp 3256759C))
|
|
*
|
|
* other fields (unused) are skipped
|
|
* A component need a reference, value, foorprint name and a full time stamp
|
|
* The full time stamp is the sheetpath time stamp + the component time stamp
|
|
*/
|
|
wxString ref;
|
|
wxString value;
|
|
wxString footprint;
|
|
wxString libpart;
|
|
wxString pathtimestamp, timestamp;
|
|
// The token comp was read, so the next data is (ref P1)
|
|
|
|
while( (token = NextTok()) != T_RIGHT )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
switch( token )
|
|
{
|
|
case T_ref:
|
|
NeedSYMBOLorNUMBER();
|
|
ref = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
break;
|
|
|
|
case T_value:
|
|
NeedSYMBOLorNUMBER();
|
|
value = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
break;
|
|
|
|
case T_footprint:
|
|
NeedSYMBOLorNUMBER();
|
|
footprint = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
break;
|
|
|
|
case T_libsource:
|
|
// Read libsource
|
|
while( (token = NextTok()) != T_RIGHT )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
if( token == T_part )
|
|
{
|
|
NeedSYMBOLorNUMBER();
|
|
libpart = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
}
|
|
else
|
|
SkipCurrent();
|
|
}
|
|
break;
|
|
|
|
case T_sheetpath:
|
|
while( ( token = NextTok() ) != T_tstamps );
|
|
NeedSYMBOLorNUMBER();
|
|
pathtimestamp = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
NeedRIGHT();
|
|
break;
|
|
|
|
case T_tstamp:
|
|
NeedSYMBOLorNUMBER();
|
|
timestamp = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
break;
|
|
|
|
default:
|
|
// Skip not used data (i.e all other tokens)
|
|
SkipCurrent();
|
|
break;
|
|
}
|
|
}
|
|
pathtimestamp += timestamp;
|
|
COMPONENT_INFO* cmp_info = new COMPONENT_INFO( footprint, ref, value, pathtimestamp );
|
|
cmp_info->m_Libpart = libpart;
|
|
|
|
return cmp_info;
|
|
}
|
|
|
|
/* Read the section "libparts" like:
|
|
* (libparts
|
|
* (libpart (lib device) (part C)
|
|
* (description "Condensateur non polarise")
|
|
* (footprints
|
|
* (fp SM*)
|
|
* (fp C?)
|
|
* (fp C1-1))
|
|
* (fields
|
|
* (field (name Reference) C)
|
|
* (field (name Value) C))
|
|
* (pins
|
|
* (pin (num 1) (name ~) (type passive))
|
|
* (pin (num 2) (name ~) (type passive))))
|
|
*
|
|
* And add the strings giving the footprint filter (subsection footprints)
|
|
* of the corresponding module info
|
|
*/
|
|
void NETLIST_READER_KICAD_PARSER::ParseKicadLibpartList() throw( IO_ERROR, PARSE_ERROR )
|
|
{
|
|
/* Parses a section like
|
|
* (libpart (lib device) (part C)
|
|
* (description "Condensateur non polarise")
|
|
* (footprints
|
|
* (fp SM*)
|
|
* (fp C?)
|
|
* (fp C1-1))
|
|
* (fields
|
|
* (field (name Reference) C)
|
|
* (field (name Value) C))
|
|
* (pins
|
|
* (pin (num 1) (name ~) (type passive))
|
|
* (pin (num 2) (name ~) (type passive))))
|
|
*
|
|
* Currently footprints section/fp are read and data stored
|
|
* other fields (unused) are skipped
|
|
*/
|
|
wxString device;
|
|
wxString filter;
|
|
LIPBART_INFO* libpart_info = NULL;
|
|
|
|
// The last token read was libpart, so read the next token
|
|
while( (token = NextTok()) != T_RIGHT )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
switch( token )
|
|
{
|
|
case T_part:
|
|
NeedSYMBOLorNUMBER();
|
|
device = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
libpart_info = new LIPBART_INFO( device );
|
|
netlist_reader->AddLibpartInfo( libpart_info );
|
|
break;
|
|
|
|
case T_footprints:
|
|
// Ensure "(part C)" was already read
|
|
if( libpart_info == NULL )
|
|
Expecting( T_part );
|
|
// Read all fp elements (footprint filter item)
|
|
while( (token = NextTok()) != T_RIGHT )
|
|
{
|
|
if( token == T_LEFT )
|
|
token = NextTok();
|
|
if( token != T_fp )
|
|
Expecting( T_fp );
|
|
NeedSYMBOLorNUMBER();
|
|
filter = FROM_UTF8( CurText() );
|
|
NeedRIGHT();
|
|
libpart_info->m_FootprintFilter.Add( filter );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Skip not used data (i.e all other tokens)
|
|
SkipCurrent();
|
|
break;
|
|
}
|
|
}
|
|
}
|