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