ADDED: Cadence Allegro PCB Designer netlist exporter

This commit is contained in:
Youbao 2023-06-28 22:47:30 +00:00 committed by Mark Roszko
parent 6d3f512e15
commit 4cb993d872
12 changed files with 1102 additions and 26 deletions

View File

@ -145,6 +145,7 @@ const std::string LtspiceSchematicExtension( "asc" );
const std::string CadstarNetlistFileExtension( "frp" );
const std::string OrCadPcb2NetlistFileExtension( "net" );
const std::string NetlistFileExtension( "net" );
const std::string AllegroNetlistFileExtension( "txt" );
const std::string FootprintAssignmentFileExtension( "cmp" );
const std::string GerberFileExtension( "gbr" );
const std::string GerberJobFileExtension( "gbrjob" );
@ -330,6 +331,13 @@ wxString NetlistFileWildcard()
}
wxString AllegroNetlistFileWildcard()
{
return _( "Allegro netlist files" )
+ AddFileExtListToFilter( { AllegroNetlistFileExtension } );
}
wxString LegacyPcbFileWildcard()
{
return _( "KiCad printed circuit board files" ) + AddFileExtListToFilter( { "brd" } );

View File

@ -355,6 +355,7 @@ set( EESCHEMA_SRCS
toolbars_sch_editor.cpp
toolbars_symbol_viewer.cpp
netlist_exporters/netlist_exporter_allegro.cpp
netlist_exporters/netlist_exporter_base.cpp
netlist_exporters/netlist_exporter_cadstar.cpp
netlist_exporters/netlist_exporter_kicad.cpp

View File

@ -59,14 +59,16 @@
/* panel (notebook page) identifiers */
enum panel_netlist_index {
PANELPCBNEW = 0, /* Handle Netlist format Pcbnew */
PANELORCADPCB2, /* Handle Netlist format OracdPcb2 */
PANELCADSTAR, /* Handle Netlist format CadStar */
PANELSPICE, /* Handle Netlist format Spice */
PANELSPICEMODEL, /* Handle Netlist format Spice Model (subcircuit) */
PANELCUSTOMBASE /* First auxiliary panel (custom netlists).
* others use PANELCUSTOMBASE+1, PANELCUSTOMBASE+2.. */
enum panel_netlist_index
{
PANELPCBNEW = 0, /* Handle Netlist format Pcbnew */
PANELORCADPCB2, /* Handle Netlist format OracdPcb2 */
PANELALLEGRO, /* Handle Netlist format Allegro */
PANELCADSTAR, /* Handle Netlist format CadStar */
PANELSPICE, /* Handle Netlist format Spice */
PANELSPICEMODEL, /* Handle Netlist format Spice Model (subcircuit) */
PANELCUSTOMBASE /* First auxiliary panel (custom netlists).
* others use PANELCUSTOMBASE+1, PANELCUSTOMBASE+2.. */
};
@ -245,14 +247,17 @@ DIALOG_EXPORT_NETLIST::DIALOG_EXPORT_NETLIST( SCH_EDIT_FRAME* parent ) :
page = nullptr;
// Add notebook pages:
m_PanelNetType[PANELPCBNEW] = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "KiCad" ),
NET_TYPE_PCBNEW, false );
m_PanelNetType[PANELPCBNEW] =
new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "KiCad" ), NET_TYPE_PCBNEW, false );
m_PanelNetType[PANELORCADPCB2] = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "OrcadPCB2" ),
NET_TYPE_ORCADPCB2, false );
m_PanelNetType[PANELORCADPCB2] =
new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "OrcadPCB2" ), NET_TYPE_ORCADPCB2, false );
m_PanelNetType[PANELCADSTAR] = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "CadStar" ),
NET_TYPE_CADSTAR, false );
m_PanelNetType[PANELALLEGRO] =
new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "Allegro" ), NET_TYPE_ALLEGRO, false );
m_PanelNetType[PANELCADSTAR] =
new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "CadStar" ), NET_TYPE_CADSTAR, false );
InstallPageSpice();
InstallPageSpiceModel();
@ -496,6 +501,9 @@ bool DIALOG_EXPORT_NETLIST::TransferDataFromWindow()
case NET_TYPE_ORCADPCB2:
break;
case NET_TYPE_ALLEGRO:
break;
default: // custom, NET_TYPE_CUSTOM1 and greater
{
title.Printf( _( "%s Export" ), currPage->m_TitleStringCtrl->GetValue() );
@ -629,6 +637,11 @@ bool DIALOG_EXPORT_NETLIST::FilenamePrms( NETLIST_TYPE_ID aType, wxString * aExt
fileWildcard = NetlistFileWildcard();
break;
case NET_TYPE_ALLEGRO:
fileExt = AllegroNetlistFileExtension;
fileWildcard = AllegroNetlistFileWildcard();
break;
default: // custom, NET_TYPE_CUSTOM1 and greater
fileWildcard = AllFilesWildcard();
ret = false;

View File

@ -1242,24 +1242,42 @@ LIB_FIELD* LIB_SYMBOL::GetFieldById( int aId ) const
}
LIB_FIELD* LIB_SYMBOL::FindField( const wxString& aFieldName )
LIB_FIELD* LIB_SYMBOL::FindField( const wxString& aFieldName, bool aCaseInsensitive )
{
for( LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
{
if( static_cast<LIB_FIELD*>( &item )->GetCanonicalName() == aFieldName )
return static_cast<LIB_FIELD*>( &item );
if( aCaseInsensitive )
{
if( static_cast<LIB_FIELD*>( &item )->GetCanonicalName().Upper() == aFieldName.Upper() )
return static_cast<LIB_FIELD*>( &item );
}
else
{
if( static_cast<LIB_FIELD*>( &item )->GetCanonicalName() == aFieldName )
return static_cast<LIB_FIELD*>( &item );
}
}
return nullptr;
}
const LIB_FIELD* LIB_SYMBOL::FindField( const wxString& aFieldName ) const
const LIB_FIELD* LIB_SYMBOL::FindField( const wxString& aFieldName,
const bool aCaseInsensitive ) const
{
for( const LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
{
if( static_cast<const LIB_FIELD*>( &item )->GetCanonicalName() == aFieldName )
return static_cast<const LIB_FIELD*>( &item );
if( aCaseInsensitive )
{
if( static_cast<const LIB_FIELD*>( &item )->GetCanonicalName().Upper()
== aFieldName.Upper() )
return static_cast<const LIB_FIELD*>( &item );
}
else
{
if( static_cast<const LIB_FIELD*>( &item )->GetCanonicalName() == aFieldName )
return static_cast<const LIB_FIELD*>( &item );
}
}
return nullptr;

View File

@ -292,10 +292,15 @@ public:
/**
* Find a field within this symbol matching \a aFieldName and returns it
* or NULL if not found.
* @param aFieldName is the name of the field to find.
* @param aCaseInsensitive ignore the filed name case if true.
*
* @return the field if found or NULL if the field was not found.
*/
LIB_FIELD* FindField( const wxString& aFieldName );
LIB_FIELD* FindField( const wxString& aFieldName, bool aCaseInsensitive = false );
const LIB_FIELD* FindField( const wxString& aFieldName ) const;
const LIB_FIELD* FindField( const wxString& aFieldName,
const bool aCaseInsensitive = false ) const;
/**
* Return pointer to the requested field.

View File

@ -40,6 +40,7 @@ enum NETLIST_TYPE_ID {
NET_TYPE_CADSTAR,
NET_TYPE_SPICE,
NET_TYPE_SPICE_MODEL,
NET_TYPE_ALLEGRO,
NET_TYPE_CUSTOM1, /* NET_TYPE_CUSTOM1
* is the first id for user netlist format
* NET_TYPE_CUSTOM1+CUSTOMPANEL_COUNTMAX-1

View File

@ -0,0 +1,797 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2018 jp.charras at wanadoo.fr
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.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 <confirm.h>
#include <refdes_utils.h>
#include <sch_edit_frame.h>
#include <sch_reference_list.h>
#include <string_utils.h>
#include <connection_graph.h>
#include <core/kicad_algo.h>
#include <symbol_library.h>
#include <symbol_lib_table.h>
#include <netlist.h>
#include "netlist_exporter_allegro.h"
#include "netlist_exporter_xml.h"
#include <regex>
bool NETLIST_EXPORTER_ALLEGRO::WriteNetlist( const wxString& aOutFileName,
unsigned /* aNetlistOptions */,
REPORTER& aReporter )
{
m_f = nullptr;
wxString field;
wxString footprint;
int ret = 0; // zero now, OR in the sign bit on error
wxString netName;
// Create the devices directory
m_exportPath = wxFileName( aOutFileName ).GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR )
+ wxString( "devices" );
if( !wxDirExists( m_exportPath ) )
{
if( !wxMkdir( m_exportPath, wxS_DIR_DEFAULT ) )
{
wxString msg = wxString::Format( _( "Failed to create directory 'devices' ." ) );
aReporter.Report( msg, RPT_SEVERITY_ERROR );
return false;
}
}
// Write the netlist file
if( ( m_f = wxFopen( aOutFileName, wxT( "wt" ) ) ) == nullptr )
{
wxString msg = wxString::Format( _( "Failed to create file '%s'." ), aOutFileName );
aReporter.Report( msg, RPT_SEVERITY_ERROR );
return false;
}
ret |= fprintf( m_f, "(Source: %s)\n", TO_UTF8( m_schematic->GetFileName() ) );
ret |= fprintf( m_f, "(Date: %s)\n", TO_UTF8( DateAndTime() ) );
m_packageProperties.clear();
m_componentGroups.clear();
m_orderedSymbolsSheetpath.clear();
m_netNameNodes.clear();
extractComponentsInfo();
// Start with package definitions, which we create from component groups.
toAllegroPackages();
// Write out nets
toAllegroNets();
// Write out package properties. NOTE: Allegro doesn't recognize much...
toAllegroPackageProperties();
// Done with the netlist
fclose( m_f );
m_f = nullptr;
return ret >= 0;
}
bool NETLIST_EXPORTER_ALLEGRO::CompareSymbolSheetpath(
const std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>& aItem1,
const std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>& aItem2 )
{
wxString refText1 = aItem1.first->GetRef( &aItem1.second );
wxString refText2 = aItem2.first->GetRef( &aItem2.second );
if( refText1 == refText2 )
{
return aItem1.second.PathHumanReadable() < aItem2.second.PathHumanReadable();
}
return CompareSymbolRef( refText1, refText2 );
}
bool NETLIST_EXPORTER_ALLEGRO::CompareSymbolRef( const wxString& aRefText1,
const wxString& aRefText2 )
{
if( removeTailDigits( aRefText1 ) == removeTailDigits( aRefText2 ) )
{
return extractTailNumber( aRefText1 ) < extractTailNumber( aRefText2 );
}
return aRefText1 < aRefText2;
}
bool NETLIST_EXPORTER_ALLEGRO::CompareLibPin( const LIB_PIN* aPin1, const LIB_PIN* aPin2 )
{
// return "lhs < rhs"
return StrNumCmp( aPin1->GetShownNumber(), aPin2->GetShownNumber(), true ) < 0;
}
void NETLIST_EXPORTER_ALLEGRO::extractComponentsInfo()
{
m_referencesAlreadyFound.Clear();
m_libParts.clear();
SCH_SHEET_LIST sheetList = m_schematic->GetSheets();
for( unsigned ii = 0; ii < sheetList.size(); ii++ )
{
SCH_SHEET_PATH sheet = sheetList[ii];
m_schematic->SetCurrentSheet( sheet );
auto cmp = [sheet]( SCH_SYMBOL* a, SCH_SYMBOL* b )
{
return ( StrNumCmp( a->GetRef( &sheet, false ),
b->GetRef( &sheet, false ), true ) < 0 );
};
std::set<SCH_SYMBOL*, decltype( cmp )> ordered_symbols( cmp );
std::multiset<SCH_SYMBOL*, decltype( cmp )> extra_units( cmp );
for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
{
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
auto test = ordered_symbols.insert( symbol );
if( !test.second )
{
if( ( *( test.first ) )->m_Uuid > symbol->m_Uuid )
{
extra_units.insert( *( test.first ) );
ordered_symbols.erase( test.first );
ordered_symbols.insert( symbol );
}
else
{
extra_units.insert( symbol );
}
}
}
for( EDA_ITEM* item : ordered_symbols )
{
SCH_SYMBOL* symbol = findNextSymbol( item, &sheet );
if( !symbol || symbol->GetExcludedFromBoard() )
{
continue;
}
LIB_PINS pinList;
pinList.clear();
symbol->GetLibPins(pinList);
if( !pinList.size() )
{
continue;
}
m_packageProperties.insert(std::pair<wxString, wxString>(sheet.PathHumanReadable(), symbol->GetRef(&sheet)));
m_orderedSymbolsSheetpath.push_back(std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>(symbol, sheet));
}
}
wxString netCodeTxt;
wxString netName;
wxString ref;
struct NET_RECORD
{
NET_RECORD( const wxString& aName ) :
m_Name( aName )
{};
wxString m_Name;
std::vector<NET_NODE> m_Nodes;
};
std::vector<NET_RECORD*> nets;
for( const auto& it : m_schematic->ConnectionGraph()->GetNetMap() )
{
wxString net_name = it.first.Name;
const std::vector<CONNECTION_SUBGRAPH*>& subgraphs = it.second;
NET_RECORD* net_record = nullptr;
if( subgraphs.empty() )
continue;
nets.emplace_back( new NET_RECORD( net_name ) );
net_record = nets.back();
for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
{
bool nc = subgraph->GetNoConnect() && subgraph->GetNoConnect()->Type() == SCH_NO_CONNECT_T;
const SCH_SHEET_PATH& sheet = subgraph->GetSheet();
for( SCH_ITEM* item : subgraph->GetItems() )
{
if( item->Type() == SCH_PIN_T )
{
SCH_PIN* pin = static_cast<SCH_PIN*>( item );
SCH_SYMBOL* symbol = pin->GetParentSymbol();
if( !symbol || symbol->GetExcludedFromBoard() )
{
continue;
}
net_record->m_Nodes.emplace_back( pin, sheet, nc );
}
}
}
}
// Netlist ordering: Net name, then ref des, then pin name
std::sort( nets.begin(), nets.end(),
[]( const NET_RECORD* a, const NET_RECORD*b )
{
return StrNumCmp( a->m_Name, b->m_Name ) < 0;
} );
for( int i = 0; i < (int) nets.size(); ++i )
{
NET_RECORD* net_record = nets[i];
// Netlist ordering: Net name, then ref des, then pin name
std::sort( net_record->m_Nodes.begin(), net_record->m_Nodes.end(),
[]( const NET_NODE& a, const NET_NODE& b )
{
wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
if( refA == refB )
return a.m_Pin->GetShownNumber() < b.m_Pin->GetShownNumber();
return refA < refB;
} );
// Some duplicates can exist, for example on multi-unit parts with duplicated pins across
// units. If the user connects the pins on each unit, they will appear on separate
// subgraphs. Remove those here:
alg::remove_duplicates( net_record->m_Nodes,
[]( const NET_NODE& a, const NET_NODE& b )
{
wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
return refA == refB && a.m_Pin->GetShownNumber() == b.m_Pin->GetShownNumber();
} );
for( const NET_NODE& netNode : net_record->m_Nodes )
{
wxString refText = netNode.m_Pin->GetParentSymbol()->GetRef( &netNode.m_Sheet );
wxString pinText = netNode.m_Pin->GetShownNumber();
// Skip power symbols and virtual symbols
if( refText[0] == wxChar( '#' ) )
{
continue;
}
m_netNameNodes.insert( std::pair<wxString, NET_NODE>( net_record->m_Name, netNode ) );
}
}
for( NET_RECORD* record : nets )
delete record;
}
void NETLIST_EXPORTER_ALLEGRO::toAllegroPackages()
{
int ret = 0; // zero now, OR in the sign bit on error
int groupCount = 1;
wxString deviceFileCreatingError = wxString( "" );
//Group the components......
while(!m_orderedSymbolsSheetpath.empty())
{
std::pair<SCH_SYMBOL*, SCH_SHEET_PATH> first_ele = m_orderedSymbolsSheetpath.front();
m_orderedSymbolsSheetpath.pop_front();
m_componentGroups.insert(
std::pair<int, std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>>( groupCount, first_ele ) );
for( auto it = m_orderedSymbolsSheetpath.begin(); it != m_orderedSymbolsSheetpath.end();
++it )
{
if( it->first->GetValueFieldText( false, &it->second, false )
!= first_ele.first->GetValueFieldText( false, &first_ele.second, false ) )
{
continue;
}
if( it->first->GetFootprintFieldText( false, &it->second, false )
!= first_ele.first->GetFootprintFieldText( false, &first_ele.second, false ) )
{
continue;
}
wxString ref1 = it->first->GetRef( &it->second );
wxString ref2 = first_ele.first->GetRef( &first_ele.second );
if( removeTailDigits( ref1 ) == removeTailDigits( ref2 ) )
{
m_componentGroups.insert( std::pair<int, std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>>(
groupCount, ( *it ) ) );
it = m_orderedSymbolsSheetpath.erase( it );
if( std::distance( it, m_orderedSymbolsSheetpath.begin() ) > 0 )
it--;
else if( it == m_orderedSymbolsSheetpath.end() )
break;
}
}
groupCount++;
}
ret |= fprintf( m_f, "%s\n", "$PACKAGES" );
struct COMP_PACKAGE_STRUCT
{
wxString m_value;
wxString m_tolerance;
std::vector<std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>> m_symbolSheetpaths;
};
COMP_PACKAGE_STRUCT compPackageStruct;
std::map<wxString, COMP_PACKAGE_STRUCT> compPackageMap;
for( int groupIndex = 1; groupIndex < groupCount; groupIndex++ )
{
auto pairIter = m_componentGroups.equal_range(groupIndex);
auto beginIter = pairIter.first;
auto endIter = pairIter.second;
SCH_SYMBOL* sym = (beginIter->second).first;
SCH_SHEET_PATH sheetPath = (beginIter->second).second;
wxString valueText = sym->GetValueFieldText( false, &sheetPath, false );
wxString footprintText = sym->GetFootprintFieldText( false, &sheetPath, false);
wxString deviceType = valueText + wxString("_") + footprintText;
while( deviceType.GetChar(deviceType.Length()-1) == '_' )
{
deviceType.RemoveLast();
}
deviceType = formatDevice( deviceType );
wxArrayString fieldArray;
fieldArray.Add("Spice_Model");
fieldArray.Add("VALUE");
wxString value = getGroupField( groupIndex, fieldArray );
fieldArray.clear();
fieldArray.Add("TOLERANCE");
fieldArray.Add("TOL");
wxString tol = getGroupField( groupIndex, fieldArray );
std::vector<std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>> symbolSheetpaths;
for( auto iter = beginIter; iter != endIter; iter++ )
{
symbolSheetpaths.push_back( std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>(
iter->second.first, iter->second.second ) );
}
std::stable_sort( symbolSheetpaths.begin(), symbolSheetpaths.end(),
NETLIST_EXPORTER_ALLEGRO::CompareSymbolSheetpath );
compPackageStruct.m_value = value;
compPackageStruct.m_tolerance = tol;
compPackageStruct.m_symbolSheetpaths = symbolSheetpaths;
compPackageMap.insert( std::pair( deviceType, compPackageStruct ) );
// Write out the corresponding device file
FILE* d = nullptr;
wxString deviceFileName = wxFileName(m_exportPath, deviceType, wxString("txt")).GetFullPath();
if( ( d = wxFopen( deviceFileName, wxT( "wt" ) ) ) == nullptr )
{
wxString msg;
msg.Printf( _( "Failed to create file '%s'.\n" ), deviceFileName );
deviceFileCreatingError += msg;
continue;
}
footprintText = footprintText.AfterLast( ':' );
wxArrayString footprintAlt;
wxArrayString footprintArray = sym->GetLibSymbolRef()->GetFPFilters();
for( auto fp : footprintArray )
{
if( ( fp.Find( '*' ) != wxNOT_FOUND ) || ( fp.Find( '?' ) != wxNOT_FOUND ) )
{
continue;
}
footprintAlt.Add( fp.AfterLast( ':' ) );
}
if( footprintText.IsEmpty() )
{
if( !footprintAlt.IsEmpty() )
{
footprintText = footprintAlt[0];
footprintAlt.RemoveAt( 0 );
}
else
{
footprintText = deviceType;
}
}
fprintf( d, "PACKAGE '%s'\n", TO_UTF8( formatDevice( footprintText ) ) );
fprintf( d, "CLASS IC\n" );
LIB_PINS pinList;
sym->GetLibSymbolRef()->GetPins( pinList, 0, 0 );
/*
* We must erase redundant Pins references in pinList
* These redundant pins exist because some pins are found more than one time when a
* symbol has multiple parts per package or has 2 representations (DeMorgan conversion).
* For instance, a 74ls00 has DeMorgan conversion, with different pin shapes, and
* therefore each pin appears 2 times in the list. Common pins (VCC, GND) can also be
* found more than once.
*/
sort( pinList.begin(), pinList.end(), NETLIST_EXPORTER_ALLEGRO::CompareLibPin );
for( int ii = 0; ii < (int) pinList.size() - 1; ii++ )
{
if( pinList[ii]->GetNumber() == pinList[ii + 1]->GetNumber() )
{ // 2 pins have the same number, remove the redundant pin at index i+1
pinList.erase( pinList.begin() + ii + 1 );
ii--;
}
}
unsigned int pinCount = pinList.size();
fprintf( d, "PINCOUNT %u\n", pinCount );
if( pinCount > 0 )
{
fprintf( d, "%s", TO_UTF8( formatFunction( "main", pinList ) ) );
}
if( !value.IsEmpty() )
{
fprintf( d, "PACKAGEPROP VALUE %s\n", TO_UTF8( value ) );
}
if( !tol.IsEmpty() )
{
fprintf( d, "PACKAGEPROP TOL %s\n", TO_UTF8( tol ) );
}
if( !footprintAlt.IsEmpty() )
{
fprintf( d, "PACKAGEPROP ALT_SYMBOLS '(" );
wxString footprintAltSymbolsText;
for( auto fp : footprintAlt )
{
footprintAltSymbolsText += fp + wxString( "," );
}
footprintAltSymbolsText.Truncate( footprintAltSymbolsText.Length() - 1 );
fprintf( d, "%s)'\n", TO_UTF8( footprintAltSymbolsText ) );
}
wxArrayString propArray;
propArray.Add( "PART_NUMBER" );
propArray.Add( "mpn" );
propArray.Add( "mfr_pn" );
wxString data = getGroupField( groupIndex, propArray );
if(!data.IsEmpty())
{
fprintf( d, "PACKAGEPROP %s %s\n", TO_UTF8( propArray[0] ), TO_UTF8( data ) );
}
propArray.clear();
propArray.Add( "HEIGHT" );
data = getGroupField( groupIndex, propArray );
if(!data.IsEmpty())
{
fprintf( d, "PACKAGEPROP %s %s\n", TO_UTF8( propArray[0] ), TO_UTF8( data ) );
}
fprintf( d, "END\n" );
fclose( d );
}
for( auto iter = compPackageMap.begin(); iter != compPackageMap.end(); iter++ )
{
wxString deviceType = iter->first;
wxString value = iter->second.m_value;
wxString tolerance = iter->second.m_tolerance;
if( value.IsEmpty() && tolerance.IsEmpty() )
{
ret |= fprintf( m_f, "!%s;", TO_UTF8( deviceType ) );
}
else if( tolerance.IsEmpty() )
{
ret |= fprintf( m_f, "!%s!%s;", TO_UTF8( deviceType ), TO_UTF8( value ) );
}
else
{
ret |= fprintf( m_f, "!%s!%s!%s;", TO_UTF8( deviceType ), TO_UTF8( value ),
TO_UTF8( tolerance ) );
}
std::vector<std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>> symbolSheetpaths =
iter->second.m_symbolSheetpaths;
for( auto it = symbolSheetpaths.begin(); it != symbolSheetpaths.end(); it++ )
{
SCH_SYMBOL* sym = it->first;
SCH_SHEET_PATH sheetPath = it->second;
wxString refText = sym->GetRef( &sheetPath );
ret |= fprintf( m_f, ",\n\t%s", TO_UTF8( refText ) );
}
ret |= fprintf( m_f, "\n" );
}
if( !deviceFileCreatingError.IsEmpty() )
{
DisplayError( nullptr, deviceFileCreatingError );
}
}
wxString NETLIST_EXPORTER_ALLEGRO::formatText( wxString aString )
{
if( aString.IsEmpty() )
{
return wxString( "" );
}
aString.Replace( "\u03BC", "u" );
std::regex reg( "[!']|[^ -~]" );
wxString processedString = wxString( std::regex_replace( std::string( aString ), reg, "?" ) );
std::regex search_reg( "[^a-zA-Z0-9_/]" );
if( std::regex_search( std::string( processedString ), search_reg ) )
{
return wxString( "'" ) + processedString + wxString( "'" );
}
return processedString;
}
wxString NETLIST_EXPORTER_ALLEGRO::formatPin( const LIB_PIN& aPin )
{
wxString pinName4Telesis = aPin.GetName() + wxString( "__" ) + aPin.GetNumber();
std::regex reg( "[^A-Za-z0-9_+?/-]" );
return wxString( std::regex_replace( std::string( pinName4Telesis ), reg, "?" ) );
}
wxString NETLIST_EXPORTER_ALLEGRO::formatFunction( wxString aName, LIB_PINS aPinList )
{
aName.MakeUpper();
std::list<wxString> pinNameList;
std::stable_sort( aPinList.begin(), aPinList.end(), NETLIST_EXPORTER_ALLEGRO::CompareLibPin );
for( auto pin : aPinList )
{
pinNameList.push_back( formatPin( *pin ) );
}
wxString out_str = "";
wxString str;
out_str.Printf( wxT( "PINORDER %s " ), TO_UTF8( aName ) );
for( auto pinName : pinNameList )
{
str.Printf( ",\n\t%s", TO_UTF8( pinName ) );
out_str += str;
}
out_str += wxString( "\n" );
str.Printf( wxT( "FUNCTION %s %s " ), TO_UTF8( aName ), TO_UTF8( aName ) );
out_str += str;
for( auto pin : aPinList )
{
str.Printf( ",\n\t%s", TO_UTF8( pin->GetNumber() ) );
out_str += str;
}
out_str += wxString( "\n" );
return out_str;
}
wxString NETLIST_EXPORTER_ALLEGRO::getGroupField( int aGroupIndex, const wxArrayString& aFieldArray,
bool aSanitize )
{
auto pairIter = m_componentGroups.equal_range( aGroupIndex );
for( auto iter = pairIter.first; iter != pairIter.second; ++iter )
{
SCH_SYMBOL* sym = ( iter->second ).first;
SCH_SHEET_PATH sheetPath = ( iter->second ).second;
for( auto field : aFieldArray )
{
SCH_FIELD* fld = sym->FindField( field, true, true );
if( fld != NULL )
{
wxString fieldText = fld->GetShownText( &sheetPath, true );
if( !fieldText.IsEmpty() )
{
if( aSanitize )
{
return formatText( fieldText );
}
else
{
return fieldText;
}
}
}
}
}
for( auto iter = pairIter.first; iter != pairIter.second; ++iter )
{
SCH_SYMBOL* sym = ( iter->second ).first;
SCH_SHEET_PATH sheetPath = ( iter->second ).second;
for( auto field : aFieldArray )
{
LIB_FIELD* fld = sym->GetLibSymbolRef()->FindField( field, true );
if( fld != NULL )
{
wxString fieldText = fld->GetShownText( &sheetPath, true );
if( !fieldText.IsEmpty() )
{
if( aSanitize )
{
return formatText( fieldText );
}
else
{
return fieldText;
}
}
}
}
}
return wxString( "" );
}
wxString NETLIST_EXPORTER_ALLEGRO::formatDevice( wxString aString )
{
aString.MakeLower();
std::regex reg( "[^a-z0-9_-]" );
return wxString( std::regex_replace( std::string( aString ), reg, "_" ) );
}
void NETLIST_EXPORTER_ALLEGRO::toAllegroPackageProperties()
{
int ret = 0; // zero now, OR in the sign bit on error
ret |= fprintf( m_f, "%s\n", "$PACKAGES" );
ret |= fprintf( m_f, "%s\n", "$A_PROPERTIES" );
while( !m_packageProperties.empty() )
{
std::multimap<wxString, wxString>::iterator iter = m_packageProperties.begin();
wxString sheetPathText = iter->first;
ret |= fprintf( m_f, "ROOM %s;", TO_UTF8( formatText( sheetPathText ) ) );
std::vector<wxString> refTexts;
auto pairIter = m_packageProperties.equal_range( sheetPathText );
for( iter = pairIter.first; iter != pairIter.second; ++iter )
{
wxString refText = iter->second;
refTexts.push_back( refText );
}
m_packageProperties.erase( pairIter.first, pairIter.second );
std::stable_sort( refTexts.begin(), refTexts.end(),
NETLIST_EXPORTER_ALLEGRO::CompareSymbolRef );
for( auto it = refTexts.begin(); it != refTexts.end(); it++ )
{
ret |= fprintf( m_f, ",\n\t%s", TO_UTF8( *it ) );
}
ret |= fprintf( m_f, "\n" );
}
}
void NETLIST_EXPORTER_ALLEGRO::toAllegroNets()
{
int ret = 0; // zero now, OR in the sign bit on error
ret |= fprintf( m_f, "%s\n", "$NETS" );
while( !m_netNameNodes.empty() )
{
std::multimap<wxString, NET_NODE>::iterator iter = m_netNameNodes.begin();
std::vector<NET_NODE> netNodes;
wxString netName = iter->first;
ret |= fprintf( m_f, "%s;", TO_UTF8( formatText( netName ).MakeUpper() ) );
auto pairIter = m_netNameNodes.equal_range( netName );
for( iter = pairIter.first; iter != pairIter.second; ++iter )
{
NET_NODE netNode = iter->second;
netNodes.push_back( netNode );
}
m_netNameNodes.erase( pairIter.first, pairIter.second );
std::stable_sort( netNodes.begin(), netNodes.end() );
for( auto it = netNodes.begin(); it != netNodes.end(); it++ )
{
NET_NODE netNode = *it;
wxString refText = netNode.m_Pin->GetParentSymbol()->GetRef( &netNode.m_Sheet );
wxString pinText = netNode.m_Pin->GetShownNumber();
ret |= fprintf( m_f, ",\n\t%s.%s", TO_UTF8( refText ), TO_UTF8( pinText ) );
}
ret |= fprintf( m_f, "\n" );
}
}
wxString NETLIST_EXPORTER_ALLEGRO::removeTailDigits( wxString aString )
{
while( ( aString.GetChar( aString.Length() - 1 ) >= '0' )
&& ( aString.GetChar( aString.Length() - 1 ) <= '9' ) )
{
aString.RemoveLast();
}
return aString;
}
unsigned int NETLIST_EXPORTER_ALLEGRO::extractTailNumber( wxString aString )
{
wxString numString;
while( ( aString.GetChar( aString.Length() - 1 ) >= '0' )
&& ( aString.GetChar( aString.Length() - 1 ) <= '9' ) )
{
numString.insert( 0, aString.GetChar( aString.Length() - 1 ) );
aString.RemoveLast();
}
unsigned long val;
//From wxWidgets 3.1.6, here we can use ToUInt instead of ToULong function.
numString.ToULong( &val );
return (unsigned int) val;
}

View File

@ -0,0 +1,215 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2013 jp.charras at wanadoo.fr
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.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
*/
#ifndef NETLIST_EXPORTER_ALLEGRO_H
#define NETLIST_EXPORTER_ALLEGRO_H
#include "netlist_exporter_base.h"
/**
* Generate a netlist compatible with Allegro.
*/
class NETLIST_EXPORTER_ALLEGRO : public NETLIST_EXPORTER_BASE
{
public:
NETLIST_EXPORTER_ALLEGRO( SCHEMATIC* aSchematic ) :
NETLIST_EXPORTER_BASE( aSchematic )
{
}
/**
* Write netlist to \a aOutFileName.
* Generate the Allegro netlist format supported by Allegro.
*/
bool WriteNetlist( const wxString& aOutFileName, unsigned aNetlistOptions,
REPORTER& aReporter ) override;
/**
* Compare two std::pair<SCH_SYMBOL*, SCH_SHEET_PATH> variables.
*
* @param aItem1 comparing object 1
* @param aItem2 comparing object 2
* @return true if aItem1 < aItem2
*/
static bool CompareSymbolSheetpath( const std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>& aItem1,
const std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>& aItem2 );
/**
* Compare two wxString variables.
*
* @param aRefText1
* @param aRefText2
* @return bool value
*/
static bool CompareSymbolRef( const wxString& aRefText1, const wxString& aRefText2 );
/**
* Compare two LIB_PIN* variables.
*
* @param aPin1
* @param aPin2
* @return bool value
*/
static bool CompareLibPin( const LIB_PIN* aPin1, const LIB_PIN* aPin2 );
private:
void extractComponentsInfo();
/**
* Write the $PACKAGES section
*
*/
void toAllegroPackages();
/**
* Write the $NETS section
*
*/
void toAllegroNets();
/**
* Write $A_PROPERTIES section
*
*/
void toAllegroPackageProperties();
/**
* Converts a string into one safe for a Telesis device name.
* These are all lowercase and have a more restricted set of characters.
* FIXME: replace unsupported characters with an encoding instead
*
* @param aString wxString to be formatted.
* @return a formatted wxString.
*/
wxString formatDevice( wxString aString );
/**
* Convert a string into Telesis-safe format. Unsupported characters are
* replaced with ?'s, and the string is quoted if necessary.
* FIXME: replace unsupported characters with an encoding instead, to avoid
* having similar strings mapped to each other.
*
* @param aString
* @return wxString
*/
wxString formatText( wxString aString );
/**
* Generates a Telesis-compatible pin name from a pin node.
* Telesis requires all pin names to be unique, and doesn't have separate
* fields for pin number and pin name/function, so we combine them together to
* make a unique name that still describes its function if you check pin info.
* FIXME: replace unsupported characters with an encoding instead
*
* @param aPin
* @return wxString
*/
wxString formatPin( const LIB_PIN& aPin );
/**
* Generates the definition of a function in Telesis format, which consists of
* multiple declarations (PINORDER, PINSWAP, and FUNCTIONs).
*
* @param aName
* @param aPinList
* @return wxString
*/
wxString formatFunction( wxString aName, LIB_PINS aPinList );
/**
* Look up a field for a component group, which may have mismatched case, or
* the component group may not have the field defined and instead the library
* entry has to be searched. Returns None if no fields exist.
*
* @param aGroupIndex the component group index to query
* @param aFieldArray one or more (equivalent) fields to query, in the order specified.
* first field that exists is returned.
* @param aSanitize if true (default), will format/escape the field for Telesis output
* @return return the found field, or return wxString("") if no field exist.
*/
wxString getGroupField( int aGroupIndex, const wxArrayString& aFieldArray,
bool aSanitize = true );
/**
* Remove the str's tailing digits.
*
* @param aString
* @return wxString
*/
static wxString removeTailDigits( wxString aString );
/**
* Extract the str's tailing number.
*
* @param aString
* @return unsigned int
*/
static unsigned int extractTailNumber( wxString aString );
struct NET_NODE
{
NET_NODE( SCH_PIN* aPin, const SCH_SHEET_PATH& aSheet, bool aNoConnect ) :
m_Pin( aPin ),
m_Sheet( aSheet ),
m_NoConnect( aNoConnect )
{}
bool operator<( const NET_NODE& aNetNode ) const
{
wxString refText1 = m_Pin->GetParentSymbol()->GetRef( &m_Sheet );
wxString refText2 = aNetNode.m_Pin->GetParentSymbol()->GetRef( &aNetNode.m_Sheet );
if( refText1 == refText2 )
{
unsigned long val1, val2;
//From wxWidgets 3.1.6, the function ToULong can be repalced with ToUInt
bool convertingResult = m_Pin->GetShownNumber().ToULong( &val1 );
convertingResult &= aNetNode.m_Pin->GetShownNumber().ToULong( &val2 );
if( convertingResult )
{
return val1 < val2;
}
else
{
return m_Pin->GetShownNumber() < aNetNode.m_Pin->GetShownNumber();
}
}
return CompareSymbolRef( refText1, refText2 );
}
SCH_PIN* m_Pin;
SCH_SHEET_PATH m_Sheet;
bool m_NoConnect;
};
FILE* m_f; ///< file pointer for netlist file writing operation
wxString m_exportPath; ///< directory to store device files
std::multimap<wxString, wxString> m_packageProperties;
std::multimap<int, std::pair<SCH_SYMBOL*, SCH_SHEET_PATH> > m_componentGroups; ///< Store the component group
std::list<std::pair<SCH_SYMBOL*, SCH_SHEET_PATH>>
m_orderedSymbolsSheetpath; ///< Store the ordered symbols with sheetpath
std::multimap<wxString, NET_NODE> m_netNameNodes; ///< Store the NET_NODE with the net name
};
#endif

View File

@ -41,6 +41,7 @@
#include <netlist_exporter_spice.h>
#include <netlist_exporter_spice_model.h>
#include <netlist_exporter_kicad.h>
#include <netlist_exporter_allegro.h>
#include <netlist_exporter_xml.h>
@ -88,6 +89,10 @@ bool SCH_EDIT_FRAME::WriteNetListFile( int aFormat, const wxString& aFullFileNam
helper = new NETLIST_EXPORTER_SPICE_MODEL( sch );
break;
case NET_TYPE_ALLEGRO:
helper = new NETLIST_EXPORTER_ALLEGRO( sch );
break;
case NET_TYPE_BOM:
// When generating the BOM, we have a bare filename so don't strip
// the extension or you might string a '.' from the middle of the filename

View File

@ -953,14 +953,23 @@ void SCH_SYMBOL::RemoveField( const wxString& aFieldName )
}
SCH_FIELD* SCH_SYMBOL::FindField( const wxString& aFieldName, bool aIncludeDefaultFields )
SCH_FIELD* SCH_SYMBOL::FindField( const wxString& aFieldName, bool aIncludeDefaultFields,
bool aCaseInsensitive )
{
unsigned start = aIncludeDefaultFields ? 0 : MANDATORY_FIELDS;
for( unsigned i = start; i < m_fields.size(); ++i )
{
if( aFieldName == m_fields[i].GetName( false ) )
return &m_fields[i];
if( aCaseInsensitive )
{
if( aFieldName.Upper() == m_fields[i].GetName( false ).Upper() )
return &m_fields[i];
}
else
{
if( aFieldName == m_fields[i].GetName( false ) )
return &m_fields[i];
}
}
return nullptr;

View File

@ -458,10 +458,12 @@ public:
*
* @param aFieldName is the name of the field to find.
* @param aIncludeDefaultFields searches the library symbol default fields if true.
* @param aCaseInsensitive ignore the filed name case if true.
*
* @return the field if found or NULL if the field was not found.
*/
SCH_FIELD* FindField( const wxString& aFieldName, bool aIncludeDefaultFields = true );
SCH_FIELD* FindField( const wxString& aFieldName, bool aIncludeDefaultFields = true,
bool aCaseInsensitive = false );
const wxString GetValueFieldText( bool aResolve, const SCH_SHEET_PATH* aPath,
bool aAllowExtraText ) const;

View File

@ -128,6 +128,7 @@ extern const std::string SpiceFileExtension;
extern const std::string CadstarNetlistFileExtension;
extern const std::string OrCadPcb2NetlistFileExtension;
extern const std::string NetlistFileExtension;
extern const std::string AllegroNetlistFileExtension;
extern const std::string GerberFileExtension;
extern const std::string GerberJobFileExtension;
extern const std::string HtmlFileExtension;
@ -211,6 +212,7 @@ extern wxString LegacySchematicFileWildcard();
extern wxString BoardFileWildcard();
extern wxString OrCadPcb2NetlistFileWildcard();
extern wxString NetlistFileWildcard();
extern wxString AllegroNetlistFileWildcard();
extern wxString HtmlFileWildcard();
extern wxString CsvFileWildcard();
extern wxString LegacyPcbFileWildcard();