kicad-source/pcbnew/pcb_io/odbpp/odb_netlist.cpp
Seth Hillbrand 0b2d4d4879 Revise Copyright statement to align with TLF
Recommendation is to avoid using the year nomenclature as this
information is already encoded in the git repo.  Avoids needing to
repeatly update.

Also updates AUTHORS.txt from current repo with contributor names
2025-01-01 14:12:04 -08:00

286 lines
9.3 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <confirm.h>
#include <gestfich.h>
#include <kiface_base.h>
#include <pcb_edit_frame.h>
#include <trigo.h>
#include <build_version.h>
#include <macros.h>
#include <wildcards_and_files_ext.h>
#include <locale_io.h>
#include <board.h>
#include <board_design_settings.h>
#include <footprint.h>
#include <pad.h>
#include <pcb_track.h>
#include <vector>
#include <cctype>
#include <odb_netlist.h>
#include <wx/filedlg.h>
#include <wx/log.h>
#include "odb_util.h"
// Compute the side code for a pad. Returns "" if there is no copper
std::string ODB_NET_LIST::ComputePadAccessSide( BOARD* aBoard, LSET aLayerMask )
{
// Non-copper is not interesting here
aLayerMask &= LSET::AllCuMask();
if( !aLayerMask.any() )
return "";
// Traditional TH pad
if( aLayerMask[F_Cu] && aLayerMask[B_Cu] )
return "B";
// Front SMD pad
if( aLayerMask[F_Cu] )
return "T";
// Back SMD pad
if( aLayerMask[B_Cu] )
return "D";
// Inner
for( int layer = In1_Cu; layer < B_Cu; ++layer )
{
if( aLayerMask[layer] )
return "I";
}
// This shouldn't happen
wxLogDebug( "Unhandled layer mask input when compute pad access side of ODB++ netlist file." );
return "";
}
void ODB_NET_LIST::InitPadNetPoints( BOARD* aBoard,
std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords )
{
VECTOR2I origin = aBoard->GetDesignSettings().GetAuxOrigin();
for( FOOTPRINT* footprint : aBoard->Footprints() )
{
for( PAD* pad : footprint->Pads() )
{
ODB_NET_RECORD net_point;
net_point.side = ComputePadAccessSide( aBoard, pad->GetLayerSet() );
// It could be a mask only pad, we only handle pads with copper here
if( !net_point.side.empty() && net_point.side != "I" )
{
if( pad->GetNetCode() == 0 )
net_point.netname = "$NONE$";
else
net_point.netname = pad->GetNetname();
// net_point.pin = pad->GetNumber();
net_point.refdes = footprint->GetReference();
const VECTOR2I& drill = pad->GetDrillSize();
net_point.hole = pad->HasHole();
if( !net_point.hole )
net_point.drill_radius = 0;
else
net_point.drill_radius = std::min( drill.x, drill.y );
net_point.smd = pad->GetAttribute() == PAD_ATTRIB::SMD
|| pad->GetAttribute() == PAD_ATTRIB::CONN;
net_point.is_via = false;
net_point.mechanical = ( pad->GetAttribute() == PAD_ATTRIB::NPTH );
net_point.x_location = pad->GetPosition().x - origin.x;
net_point.y_location = origin.y - pad->GetPosition().y;
net_point.x_size = pad->GetSize( PADSTACK::ALL_LAYERS ).x;
// Rule: round pads have y = 0
if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE )
net_point.y_size = net_point.x_size;
else
net_point.y_size = pad->GetSize( PADSTACK::ALL_LAYERS ).y;
// net_point.rotation = ( ANGLE_360 - pad->GetOrientation() ).Normalize().AsDegrees();
// if( net_point.rotation < 0 )
// net_point.rotation += 360;
// always output NET end point as net test point
net_point.epoint = "e";
// the value indicates which sides are *not* accessible
net_point.soldermask = 3;
if( pad->GetLayerSet()[F_Mask] )
net_point.soldermask &= ~1;
if( pad->GetLayerSet()[B_Mask] )
net_point.soldermask &= ~2;
aRecords[pad->GetNetCode()].push_back( net_point );
}
}
}
}
// Compute the side code for a via.
std::string ODB_NET_LIST::ComputeViaAccessSide( BOARD* aBoard, int top_layer, int bottom_layer )
{
// Easy case for through vias: top_layer is component, bottom_layer is
// solder, side code is Both
if( ( top_layer == F_Cu ) && ( bottom_layer == B_Cu ) )
return "B";
// Blind via, reachable from front, Top
if( top_layer == F_Cu )
return "T";
// Blind via, reachable from bottom, Down
if( bottom_layer == B_Cu )
return "D";
// It's a buried via, accessible from some inner layer, Inner
return "I";
}
void ODB_NET_LIST::InitViaNetPoints( BOARD* aBoard,
std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords )
{
VECTOR2I origin = aBoard->GetDesignSettings().GetAuxOrigin();
// Enumerate all the track segments and keep the vias
for( auto track : aBoard->Tracks() )
{
if( track->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( track );
PCB_LAYER_ID top_layer, bottom_layer;
via->LayerPair( &top_layer, &bottom_layer );
ODB_NET_RECORD net_point;
net_point.side = ComputeViaAccessSide( aBoard, top_layer, bottom_layer );
if( net_point.side != "I" )
{
NETINFO_ITEM* net = track->GetNet();
net_point.smd = false;
net_point.hole = true;
if( net->GetNetCode() == 0 )
net_point.netname = "$NONE$";
else
net_point.netname = net->GetNetname();
net_point.refdes = "VIA";
net_point.is_via = true;
net_point.drill_radius = via->GetDrillValue();
net_point.mechanical = false;
net_point.x_location = via->GetPosition().x - origin.x;
net_point.y_location = origin.y - via->GetPosition().y;
// via always has drill radius, Width and Height are 0
net_point.x_size = 0;
net_point.y_size = 0; // Round so height = 0
net_point.epoint = "e"; // only buried via is "m" net mid point
// the value indicates which sides are *not* accessible
net_point.soldermask = 3;
if( via->GetLayerSet()[F_Mask] )
net_point.soldermask &= ~1;
if( via->GetLayerSet()[B_Mask] )
net_point.soldermask &= ~2;
aRecords[net->GetNetCode()].push_back( net_point );
}
}
}
}
void ODB_NET_LIST::WriteNetPointRecords( std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords,
std::ostream& aStream )
{
aStream << "H optimize n staggered n" << std::endl;
for( const auto& [key, vec] : aRecords )
{
aStream << "$" << key << " " << ODB::GenLegalNetName( vec.front().netname ) << std::endl;
}
aStream << "#" << std::endl << "#Netlist points" << std::endl << "#" << std::endl;
for( const auto& [key, vec] : aRecords )
{
for( const auto& net_point : vec )
{
aStream << key << " ";
if( net_point.hole )
aStream << ODB::Data2String( net_point.drill_radius );
else
aStream << 0;
aStream << " " << ODB::Data2String( net_point.x_location ) << " "
<< ODB::Data2String( net_point.y_location ) << " " << net_point.side << " ";
if( !net_point.hole )
aStream << ODB::Data2String( net_point.x_size ) << " "
<< ODB::Data2String( net_point.y_size ) << " ";
std::string exp;
if( net_point.soldermask == 3 )
exp = "c";
else if( net_point.soldermask == 2 )
exp = "s";
else if( net_point.soldermask == 1 )
exp = "p";
else if( net_point.soldermask == 0 )
exp = "e";
aStream << net_point.epoint << " " << exp;
if( net_point.hole )
aStream << " staggered 0 0 0";
if( net_point.is_via )
aStream << " v";
aStream << std::endl;
}
}
}
void ODB_NET_LIST::Write( std::ostream& aStream )
{
std::map<size_t, std::vector<ODB_NET_RECORD>> net_point_records;
InitViaNetPoints( m_board, net_point_records );
InitPadNetPoints( m_board, net_point_records );
WriteNetPointRecords( net_point_records, aStream );
}