mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 10:13:19 +02:00
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
286 lines
9.3 KiB
C++
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 );
|
|
}
|