KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_kibis.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 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
26#include <sim/kibis/kibis.h>
29#include <sim/sim_model_ibis.h>
30#include <sim/spice_generator.h>
31#include <sim/spice_simulator.h>
33#include <sch_pin.h>
34#include <wx/utils.h>
35
36namespace
37{
38std::string GetLibraryPath( const std::string& aBaseName )
39{
40 wxFileName fn( KI_TEST::GetEeschemaTestDataDir() );
41 fn.SetName( aBaseName );
42 fn.SetExt( "ibs" );
43 return std::string( fn.GetFullPath().ToUTF8() );
44}
45} // namespace
46
47
49
50
52{
53 KIBIS kibis;
54
55 BOOST_TEST( !kibis.m_valid );
56
57 // IBIS_BASE interface
58 // If this isn't null, it's uninited and access will crash
59 BOOST_REQUIRE( !kibis.m_Reporter );
60
61 // Doesn't crash (also doesn't do anything)
62 kibis.Report( "Dummy", RPT_SEVERITY_INFO );
63}
64
65
67{
69
70 std::string path = GetLibraryPath( "ibis_v1_1" );
72
73 BOOST_TEST_INFO( "Parsed: " << path );
74 BOOST_TEST_INFO( "Reported: " << reporter.GetMessages() );
75
76 BOOST_TEST( top.m_valid );
77
78 KIBIS_MODEL* model = top.GetModel( "Input" );
79
80 BOOST_REQUIRE( model != nullptr );
81 BOOST_TEST_INFO( "Model: " << model->m_name );
82
83 BOOST_TEST( model->m_name == "Input" );
84 BOOST_TEST( (int) model->m_type == (int) IBIS_MODEL_TYPE::INPUT_STD );
85 BOOST_TEST( (int) model->m_polarity == (int) IBIS_MODEL_POLARITY::NON_INVERTING );
86 BOOST_TEST( (int) model->m_enable = (int) IBIS_MODEL_ENABLE::ACTIVE_HIGH );
87
88 BOOST_TEST( model->HasGNDClamp() );
89
90 KIBIS_COMPONENT* comp = top.GetComponent( "Virtual" );
91
92 BOOST_REQUIRE( comp != nullptr );
93
94 BOOST_TEST_INFO( "Component: " << comp->m_name );
95
96 BOOST_TEST( comp->m_name == "Virtual" );
97 BOOST_TEST( comp->m_pins.size() == 4 );
98}
99
100BOOST_AUTO_TEST_CASE( Load_v5_1, * boost::unit_test::tolerance( 1e-15 ) )
101{
103
104 std::string path = GetLibraryPath( "ibis_v5_1" );
106
107 BOOST_TEST_INFO( "Parsed: " << path );
108 BOOST_TEST_INFO( "Reported: " << reporter.GetMessages() );
109
110 BOOST_TEST( top.m_valid );
111
112 KIBIS_COMPONENT* comp = top.GetComponent( "Virtual" );
113
114 BOOST_REQUIRE( comp != nullptr );
115 BOOST_TEST_INFO( "Component: " << comp->m_name );
116
117 BOOST_TEST( comp->m_name == "Virtual" );
118 BOOST_TEST( comp->m_manufacturer == "KiCad" );
119 BOOST_TEST( comp->m_pins.size() == 4 );
120
122
123 pin = comp->GetPin( "A1" );
124
125 BOOST_REQUIRE( pin != nullptr );
126
127 BOOST_TEST( pin->m_signalName == "VSS" );
128 BOOST_TEST( pin->m_pinNumber == "A1" );
129 BOOST_TEST( pin->m_Rpin.value[0] == 0.246 );
130 BOOST_TEST( pin->m_Rpin.value[1] == 0.165 );
131 BOOST_TEST( pin->m_Rpin.value[2] == 0.377 );
132 BOOST_TEST( pin->m_Lpin.value[0] == 1.49e-9 );
133 BOOST_TEST( pin->m_Lpin.value[1] == 0.98e-9 );
134 BOOST_TEST( pin->m_Lpin.value[2] == 2.23e-9 );
135 BOOST_TEST( pin->m_Cpin.value[0] == 0.40e-12 );
136 BOOST_TEST( pin->m_Cpin.value[1] == 0.29e-12 );
137 BOOST_TEST( pin->m_Cpin.value[2] == 0.56e-12 );
138 BOOST_TEST( pin->m_models.size() == 0 );
139
140 pin = comp->GetPin( "A2" );
141
142 BOOST_REQUIRE( pin != nullptr );
143
144 BOOST_TEST( pin->m_signalName == "VDD" );
145 BOOST_TEST( pin->m_pinNumber == "A2" );
146 BOOST_TEST( pin->m_Rpin.value[0] == 0.246 );
147 BOOST_TEST( pin->m_Rpin.value[1] == 0.165 );
148 BOOST_TEST( pin->m_Rpin.value[2] == 0.377 );
149 BOOST_TEST( pin->m_Lpin.value[0] == 1.49e-9 );
150 BOOST_TEST( pin->m_Lpin.value[1] == 0.98e-9 );
151 BOOST_TEST( pin->m_Lpin.value[2] == 2.23e-9 );
152 BOOST_TEST( pin->m_Cpin.value[0] == 0.40e-12 );
153 BOOST_TEST( pin->m_Cpin.value[1] == 0.29e-12 );
154 BOOST_TEST( pin->m_Cpin.value[2] == 0.56e-12 );
155 BOOST_TEST( pin->m_models.size() == 0 );
156
157 pin = comp->GetPin( "B1" );
158
159 BOOST_REQUIRE( pin != nullptr );
160
161 BOOST_TEST( pin->m_signalName == "A0" );
162 BOOST_TEST( pin->m_pinNumber == "B1" );
163 BOOST_TEST( pin->m_Rpin.value[0] == 0.225 );
164 BOOST_TEST( pin->m_Rpin.value[1] == 0.225 );
165 BOOST_TEST( pin->m_Rpin.value[2] == 0.225 );
166 BOOST_TEST( pin->m_Lpin.value[0] == 1.18e-9 );
167 BOOST_TEST( pin->m_Lpin.value[1] == 1.18e-9 );
168 BOOST_TEST( pin->m_Lpin.value[2] == 1.18e-9 );
169 BOOST_TEST( pin->m_Cpin.value[0] == 0.39e-12 );
170 BOOST_TEST( pin->m_Cpin.value[1] == 0.39e-12 );
171 BOOST_TEST( pin->m_Cpin.value[2] == 0.39e-12 );
172 BOOST_TEST( pin->m_models.size() == 1 );
173 BOOST_TEST( pin->m_models[0]->m_name == "AC40" );
174
175 pin = comp->GetPin( "B2" );
176
177 BOOST_REQUIRE( pin != nullptr );
178
179 BOOST_TEST( pin->m_signalName == "DQ0" );
180 BOOST_TEST( pin->m_pinNumber == "B2" );
181 BOOST_TEST( pin->m_Rpin.value[0] == 0.214 );
182 BOOST_TEST( pin->m_Rpin.value[1] == 0.214 );
183 BOOST_TEST( pin->m_Rpin.value[2] == 0.214 );
184 BOOST_TEST( pin->m_Lpin.value[0] == 1.05e-9 );
185 BOOST_TEST( pin->m_Lpin.value[1] == 1.05e-9 );
186 BOOST_TEST( pin->m_Lpin.value[2] == 1.05e-9 );
187 BOOST_TEST( pin->m_Cpin.value[0] == 0.39e-12 );
188 BOOST_TEST( pin->m_Cpin.value[1] == 0.39e-12 );
189 BOOST_TEST( pin->m_Cpin.value[2] == 0.39e-12 );
190 BOOST_TEST( pin->m_models.size() == 2 );
191 BOOST_TEST( pin->m_models[0]->m_name == "DQ40" );
192 BOOST_TEST( pin->m_models[1]->m_name == "DQ40_ODT40" );
193
195
196 model = top.GetModel( "AC40" );
197
198 BOOST_REQUIRE( model != nullptr );
199 BOOST_TEST_INFO( "Model: " << model->m_name );
200
201 BOOST_TEST( model->m_name == "AC40" );
202 BOOST_TEST( (int) model->m_type == (int) IBIS_MODEL_TYPE::OUTPUT );
203 BOOST_TEST( (int) model->m_polarity == (int) IBIS_MODEL_POLARITY::NON_INVERTING );
204 BOOST_TEST( (int) model->m_enable = (int) IBIS_MODEL_ENABLE::ACTIVE_HIGH );
205 BOOST_TEST( model->m_vmeas == 0.675 );
206 BOOST_TEST( model->m_cref == 5e-12 );
207 BOOST_TEST( model->m_rref == 50.0 );
208 BOOST_TEST( model->m_vref == 0.675 );
209 BOOST_TEST( model->m_C_comp.value[0] == 2.68e-12 );
210 BOOST_TEST( model->m_C_comp.value[1] == 2.64e-12 );
211 BOOST_TEST( model->m_C_comp.value[2] == 2.75e-12 );
212 BOOST_TEST( model->m_voltageRange.value[0] == 1.35 );
213 BOOST_TEST( model->m_voltageRange.value[1] == 1.28 );
214 BOOST_TEST( model->m_voltageRange.value[2] == 1.42 );
215 BOOST_TEST( model->m_temperatureRange.value[0] == 50.0 );
216 BOOST_TEST( model->m_temperatureRange.value[1] == 100.0 );
217 BOOST_TEST( model->m_temperatureRange.value[2] == 0.0 );
218
219 BOOST_TEST( model->HasGNDClamp() );
220 BOOST_TEST( model->m_GNDClamp.m_entries.size() == 4 );
221 BOOST_TEST( model->m_GNDClamp.m_entries[0].V == -1.35 );
222 BOOST_TEST( model->m_GNDClamp.m_entries[0].I.value[0] == -26.89e-3 );
223 BOOST_TEST( model->m_GNDClamp.m_entries[0].I.value[1] == -20.44e-3 );
224 BOOST_TEST( model->m_GNDClamp.m_entries[0].I.value[2] == -35.94e-3 );
225 BOOST_TEST( model->m_GNDClamp.m_entries[1].V == 0.0 );
226 BOOST_TEST( model->m_GNDClamp.m_entries[1].I.value[0] == -29.09e-9 );
227 BOOST_TEST( model->m_GNDClamp.m_entries[1].I.value[1] == -44.53e-9 );
228 BOOST_TEST( model->m_GNDClamp.m_entries[1].I.value[2] == -51.87e-9 );
229 BOOST_TEST( model->m_GNDClamp.m_entries[2].V == 1.35 );
230 BOOST_TEST( model->m_GNDClamp.m_entries[2].I.value[0] == 0.518e-6 );
231 BOOST_TEST( model->m_GNDClamp.m_entries[2].I.value[1] == 0.0 );
232 BOOST_TEST( model->m_GNDClamp.m_entries[2].I.value[2] == 0.444e-6 );
233 BOOST_TEST( model->m_GNDClamp.m_entries[3].V == 2.7 );
234 BOOST_TEST( model->m_GNDClamp.m_entries[3].I.value[0] == 0.0 );
235 BOOST_TEST( model->m_GNDClamp.m_entries[3].I.value[1] == 0.0 );
236 BOOST_TEST( model->m_GNDClamp.m_entries[3].I.value[2] == 0.0 );
237
238 BOOST_TEST( model->HasPOWERClamp() );
239 BOOST_TEST( model->m_POWERClamp.m_entries.size() == 4 );
240 BOOST_TEST( model->m_POWERClamp.m_entries[0].V == -1.35 );
241 BOOST_TEST( model->m_POWERClamp.m_entries[0].I.value[0] == 55.06e-3 );
242 BOOST_TEST( model->m_POWERClamp.m_entries[0].I.value[1] == 46.76e-3 );
243 BOOST_TEST( model->m_POWERClamp.m_entries[0].I.value[2] == 74.09e-3 );
244 BOOST_TEST( model->m_POWERClamp.m_entries[1].V == 0.0 );
245 BOOST_TEST( model->m_POWERClamp.m_entries[1].I.value[0] == 0.0 );
246 BOOST_TEST( model->m_POWERClamp.m_entries[1].I.value[1] == 0.515e-6 );
247 BOOST_TEST( model->m_POWERClamp.m_entries[1].I.value[2] == 74.94e-9 );
248 BOOST_TEST( model->m_POWERClamp.m_entries[2].V == 1.35 );
249 BOOST_TEST( model->m_POWERClamp.m_entries[2].I.value[0] == 0.0 );
250 BOOST_TEST( model->m_POWERClamp.m_entries[2].I.value[1] == 0.0 );
251 BOOST_TEST( model->m_POWERClamp.m_entries[2].I.value[2] == 0.0 );
252 BOOST_TEST( model->m_POWERClamp.m_entries[3].V == 2.7 );
253 BOOST_TEST( model->m_POWERClamp.m_entries[3].I.value[0] == 0.0 );
254 BOOST_TEST( model->m_POWERClamp.m_entries[3].I.value[1] == 0.0 );
255 BOOST_TEST( model->m_POWERClamp.m_entries[3].I.value[2] == 0.0 );
256
257 BOOST_TEST( model->HasPullup() );
258 BOOST_TEST( model->m_pullup.m_entries.size() == 4 );
259 BOOST_TEST( model->m_pullup.m_entries[0].V == -1.35 );
260 BOOST_TEST( model->m_pullup.m_entries[0].I.value[0] == 18.04e-3 );
261 BOOST_TEST( model->m_pullup.m_entries[0].I.value[1] == 16.15e-3 );
262 BOOST_TEST( model->m_pullup.m_entries[0].I.value[2] == 17.46e-3 );
263 BOOST_TEST( model->m_pullup.m_entries[1].V == 0.0 );
264 BOOST_TEST( model->m_pullup.m_entries[1].I.value[0] == 3.412e-9 );
265 BOOST_TEST( model->m_pullup.m_entries[1].I.value[1] == 0.527e-6 );
266 BOOST_TEST( model->m_pullup.m_entries[1].I.value[2] == 0.327e-6 );
267 BOOST_TEST( model->m_pullup.m_entries[2].V == 1.35 );
268 BOOST_TEST( model->m_pullup.m_entries[2].I.value[0] == -26.46e-3 );
269 BOOST_TEST( model->m_pullup.m_entries[2].I.value[1] == -24.75e-3 );
270 BOOST_TEST( model->m_pullup.m_entries[2].I.value[2] == -22.82e-3 );
271 BOOST_TEST( model->m_pullup.m_entries[3].V == 2.7 );
272 BOOST_TEST( model->m_pullup.m_entries[3].I.value[0] == -31.03e-3 );
273 BOOST_TEST( model->m_pullup.m_entries[3].I.value[1] == -28.82e-3 );
274 BOOST_TEST( model->m_pullup.m_entries[3].I.value[2] == -27.31e-3 );
275
276 BOOST_TEST( model->HasPulldown() );
277 BOOST_TEST( model->m_pulldown.m_entries.size() == 4 );
278 BOOST_TEST( model->m_pulldown.m_entries[0].V == -1.35 );
279 BOOST_TEST( model->m_pulldown.m_entries[0].I.value[0] == -23.01e-3 );
280 BOOST_TEST( model->m_pulldown.m_entries[0].I.value[1] == -20.02e-3 );
281 BOOST_TEST( model->m_pulldown.m_entries[0].I.value[2] == -20.46e-3 );
282 BOOST_TEST( model->m_pulldown.m_entries[1].V == 0.0 );
283 BOOST_TEST( model->m_pulldown.m_entries[1].I.value[0] == -38.81e-9 );
284 BOOST_TEST( model->m_pulldown.m_entries[1].I.value[1] == -32.33e-9 );
285 BOOST_TEST( model->m_pulldown.m_entries[1].I.value[2] == -49.77e-9 );
286 BOOST_TEST( model->m_pulldown.m_entries[2].V == 1.35 );
287 BOOST_TEST( model->m_pulldown.m_entries[2].I.value[0] == 26.98e-3 );
288 BOOST_TEST( model->m_pulldown.m_entries[2].I.value[1] == 23.63e-3 );
289 BOOST_TEST( model->m_pulldown.m_entries[2].I.value[2] == 26.60e-3 );
290 BOOST_TEST( model->m_pulldown.m_entries[3].V == 2.7 );
291 BOOST_TEST( model->m_pulldown.m_entries[3].I.value[0] == 29.20e-3 );
292 BOOST_TEST( model->m_pulldown.m_entries[3].I.value[1] == 25.40e-3 );
293 BOOST_TEST( model->m_pulldown.m_entries[3].I.value[2] == 29.03e-3 );
294
295 BOOST_TEST( model->m_ramp.m_Rload == 50.0 );
296 BOOST_TEST( model->m_ramp.m_rising.value[0].m_dv == 0.462 );
297 BOOST_TEST( model->m_ramp.m_rising.value[0].m_dt == 0.131e-9 );
298 BOOST_TEST( model->m_ramp.m_rising.value[1].m_dv == 0.439 );
299 BOOST_TEST( model->m_ramp.m_rising.value[1].m_dt == 0.138e-9 );
300 BOOST_TEST( model->m_ramp.m_rising.value[2].m_dv == 0.462 );
301 BOOST_TEST( model->m_ramp.m_rising.value[2].m_dt == 0.158e-9 );
302 BOOST_TEST( model->m_ramp.m_falling.value[0].m_dv == 0.465 );
303 BOOST_TEST( model->m_ramp.m_falling.value[0].m_dt == 0.116e-9 );
304 BOOST_TEST( model->m_ramp.m_falling.value[1].m_dv == 0.438 );
305 BOOST_TEST( model->m_ramp.m_falling.value[1].m_dt == 0.126e-9 );
306 BOOST_TEST( model->m_ramp.m_falling.value[2].m_dv == 0.468 );
307 BOOST_TEST( model->m_ramp.m_falling.value[2].m_dt == 0.117e-9 );
308
309 BOOST_TEST( model->m_risingWaveforms.size() == 2 );
310 BOOST_TEST( (int) model->m_risingWaveforms[1]->m_type == (int) IBIS_WAVEFORM_TYPE::RISING );
311 BOOST_TEST( model->m_risingWaveforms[1]->m_R_fixture == 50.0 );
312 BOOST_TEST( model->m_risingWaveforms[1]->m_V_fixture == 1.35 );
313 BOOST_TEST( model->m_risingWaveforms[1]->m_V_fixture_min == 1.28 );
314 BOOST_TEST( model->m_risingWaveforms[1]->m_V_fixture_max == 1.42 );
315 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries.size() == 5 );
316 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[0].t == 0 );
317 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[0].V.value[0] == 0.573 );
318 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[0].V.value[1] == 0.550 );
319 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[0].V.value[2] == 0.636 );
320 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[1].t == 1.001e-9 );
321 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[1].V.value[0] == 0.574 );
322 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[1].V.value[1] == 0.551 );
323 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[1].V.value[2] == 0.906 );
324 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[2].t == 2e-9 );
325 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[2].V.value[0] == 1.348 );
326 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[2].V.value[1] == 1.269 );
327 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[2].V.value[2] == 1.416 );
328 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[3].t == 2.491e-9 );
329 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[3].V.value[0] == 1.349 );
330 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[3].V.value[1] == 1.280 );
331 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[3].V.value[2] == 1.416 );
332 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[4].t == 10e-9 );
333 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[4].V.value[0] == 1.349 );
334 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[4].V.value[1] == 1.282 );
335 BOOST_TEST( model->m_risingWaveforms[1]->m_table.m_entries[4].V.value[2] == 1.417 );
336
337 BOOST_TEST( model->m_fallingWaveforms.size() == 2 );
338 BOOST_TEST( (int) model->m_fallingWaveforms[1]->m_type == (int) IBIS_WAVEFORM_TYPE::FALLING );
339 BOOST_TEST( model->m_fallingWaveforms[1]->m_R_fixture == 50.0 );
340 BOOST_TEST( model->m_fallingWaveforms[1]->m_V_fixture == 1.35 );
341 BOOST_TEST( model->m_fallingWaveforms[1]->m_V_fixture_min == 1.28 );
342 BOOST_TEST( model->m_fallingWaveforms[1]->m_V_fixture_max == 1.42 );
343 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries.size() == 5 );
344 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[0].t == 0 );
345 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[0].V.value[0] == 1.349 );
346 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[0].V.value[1] == 1.282 );
347 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[0].V.value[2] == 1.417 );
348 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[1].t == 1.02e-9 );
349 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[1].V.value[0] == 1.350 );
350 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[1].V.value[1] == 1.283 );
351 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[1].V.value[2] == 1.382 );
352 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[2].t == 1.988e-9 );
353 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[2].V.value[0] == 0.576 );
354 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[2].V.value[1] == 0.606 );
355 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[2].V.value[2] == 0.638 );
356 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[3].t == 2.51e-9 );
357 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[3].V.value[0] == 0.574 );
358 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[3].V.value[1] == 0.552 );
359 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[3].V.value[2] == 0.637 );
360 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[4].t == 10e-9 );
361 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[4].V.value[0] == 0.573 );
362 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[4].V.value[1] == 0.550 );
363 BOOST_TEST( model->m_fallingWaveforms[1]->m_table.m_entries[4].V.value[2] == 0.636 );
364
365 BOOST_TEST( model->m_submodels.size() == 0 );
366
367 model = top.GetModel( "DQ40_ODT40" );
368
369 BOOST_REQUIRE( model != nullptr );
370 BOOST_TEST_INFO( "Model: " << model->m_name );
371
372 BOOST_TEST( model->m_name == "DQ40_ODT40" );
373 BOOST_TEST( (int) model->m_type == (int) IBIS_MODEL_TYPE::IO );
374 BOOST_TEST( model->m_vinl == 0.515 );
375 BOOST_TEST( model->m_vinh == 0.835 );
376 BOOST_TEST( model->m_vmeas == 0.675 );
377 BOOST_TEST( model->m_cref == 0.0 );
378 BOOST_TEST( model->m_rref == 25.0 );
379 BOOST_TEST( model->m_vref == 0.675 );
380 BOOST_TEST( model->m_C_comp.value[0] == 1.36e-12 );
381 BOOST_TEST( model->m_C_comp.value[1] == 1.26e-12 );
382 BOOST_TEST( model->m_C_comp.value[2] == 1.46e-12 );
383 BOOST_TEST( model->m_voltageRange.value[0] == 1.35 );
384 BOOST_TEST( model->m_voltageRange.value[1] == 1.28 );
385 BOOST_TEST( model->m_voltageRange.value[2] == 1.42 );
386 BOOST_TEST( model->m_temperatureRange.value[0] == 50.0 );
387 BOOST_TEST( model->m_temperatureRange.value[1] == 100.0 );
388 BOOST_TEST( model->m_temperatureRange.value[2] == 0.0 );
389
390 BOOST_TEST( model->HasGNDClamp() );
391 BOOST_TEST( model->m_GNDClamp.m_entries.size() == 4 );
392 BOOST_TEST( model->m_GNDClamp.m_entries[0].V == -1.35 );
393 BOOST_TEST( model->m_GNDClamp.m_entries[0].I.value[0] == -39.74e-3 );
394 BOOST_TEST( model->m_GNDClamp.m_entries[0].I.value[1] == -37.76e-3 );
395 BOOST_TEST( model->m_GNDClamp.m_entries[0].I.value[2] == -41.37e-3 );
396 BOOST_TEST( model->m_GNDClamp.m_entries[1].V == -0.175 );
397 BOOST_TEST( std::isnan( model->m_GNDClamp.m_entries[1].I.value[0] ) );
398 BOOST_TEST( std::isnan( model->m_GNDClamp.m_entries[1].I.value[1] ) );
399 BOOST_TEST( model->m_GNDClamp.m_entries[1].I.value[2] == 0.0 );
400 BOOST_TEST( model->m_GNDClamp.m_entries[2].V == 1.35 );
401 BOOST_TEST( model->m_GNDClamp.m_entries[2].I.value[0] == 0.0 );
402 BOOST_TEST( model->m_GNDClamp.m_entries[2].I.value[1] == 0.0 );
403 BOOST_TEST( model->m_GNDClamp.m_entries[2].I.value[2] == 0.0 );
404 BOOST_TEST( model->m_GNDClamp.m_entries[3].V == 2.7 );
405 BOOST_TEST( model->m_GNDClamp.m_entries[3].I.value[0] == 0.0 );
406 BOOST_TEST( model->m_GNDClamp.m_entries[3].I.value[1] == 0.0 );
407 BOOST_TEST( model->m_GNDClamp.m_entries[3].I.value[2] == 0.0 );
408
409 BOOST_TEST( model->HasPOWERClamp() );
410 BOOST_TEST( model->m_POWERClamp.m_entries.size() == 4 );
411 BOOST_TEST( model->m_POWERClamp.m_entries[0].V == -1.35 );
412 BOOST_TEST( model->m_POWERClamp.m_entries[0].I.value[0] == 88.45e-3 );
413 BOOST_TEST( model->m_POWERClamp.m_entries[0].I.value[1] == 79.46e-3 );
414 BOOST_TEST( model->m_POWERClamp.m_entries[0].I.value[2] == 95.25e-3 );
415 BOOST_TEST( model->m_POWERClamp.m_entries[1].V == -0.28 );
416 BOOST_TEST( std::isnan( model->m_POWERClamp.m_entries[1].I.value[0] ) );
417 BOOST_TEST( model->m_POWERClamp.m_entries[1].I.value[1] == 0.0 );
418 BOOST_TEST( std::isnan( model->m_POWERClamp.m_entries[1].I.value[2] ) );
419 BOOST_TEST( model->m_POWERClamp.m_entries[2].V == 1.35 );
420 BOOST_TEST( model->m_POWERClamp.m_entries[2].I.value[0] == 0.0 );
421 BOOST_TEST( model->m_POWERClamp.m_entries[2].I.value[1] == 0.0 );
422 BOOST_TEST( model->m_POWERClamp.m_entries[2].I.value[2] == 0.0 );
423 BOOST_TEST( model->m_POWERClamp.m_entries[3].V == 2.7 );
424 BOOST_TEST( model->m_POWERClamp.m_entries[3].I.value[0] == 0.0 );
425 BOOST_TEST( model->m_POWERClamp.m_entries[3].I.value[1] == 0.0 );
426 BOOST_TEST( model->m_POWERClamp.m_entries[3].I.value[2] == 0.0 );
427
428 BOOST_TEST( model->HasPullup() );
429 BOOST_TEST( model->m_pullup.m_entries.size() == 4 );
430 BOOST_TEST( model->m_pullup.m_entries[0].V == -1.35 );
431 BOOST_TEST( model->m_pullup.m_entries[0].I.value[0] == 7.027e-3 );
432 BOOST_TEST( model->m_pullup.m_entries[0].I.value[1] == 7.411e-3 );
433 BOOST_TEST( model->m_pullup.m_entries[0].I.value[2] == 8.620e-3 );
434 BOOST_TEST( model->m_pullup.m_entries[1].V == 0.0 );
435 BOOST_TEST( model->m_pullup.m_entries[1].I.value[0] == 31.17e-6 );
436 BOOST_TEST( model->m_pullup.m_entries[1].I.value[1] == 35.34e-6 );
437 BOOST_TEST( model->m_pullup.m_entries[1].I.value[2] == 10.66e-6 );
438 BOOST_TEST( model->m_pullup.m_entries[2].V == 1.35 );
439 BOOST_TEST( model->m_pullup.m_entries[2].I.value[0] == -21.21e-3 );
440 BOOST_TEST( model->m_pullup.m_entries[2].I.value[1] == -19.19e-3 );
441 BOOST_TEST( model->m_pullup.m_entries[2].I.value[2] == -25.02e-3 );
442 BOOST_TEST( model->m_pullup.m_entries[3].V == 2.7 );
443 BOOST_TEST( model->m_pullup.m_entries[3].I.value[0] == -25.77e-3 );
444 BOOST_TEST( model->m_pullup.m_entries[3].I.value[1] == -23.32e-3 );
445 BOOST_TEST( model->m_pullup.m_entries[3].I.value[2] == -30.53e-3 );
446
447 BOOST_TEST( model->HasPulldown() );
448 BOOST_TEST( model->m_pulldown.m_entries.size() == 4 );
449 BOOST_TEST( model->m_pulldown.m_entries[0].V == -1.35 );
450 BOOST_TEST( model->m_pulldown.m_entries[0].I.value[0] == -13.96e-3 );
451 BOOST_TEST( model->m_pulldown.m_entries[0].I.value[1] == -12.59e-3 );
452 BOOST_TEST( model->m_pulldown.m_entries[0].I.value[2] == -15.56e-3 );
453 BOOST_TEST( model->m_pulldown.m_entries[1].V == 0.0 );
454 BOOST_TEST( model->m_pulldown.m_entries[1].I.value[0] == -3.893e-6 );
455 BOOST_TEST( model->m_pulldown.m_entries[1].I.value[1] == -4.109e-6 );
456 BOOST_TEST( model->m_pulldown.m_entries[1].I.value[2] == -1.483e-6 );
457 BOOST_TEST( model->m_pulldown.m_entries[2].V == 1.35 );
458 BOOST_TEST( model->m_pulldown.m_entries[2].I.value[0] == 26.08e-3 );
459 BOOST_TEST( model->m_pulldown.m_entries[2].I.value[1] == 22.37e-3 );
460 BOOST_TEST( model->m_pulldown.m_entries[2].I.value[2] == 30.44e-3 );
461 BOOST_TEST( model->m_pulldown.m_entries[3].V == 2.7 );
462 BOOST_TEST( model->m_pulldown.m_entries[3].I.value[0] == 30.20e-3 );
463 BOOST_TEST( model->m_pulldown.m_entries[3].I.value[1] == 25.46e-3 );
464 BOOST_TEST( model->m_pulldown.m_entries[3].I.value[2] == 36.99e-3 );
465
466 BOOST_TEST( model->m_ramp.m_Rload == 50.0 );
467 BOOST_TEST( model->m_ramp.m_rising.value[0].m_dv == 0.451 );
468 BOOST_TEST( model->m_ramp.m_rising.value[0].m_dt == 134e-12 );
469 BOOST_TEST( model->m_ramp.m_rising.value[1].m_dv == 0.424 );
470 BOOST_TEST( model->m_ramp.m_rising.value[1].m_dt == 184e-12 );
471 BOOST_TEST( model->m_ramp.m_rising.value[2].m_dv == 0.495 );
472 BOOST_TEST( model->m_ramp.m_rising.value[2].m_dt == 103e-12 );
473 BOOST_TEST( model->m_ramp.m_falling.value[0].m_dv == 0.438 );
474 BOOST_TEST( model->m_ramp.m_falling.value[0].m_dt == 120e-12 );
475 BOOST_TEST( model->m_ramp.m_falling.value[1].m_dv == 0.411 );
476 BOOST_TEST( model->m_ramp.m_falling.value[1].m_dt == 159e-12 );
477 BOOST_TEST( model->m_ramp.m_falling.value[2].m_dv == 0.475 );
478 BOOST_TEST( model->m_ramp.m_falling.value[2].m_dt == 90e-12 );
479
480 BOOST_TEST( model->m_risingWaveforms.size() == 2 );
481 BOOST_TEST( (int) model->m_risingWaveforms[0]->m_type == (int) IBIS_WAVEFORM_TYPE::RISING );
482 BOOST_TEST( model->m_risingWaveforms[0]->m_R_fixture == 50.0 );
483 BOOST_TEST( model->m_risingWaveforms[0]->m_V_fixture == 1.35 );
484 BOOST_TEST( model->m_risingWaveforms[0]->m_V_fixture_min == 1.28 );
485 BOOST_TEST( model->m_risingWaveforms[0]->m_V_fixture_max == 1.42 );
486 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries.size() == 4 );
487 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[0].t == 0 );
488 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[0].V.value[0] == 0.6179 );
489 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[0].V.value[1] == 0.5969 );
490 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[0].V.value[2] == 0.6325 );
491 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[1].t == 199e-12 );
492 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[1].V.value[0] == 0.9465 );
493 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[1].V.value[1] == 0.8243 );
494 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[1].V.value[2] == 1.1430 );
495 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[2].t == 399e-12 );
496 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[2].V.value[0] == 1.340 );
497 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[2].V.value[1] == 1.261 );
498 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[2].V.value[2] == 1.420 );
499 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[3].t == 937e-12 );
500 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[3].V.value[0] == 1.349 );
501 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[3].V.value[1] == 1.282 );
502 BOOST_TEST( model->m_risingWaveforms[0]->m_table.m_entries[3].V.value[2] == 1.424 );
503
504 BOOST_TEST( model->m_fallingWaveforms.size() == 2 );
505 BOOST_TEST( (int) model->m_fallingWaveforms[0]->m_type == (int) IBIS_WAVEFORM_TYPE::FALLING );
506 BOOST_TEST( model->m_fallingWaveforms[0]->m_R_fixture == 50.0 );
507 BOOST_TEST( model->m_fallingWaveforms[0]->m_V_fixture == 1.35 );
508 BOOST_TEST( model->m_fallingWaveforms[0]->m_V_fixture_min == 1.28 );
509 BOOST_TEST( model->m_fallingWaveforms[0]->m_V_fixture_max == 1.42 );
510 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries.size() == 4 );
511 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[0].t == 0 );
512 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[0].V.value[0] == 1.349 );
513 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[0].V.value[1] == 1.282 );
514 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[0].V.value[2] == 1.424 );
515 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[1].t == 198e-12 );
516 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[1].V.value[0] == 1.270 );
517 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[1].V.value[1] == 1.248 );
518 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[1].V.value[2] == 1.203 );
519 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[2].t == 398e-12 );
520 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[2].V.value[0] == 0.6815 );
521 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[2].V.value[1] == 0.7726 );
522 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[2].V.value[2] == 0.6551 );
523 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[3].t == 937e-12 );
524 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[3].V.value[0] == 0.6179 );
525 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[3].V.value[1] == 0.5969);
526 BOOST_TEST( model->m_fallingWaveforms[0]->m_table.m_entries[3].V.value[2] == 0.6325 );
527
528 BOOST_TEST( model->m_submodels.size() == 1 );
529 BOOST_TEST( model->m_submodels[0].m_name == "40ohm_ODT" );
530 BOOST_TEST( (int) model->m_submodels[0].m_type == (int) IBIS_SUBMODEL_TYPE::DYNAMIC_CLAMP );
531 BOOST_TEST( (int) model->m_submodels[0].m_mode == (int) IBIS_SUBMODEL_MODE::NON_DRIVING );
532
533 BOOST_TEST( model->m_submodels[0].HasGNDClamp() );
534 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries.size() == 4 );
535 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[0].V == -1.35 );
536 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[0].I.value[0] == -6.975e-3 );
537 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[0].I.value[1] == -6.287e-3 );
538 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[0].I.value[2] == -7.776e-3 );
539 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[1].V == 0.0 );
540 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[1].I.value[0] == -1.585e-6 );
541 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[1].I.value[1] == -2.224e-6 );
542 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[1].I.value[2] == -819.6e-9 );
543 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[2].V == 1.35 );
544 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[2].I.value[0] == 12.86e-3 );
545 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[2].I.value[1] == 11.10e-3 );
546 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[2].I.value[2] == 14.96e-3 );
547 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[3].V == 2.7 );
548 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[3].I.value[0] == 15.10e-3 );
549 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[3].I.value[1] == 12.73e-3 );
550 BOOST_TEST( model->m_submodels[0].m_GNDClamp.m_entries[3].I.value[2] == 18.50e-3 );
551
552 BOOST_TEST( model->m_submodels[0].HasPOWERClamp() );
553 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries.size() == 4 );
554 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[0].V == -1.35 );
555 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[0].I.value[0] == 3.515e-3 );
556 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[0].I.value[1] == 3.706e-3 );
557 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[0].I.value[2] == 4.312e-3 );
558 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[1].V == 0.0 );
559 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[1].I.value[0] == 13.01e-6 );
560 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[1].I.value[1] == 18.85e-6 );
561 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[1].I.value[2] == 5.723e-6 );
562 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[2].V == 1.35 );
563 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[2].I.value[0] == -10.50e-3 );
564 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[2].I.value[1] == -9.514e-3 );
565 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[2].I.value[2] == -12.39e-3 );
566 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[3].V == 2.7 );
567 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[3].I.value[0] == -12.88e-3 );
568 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[3].I.value[1] == -11.66e-3 );
569 BOOST_TEST( model->m_submodels[0].m_POWERClamp.m_entries[3].I.value[2] == -15.26e-3 );
570}
571
572// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23568
573// The [Series Pin Mapping] keyword was added in IBIS 4.1. Before this fix,
574// encountering it inside a [Component] section produced the fatal parse error
575// "Unknown keyword in COMPONENT context: series_pin_mapping".
576BOOST_AUTO_TEST_CASE( Load_v4_1_SeriesPinMapping )
577{
579
580 std::string path = GetLibraryPath( "ibis_v4_1_series_pin_mapping" );
581 KIBIS top( path, &reporter );
582
583 BOOST_TEST_INFO( "Parsed: " << path );
584 BOOST_TEST_INFO( "Reported: " << reporter.GetMessages() );
585
586 BOOST_TEST( top.m_valid );
587 BOOST_TEST( !reporter.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) );
588
589 KIBIS_COMPONENT* comp = top.GetComponent( "SeriesSwitchDevice" );
590
591 BOOST_REQUIRE( comp != nullptr );
592 BOOST_TEST( comp->m_name == "SeriesSwitchDevice" );
593 BOOST_TEST( comp->m_pins.size() == 4 );
594}
595
596
597// Regression test for #24228: parse → KIBIS_MODEL → writeSpiceDevice.
598BOOST_AUTO_TEST_CASE( Load_v4_1_RSeries, *boost::unit_test::tolerance( 1e-15 ) )
599{
601
602 std::string path = GetLibraryPath( "ibis_v4_1_r_series" );
603 KIBIS top( path, &reporter );
604
605 BOOST_TEST_INFO( "Parsed: " << path );
606 BOOST_TEST_INFO( "Reported: " << reporter.GetMessages() );
607
608 BOOST_TEST( top.m_valid );
609 BOOST_TEST( !reporter.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) );
610
611 KIBIS_MODEL* model = top.GetModel( "series_r" );
612 BOOST_REQUIRE( model != nullptr );
613 BOOST_TEST( model->m_name == "series_r" );
614 BOOST_TEST( (int) model->m_type == (int) IBIS_MODEL_TYPE::SERIES );
615
616 KIBIS_MODEL* swModel = top.GetModel( "series_sw" );
617 BOOST_REQUIRE( swModel != nullptr );
619
620 KIBIS_MODEL* mosfetModel = top.GetModel( "series_mosfet" );
623
625 BOOST_REQUIRE( parser.ParseFile( path ) );
626
627 const IbisModel* parsedSeries = nullptr;
628 const IbisModel* parsedSwitch = nullptr;
629 const IbisModel* parsedMosfet = nullptr;
630
631 for( const IbisModel& m : parser.m_ibisFile.m_models )
632 {
633 if( m.m_name == "series_r" )
634 parsedSeries = &m;
635 else if( m.m_name == "series_sw" )
636 parsedSwitch = &m;
637 else if( m.m_name == "series_mosfet" )
638 parsedMosfet = &m;
639 }
640
642 BOOST_TEST( parsedSeries->m_series.m_Rseries.value[0] == 25.0 );
643 BOOST_TEST( parsedSeries->m_series.m_Rseries.value[1] == 22.0 );
644 BOOST_TEST( parsedSeries->m_series.m_Rseries.value[2] == 28.0 );
645 BOOST_TEST( parsedSeries->m_series.m_Lseries.value[0] == 1.0e-9 );
646 BOOST_TEST( parsedSeries->m_series.m_Cseries.value[0] == 0.5e-12 );
647 BOOST_TEST( parsedSeries->m_series.m_RlSeries.value[0] == 0.1 );
648 BOOST_TEST( parsedSeries->m_series.m_RcSeries.value[0] == 1.0 );
649 BOOST_TEST( parsedSeries->m_series.m_LcSeries.value[0] == 0.5e-9 );
650
651 BOOST_TEST( parsedSeries->m_series.m_seriesCurrent.m_entries.size() == 3 );
652 BOOST_TEST( parsedSeries->m_series.m_seriesCurrent.m_entries[0].V == -1.0 );
653 BOOST_TEST( parsedSeries->m_series.m_seriesCurrent.m_entries[0].I.value[0] == -40.0e-3 );
654 BOOST_TEST( parsedSeries->m_series.m_seriesCurrent.m_entries[2].V == 1.0 );
655 BOOST_TEST( parsedSeries->m_series.m_seriesCurrent.m_entries[2].I.value[0] == 40.0e-3 );
656
658 BOOST_TEST( parsedSwitch->m_seriesOn.m_Rseries.value[0] == 5.0 );
659 BOOST_TEST( parsedSwitch->m_seriesOff.m_Rseries.value[0] == 1.0e6 );
660 BOOST_TEST( parsedSwitch->m_seriesOn.m_seriesCurrent.m_entries.size() == 3 );
661 BOOST_TEST( parsedSwitch->m_seriesOn.m_seriesCurrent.m_entries[0].I.value[0] == -200.0e-3 );
662 BOOST_TEST( parsedSwitch->m_seriesOff.m_seriesCurrent.m_entries.size() == 0 );
663
665 BOOST_REQUIRE( parsedMosfet->m_seriesOn.m_seriesMosfet.size() == 1 );
666
667 const IbisMosfetEntry& mosfet = parsedMosfet->m_seriesOn.m_seriesMosfet[0];
668 BOOST_TEST( mosfet.m_Vds == 1.0 );
669 BOOST_REQUIRE( mosfet.m_table.m_entries.size() == 4 );
670 BOOST_TEST( mosfet.m_table.m_entries[0].V == 0.0 );
671 BOOST_TEST( mosfet.m_table.m_entries[3].V == 3.3 );
672 BOOST_TEST( mosfet.m_table.m_entries[3].I.value[0] == 180.0e-3 );
673
674 KIBIS_COMPONENT* comp = top.GetComponent( "SeriesDevice" );
675 BOOST_REQUIRE( comp != nullptr );
676 BOOST_REQUIRE( comp->m_seriesPinMappings.size() == 3 );
677 BOOST_TEST( comp->m_seriesPinMappings[0].m_pin1 == "1" );
678 BOOST_TEST( comp->m_seriesPinMappings[0].m_pin2 == "2" );
679 BOOST_TEST( comp->m_seriesPinMappings[0].m_modelName == "series_r" );
680 BOOST_TEST( comp->m_seriesPinMappings[1].m_modelName == "series_sw" );
681 BOOST_TEST( comp->m_seriesPinMappings[1].m_groupName == "group_a" );
682
683 KIBIS_PIN* pin1 = comp->GetPin( "1" );
684 BOOST_REQUIRE( pin1 != nullptr );
685
686 std::string seriesModelName;
687 KIBIS_PIN* partner = pin1->SeriesPartner( &seriesModelName );
688 BOOST_REQUIRE( partner != nullptr );
689 BOOST_TEST( partner->m_pinNumber == "2" );
690 BOOST_TEST( seriesModelName == "series_r" );
691
692 BOOST_TEST( model->m_series.m_Rseries.value[0] == 25.0 );
693 BOOST_TEST( swModel->m_seriesOn.m_Rseries.value[0] == 5.0 );
694 BOOST_TEST( swModel->m_seriesOff.m_Rseries.value[0] == 1.0e6 );
695 BOOST_TEST( mosfetModel->m_seriesOn.m_seriesMosfet.size() == 1 );
696
697 std::string netlist;
699 BOOST_REQUIRE( pin1->writeSpiceDevice( netlist, "DEVICE_R", *model, kparams ) );
700
701 BOOST_TEST_INFO( "Emitted netlist for series_r:\n" << netlist );
702 BOOST_TEST( netlist.find( ".SUBCKT DEVICE_R PIN_A PIN_B" ) != std::string::npos );
703 BOOST_TEST( netlist.find( "R_S0 DIE_A " ) != std::string::npos );
704 BOOST_TEST( netlist.find( "L_S0 DIE_A " ) != std::string::npos );
705 BOOST_TEST( netlist.find( "C_S0 " ) != std::string::npos );
706 BOOST_TEST( netlist.find( "B_SC0 " ) != std::string::npos );
707 BOOST_TEST( netlist.find( ".ENDS DEVICE" ) != std::string::npos );
708 BOOST_TEST( netlist.find( "SW_STATE" ) == std::string::npos );
709
710 KIBIS_PIN* pin3 = comp->GetPin( "3" );
711 BOOST_REQUIRE( pin3 != nullptr );
712
713 netlist.clear();
714 BOOST_REQUIRE( pin3->writeSpiceDevice( netlist, "DEVICE_SW", *swModel, kparams ) );
715
716 BOOST_TEST_INFO( "Emitted netlist for series_sw:\n" << netlist );
717 BOOST_TEST( netlist.find( ".SUBCKT DEVICE_SW PIN_A PIN_B" ) != std::string::npos );
718 BOOST_TEST( netlist.find( ".param SW_STATE=1" ) != std::string::npos );
719 BOOST_TEST( netlist.find( "R_S1 DIE_A ARM_OUT1" ) != std::string::npos );
720 BOOST_TEST( netlist.find( "R_S2 DIE_A ARM_OUT2" ) != std::string::npos );
721 BOOST_TEST( netlist.find( "R_G1 ARM_OUT1 DIE_B R='0.001 / ((SW_STATE)" ) != std::string::npos );
722 BOOST_TEST( netlist.find( "R_G2 ARM_OUT2 DIE_B R='0.001 / (((1 - SW_STATE))" )
723 != std::string::npos );
724
725 KIBIS_PIN* pin5 = comp->GetPin( "5" );
726 BOOST_REQUIRE( pin5 != nullptr );
727
728 netlist.clear();
729 BOOST_REQUIRE( pin5->writeSpiceDevice( netlist, "DEVICE_M", *mosfetModel, kparams ) );
730
731 BOOST_TEST_INFO( "Emitted netlist for series_mosfet:\n" << netlist );
732 BOOST_TEST( netlist.find( "B_M1_0" ) != std::string::npos );
733 BOOST_TEST( netlist.find( "pwl(" ) != std::string::npos );
734}
735
736
737namespace
738{
739class CAPTURE_REPORTER : public SIMULATOR_REPORTER
740{
741public:
742 REPORTER& Report( const wxString& aText,
743 SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
744 {
745 m_messages << aText.ToStdString() << "\n";
746 return *this;
747 }
748
749 bool HasMessage() const override { return !m_messages.str().empty(); }
750
751 void OnSimStateChange( SIMULATOR* aObject, SIM_STATE aNewState ) override {}
752
753 std::stringstream m_messages;
754};
755
756
757// Wrap an emitted SUBCKT in a 1V .op rig. Returns NaN if ngspice fails.
758double RunSeriesOP( const std::string& aSubckt, const std::string& aSubcktName,
759 const std::string& aExtraParams = "" )
760{
761 auto sim = SPICE_SIMULATOR::CreateInstance( "ngspice" );
762
763 if( !sim )
764 {
765 BOOST_TEST_MESSAGE( "RunSeriesOP: CreateInstance(ngspice) returned null" );
766 return std::nan( "" );
767 }
768
769 CAPTURE_REPORTER reporter;
770 sim->SetReporter( &reporter );
771
772 // ngspice is a singleton; clear prior plots/circuits.
773 sim->Command( "bg_halt" );
774 sim->Command( "remcirc" );
775 sim->Command( "destroy all" );
776
777 // 1Ω sense resistor exposes current as a node-voltage delta.
778 std::string deck = "* KiCad QA Series harness\n";
779 deck += "V1 SRC 0 1\n";
780 deck += "R_sense SRC PIN_A 1\n";
781 deck += "V2 PIN_B 0 0\n";
782 deck += "X_dut PIN_A PIN_B " + aSubcktName;
783
784 if( !aExtraParams.empty() )
785 deck += " PARAMS: " + aExtraParams;
786
787 deck += "\n";
788 deck += aSubckt;
789 deck += "\n.save all\n.op\n.end\n";
790
791 if( !sim->LoadNetlist( deck ) )
792 {
793 BOOST_TEST_MESSAGE( "RunSeriesOP: LoadNetlist failed for deck:\n" << deck );
794 return std::nan( "" );
795 }
796
797 if( !sim->Run() )
798 {
799 BOOST_TEST_MESSAGE( "RunSeriesOP: Run() failed" );
800 return std::nan( "" );
801 }
802
803 while( sim->IsRunning() )
804 wxMilliSleep( 20 );
805
806 // bg callbacks may still fire briefly after IsRunning() flips.
807 sim->SetReporter( nullptr );
808
809 BOOST_TEST_MESSAGE( "RunSeriesOP: ngspice log:\n" << reporter.m_messages.str() );
810 BOOST_TEST_MESSAGE( "RunSeriesOP: current plot = " << sim->CurrentPlotName().ToStdString() );
811
812 // Node voltages survive plot indirection that branch currents do not.
813 std::vector<double> vSrc = sim->GetRealVector( "src" );
814 std::vector<double> vPinA = sim->GetRealVector( "pin_a" );
815
816 if( vSrc.empty() || vPinA.empty() )
817 {
818 BOOST_TEST_MESSAGE( "RunSeriesOP: node-voltage vectors unavailable. Available:" );
819
820 for( const std::string& name : sim->AllVectors() )
821 BOOST_TEST_MESSAGE( " " << name );
822
823 return std::nan( "" );
824 }
825
826 return ( vSrc.back() - vPinA.back() ) / 1.0;
827}
828} // namespace
829
830
831// End-to-end ngspice .op against the emitted SUBCKT. Disabled by default;
832// ngspice singleton state from prior suites can segfault subsequent runs.
833BOOST_AUTO_TEST_CASE( Run_v4_1_RSeries_Ngspice, * boost::unit_test::disabled() )
834{
836 std::string path = GetLibraryPath( "ibis_v4_1_r_series" );
837 KIBIS top( path, &reporter );
838
839 BOOST_REQUIRE( top.m_valid );
840
841 KIBIS_COMPONENT* comp = top.GetComponent( "SeriesDevice" );
843
845
846 // Cold-start warmup: ngspice singleton can drop the first .op otherwise.
847 {
848 auto warmup = SPICE_SIMULATOR::CreateInstance( "ngspice" );
849
850 if( warmup && warmup->LoadNetlist( "* warmup\nR1 1 0 1k\nV1 1 0 1\n.op\n.end\n" )
851 && warmup->Run() )
852 {
853 while( warmup->IsRunning() )
854 wxMilliSleep( 20 );
855 }
856 }
857
858 // Branches parallel per BIRD 41.8; L+Rl path (Rl=0.1Ω) dominates DC.
859 // Expect 0 < I < ~2A; exact value covered by static asserts.
860 {
861 KIBIS_PIN* pinA = comp->GetPin( "1" );
862 KIBIS_MODEL* model = top.GetModel( "series_r" );
864
865 std::string subckt;
866 BOOST_REQUIRE( pinA->writeSpiceDevice( subckt, "DEV_R", *model, kparams ) );
867
868 double iSeries = RunSeriesOP( subckt, "DEV_R" );
869
870 if( std::isnan( iSeries ) )
871 {
872 BOOST_TEST_MESSAGE( "ngspice unavailable; skipping Series .op" );
873 }
874 else
875 {
876 BOOST_TEST_INFO( "Series device .op current = " << iSeries );
878 BOOST_TEST( iSeries < 2.0 );
879 }
880 }
881
882 // Series_switch: On state 5Ω + 200 mA IV table; Off state 1 MΩ.
883 {
884 KIBIS_PIN* pinB = comp->GetPin( "3" );
885 KIBIS_MODEL* model = top.GetModel( "series_sw" );
887
888 std::string subckt;
890
891 double iOn = RunSeriesOP( subckt, "DEV_SW", "SW_STATE=1" );
892 double iOff = RunSeriesOP( subckt, "DEV_SW", "SW_STATE=0" );
893
894 if( std::isnan( iOn ) || std::isnan( iOff ) )
895 {
896 BOOST_TEST_MESSAGE( "ngspice unavailable; skipping Series_switch .op" );
897 }
898 else
899 {
900 BOOST_TEST_INFO( "SW On current = " << iOn << ", Off current = " << iOff );
901 BOOST_TEST( iOn > 0.1 );
902 BOOST_TEST( iOff < 1.0e-5 );
903 BOOST_TEST( iOn / iOff > 1.0e4 );
904 }
905 }
906}
907
908
909namespace
910{
911// Load fixture and return the SeriesDevice's SIM_MODEL_IBIS.
912std::unique_ptr<SIM_LIBRARY_IBIS> MakeLoadedSeriesLibrary( const std::string& aIbsPath,
913 SIM_MODEL_IBIS** aOutModel )
914{
915 auto lib = std::make_unique<SIM_LIBRARY_IBIS>();
917 lib->ReadFile( aIbsPath, reporter );
918
919 if( reporter.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
920 return nullptr;
921
922 for( const SIM_LIBRARY::MODEL& sm : lib->GetModels() )
923 {
924 SIM_MODEL_IBIS* candidate = dynamic_cast<SIM_MODEL_IBIS*>( &sm.model );
925
926 if( candidate && candidate->GetComponentName() == "SeriesDevice" )
927 {
928 *aOutModel = candidate;
929 return lib;
930 }
931 }
932
933 return nullptr;
934}
935} // namespace
936
937
938// SetIbisModel picks SERIES IO mode and resolves the [Series Pin Mapping] partner.
939BOOST_AUTO_TEST_CASE( Dialog_Series_PinMap_v4_1 )
940{
941 SIM_MODEL_IBIS* ibisModel = nullptr;
942 auto lib = MakeLoadedSeriesLibrary( GetLibraryPath( "ibis_v4_1_r_series" ), &ibisModel );
943
944 BOOST_REQUIRE( lib );
945 BOOST_REQUIRE( ibisModel );
946
947 BOOST_REQUIRE( ibisModel->ChangePin( *lib, "1" ) );
948
949 BOOST_TEST( !ibisModel->IsSeries() );
950 BOOST_TEST( ibisModel->GetPinCount() == 2 );
951 BOOST_TEST( ibisModel->GetPin( 0 ).modelPinName == "GND" );
952 BOOST_TEST( ibisModel->GetPin( 1 ).modelPinName == "IN/OUT" );
953
954 BOOST_REQUIRE( ibisModel->SetIbisModel( *lib, "1", "series_r" ) );
955
956 BOOST_TEST( ibisModel->IsSeries() );
957 BOOST_TEST( (int) ibisModel->GetIOMode() == (int) IBIS_IO_MODE::SERIES );
958 BOOST_TEST( ibisModel->GetPinCount() == 2 );
959 BOOST_TEST( ibisModel->GetPin( 0 ).modelPinName == "PIN_A" );
960 BOOST_TEST( ibisModel->GetPin( 1 ).modelPinName == "PIN_B" );
961 BOOST_TEST( ibisModel->GetSeriesPartnerPin() == "2" );
962 BOOST_TEST( ibisModel->FindParam( "sw_state" ) == nullptr );
963
964 // Series → single-ended round-trip must drop the SERIES layout.
965 BOOST_REQUIRE( ibisModel->SetIbisModel( *lib, "1", "series_r" ) );
966 ibisModel->SwitchSingleEndedDiff( false );
967 BOOST_TEST( !ibisModel->IsSeries() );
968 BOOST_TEST( ibisModel->GetPin( 0 ).modelPinName == "GND" );
969 BOOST_TEST( ibisModel->FindParam( "sw_state" ) == nullptr );
970}
971
972
973// Series_switch carries a sw_state instance param (default 1) onto the X-line.
974BOOST_AUTO_TEST_CASE( Dialog_Series_Switch_Params_v4_1 )
975{
976 SIM_MODEL_IBIS* ibisModel = nullptr;
977 auto lib = MakeLoadedSeriesLibrary( GetLibraryPath( "ibis_v4_1_r_series" ), &ibisModel );
978
979 BOOST_REQUIRE( lib );
980 BOOST_REQUIRE( ibisModel );
981 BOOST_REQUIRE( ibisModel->ChangePin( *lib, "3" ) );
982 BOOST_REQUIRE( ibisModel->SetIbisModel( *lib, "3", "series_sw" ) );
983
984 BOOST_TEST( ibisModel->IsSeries() );
985 BOOST_TEST( ibisModel->GetSeriesPartnerPin() == "4" );
986
987 const SIM_MODEL::PARAM* sw = ibisModel->FindParam( "sw_state" );
988 BOOST_REQUIRE( sw != nullptr );
990 BOOST_TEST( sw->info.spiceInstanceName == "SW_STATE" );
991 BOOST_TEST( sw->info.defaultValue == "1" );
992
993 ibisModel->SetParamValue( "sw_state", "0.5" );
994
995 std::string itemParams = ibisModel->SpiceGenerator().ItemParams();
996 BOOST_TEST_INFO( "Emitted ItemParams: " << itemParams );
997 BOOST_TEST( itemParams.find( "SW_STATE=0.5" ) != std::string::npos );
998
999 // Waveform-choice path copy-constructs the model; sw_state must come along.
1000 {
1001 ibisModel->SetParamValue( "sw_state", "0.25" );
1002 SIM_MODEL_IBIS copy( SIM_MODEL::TYPE::KIBIS_DEVICE, *ibisModel );
1003
1004 BOOST_TEST( copy.IsSeries() );
1005 BOOST_TEST( copy.GetSeriesPartnerPin() == "4" );
1006
1007 const SIM_MODEL::PARAM* copiedSw = copy.FindParam( "sw_state" );
1008 BOOST_REQUIRE( copiedSw != nullptr );
1009 BOOST_TEST( copiedSw->value == "0.25" );
1010 }
1011
1012 BOOST_REQUIRE( ibisModel->SetIbisModel( *lib, "1", "series_r" ) );
1013 BOOST_TEST( ibisModel->FindParam( "sw_state" ) == nullptr );
1014}
1015
1016
1017// Two-port SUBCKT round-trip through ngspice. Disabled by default — see
1018// Run_v4_1_RSeries_Ngspice.
1019BOOST_AUTO_TEST_CASE( Run_Series_Schematic_Ngspice, * boost::unit_test::disabled() )
1020{
1022 std::string path = GetLibraryPath( "ibis_v4_1_r_series" );
1023 KIBIS top( path, &reporter );
1024 BOOST_REQUIRE( top.m_valid );
1025
1026 KIBIS_COMPONENT* comp = top.GetComponent( "SeriesDevice" );
1027 KIBIS_PIN* pinA = comp ? comp->GetPin( "1" ) : nullptr;
1028 KIBIS_MODEL* model = top.GetModel( "series_r" );
1029 BOOST_REQUIRE( pinA && model );
1030
1032 std::string subckt;
1033 BOOST_REQUIRE( pinA->writeSpiceDevice( subckt, "DEV_R", *model, kparams ) );
1034
1035 // Header must be two-port; a stray GND there breaks X-line port mapping.
1036 BOOST_TEST( subckt.find( ".SUBCKT DEV_R PIN_A PIN_B" ) != std::string::npos );
1037 BOOST_TEST( subckt.find( ".SUBCKT DEV_R GND" ) == std::string::npos );
1038
1041
1042 std::string deck = "* QA harness for two-port Series subckt\n";
1043 deck += "V1 SRC 0 1\n";
1044 deck += "R_sense SRC PIN_A 1\n";
1045 deck += "X_dut PIN_A 0 DEV_R\n"; // PIN_B tied to global ground via 0
1046 deck += subckt;
1047 deck += "\n.save all\n.op\n.end\n";
1048
1049 sim->Command( "bg_halt" );
1050 sim->Command( "remcirc" );
1051 sim->Command( "destroy all" );
1052
1053 if( !sim->LoadNetlist( deck ) || !sim->Run() )
1054 {
1055 BOOST_TEST_MESSAGE( "ngspice unavailable; skipping Run_Series_Schematic_Ngspice .op" );
1056 return;
1057 }
1058
1059 while( sim->IsRunning() )
1060 wxMilliSleep( 20 );
1061
1062 std::vector<double> vSrc = sim->GetRealVector( "src" );
1063 std::vector<double> vA = sim->GetRealVector( "pin_a" );
1064
1065 if( vSrc.empty() || vA.empty() )
1066 {
1067 BOOST_TEST_MESSAGE( "ngspice OP vectors unavailable; skipping current check" );
1068 return;
1069 }
1070
1071 double iDevice = ( vSrc.back() - vA.back() ) / 1.0;
1072 BOOST_TEST_INFO( "Two-port Series .op current = " << iDevice );
1074 BOOST_TEST( iDevice < 2.0 );
1075}
1076
1077
const char * name
void Report(const std::string &aMsg, SEVERITY aSeverity=RPT_SEVERITY_INFO) const
Print a message.
Definition ibis_parser.h:70
REPORTER * m_Reporter
Definition ibis_parser.h:77
std::string m_name
bool m_valid
Definition kibis.h:57
bool writeSpiceDevice(std::string &aDest, const std::string &aName, KIBIS_MODEL &aModel, const KIBIS_PARAMETER &aParam)
Definition kibis.cpp:1507
Definition kibis.h:513
Interface to receive simulation updates from SPICE_SIMULATOR class.
static std::shared_ptr< SPICE_SIMULATOR > CreateInstance(const std::string &aName)
bool SetIbisModel(const SIM_LIBRARY_IBIS &aLib, const std::string &aPinNumber, const std::string &aModelName)
Bind to a KIBIS model, set IO mode from its type, manage sw_state.
void SwitchSingleEndedDiff(bool aDiff) override
IBIS_IO_MODE GetIOMode() const
bool IsSeries() const
const std::string & GetSeriesPartnerPin() const
bool ChangePin(const SIM_LIBRARY_IBIS &aLib, const std::string &aPinNumber)
update the list of available models based on the pin number.
std::string GetComponentName() const
int GetPinCount() const
Definition sim_model.h:468
const SPICE_GENERATOR & SpiceGenerator() const
Definition sim_model.h:431
const PARAM * FindParam(const std::string &aParamName) const
void SetParamValue(int aParamIndex, const std::string &aValue, SIM_VALUE::NOTATION aNotation=SIM_VALUE::NOTATION::SI)
const SIM_MODEL_PIN & GetPin(unsigned aIndex) const
Definition sim_model.h:469
virtual std::string ItemParams() const
A wrapper for reporting to a wxString object.
Definition reporter.h:193
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
SEVERITY
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
@ RPT_SEVERITY_INFO
std::string defaultValue
Definition sim_model.h:382
std::string spiceInstanceName
Definition sim_model.h:387
std::string value
Definition sim_model.h:400
const INFO & info
Definition sim_model.h:401
const std::string modelPinName
Definition sim_model.h:70
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
double iDevice
std::string deck
BOOST_TEST(netlist.find("R_G1 ARM_OUT1 DIE_B R='0.001 / ((SW_STATE)") !=std::string::npos)
std::string netlist
double iOff
KIBIS_PIN * pin3
std::string subckt
auto sim
std::string path
IbisParser parser & reporter
BOOST_AUTO_TEST_CASE(Null)
std::string seriesModelName
KIBIS_PIN * partner
KIBIS_MODEL * swModel
double iSeries
double iOn
KIBIS top(path, &reporter)
KIBIS_PIN * pin1
KIBIS_MODEL * model
const IbisMosfetEntry & mosfet
std::vector< double > vSrc
KIBIS_PARAMETER kparams
std::vector< double > vA
KIBIS_PIN * pin5
const IbisModel * parsedSwitch
const IbisModel * parsedMosfet
KIBIS_COMPONENT * comp
KIBIS_MODEL * mosfetModel
const IbisModel * parsedSeries
KIBIS_PIN * pin
BOOST_TEST_INFO("Two-port Series .op current = "<< iDevice)
BOOST_REQUIRE(comp !=nullptr)
KIBIS_PIN * pinA
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))