KiCad PCB EDA Suite
Loading...
Searching...
No Matches
odb_netlist.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * Author: SYSUEric <[email protected]>.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include "odb_netlist.h"
22
23#include <string_view>
24
25#include <board.h>
26#include <footprint.h>
27#include <pad.h>
28#include <pcb_track.h>
29
32
33#include <odb_defines.h>
34#include <odb_util.h>
35#include <pcb_io_odbpp.h>
36
37
38// Compute the side code for a via.
39static ODB_NET_RECORD::SIDE ComputeViaAccessSide( int top_layer, int bottom_layer )
40{
41 // Easy case for through vias: top_layer is component, bottom_layer is
42 // solder, side code is Both
43 if( ( top_layer == F_Cu ) && ( bottom_layer == B_Cu ) )
45
46 // Blind via, reachable from front, Top
47 if( top_layer == F_Cu )
49
50 // Blind via, reachable from bottom, Down
51 if( bottom_layer == B_Cu )
53
54 // It's a buried via, accessible from some inner layer, Inner
56}
57
58
59void ODB_NET_LIST::InitNetPoints( std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords )
60{
61 for( const FAB_TEST_POINT& point : FabTestPoints( *m_board ) )
62 {
63 ODB_NET_RECORD record;
64 record.testpoint = false;
65 record.is_via = point.m_via != nullptr;
66 record.x_location = point.m_position.x;
67 record.y_location = -point.m_position.y;
68 record.soldermask = 3;
69 record.epoint = point.m_netEnd ? "e" : "m";
70
71 if( point.m_frontMask )
72 record.soldermask &= ~1;
73
74 if( point.m_backMask )
75 record.soldermask &= ~2;
76
77 if( const PCB_VIA* via = point.m_via )
78 {
79 PCB_LAYER_ID top, bottom;
80 via->LayerPair( &top, &bottom );
81 record.side = ComputeViaAccessSide( top, bottom );
82
83 if( record.side == ODB_NET_RECORD::SIDE::INNER )
84 continue;
85
86 record.refdes = "VIA";
87 record.smd = false;
88 record.hole = true;
89 record.drill_radius = point.m_drill / 2;
90 record.mechanical = false;
91 record.x_size = 0;
92 record.y_size = 0;
93 }
94 else
95 {
96 const PAD* pad = point.m_pad;
97
98 if( !point.m_front && !point.m_back )
99 continue;
100
101 if( point.m_front && point.m_back )
103 else if( point.m_front )
105 else
107
108 record.refdes = pad->GetParentFootprint()->GetReference();
109 record.hole = pad->HasHole();
110 record.drill_radius = record.hole ? point.m_drill / 2 : 0;
111 record.smd = pad->GetAttribute() == PAD_ATTRIB::SMD || pad->GetAttribute() == PAD_ATTRIB::CONN;
112 record.mechanical = pad->GetAttribute() == PAD_ATTRIB::NPTH;
114
115 // The land size is written only for undrilled pads, which are single-sided, so
116 // the access side gives the copper to report
117 PCB_LAYER_ID sideLayer =
118 pad->Padstack().EffectiveLayerFor( record.side == ODB_NET_RECORD::SIDE::DOWN ? B_Cu : F_Cu );
119 record.x_size = pad->GetSize( sideLayer ).x;
120
121 // A circle's stored second size can differ from its diameter
122 if( pad->GetShape( sideLayer ) == PAD_SHAPE::CIRCLE )
123 record.y_size = record.x_size;
124 else
125 record.y_size = pad->GetSize( sideLayer ).y;
126 }
127
128 aRecords[point.m_netCode].push_back( record );
129 }
130}
131
132
133static std::string_view SideToString( ODB_NET_RECORD::SIDE aSide )
134{
135 switch( aSide )
136 {
137 case ODB_NET_RECORD::SIDE::TOP: return "T";
138 case ODB_NET_RECORD::SIDE::DOWN: return "D";
139 case ODB_NET_RECORD::SIDE::BOTH: return "B";
140 case ODB_NET_RECORD::SIDE::INNER: return "I";
141 // No default
142 }
143
144 return {};
145}
146
147
148void ODB_NET_LIST::WriteNetPointRecords( std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords,
149 std::ostream& aStream,
150 const ODB_FORMAT& aFormat )
151{
152 aStream << "H optimize n staggered n" << std::endl;
153 aStream << ODB_UNITS << "=" << aFormat.m_unitsStr << std::endl;
154
155 for( const auto& [key, vec] : aRecords )
156 {
157 aStream << "$" << key << " " << m_plugin->GetLegalNetName( static_cast<int>( key ) ) << std::endl;
158 }
159
160 aStream << "#" << std::endl << "#Netlist points" << std::endl << "#" << std::endl;
161
162 for( const auto& [key, vec] : aRecords )
163 {
164 for( const auto& net_point : vec )
165 {
166 aStream << key << " ";
167
168 if( net_point.hole )
169 aStream << ODB::Data2String( aFormat, net_point.drill_radius );
170 else
171 aStream << "0.002";
172
173 aStream << " " << ODB::Data2String( aFormat, net_point.x_location - aFormat.m_originOffset.x ) << " "
174 << ODB::Data2String( aFormat, net_point.y_location + aFormat.m_originOffset.y ) << " "
175 << SideToString( net_point.side ) << " ";
176
177 if( !net_point.hole )
178 aStream << ODB::Data2String( aFormat, net_point.x_size ) << " "
179 << ODB::Data2String( aFormat, net_point.y_size ) << " ";
180
181 std::string exp;
182
183 if( net_point.soldermask == 3 )
184 exp = "c";
185 else if( net_point.soldermask == 2 )
186 exp = "s";
187 else if( net_point.soldermask == 1 )
188 exp = "p";
189 else if( net_point.soldermask == 0 )
190 exp = "e";
191
192 aStream << net_point.epoint << " " << exp;
193
194 if( net_point.hole )
195 aStream << " staggered 0 0 0";
196
197 if( net_point.is_via )
198 aStream << " v";
199
200 if( net_point.testpoint )
201 aStream << " t";
202
203 aStream << std::endl;
204 }
205 }
206}
207
208
209void ODB_NET_LIST::Write( std::ostream& aStream, const ODB_FORMAT& aFormat )
210{
211 std::map<size_t, std::vector<ODB_NET_RECORD>> net_point_records;
212
213 InitNetPoints( net_point_records );
214
215 WriteNetPointRecords( net_point_records, aStream, aFormat );
216}
BOARD * m_board
Definition odb_netlist.h:85
void WriteNetPointRecords(std::map< size_t, std::vector< ODB_NET_RECORD > > &aRecords, std::ostream &aStream, const ODB_FORMAT &aFormat)
Writes a list of records to the given output stream.
PCB_IO_ODBPP * m_plugin
Definition odb_netlist.h:86
void Write(std::ostream &aStream, const ODB_FORMAT &aFormat)
void InitNetPoints(std::map< size_t, std::vector< ODB_NET_RECORD > > &aRecords)
Definition pad.h:61
FAB_PAD_ROLE GetFabPadRole(const PAD &aPad)
Explicit fabrication properties take precedence over the unnumbered NPTH fallback.
Definition fab_pin.cpp:26
std::vector< FAB_TEST_POINT > FabTestPoints(const BOARD &aBoard)
The caller must build board connectivity before enumerating net endpoints.
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:60
wxString Data2String(const ODB_FORMAT &aFormat, double aVal)
Definition odb_util.cpp:406
#define ODB_UNITS
Definition odb_defines.h:28
static std::string_view SideToString(ODB_NET_RECORD::SIDE aSide)
static ODB_NET_RECORD::SIDE ComputeViaAccessSide(int top_layer, int bottom_layer)
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:99
VECTOR2I m_originOffset
Definition odb_util.h:77
std::string m_unitsStr
Definition odb_util.h:73
std::string epoint
Definition odb_netlist.h:63
SIDE
Possible values for the "side" of an ODB net point.
Definition odb_netlist.h:39
@ DOWN
D (i.e. bottom)
Definition odb_netlist.h:41
std::string refdes
Definition odb_netlist.h:50
KIBIS top(path, &reporter)