From 0d35d69f708273d63981ec19498fa293483c80fd Mon Sep 17 00:00:00 2001 From: Fabien Corona Date: Thu, 9 Jun 2022 18:30:51 +0000 Subject: [PATCH] ADDED: Read IBIS files --- CMakeLists.txt | 9 + LICENSE.3-CLAUSE-BSD | 25 + LICENSE.README | 2 + pcbnew/signalIntegrity/ibisParser.cpp | 2531 +++++++++++++++++++++++++ pcbnew/signalIntegrity/ibisParser.h | 765 ++++++++ pcbnew/signalIntegrity/kibis.cpp | 1238 ++++++++++++ pcbnew/signalIntegrity/kibis.h | 376 ++++ qa/CMakeLists.txt | 3 + qa/signalIntegrity/CMakeLists.txt | 84 + qa/signalIntegrity/ibis_v1_1.ibs | 89 + qa/signalIntegrity/ibis_v2_1.ibs | 147 ++ qa/signalIntegrity/ibis_v2_1.pkg | 110 ++ qa/signalIntegrity/ibis_v3_2.ibs | 419 ++++ qa/signalIntegrity/ibis_v3_2.pkg | 46 + qa/signalIntegrity/qaIbisParser.cpp | 89 + 15 files changed, 5933 insertions(+) create mode 100644 LICENSE.3-CLAUSE-BSD create mode 100644 pcbnew/signalIntegrity/ibisParser.cpp create mode 100644 pcbnew/signalIntegrity/ibisParser.h create mode 100644 pcbnew/signalIntegrity/kibis.cpp create mode 100644 pcbnew/signalIntegrity/kibis.h create mode 100644 qa/signalIntegrity/CMakeLists.txt create mode 100644 qa/signalIntegrity/ibis_v1_1.ibs create mode 100644 qa/signalIntegrity/ibis_v2_1.ibs create mode 100644 qa/signalIntegrity/ibis_v2_1.pkg create mode 100644 qa/signalIntegrity/ibis_v3_2.ibs create mode 100644 qa/signalIntegrity/ibis_v3_2.pkg create mode 100644 qa/signalIntegrity/qaIbisParser.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 02798d9f53..7ffcf669cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,10 @@ option( KICAD_SANITIZE_THREADS "Build KiCad with sanitizer options. WARNING: Not compatible with gold linker" OFF ) +option( KICAD_SIGNAL_INTEGRITY + "Build tools for signal integrity analysis ( default ON )" + ON ) + option( KICAD_STDLIB_DEBUG "Build KiCad with libstdc++ debug flags enabled." OFF ) @@ -510,6 +514,11 @@ if( KICAD_USE_OCC ) add_definitions( -DKICAD_USE_OCC ) endif() +if( KICAD_SIGNAL_INTEGRITY ) + add_definitions( -DKICAD_SIGNAL_INTEGRITY ) +endif() + + if( KICAD_USE_EGL AND UNIX AND NOT APPLE ) message( STATUS "Configuring KiCad for the wxGLCanvas EGL backend" ) add_definitions( -DKICAD_USE_EGL ) diff --git a/LICENSE.3-CLAUSE-BSD b/LICENSE.3-CLAUSE-BSD new file mode 100644 index 0000000000..ba80dcf7d7 --- /dev/null +++ b/LICENSE.3-CLAUSE-BSD @@ -0,0 +1,25 @@ +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice,this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. \ No newline at end of file diff --git a/LICENSE.README b/LICENSE.README index 7614cf1782..ae6fdb2d0a 100644 --- a/LICENSE.README +++ b/LICENSE.README @@ -34,5 +34,7 @@ Licensed in the public domain: - lemon in thirdparty/lemon Licensed under CC BY-SA 4.0: - All the demo files provided in demos/* +Licensed under clause-3 BSD: +- ibis / kibis files in pcbnew/signalIntegrity Licensed under GPLv3 (or later): - All remaining code not listed above diff --git a/pcbnew/signalIntegrity/ibisParser.cpp b/pcbnew/signalIntegrity/ibisParser.cpp new file mode 100644 index 0000000000..003e853629 --- /dev/null +++ b/pcbnew/signalIntegrity/ibisParser.cpp @@ -0,0 +1,2531 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Fabien Corona f.coronalaposte.net + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "ibisParser.h" +#include +#include +#include // KiCad header + +bool IbisParser::compareIbisWord( const std::string& a, const std::string& b ) +{ + return std::equal(a.begin(), a.end(), + b.begin(), b.end(), + [](char a, char b) { + return std::tolower(a) == std::tolower(b); + }); +} + +bool IBIS_MATRIX_BANDED::Check() +{ + bool status = true; + + if( m_dim < 1 ) + { + Report( _( "Dimension of matrices should be >= 1." ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_bandwidth < 1 ) + { + Report( _( "Bandwith of banded matrices should be >= 1." ), RPT_SEVERITY_ERROR ); + status = false; + } + + for( int i = 0; i < m_bandwidth * m_dim; i++ ) + { + if( std::isnan( m_data[i] ) ) + { + Report( _( "There are NaN elements in a matrix." ), RPT_SEVERITY_ERROR ); + status = false; + } + } + + + return status; +} + +bool IBIS_MATRIX_FULL::Check() +{ + bool status = true; + + if( m_dim < 1 ) + { + Report( _( "Dimension of matrices should be >= 1." ), RPT_SEVERITY_ERROR ); + status = false; + } + + for( int i = 0; i < m_dim * m_dim; i++ ) + { + if( std::isnan( m_data[i] ) ) + { + Report( _( "There are NaN elements in a matrix." ), RPT_SEVERITY_ERROR ); + status = false; + } + } + + + return status; +} + +bool IBIS_MATRIX_SPARSE::Check() +{ + bool status = true; + + if( m_dim < 1 ) + { + Report( _( "Dimension of matrices should be >= 1." ), RPT_SEVERITY_ERROR ); + status = false; + } + + for( int i = 0; i < m_dim * m_dim; i++ ) + { + if( std::isnan( m_data[i] ) ) + { + Report( _( "There are NaN elements in a matrix." ), RPT_SEVERITY_ERROR ); + status = false; + } + } + + + return status; +} + +bool isNumberNA( double aNumber ) +{ + bool result = false; + double NA = std::nan( NAN_NA ); + return *reinterpret_cast(&aNumber) == *reinterpret_cast( &NA ); +} + +bool TypMinMaxValue::Check() +{ + bool status = true; + + if( std::isnan( value[IBIS_CORNER::TYP] ) ) + status = false; + if( std::isnan( value[IBIS_CORNER::MIN] ) && !isNumberNA( value[IBIS_CORNER::MIN] ) ) + status = false; + if( std::isnan( value[IBIS_CORNER::MAX] ) && !isNumberNA( value[IBIS_CORNER::MAX] ) ) + status = false; + + return status; +} + +bool IbisComponentPackage::Check() +{ + bool status = true; + + if( !m_Rpkg->Check() ) + { + Report( _( "Invalid R_pkg value." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( !m_Lpkg->Check() ) + { + Report( _( "Invalid L_pkg value." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( !m_Cpkg->Check() ) + { + Report( _( "Invalid C_pkg value." ), RPT_SEVERITY_ERROR ); + status = false; + } + + return status; +} + +bool IbisComponentPin::Check() +{ + bool status = true; + + if( !m_dummy ) + { + int size = std::snprintf( nullptr, 0, _( "Checking pin %s" ), m_pinName.c_str() ) + 1; + char cstr[size]; + std::snprintf( cstr, size, _( "Checking pin %s" ), m_pinName.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ACTION ); + + if( m_pinName.empty() ) + { + Report( _( "Pin name cannot be empty." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( m_signalName.empty() ) + { + Report( _( "Signal name cannot be empty." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( m_modelName.empty() ) + { + Report( _( "Model name cannot be empty." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( std::isnan( m_Rpin ) && !isNumberNA( m_Rpin ) ) + { + Report( _( "Rpin is not valid." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( std::isnan( m_Lpin )&& !isNumberNA( m_Lpin ) ) + { + Report( _( "Lpin is not valid." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( std::isnan( m_Cpin )&& !isNumberNA( m_Cpin ) ) + { + Report( _( "Cpin is not valid." ), RPT_SEVERITY_ERROR ); + status = false; + } + } + return status; +} + + +bool IbisComponent::Check() +{ + bool status = true; + + if( m_name.empty() ) + { + Report( _( "Component name cannot be empty." ), RPT_SEVERITY_ERROR ); + status = false; + } + + int size = std::snprintf( nullptr, 0, _( "Checking component %s" ), m_name.c_str() ) + 1; + char cstr[size]; + std::snprintf( cstr, size, _( "Checking component %s" ), m_name.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ACTION ); + + if( m_manufacturer.empty() ) + { + Report( _( "Component: manufacturer cannot be empty." ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( !m_package.Check() ) + { + Report( _( "Component: Invalid Package." ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_pins.size() < 1 ) + { + Report( _( "Component: no pin" ), RPT_SEVERITY_ERROR ); + status = false; + } + + for( IbisComponentPin& pin : m_pins ) + { + status &= pin.Check(); + } + + return status; +} + + +bool IbisModelSelector::Check() +{ + return true; +} + + +std::string IBIS_ANY::doubleToString( double aNumber ) +{ + std::ostringstream ss; + ss.setf( std::ios_base::scientific, std::ios_base::floatfield ); + ss << aNumber; + return ss.str(); +} + + +std::string IVtable::Spice( int aN, std::string aPort1, std::string aPort2, std::string aModelName, + IBIS_CORNER aCorner ) +{ + std::string result = ""; + + if( m_entries.size() > 0 ) + { + result += "a"; + result += std::to_string( aN ); + result += " %vd("; + result += aPort1; + result += " "; + result += aPort2; + result += ") %id("; + result += aPort1; + result += " "; + result += aPort2; + result += ") "; + result += aModelName; + result += "\n"; + result += "\n"; + result += ".model "; + result += aModelName; + result += " pwl(\n+ x_array=["; + + for( IVtableEntry& entry : m_entries ) + { + result += doubleToString( entry.V ); + result += " "; + } + result += "]\n+ y_array=["; + + for( IVtableEntry& entry : m_entries ) + { + result += doubleToString( entry.I->value[aCorner] ); + result += " "; + } + result += "]\n+ input_domain=0.05 fraction=TRUE)\n\n"; + } + return result; +} + +double IVtable::InterpolatedI( double aV, IBIS_CORNER aCorner ) +{ + double result; + // @TODO change this algorithm + + if( m_entries.back().V > m_entries.at( 0 ).V ) + { + if( aV >= m_entries.back().V ) + { + return m_entries.back().I->value[aCorner]; + } + + if( aV <= m_entries.at( 0 ).V ) + { + return m_entries.at( 0 ).I->value[aCorner]; + } + + for( int i = 1; i < m_entries.size(); i++ ) + { + if( m_entries.at( i ).V > aV ) + { + return m_entries.at( i - 1 ).I->value[aCorner] + + ( m_entries.at( i ).I->value[aCorner] + - m_entries.at( i - 1 ).I->value[aCorner] ) + / ( m_entries.at( i ).V - m_entries.at( i - 1 ).V ) + * ( aV - m_entries.at( i - 1 ).V ); + } + } + Report( _( "Cannot interpolate the current based on this IV table." ), RPT_SEVERITY_ERROR ); + return std::nan( "" ); + } + else + { + // exiting the function here would mean the IV table is reversed. + return std::nan( "" ); + } + // @TODO prefer another method such as a dichotomy +} + +bool IVtable::Check() +{ + bool status = true; + for( IVtableEntry& entry : m_entries ) + { + if( std::isnan( entry.V ) ) + { + Report( _( "There is a Nan voltage in an IV table" ), RPT_SEVERITY_ERROR ); + status = false; + break; + } + + if( !entry.I->Check() ) + { + Report( _( "There is an invalid current in an IV table" ), RPT_SEVERITY_ERROR ); + status = false; + break; + } + } + // TODO: Check if the IV table is monotonic : + // IBIS standard defines 8 criteria for an IV table to be monotonic + // Issue a warning, not an error + + return status; +} + + +bool dvdtTypMinMax::Check() +{ + bool status = true; + + if( std::isnan( value[IBIS_CORNER::TYP].m_dv ) ) + status = false; + if( std::isnan( value[IBIS_CORNER::TYP].m_dt ) ) + status = false; + + + if( std::isnan( value[IBIS_CORNER::MIN].m_dv ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dv ) ) + status = false; + if( std::isnan( value[IBIS_CORNER::MIN].m_dt ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dt ) ) + status = false; + + if( std::isnan( value[IBIS_CORNER::MIN].m_dv ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dv ) ) + status = false; + if( std::isnan( value[IBIS_CORNER::MIN].m_dt ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dt ) ) + status = false; + + return status; +} + +bool IbisRamp::Check() +{ + bool status = true; + + if( std::isnan( m_Rload ) ) + { + status = false; + Report( _( "Invalid R_load." ), RPT_SEVERITY_ERROR ); + } + if( !m_falling->Check() ) + { + Report( _( "Invalid falling dv/dt." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( !m_rising->Check() ) + { + Report( _( "Invalid rising dv/dt." ), RPT_SEVERITY_ERROR ); + status = false; + } + + return status; +} + + + +bool IbisModel::Check() +{ + bool status = true; + + if( m_name.empty() ) + { + Report( _( "Model name cannot be empty" ), RPT_SEVERITY_ERROR ); + status = false; + } + + int size = std::snprintf( nullptr, 0, _( "Checking model %s" ), m_name.c_str() ) + 1; + char cstr[size]; + std::snprintf( cstr, size, _( "Checking model %s" ), m_name.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ACTION ); + + if(m_type == IBIS_MODEL_TYPE::UNDEFINED) + { + Report( _( "Undefined model type" ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( isnan( m_vinh ) && !isNumberNA( m_vinh ) ) + { + Report( _( "Invalid Vinh value." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( isnan( m_vinl ) && !isNumberNA( m_vinl ) ) + { + Report( _( "Invalid Vinl value." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( isnan( m_rref ) && !isNumberNA( m_rref ) ) + { + Report( _( "Invalid R_ref value." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( isnan( m_cref ) && !isNumberNA( m_cref ) ) + { + Report( _( "Invalid C_ref value." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( isnan( m_vref ) && !isNumberNA( m_vref ) ) + { + Report( _( "invalid V_ref value." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( isnan( m_vmeas ) && !isNumberNA( m_vmeas ) ) + { + Report( _( "Invalid V_meas value." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( !m_C_comp->Check() ) + { + Report( _( "C_comp is invalid." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( !m_temperatureRange->Check() ) + { + Report( _( "Temperature Range is invalid." ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( !m_voltageRange->Check() ) + { + // If the voltage range is not valid, it's ok, only if we have pulls and clamps + if( !m_pulldownReference->Check() ) + { + status = false; + } + if( !m_pullupReference->Check() ) + { + status = false; + } + if( !m_GNDClampReference->Check() ) + { + status = false; + } + if( !m_POWERClampReference->Check() ) + { + status = false; + } + if( !status ) + { + Report( _( "Voltage Range is invalid." ), RPT_SEVERITY_ERROR ); + } + status = false; + } + if( !m_pulldown->Check() ) + { + Report( _( "Invalid pulldown." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( !m_pullup->Check() ) + { + Report( _( "Invalid pullup." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( !m_POWERClamp->Check() ) + { + Report( _( "Invalid POWER clamp." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( !m_GNDClamp->Check() ) + { + Report( _( "Invalid GND clamp." ), RPT_SEVERITY_ERROR ); + status = false; + } + if( m_type != IBIS_MODEL_TYPE::INPUT_ECL && m_type != IBIS_MODEL_TYPE::INPUT + && m_type != IBIS_MODEL_TYPE::TERMINATOR && m_type != IBIS_MODEL_TYPE::SERIES + && m_type != IBIS_MODEL_TYPE::SERIES_SWITCH ) + { + if( !m_ramp->Check() ) + { + Report( _( "Invalid Ramp" ), RPT_SEVERITY_ERROR ); + } + } + return status; +} + +bool IbisHeader::Check() +{ + bool status = true; + + Report( _( "Checking Header..." ), RPT_SEVERITY_ACTION ); + + if( m_ibisVersion == -1 ) + { + Report( _( "Missing [IBIS Ver]" ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_ibisVersion > IBIS_MAX_VERSION ) + { + Report( _( "The parser does not handle this IBIS version" ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_fileRevision == -1 ) + { + Report( _( "Missing [File Rev]" ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_fileName.empty() ) + { + Report( _( "Missing [File Name]" ), RPT_SEVERITY_ERROR ); + status = false; + } + + std::string ext = m_fileName.substr( m_fileName.length() - 4 ); + + if( !( !strcmp( ext.c_str(), ".ibs" ) || !strcmp( ext.c_str(), ".pkg" ) + || !strcmp( ext.c_str(), ".ebd" ) || !strcmp( ext.c_str(), ".ims" ) ) ) + { + Report( "Invalid file extension in [File Name]", RPT_SEVERITY_ERROR ); + status = false; + } + + + return status; +} + +bool IbisPackageModel::Check() +{ + bool status = true; + + if( m_name.empty() ) + { + Report( _( "Package model name cannot be empty." ), RPT_SEVERITY_ERROR ); + status = false; + } + + int size = std::snprintf( nullptr, 0, _( "Checking package model %s" ), m_name.c_str() ) + 1; + char cstr[size]; + std::snprintf( cstr, size, _( "Checking package model %s" ), m_name.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ACTION ); + + if( m_manufacturer.empty() ) + { + Report( _( "Manufacturer cannot be empty." ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_OEM.empty() ) + { + Report( _( "OEM cannot be empty." ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( std::isnan( m_numberOfPins ) ) + { + Report( _( "Number of pins is invalid." ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_numberOfPins <= 0 ) + { + Report( _( "Negative number of pins." ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_pins.size() != m_numberOfPins ) + { + Report( "Number of pins does not match [Pin Numbers] size", RPT_SEVERITY_ERROR ); + status = false; + } + + for( int i = 0; i < m_pins.size(); i++ ) + { + if( m_pins.at( i ).empty() ) + { + Report( _( "Empty pin number" ), RPT_SEVERITY_ERROR ); + status = false; + } + } + // resistance matrix is not required + + if( !( m_resistanceMatrix )->Check() ) + { + Report( _( "Resistance matrix is incorrect" ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_capacitanceMatrix != nullptr ) + { + if( m_capacitanceMatrix->m_type == IBIS_MATRIX_TYPE::UNDEFINED ) + { + Report( _( "Capacitance matrix is undefined" ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( !m_capacitanceMatrix->Check() ) + { + Report( _( "Capacitance matrix is incorrect" ), RPT_SEVERITY_ERROR ); + status = false; + } + } + else + { + Report( _( "Capacitance matrix is nullptr" ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( m_inductanceMatrix != nullptr ) + { + if( m_inductanceMatrix->m_type == IBIS_MATRIX_TYPE::UNDEFINED ) + { + Report( _( "Inductance matrix is undefined" ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( !m_inductanceMatrix->Check() ) + { + Report( _( "Inductance matrix is incorrect" ), RPT_SEVERITY_ERROR ); + status = false; + } + } + else + { + Report( _( "Inductance matrix is nullptr" ), RPT_SEVERITY_ERROR ); + status = false; + } + return status; +} + + +bool IbisParser::ParseFile( std::string& aFileName ) +{ + std::string err_msg; + + std::ifstream ibisFile; + ibisFile.open( aFileName ); + + if( !ibisFile.is_open() ) + { + int size = std::snprintf( nullptr, 0, _( "Cannot open file %s" ), aFileName.c_str() ) + 1; + char cstr[size]; + std::snprintf( cstr, size, _( "Cannot open file %s" ), aFileName.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ERROR ); + return false; + } + + int size1 = std::snprintf( nullptr, 0, _( "Reading file %s..." ), aFileName.c_str() ) + 1; + char cstr[size1]; + std::snprintf( cstr, size1, _( "Reading file %s..." ), aFileName.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ERROR ); + + std::ostringstream ss; + ss << ibisFile.rdbuf(); + const std::string& s = ss.str(); + m_buffer = std::vector( s.begin(), s.end() ); + + long size = m_buffer.size(); + + m_lineCounter = 0; + m_bufferIndex = 0; + + bool status = true; + + LOCALE_IO toggle; // Temporary switch the locale to standard C to r/w floats + + while( ( m_bufferIndex < size ) && status ) + { + if( !getNextLine() ) + { + Report( _( "Unexpected end of file. Missing [END] ?" ), RPT_SEVERITY_ERROR ); + status = false; + } + + if( status && m_parrot ) + { + printLine(); + } + + if( status && !onNewLine() ) + { + int size2 = std::snprintf( nullptr, 0, _( "Error on line %i..." ), m_lineCounter ) + 1; + char cstr2[size2]; + std::snprintf( cstr2, size2, _( "Error on line %i..." ), m_lineCounter ); + Report( std::string( cstr2 ), RPT_SEVERITY_ACTION ); + status = false; + } + if( m_context == IBIS_PARSER_CONTEXT::END ) + { + break; + } + } + + m_buffer.clear(); + return status; +} + +void IbisParser::skipWhitespaces() +{ + while( ( isspace( m_buffer[m_lineOffset + m_lineIndex] ) ) && ( m_lineIndex < m_lineLength ) ) + { + m_lineIndex++; + } +} + +bool IbisParser::checkEndofLine() +{ + skipWhitespaces(); + + if( m_lineIndex < m_lineLength ) + { + Report( _( "A line did not end properly.." ), RPT_SEVERITY_ERROR ); + return false; + } + return true; +} + + +bool IbisParser::isLineEmptyFromCursor() +{ + int cursor = m_lineIndex; + + while( ( isspace( m_buffer[m_lineOffset + cursor] ) ) && ( cursor < m_lineLength ) ) + { + cursor++; + } + + return ( cursor >= m_lineLength ); +} + +bool IbisParser::readDvdt( std::string& aString, dvdt& aDest ) +{ + bool status = true; + + int i = 0; + + for( i = 1; i < aString.length(); i++ ) + { + if( aString.at( i ) == '/' ) + { + break; + } + } + + if( aString.at( i ) == '/' ) + { + std::string str1 = aString.substr( 0, i ); + std::string str2 = aString.substr( i + 2, aString.size() ); + + if( !parseDouble( aDest.m_dv, str1, true ) || !parseDouble( aDest.m_dt, str2, true ) ) + { + status = false; + } + } + + else if( aString == "NA" ) + { + aDest.m_dv = std::nan( NAN_NA ); + aDest.m_dt = std::nan( NAN_NA ); + } + else + { + status = false; + } + + return status; +} + + +bool IbisParser::parseDouble( double& aDest, std::string& aStr, bool aAllowModifiers ) +{ + // " an entry of the C matrix could be given as 1.23e-12 or as 1.23p or 1.23pF." + // Kibis: This implementation will also allow 1.23e-3n + + skipWhitespaces(); + bool status = true; + bool converted = false; + + std::string str = aStr; + + double result; + size_t size = 0; + + try + { + result = std::stod( str, &size ); + converted = true; + } + catch( ... ) + { + if( str == "NA" ) + { + result = std::nan( NAN_NA ); + } + else + { + result = std::nan( NAN_INVALID ); + status = false; + } + } + + if( converted && ( size < str.length() ) ) + { + switch( static_cast( str.at( size ) ) ) + { + case 'T': result *= 1e12; break; + case 'G': result *= 1e9; break; + case 'M': result *= 1e6; break; + case 'k': result *= 1e3; break; + case 'm': result *= 1e-3; break; + case 'u': result *= 1e-6; break; + case 'n': result *= 1e-9; break; + case 'p': result *= 1e-12; break; + case 'f': result *= 1e-15; break; + default: + break; + // In some files, "vinh = 3.0V", therefore we can't return false in the default case + } + } + + aDest = result; + + return status; +} + + +bool IbisParser::getNextLine() +{ + m_lineCounter++; + + long tmpIndex = m_bufferIndex; + + m_lineOffset = m_bufferIndex; + + char c = m_buffer[m_bufferIndex++]; + + int i = 1; + while( c != m_commentChar && c != 0 && c != '\n' && i < IBIS_MAX_LINE_LENGTH ) + { + c = m_buffer[m_bufferIndex++]; + i++; + } + + if( i == IBIS_MAX_LINE_LENGTH ) + { + Report( _( "Line exceeds maximum length." ), RPT_SEVERITY_ERROR ); + return false; + } + + m_lineLength = m_bufferIndex - tmpIndex - 1; // Don't add the end of line condition + m_lineIndex = 0; + + if( c == m_commentChar ) + { + while( c != 0 && c != '\n' ) + { + c = m_buffer[m_bufferIndex++]; + } + } + + if( i == IBIS_MAX_LINE_LENGTH ) + { + Report( _( "Line exceeds maximum length." ), RPT_SEVERITY_ERROR ); + return false; + } + + return true; +} + + +void IbisParser::printLine() +{ + for( int i = 0; i < m_lineLength; i++ ) + { + std::cout << m_buffer[m_lineOffset + i]; + } + std::cout << std::endl; +} + +bool IbisParser::readDouble( double& aDest ) +{ + bool status = true; + std::string str; + + if( readWord( str ) ) + { + if( !parseDouble( aDest, str, true ) ) + { + Report( _( "Failed to read a double." ), RPT_SEVERITY_WARNING ); + status = false; + } + } + else + { + Report( _( "Failed to read a word." ), RPT_SEVERITY_WARNING ); + status = false; + } + + return status; +} + +bool IbisParser::readInt( int& aDest ) +{ + bool status = true; + std::string str; + double buffer; + + if( readWord( str ) ) + { + double result; + size_t size; + + try + { + result = std::stoi( str, &size ); + } + catch( ... ) + { + if( str == "NA" ) + { + result = std::nan( NAN_NA ); + } + else + { + result = std::nan( NAN_INVALID ); + status = false; + } + } + + if( size != str.size() ) + { + status = false; + Report( _( "Number is not an integer" ), RPT_SEVERITY_WARNING ); + } + + aDest = result; + } + else + { + Report( _( "Failed to read a word." ), RPT_SEVERITY_WARNING ); + status = false; + } + + return status; +} + +bool IbisParser::readWord( std::string& aDest ) +{ + skipWhitespaces(); + + int startIndex = m_lineIndex; + + while( ( !isspace( m_buffer[m_lineOffset + m_lineIndex] ) ) && ( m_lineIndex < m_lineLength ) ) + { + m_lineIndex++; + } + + std::vector::iterator start = std::next( m_buffer.begin(), m_lineOffset + startIndex ); + std::vector::iterator end = std::next( m_buffer.begin(), m_lineOffset + m_lineIndex ); + aDest = std::string( start, end ); + + return ( aDest.size() > 0 ); +} + +bool IbisParser::readString( std::string& aDest ) +{ + while( m_lineIndex < m_lineLength ) + { + aDest += m_buffer[m_lineOffset + m_lineIndex++]; + } + + // Remove extra whitespace characters + int len = aDest.length(); + + if( len < 1 ) + { + Report( _( "Unable to read string, input is empty." ), RPT_SEVERITY_ERROR ); + return false; + } + + char c = aDest[len - 1]; + int i = 0; + + while( isspace( c ) && ( i < len ) ) + { + c = aDest[len - 1 - i]; + i++; + } + aDest = aDest.substr( 0, len - i + 1 ); + + return true; +} + +bool IbisParser::storeString( std::string& aDest, bool aMultiline ) +{ + + skipWhitespaces(); + + readString( aDest ); + + m_continue = aMultiline ? IBIS_PARSER_CONTINUE::STRING : IBIS_PARSER_CONTINUE::NONE; + m_continuingString = &aDest; + + return checkEndofLine(); +} + + +bool IbisParser::changeCommentChar() +{ + skipWhitespaces(); + + std::string strChar; + + // We cannot stop at m_lineLength here, because lineLength could stop before |_char + // if the char is remains the same + + char c = m_buffer[m_lineOffset + m_lineIndex++]; + char d = c; + + if( !( c == '!' || c == '"' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' + || c == '(' || c == ')' || c == '*' || c == ',' || c == ':' || c == ';' || c == '<' + || c == '>' || c == '?' || c == '@' || c == '\\' || c == '^' || c == '`' || c == '{' + || c == '|' || c == '}' || c == '~' || c == ')' ) ) + { + Report( _( "New comment character is invalid." ), RPT_SEVERITY_ERROR ); + } + + c = m_buffer[m_lineOffset + m_lineIndex++]; + + while( ( !isspace( c ) ) && c != 0 && c != '\n' ) + { + strChar += c; + c = m_buffer[m_lineOffset + m_lineIndex++]; + } + + if( !strcmp( strChar.c_str(), "_char" ) ) + { + Report( _( "Invalid syntax. Should be |_char or &_char, etc..." ), RPT_SEVERITY_ERROR ); + return false; + } + + int i = 0; + while( isspace( c ) && c != 0 && c != '\n' && c != d ) + { + c = m_buffer[m_lineOffset + m_lineIndex++]; + } + + if( ( !isspace( c ) ) && c != d ) + { + Report( _( "No extra argument was expected" ), RPT_SEVERITY_ERROR ); + return false; + } + + m_commentChar = d; + + + m_continue = IBIS_PARSER_CONTINUE::NONE; + + return true; +} + +std::string IbisParser::getKeyword() +{ + std::string keyword = ""; + //"Keywords must be enclosed in square brackets, “[]”, and must start in column 1 of the line." + //"No space or tab is allowed immediately after the opening bracket “[” or immediately" + // "before the closing bracket “]" + + if( m_buffer[m_lineOffset + m_lineIndex] != '[' ) + { + // We return an empty keyword, this should stop the parser. + return ""; + } + + m_lineIndex++; + + char c; + c = m_buffer[m_lineOffset + m_lineIndex++]; + + while( ( c != ']' ) + && ( m_lineIndex + < m_lineLength ) ) // We know the maximum keyword length, we could add a condition. + { + // "Underscores and spaces are equivalent in keywords" + if( c == ' ' ) + { + c = '_'; + } + keyword += c; + c = m_buffer[m_lineOffset + m_lineIndex++]; + } + + return keyword; +} + +bool IbisParser::changeContext( std::string& aKeyword ) +{ + bool status = true; + + // Old context; + IBIS_PARSER_CONTEXT old_context = m_context; + IbisComponent* old_component = m_currentComponent; + IbisModel* old_model = m_currentModel; + IbisModelSelector* old_modelSelector = m_currentModelSelector; + IbisPackageModel* old_packageModel = m_currentPackageModel; + + + if( status ) + { + switch( m_context ) + { + case IBIS_PARSER_CONTEXT::HEADER: status &= m_ibisFile.m_header->Check(); break; + case IBIS_PARSER_CONTEXT::COMPONENT: status &= m_currentComponent->Check(); break; + case IBIS_PARSER_CONTEXT::MODEL: status &= m_currentModel->Check(); break; + case IBIS_PARSER_CONTEXT::MODELSELECTOR: status &= m_currentModelSelector->Check(); break; + case IBIS_PARSER_CONTEXT::PACKAGEMODEL: status &= m_currentPackageModel->Check(); break; + case IBIS_PARSER_CONTEXT::END: + Report( "Cannot change context after [END]" ); + status = false; + break; + + default: Report( "Changing context from an undefined context" ); + } + } + + if( !compareIbisWord( aKeyword.c_str(), "End" ) && status ) + { + //New context + if( compareIbisWord( aKeyword.c_str(), "Component" ) ) + { + IbisComponent comp( m_reporter ); + storeString( comp.m_name, false ); + m_ibisFile.m_components.push_back( comp ); + m_currentComponent = &( m_ibisFile.m_components.back() ); + m_context = IBIS_PARSER_CONTEXT::COMPONENT; + } + else if( compareIbisWord( aKeyword.c_str(), "Model_Selector" ) ) + { + IbisModelSelector MS( m_reporter ); + storeString( MS.m_name, false ); + m_ibisFile.m_modelSelectors.push_back( MS ); + m_currentModelSelector = &( m_ibisFile.m_modelSelectors.back() ); + m_context = IBIS_PARSER_CONTEXT::MODELSELECTOR; + m_continue = IBIS_PARSER_CONTINUE::MODELSELECTOR; + } + else if( compareIbisWord( aKeyword.c_str(), "Model" ) ) + { + IbisModel model( m_reporter ); + model.m_temperatureRange->value[IBIS_CORNER::MIN] = 0; + model.m_temperatureRange->value[IBIS_CORNER::TYP] = 50; + model.m_temperatureRange->value[IBIS_CORNER::MAX] = 100; + storeString( model.m_name, false ); + m_ibisFile.m_models.push_back( model ); + m_currentModel = &( m_ibisFile.m_models.back() ); + m_context = IBIS_PARSER_CONTEXT::MODEL; + m_continue = IBIS_PARSER_CONTINUE::MODEL; + } + else if( compareIbisWord( aKeyword.c_str(), "Define_Package_Model" ) ) + { + IbisPackageModel PM( m_reporter ); + PM.m_resistanceMatrix = std::unique_ptr( new IBIS_MATRIX( m_reporter ) ); + PM.m_capacitanceMatrix = std::unique_ptr( new IBIS_MATRIX( m_reporter ) ); + PM.m_inductanceMatrix = std::unique_ptr( new IBIS_MATRIX( m_reporter ) ); + + PM.m_resistanceMatrix->m_type = IBIS_MATRIX_TYPE::UNDEFINED; + PM.m_capacitanceMatrix->m_type = IBIS_MATRIX_TYPE::UNDEFINED; + PM.m_inductanceMatrix->m_type = IBIS_MATRIX_TYPE::UNDEFINED; + + PM.m_resistanceMatrix->m_dim = -1; + PM.m_capacitanceMatrix->m_dim = -1; + PM.m_inductanceMatrix->m_dim = -1; + + storeString( PM.m_name, false ); + + m_ibisFile.m_packageModels.push_back( PM ); + m_currentPackageModel = &( m_ibisFile.m_packageModels.back() ); + m_context = IBIS_PARSER_CONTEXT::PACKAGEMODEL; + } + else if( compareIbisWord( aKeyword.c_str(), "End_Package_Model" ) ) + { + if( m_currentComponent != nullptr ) + { + m_context = IBIS_PARSER_CONTEXT::COMPONENT; + m_continue = IBIS_PARSER_CONTINUE::NONE; + } + else // .pkg file, we just go back to header, to get the [END] keyword + { // This will cause the header to be checked twice. + m_context = IBIS_PARSER_CONTEXT::HEADER; + m_continue = IBIS_PARSER_CONTINUE::NONE; + } + } + else + { + status = false; + std::string context_string; + + switch( m_context ) + { + case IBIS_PARSER_CONTEXT::HEADER: context_string += "HEADER"; break; + case IBIS_PARSER_CONTEXT::COMPONENT: context_string += "COMPONENT"; break; + case IBIS_PARSER_CONTEXT::MODELSELECTOR: context_string += "MODEL_SELECTOR"; break; + case IBIS_PARSER_CONTEXT::MODEL: context_string += "MODEL"; break; + case IBIS_PARSER_CONTEXT::PACKAGEMODEL: context_string += "PACKAGE_MODEL"; break; + case IBIS_PARSER_CONTEXT::PACKAGEMODEL_MODELDATA: + context_string += "PACKAGE_MODEL_MODEL_DATA"; + break; + default: context_string += "???"; break; + } + + int size = std::snprintf( nullptr, 0, _( "Unknown keyword in %s context: %s" ), + context_string.c_str(), aKeyword.c_str() ) + + 1; + char cstr[size]; + std::snprintf( cstr, size, _( "Unknown keyword in %s context: %s" ), + context_string.c_str(), aKeyword.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ERROR ); + } + } + else + { + m_context = IBIS_PARSER_CONTEXT::END; + } + + return status; +} + + +bool IbisParser::parseModelSelector( std::string& aKeyword ) +{ + bool status = true; + + if( !changeContext( aKeyword ) ) + { + status = false; + } + return status; +} + + +bool IbisParser::readRampdvdt( dvdtTypMinMax& aDest ) +{ + bool status = true; + std::string str; + + if( readWord( str ) ) + { + status &= readDvdt( str, aDest.value[IBIS_CORNER::TYP] ) + && readDvdt( str, aDest.value[IBIS_CORNER::MIN] ) + && readDvdt( str, aDest.value[IBIS_CORNER::MAX] ); + } + else + { + status = false; + } + + return status; +} + +bool IbisParser::readRamp() +{ + bool status = true; + + m_continue = IBIS_PARSER_CONTINUE::RAMP; + + std::string keyword = std::string( "R_load " ); + + if( !readNumericSubparam( std::string( "R_load " ), m_currentModel->m_ramp->m_Rload ) ) + { + std::string str; + + if( readWord( str ) ) + { + if( !strcmp( str.c_str(), "dV/dt_r" ) ) + { + readRampdvdt( *( m_currentModel->m_ramp->m_rising ) ); + } + else if( !strcmp( str.c_str(), "dV/dt_f" ) ) + { + readRampdvdt( *( m_currentModel->m_ramp->m_falling ) ); + } + else + { + Report( _( "Invalid ramp data" ), RPT_SEVERITY_ERROR ); + status = false; + } + } + } + return status; +} + + +bool IbisParser::parseModel( std::string& aKeyword ) +{ + bool status = false; + + if( compareIbisWord( aKeyword.c_str(), "Voltage_Range" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_voltageRange ) ); + else if( compareIbisWord( aKeyword.c_str(), "Temperature_Range" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_temperatureRange ) ); + else if( compareIbisWord( aKeyword.c_str(), "GND_Clamp" ) ) + status = readIVtableEntry( *( m_currentModel->m_GNDClamp ) ); + else if( compareIbisWord( aKeyword.c_str(), "POWER_Clamp" ) ) + status = readIVtableEntry( *( m_currentModel->m_POWERClamp ) ); + else if( compareIbisWord( aKeyword.c_str(), "Pulldown" ) ) + status = readIVtableEntry( *( m_currentModel->m_pulldown ) ); + else if( compareIbisWord( aKeyword.c_str(), "Pullup" ) ) + status = readIVtableEntry( *( m_currentModel->m_pullup ) ); + else if( compareIbisWord( aKeyword.c_str(), "Rising_Waveform" ) ) + status = readWaveform( nullptr, IBIS_WAVEFORM_TYPE::RISING ); + else if( compareIbisWord( aKeyword.c_str(), "Falling_Waveform" ) ) + status = readWaveform( nullptr, IBIS_WAVEFORM_TYPE::FALLING ); + else if( compareIbisWord( aKeyword.c_str(), "Ramp" ) ) + status = readRamp(); + else if( compareIbisWord( aKeyword.c_str(), "Pullup_Reference" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_pullupReference ) ); + else if( compareIbisWord( aKeyword.c_str(), "Pulldown_Reference" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_pulldownReference ) ); + else if( compareIbisWord( aKeyword.c_str(), "POWER_Clamp_Reference" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_POWERClampReference ) ); + else if( compareIbisWord( aKeyword.c_str(), "GND_Clamp_Reference" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_GNDClampReference ) ); + else if( compareIbisWord( aKeyword.c_str(), "Rac" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_Rac ) ); + else if( compareIbisWord( aKeyword.c_str(), "Cac" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_Cac ) ); + else if( compareIbisWord( aKeyword.c_str(), "Rpower" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_Rpower ) ); + else if( compareIbisWord( aKeyword.c_str(), "Rgnd" ) ) + status = readTypMinMaxValue( *( m_currentModel->m_Rgnd ) ); + else + { + status = changeContext( aKeyword ); + } + return status; +} + +bool IbisParser::readPackageModelPins() +{ + m_continue = IBIS_PARSER_CONTINUE::PACKAGEMODEL_PINS; + std::string str; + + if( readWord( str ) ) + m_currentPackageModel->m_pins.push_back( str ); + + return true; +} + + +bool IbisParser::readMatrixBanded( std::string aKeyword, IBIS_MATRIX_BANDED& aDest ) +{ + bool status = true; + m_continue = IBIS_PARSER_CONTINUE::MATRIX; + + if( compareIbisWord( aKeyword.c_str(), "Bandwidth" ) ) + { + if( m_currentMatrix->m_type == IBIS_MATRIX_TYPE::BANDED ) + { + status &= readInt( aDest.m_bandwidth ); + if( status ) + { + aDest.m_data.resize( aDest.m_bandwidth * aDest.m_dim ); + } + } + else + { + status = false; + Report( _( "Cannot specify a bandwidth for that kind of matrix" ), RPT_SEVERITY_ERROR ); + } + } + if( !compareIbisWord( aKeyword.c_str(), "Dummy" ) ) + { + int i; + for( i = 0; i < aDest.m_bandwidth; i++ ) + { + if( i + m_currentMatrixRowIndex >= aDest.m_bandwidth ) + { + Report( "Too much data for this matrix row", RPT_SEVERITY_ERROR ); + status = false; + break; + } + + int index = i + m_currentMatrixRow * aDest.m_bandwidth; + + if( !readDouble( aDest.m_data[index] ) ) + { + Report( _( "Invalid row in matrix" ), RPT_SEVERITY_ERROR ); + status = false; + break; + } + } + m_currentMatrixRowIndex = i; + } + + return status; +} + + +bool IbisParser::readMatrixFull( std::string aKeyword, IBIS_MATRIX_FULL& aDest ) +{ + bool status = true; + m_continue = IBIS_PARSER_CONTINUE::MATRIX; + + if( !compareIbisWord( aKeyword.c_str(), "Dummy" ) ) + { + std::vector values; + + status &= readTableLine( values ); + int i; + for( i = 0; i < values.size(); i++ ) + { + int index = i + m_currentMatrixRow * aDest.m_dim + m_currentMatrixRow; + // + final m_currentMatrixRow because we don't fill the lower triangle. + + if( i >= ( aDest.m_dim - m_currentMatrixRow - m_currentMatrixRowIndex ) ) + { + Report( _( "Too much data for this matrix row." ), RPT_SEVERITY_ERROR ); + status = false; + break; + } + + if( index >= aDest.m_dim * aDest.m_dim ) + { + status = false; + Report( _( "Too much data for this matrix." ), RPT_SEVERITY_ERROR ); + break; + } + if( !parseDouble( aDest.m_data[index], values.at( i ), true ) ) + { + Report( _( "Can't read a matrix element" ), RPT_SEVERITY_ERROR ); + status = false; + } + else + { + } + } + m_currentMatrixRowIndex = i; + } + return status; +} + + +bool IbisParser::readMatrixSparse( std::string aKeyword, IBIS_MATRIX_SPARSE& aDest ) +{ + bool status = true; + + if( !compareIbisWord( aKeyword.c_str(), "Dummy" ) ) + { + int subindex; + double value; + + if( readInt( subindex ) ) + { + if( readDouble( value ) ) + { + int index = subindex + m_currentMatrixRow * aDest.m_dim + m_currentMatrixRow; + } + else + { + Report( _( "Can't read a matrix element" ), RPT_SEVERITY_ERROR ); + } + } + else + { + Report( _( "Can't read a matrix index" ), RPT_SEVERITY_ERROR ); + } + } + return status; +} + +bool IbisParser::readMatrix( std::shared_ptr aDest ) +{ + bool status = true; + std::string str; + + bool init = false; + + if( aDest != nullptr ) + { + if( aDest->m_type != IBIS_MATRIX_TYPE::BANDED && aDest->m_type != IBIS_MATRIX_TYPE::FULL + && aDest->m_type != IBIS_MATRIX_TYPE::SPARSE ) + { + init = false; + } + else + { + init = true; + } + } + else + { + Report( "Matrix pointer is null." ); + status = false; + } + + if( m_continue != IBIS_PARSER_CONTINUE::MATRIX && status ) + { + if( !init ) + { + if( readWord( str ) ) + { + IBIS_MATRIX* matrix; + + if( compareIbisWord( str.c_str(), "Banded_matrix" ) ) + { + matrix = static_cast( new IBIS_MATRIX_BANDED( m_reporter ) ); + aDest = static_cast>( matrix ); + m_currentMatrix = aDest; + m_currentMatrix->m_type = IBIS_MATRIX_TYPE::BANDED; + m_continue = IBIS_PARSER_CONTINUE::MATRIX; + } + else if( compareIbisWord( str.c_str(), "Full_matrix" ) ) + { + matrix = static_cast( new IBIS_MATRIX_FULL( m_reporter ) ); + aDest = static_cast>( matrix ); + m_currentMatrix = aDest; + matrix->m_dim = m_currentPackageModel->m_numberOfPins; + m_currentMatrix->m_type = IBIS_MATRIX_TYPE::FULL; + m_continue = IBIS_PARSER_CONTINUE::MATRIX; + } + else if( compareIbisWord( str.c_str(), "Sparse_matrix" ) ) + { + matrix = static_cast( new IBIS_MATRIX_SPARSE( m_reporter ) ); + aDest = static_cast>( matrix ); + m_currentMatrix = aDest; + m_currentMatrix->m_data.resize( matrix->m_dim * matrix->m_dim ); + m_currentMatrix->m_type = IBIS_MATRIX_TYPE::SPARSE; + m_continue = IBIS_PARSER_CONTINUE::MATRIX; + } + else + { + status = false; + Report( _( "Unknown matrix type" ), RPT_SEVERITY_ERROR ); + Report( str, RPT_SEVERITY_INFO ); + m_currentMatrix->m_dim = m_currentPackageModel->m_numberOfPins; + } + } + else + { + status = false; + Report( _( "Missing matrix type" ), RPT_SEVERITY_ERROR ); + } + } + else + { + status = false; + Report( _( " Matrix is already init. But m_continue was not set ( internal error )" ) ); + } + } + else + { + if( aDest != nullptr ) + { + // If m_continue is set, ( and no keyword ) then it is a row + switch( aDest->m_type ) + { + case IBIS_MATRIX_TYPE::BANDED: + readMatrixBanded( std::string( "Dummy" ), + *static_cast( aDest.get() ) ); + break; + case IBIS_MATRIX_TYPE::FULL: + readMatrixFull( std::string( "Dummy" ), + *static_cast( aDest.get() ) ); + break; + case IBIS_MATRIX_TYPE::SPARSE: + readMatrixSparse( std::string( "Dummy" ), + *static_cast( aDest.get() ) ); + break; + case IBIS_MATRIX_TYPE::UNDEFINED: + default: + { + status = false; + Report( _( "Tried to read a row from an undefined matrix" ) ); + } + } + } + else + { + Report( _( "matrix pointer is null" ) ); + } + } + return status; +} + +bool IbisParser::parsePackageModelModelData( std::string& aKeyword ) +{ + bool status = true; + + if( compareIbisWord( aKeyword.c_str(), "Resistance_Matrix" ) ) + { + IBIS_MATRIX dest( m_reporter ), source( m_reporter ); + status &= readMatrix( m_currentPackageModel->m_resistanceMatrix ); + } + else if( compareIbisWord( aKeyword.c_str(), "Capacitance_Matrix" ) ) + { + status &= readMatrix( m_currentPackageModel->m_capacitanceMatrix ); + } + else if( compareIbisWord( aKeyword.c_str(), "Inductance_Matrix" ) ) + { + status &= readMatrix( m_currentPackageModel->m_inductanceMatrix ); + } + else if( compareIbisWord( aKeyword.c_str(), "Bandwidth" ) ) + { + status &= readMatrixBanded( aKeyword, + *static_cast( m_currentMatrix.get() ) ); + } + else if( compareIbisWord( aKeyword.c_str(), "Row" ) ) + { + status &= readInt( m_currentMatrixRow ); + m_currentMatrixRow--; // The matrix starts at 0 + m_currentMatrixRowIndex = 0; // The matrix starts at 0*/ + m_continue = IBIS_PARSER_CONTINUE::MATRIX; + } + else if( compareIbisWord( aKeyword.c_str(), "End_Model_Data" ) ) + { + m_context = IBIS_PARSER_CONTEXT::PACKAGEMODEL; + m_continue = IBIS_PARSER_CONTINUE::NONE; + } + else + { + if( !changeContext( aKeyword ) ) + { + status = false; + } + } + return status; +} + +bool IbisParser::parsePackageModel( std::string& aKeyword ) +{ + bool status = true; + + if( compareIbisWord( aKeyword.c_str(), "Manufacturer" ) ) + status &= storeString( m_currentPackageModel->m_manufacturer, false ); + else if( compareIbisWord( aKeyword.c_str(), "OEM" ) ) + status &= storeString( m_currentPackageModel->m_OEM, false ); + else if( compareIbisWord( aKeyword.c_str(), "Description" ) ) + status &= storeString( m_currentPackageModel->m_description, false ); + else if( compareIbisWord( aKeyword.c_str(), "Number_of_Pins" ) ) + status &= readInt( m_currentPackageModel->m_numberOfPins ); + else if( compareIbisWord( aKeyword.c_str(), "Pin_Numbers" ) ) + status &= readPackageModelPins(); + else if( compareIbisWord( aKeyword.c_str(), "Model_Data" ) ) + { + m_context = IBIS_PARSER_CONTEXT::PACKAGEMODEL_MODELDATA; + m_continue = IBIS_PARSER_CONTINUE::NONE; + } + else + { + if( !changeContext( aKeyword ) ) + { + status = false; + } + } + return status; +} + +bool IbisParser::readModelSelector() +{ + bool status = true; + + IbisModelSelectorEntry model; + + if( readWord( model.m_modelName ) ) + { + if( !readString( model.m_modelDescription ) ) + { + status &= false; + } + m_currentModelSelector->m_models.push_back( model ); + } + else + { + status = false; + } + return status; +} + +bool IbisParser::readNumericSubparam( std::string aSubparam, double& aDest ) +{ + std::string paramName; + bool status = true; + + if( aSubparam.size() >= m_lineLength ) + { + // Continuing would result in an overflow + return false; + } + + int old_index = m_lineIndex; + m_lineIndex = 0; + + for( int i = 0; i < aSubparam.size(); i++ ) + { + paramName += m_buffer[m_lineOffset + m_lineIndex++]; + } + + if( strcmp( paramName.c_str(), aSubparam.c_str() ) ) + { + m_lineIndex = old_index; + return false; + } + + skipWhitespaces(); + + status &= m_buffer[m_lineOffset + m_lineIndex++] == '='; + + if( status ) + { + skipWhitespaces(); + status &= readDouble( aDest ); + } + + if( !status ) + { + m_lineIndex = old_index; + } + + return status; +} + + +bool IbisParser::readTypMinMaxValue( TypMinMaxValue& aDest ) +{ + bool status = true; + + skipWhitespaces(); + + std::string strValue; + + if( !readDouble( aDest.value[IBIS_CORNER::TYP] ) ) + { + Report( _( "Typ-Min-Max Values requires at least Typ." ), RPT_SEVERITY_ERROR ); + return false; + } + + readDouble( aDest.value[IBIS_CORNER::MIN] ); + readDouble( aDest.value[IBIS_CORNER::MAX] ); + + return status; +} + +bool IbisParser::readTypMinMaxValueSubparam( std::string aSubparam, TypMinMaxValue& aDest ) +{ + std::string paramName; + bool status = true; + + m_lineIndex = 0; // rewind + + if( aSubparam.size() < m_lineLength ) + { + for( int i = 0; i < aSubparam.size(); i++ ) + { + paramName += m_buffer[m_lineOffset + m_lineIndex++]; + } + + if( !strcmp( paramName.c_str(), aSubparam.c_str() ) ) + { + readTypMinMaxValue( aDest ); + } + else + { + status = false; + } + } + else + { + status = false; + } + + return status; +} + +bool IbisParser::readModel() +{ + bool status = true; + + int startOfLine = m_lineIndex; + + std::string subparam; + if( readWord( subparam ) ) + { + switch( m_continue ) + { + case IBIS_PARSER_CONTINUE::MODEL: + + if( !strcmp( subparam.substr( 0, 10 ).c_str(), "Model_type" ) ) + { + if( readWord( subparam ) ) + { + if( !strcmp( subparam.c_str(), "Input" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::INPUT; + else if( !strcmp( subparam.c_str(), "Output" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::OUTPUT; + else if( !strcmp( subparam.c_str(), "I/O" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::IO; + else if( !strcmp( subparam.c_str(), "3-state" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::THREE_STATE; + else if( !strcmp( subparam.c_str(), "Open_drain" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::OPEN_DRAIN; + else if( !strcmp( subparam.c_str(), "I/O_Open_drain" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::IO_OPEN_DRAIN; + else if( !strcmp( subparam.c_str(), "Open_sink" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::OPEN_SINK; + else if( !strcmp( subparam.c_str(), "I/O_open_sink" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::IO_OPEN_SINK; + else if( !strcmp( subparam.c_str(), "Open_source" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::OPEN_SOURCE; + else if( !strcmp( subparam.c_str(), "I/O_open_source" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::IO_OPEN_SOURCE; + else if( !strcmp( subparam.c_str(), "Input_ECL" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::INPUT_ECL; + else if( !strcmp( subparam.c_str(), "Output_ECL" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::OUTPUT_ECL; + else if( !strcmp( subparam.c_str(), "I/O_ECL" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::IO_ECL; + else if( !strcmp( subparam.c_str(), "3-state_ECL" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::THREE_STATE_ECL; + else if( !strcmp( subparam.c_str(), "Terminator" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::TERMINATOR; + else if( !strcmp( subparam.c_str(), "Series" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::SERIES; + else if( !strcmp( subparam.c_str(), "Series_switch" ) ) + m_currentModel->m_type = IBIS_MODEL_TYPE::SERIES_SWITCH; + else + { + int size = std::snprintf( nullptr, 0, _( "Unknown Model_type: %s" ), + subparam.c_str() ) + + 1; + char cstr[size]; + std::snprintf( cstr, size, _( "Unknown Model_type: %s" ), + subparam.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ACTION ); + status = false; + } + } + else + { + Report( _( "Internal Error while reading model_type" ), RPT_SEVERITY_ERROR ); + status = false; + } + } + else if( !strcmp( subparam.substr( 0, 7 ).c_str(), "Enable" ) ) + { + if( readWord( subparam ) ) + { + if( !strcmp( subparam.c_str(), "Active-High" ) ) + m_currentModel->m_enable = IBIS_MODEL_ENABLE::ACTIVE_HIGH; + else if( !strcmp( subparam.c_str(), "Active-Low" ) ) + m_currentModel->m_enable = IBIS_MODEL_ENABLE::ACTIVE_LOW; + else + { + int size = std::snprintf( nullptr, 0, _( "Unknown Enable: %s" ), + subparam.c_str() ) + + 1; + char cstr[size]; + std::snprintf( cstr, size, _( "Unknown Enable: %s" ), subparam.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ACTION ); + status = false; + } + } + else + { + Report( _( "Internal Error while reading Enable" ), RPT_SEVERITY_ERROR ); + status = false; + } + } + else if( subparam.substr( 0, 9 ) == "Polarity" ) + { + if( readWord( subparam ) ) + { + if( subparam == "Inverting" ) + m_currentModel->m_enable = IBIS_MODEL_ENABLE::ACTIVE_HIGH; + else if( subparam == "Non-Inverting" ) + m_currentModel->m_enable = IBIS_MODEL_ENABLE::ACTIVE_LOW; + else + { + int size = std::snprintf( nullptr, 0, _( "Unknown polarity: %s" ), + subparam.c_str() ) + + 1; + char cstr[size]; + std::snprintf( cstr, size, _( "Unknown polarity: %s" ), subparam.c_str() ); + Report( std::string( cstr ), RPT_SEVERITY_ACTION ); + status = false; + } + } + else + { + Report( _( "Internal Error while reading Enable" ), RPT_SEVERITY_ERROR ); + status = false; + } + } + else if( readNumericSubparam( std::string( "Vinl" ), m_currentModel->m_vinl ) ) + ; + else if( readNumericSubparam( std::string( "Vinh" ), m_currentModel->m_vinh ) ) + ; + else if( readNumericSubparam( std::string( "Vref" ), m_currentModel->m_vref ) ) + ; + else if( readNumericSubparam( std::string( "Rref" ), m_currentModel->m_rref ) ) + ; + else if( readNumericSubparam( std::string( "Cref" ), m_currentModel->m_cref ) ) + ; + else if( readNumericSubparam( std::string( "Vmeas" ), m_currentModel->m_vmeas ) ) + ; + else if( readTypMinMaxValueSubparam( std::string( "C_comp" ), + *( m_currentModel->m_C_comp ) ) ) + ; + else + { + status = false; + } + + m_continue = IBIS_PARSER_CONTINUE::MODEL; + + break; + default: + status = false; + Report( _( "Continued reading a model that did not begin. ( internal error )" ), + RPT_SEVERITY_ERROR ); + } + } + + return status; +} + + +bool IbisParser::parseHeader( std::string& aKeyword ) +{ + bool status = true; + + if( compareIbisWord( aKeyword.c_str(), "IBIS_Ver" ) ) + { + status &= readDouble( m_ibisFile.m_header->m_ibisVersion ); + } + else if( compareIbisWord( aKeyword.c_str(), "Comment_char" ) ) + { + changeCommentChar(); + } + else if( compareIbisWord( aKeyword.c_str(), "File_Name" ) ) + { + storeString( m_ibisFile.m_header->m_fileName, false ); + } + else if( compareIbisWord( aKeyword.c_str(), "File_Rev" ) ) + { + status &= readDouble( m_ibisFile.m_header->m_fileRevision ); + } + else if( compareIbisWord( aKeyword.c_str(), "Source" ) ) + { + storeString( m_ibisFile.m_header->m_source, true ); + } + else if( compareIbisWord( aKeyword.c_str(), "Notes" ) ) + { + storeString( m_ibisFile.m_header->m_notes, true ); + } + else if( compareIbisWord( aKeyword.c_str(), "Disclaimer" ) ) + { + storeString( m_ibisFile.m_header->m_disclaimer, true ); + } + else if( compareIbisWord( aKeyword.c_str(), "Copyright" ) ) + { + storeString( m_ibisFile.m_header->m_copyright, true ); + } + else if( compareIbisWord( aKeyword.c_str(), "Date" ) ) + { + storeString( m_ibisFile.m_header->m_date, false ); + } + else + { + if( !changeContext( aKeyword ) ) + { + status = false; + } + } + return status; +} + + +bool IbisParser::parseComponent( std::string& aKeyword ) +{ + bool status = true; + if( compareIbisWord( aKeyword.c_str(), "Manufacturer" ) ) + { + status &= storeString( m_currentComponent->m_manufacturer, true ); + } + else if( compareIbisWord( aKeyword.c_str(), "Package" ) ) + { + status &= readPackage(); + } + else if( compareIbisWord( aKeyword.c_str(), "Pin" ) ) + { + status &= readPin(); + } + else if( compareIbisWord( aKeyword.c_str(), "Pin_Mapping" ) ) + { + status &= readPinMapping(); + } + else if( compareIbisWord( aKeyword.c_str(), "Diff_Pin" ) ) + { + status &= readDiffPin(); + } + /* + // Not supported yet + else if( aKeyword == "Die_Supply_Pads" ) + { + status &= ReadDieSupplyPads(); + }*/ + else if( compareIbisWord( aKeyword.c_str(), "Package_Model" ) ) + { + status &= storeString( m_currentComponent->m_packageModel, true ); + } + else + { + if( !changeContext( aKeyword ) ) + { + status = false; + } + } + return status; +} + +bool IbisParser::readTableLine( std::vector& aDest ) +{ + aDest.clear(); + + while( m_lineIndex < m_lineLength ) + { + std::string str; + + while( ( !isspace( m_buffer[m_lineOffset + m_lineIndex] ) ) + && ( m_lineIndex < m_lineLength ) ) + { + str += m_buffer[m_lineOffset + m_lineIndex++]; + } + + if( str.size() > 0 ) + { + aDest.push_back( str ); + } + while( isspace( m_buffer[m_lineOffset + m_lineIndex] ) && ( m_lineIndex < m_lineLength ) ) + { + m_lineIndex++; + } + } + return true; +} + +bool IbisParser::readPackage() +{ + bool status = true; + + std::vector fields; + + TypMinMaxValue* R = m_currentComponent->m_package.m_Rpkg; + TypMinMaxValue* L = m_currentComponent->m_package.m_Lpkg; + TypMinMaxValue* C = m_currentComponent->m_package.m_Cpkg; + + readTableLine( fields ); + + int extraArg = ( m_continue == IBIS_PARSER_CONTINUE::NONE ) ? 1 : 0; + + if( fields.size() == ( 4 + extraArg ) ) + { + if( fields.at( 0 ) == "R_pkg" ) + { + if( parseDouble( R->value[IBIS_CORNER::TYP], fields.at( 1 ), true ) ) + { + status = false; + } + + parseDouble( R->value[IBIS_CORNER::MIN], fields.at( 2 ), true ); + parseDouble( R->value[IBIS_CORNER::MAX], fields.at( 3 ), true ); + } + else if( fields.at( 0 ) == "L_pkg" ) + { + if( parseDouble( L->value[IBIS_CORNER::TYP], fields.at( 1 ), true ) ) + { + status = false; + } + + parseDouble( L->value[IBIS_CORNER::MIN], fields.at( 2 ), true ); + parseDouble( L->value[IBIS_CORNER::MAX], fields.at( 3 ), true ); + } + else if( fields.at( 0 ) == "C_pkg" ) + { + if( parseDouble( C->value[IBIS_CORNER::TYP], fields.at( 1 ), true ) ) + { + status = false; + } + + parseDouble( C->value[IBIS_CORNER::MIN], fields.at( 2 ), true ); + parseDouble( C->value[IBIS_CORNER::MAX], fields.at( 3 ), true ); + } + } + else + { + if( fields.size() != 0 ) + { + Report( _( "A [Package] line requires exactly 4 elements." ), RPT_SEVERITY_ERROR ); + status = false; + } + } + m_continue = IBIS_PARSER_CONTINUE::COMPONENT_PACKAGE; + + return true; +} + + +bool IbisParser::readPin() +{ + bool status = true; + + std::vector fields; + + m_lineIndex = 0; + status &= readTableLine( fields ); + + IbisComponentPin pin( m_reporter ); + + if( ( fields.size() == 3 ) ) + { + if( m_continue == IBIS_PARSER_CONTINUE::COMPONENT_PIN ) // No info on first line + { + pin.m_pinName = fields.at( 0 ); + pin.m_signalName = fields.at( 1 ); + pin.m_modelName = fields.at( 2 ); + pin.m_Rcol = m_currentComponent->m_pins.back().m_Rcol; + pin.m_Lcol = m_currentComponent->m_pins.back().m_Lcol; + pin.m_Ccol = m_currentComponent->m_pins.back().m_Ccol; + + m_currentComponent->m_pins.push_back( pin ); + } + else + { + pin.m_dummy = true; + } + } + else + { + if( m_continue == IBIS_PARSER_CONTINUE::COMPONENT_PIN ) // Not on the first line + { + pin.m_pinName = fields.at( 0 ); + pin.m_signalName = fields.at( 1 ); + pin.m_modelName = fields.at( 2 ); + + pin.m_Rcol = m_currentComponent->m_pins.back().m_Rcol; + pin.m_Lcol = m_currentComponent->m_pins.back().m_Lcol; + pin.m_Ccol = m_currentComponent->m_pins.back().m_Ccol; + + if( pin.m_Rcol == 0 || pin.m_Lcol == 0 || pin.m_Ccol == 0 ) + { + Report( _( "Invalid pin entry: 6 values from a table with only 3." ), + RPT_SEVERITY_ERROR ); + status = false; // Did we just try to go from a 3 column table to a 6 ? + } + else + { + if( !parseDouble( pin.m_Rpin, fields.at( pin.m_Rcol ), true ) + || !parseDouble( pin.m_Lpin, fields.at( pin.m_Lcol ), true ) + || !parseDouble( pin.m_Cpin, fields.at( pin.m_Ccol ), true ) ) + { + Report( _( "Can't read a R, L or C value for a pin." ), RPT_SEVERITY_ERROR ); + status = false; + } + } + } + else + { + for( int i = 3; i < 6; i++ ) + { + if( fields.at( i ) == "R_pin" ) + { + pin.m_Rcol = i; + } + else if( fields.at( i ) == "L_pin" ) + { + pin.m_Lcol = i; + } + else if( fields.at( i ) == "C_pin" ) + { + pin.m_Ccol = i; + } + else + { + Report( _( "Invalid field name in [Pin]" ), RPT_SEVERITY_ERROR ); + status = false; + } + } + + if( pin.m_Rcol == 0 || pin.m_Lcol == 0 || pin.m_Ccol == 0 ) + { + Report( _( "Missing argument in [Pin]" ), RPT_SEVERITY_ERROR ); + status = false; + } + pin.m_dummy = true; + } + } + + m_currentComponent->m_pins.push_back( pin ); + m_continue = IBIS_PARSER_CONTINUE::COMPONENT_PIN; + + + return status; +} + + +bool IbisParser::readPinMapping() +{ + bool status = true; + + std::vector fields; + + status &= readTableLine( fields ); + + IbisComponentPinMapping pinMapping( m_reporter ); + + if( m_continue == IBIS_PARSER_CONTINUE::NONE ) // No info on first line + { + m_continue = IBIS_PARSER_CONTINUE::COMPONENT_PINMAPPING; + } + else + { + if( fields.size() != 0 ) + { + if( fields.size() > 6 && fields.size() < 3 ) + { + Report( _( "Wrong number of columns for pin mapping." ), RPT_SEVERITY_ERROR ); + status = false; + } + else + { + pinMapping.m_pinName = fields.at( 0 ); + pinMapping.m_PDref = fields.at( 1 ); + pinMapping.m_PUref = fields.at( 2 ); + + if( fields.size() > 3 ) + pinMapping.m_GNDClampRef = fields.at( 3 ); + + if( fields.size() > 4 ) + pinMapping.m_POWERClampRef = fields.at( 4 ); + + if( fields.size() > 5 ) + pinMapping.m_extRef = fields.at( 5 ); + } + m_currentComponent->m_pinMappings.push_back( pinMapping ); + } + } + return status; +} + + +bool IbisParser::readDiffPin() +{ + bool status = true; + + IbisDiffPinEntry entry( m_reporter ); + + if( m_continue == IBIS_PARSER_CONTINUE::NONE ) // No info on first line + { + m_continue = IBIS_PARSER_CONTINUE::COMPONENT_DIFFPIN; + } + else + { + if( !readWord( entry.pinA ) ) + { + Report( _( "Incorrect diff pin name" ), RPT_SEVERITY_ERROR ); + status = false; + } + if( !readWord( entry.pinB ) ) + { + Report( _( "Incorrect inv_pin name" ), RPT_SEVERITY_ERROR ); + status = false; + } + if( status && readWord( entry.pinB ) ) + { + m_currentComponent->m_diffPin->m_entries.push_back( entry ); + } + } + return status; +} + + +bool IbisParser::readIVtableEntry( IVtable& aDest ) +{ + bool status = true; + + skipWhitespaces(); + + IVtableEntry entry( m_reporter ); + + if( m_lineIndex < m_lineLength ) + { + std::string str; + + status &= readDouble( entry.V ); + + if( status && readTypMinMaxValue( *( entry.I ) ) ) + { + aDest.m_entries.push_back( entry ); + } + } + + m_continue = IBIS_PARSER_CONTINUE::IV_TABLE; + m_currentIVtable = &aDest; + + + return status; +} + +bool IbisParser::readVTtableEntry( VTtable& aDest ) +{ + bool status = true; + skipWhitespaces(); + + VTtableEntry entry( m_reporter ); + + if( m_lineIndex < m_lineLength ) + { + std::string str; + status &= readDouble( entry.t ); + status &= readTypMinMaxValue( *( entry.V ) ); + } + + m_continue = IBIS_PARSER_CONTINUE::IV_TABLE; + m_currentVTtable = &aDest; + + if( status ) + { + aDest.m_entries.push_back( entry ); + } + + return status; +} + +bool IbisParser::readWaveform( IbisWaveform* aDest, IBIS_WAVEFORM_TYPE aType ) +{ + bool status = true; + + + IbisWaveform* wf; + + if( m_continue != IBIS_PARSER_CONTINUE::WAVEFORM ) + { + wf = new IbisWaveform( m_reporter ); + wf->m_type = aType; + + switch( aType ) + { + case IBIS_WAVEFORM_TYPE::FALLING: m_currentModel->m_fallingWaveforms.push_back( wf ); break; + case IBIS_WAVEFORM_TYPE::RISING: m_currentModel->m_risingWaveforms.push_back( wf ); break; + default: Report( _( "Unknown waveform type" ), RPT_SEVERITY_ERROR ); status = false; + } + } + else + { + if( aDest != nullptr ) + { + wf = aDest; + } + } + + + if( status & !isLineEmptyFromCursor() ) + { + // readNumericSubparam() returns true if it could read the subparameter and store it + // Testing all subparameters + if( readNumericSubparam( std::string( "R_fixture" ), wf->m_R_fixture ) ) + ; + else if( readNumericSubparam( std::string( "L_fixture" ), wf->m_L_fixture ) ) + ; + else if( readNumericSubparam( std::string( "C_fixture" ), wf->m_C_fixture ) ) + ; + else if( readNumericSubparam( std::string( "V_fixture" ), wf->m_V_fixture ) ) + ; + else if( readNumericSubparam( std::string( "V_fixture_min" ), wf->m_V_fixture_min ) ) + ; + else if( readNumericSubparam( std::string( "V_fixture_max" ), wf->m_V_fixture_max ) ) + ; + else if( readNumericSubparam( std::string( "R_dut" ), wf->m_R_fixture ) ) + ; + else if( readNumericSubparam( std::string( "L_dut" ), wf->m_L_fixture ) ) + ; + else if( readNumericSubparam( std::string( "C_dut" ), wf->m_C_fixture ) ) + ; + // The line is not a subparameter, then let's try to read a VT table entry + else if( !readVTtableEntry( *( m_currentWaveform->m_table ) ) ) + { + status = false; + } + } + m_currentWaveform = wf; + m_continue = IBIS_PARSER_CONTINUE::WAVEFORM; + return status; +} + +bool IbisParser::onNewLine() +{ + bool status = true; + char c; + std::string keyword = getKeyword(); + + if( keyword.size() > 0 ) // New keyword + { + + if( m_continue != IBIS_PARSER_CONTINUE::NONE ) + { + m_continue = IBIS_PARSER_CONTINUE::NONE; + } + switch( m_context ) + { + case IBIS_PARSER_CONTEXT::HEADER: status &= parseHeader( keyword ); break; + case IBIS_PARSER_CONTEXT::COMPONENT: status &= parseComponent( keyword ); break; + case IBIS_PARSER_CONTEXT::MODELSELECTOR: status &= parseModelSelector( keyword ); break; + case IBIS_PARSER_CONTEXT::MODEL: status &= parseModel( keyword ); break; + case IBIS_PARSER_CONTEXT::PACKAGEMODEL: status &= parsePackageModel( keyword ); break; + case IBIS_PARSER_CONTEXT::PACKAGEMODEL_MODELDATA: + status &= parsePackageModelModelData( keyword ); + break; + default: + { + status = false; + Report( _( "Internal error: Bad parser context." ), RPT_SEVERITY_ERROR ); + } + } + } + else + { + skipWhitespaces(); + if( m_lineIndex == m_lineLength ) + { + // That was an empty line + return true; + } + + // No new keyword ? Then it is the continuatino of the previous one ! + switch( m_continue ) + { + case IBIS_PARSER_CONTINUE::STRING: + skipWhitespaces(); + *m_continuingString += '\n'; + readString( *m_continuingString ); + break; + case IBIS_PARSER_CONTINUE::COMPONENT_PACKAGE: status &= readPackage(); break; + case IBIS_PARSER_CONTINUE::COMPONENT_PIN: status &= readPin(); break; + case IBIS_PARSER_CONTINUE::COMPONENT_PINMAPPING: status &= readPinMapping(); break; + case IBIS_PARSER_CONTINUE::COMPONENT_DIFFPIN: status &= readDiffPin(); break; + case IBIS_PARSER_CONTINUE::MODELSELECTOR: status &= readModelSelector(); break; + case IBIS_PARSER_CONTINUE::MODEL: status &= readModel(); break; + case IBIS_PARSER_CONTINUE::IV_TABLE: + status &= readIVtableEntry( *( m_currentIVtable ) ); + break; + case IBIS_PARSER_CONTINUE::VT_TABLE: + status &= readVTtableEntry( *( m_currentVTtable ) ); + break; + case IBIS_PARSER_CONTINUE::WAVEFORM: + status &= readWaveform( m_currentWaveform, m_currentWaveform->m_type ); + break; + case IBIS_PARSER_CONTINUE::RAMP: status &= readRamp(); break; + case IBIS_PARSER_CONTINUE::PACKAGEMODEL_PINS: status &= readPackageModelPins(); break; + case IBIS_PARSER_CONTINUE::MATRIX: status &= readMatrix( m_currentMatrix ); break; + case IBIS_PARSER_CONTINUE::NONE: + default: + Report( _( "Missing keyword." ), RPT_SEVERITY_ERROR ); + return false; + break; + } + } + c = m_buffer[m_lineOffset + m_lineIndex]; + + while( ( c != '\n' ) && ( c != 0 ) ) // Go to the end of line + { + c = m_buffer[m_lineOffset + m_lineIndex++]; + } + return status; +} \ No newline at end of file diff --git a/pcbnew/signalIntegrity/ibisParser.h b/pcbnew/signalIntegrity/ibisParser.h new file mode 100644 index 0000000000..ac76d3d2a4 --- /dev/null +++ b/pcbnew/signalIntegrity/ibisParser.h @@ -0,0 +1,765 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Fabien Corona f.coronalaposte.net + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef IBIS_PARSER_H +#define IBIS_PARSER_H + +#define _( x ) x + +#define NAN_NA "1" +#define NAN_INVALID "0" + +#define IBIS_MAX_VERSION 7.0 // Up to v7.0, IBIS is fully backward compatible +#define IBIS_MAX_LINE_LENGTH 2048 // official limit is 1024 + +#include +#include +//#include "common.h" +#include +#include +#include +#include +#include + + +class IBIS_REPORTER +{ +public: + /** @brief Print a message + * + * In the future, this function could do more than just printing a message. + * All KIBIS messages are concentrated at a single point in the code. + * + * @param aMsg Message + * @param aSeverity Message sevirity + */ + void Report( std::string aMsg, SEVERITY aSeverity ) { std::cout << aMsg << std::endl; }; +}; + +class IBIS_ANY +{ +public: + IBIS_ANY( IBIS_REPORTER* aReporter ) { m_reporter = aReporter; }; + IBIS_REPORTER* m_reporter; + + /** @brief Print a message + * + * Call m_reporter->Report if m_reporter exists. + * + * @param aMsg Message + * @param aSeverity Message sevirity + */ + void Report( std::string aMsg, SEVERITY aSeverity = RPT_SEVERITY_INFO ) + { + if( m_reporter ) + { + m_reporter->Report( aMsg, aSeverity ); + } + }; +protected: + /** @brief Convert a double to string using scientific notation + * + * @param aNumber Number + * @return Output string + */ + std::string doubleToString( double aNumber ); +}; + + +class IBIS_INPUT : public IBIS_ANY +{ +public: + IBIS_INPUT( IBIS_REPORTER* aReporter ) : IBIS_ANY( aReporter ){}; + /** @brief Check if the data held by the object is valid. + * + * @return true in case of success + */ + bool virtual Check() { return false; }; +}; + + +enum IBIS_CORNER +{ + TYP = 0, + MIN, + MAX +}; + + +enum class IBIS_MATRIX_TYPE +{ + // All matrices are supposed to be symmetrical, only upper right triangle is given + UNDEFINED, + BANDED, // Give the main diagonal + [bandwith] elements on the right + SPARSE, // Only give non-zero values. + FULL, // Give the whole upper triangle. +}; + +class IBIS_MATRIX : public IBIS_INPUT +{ +public: + IBIS_MATRIX( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + IBIS_MATRIX_TYPE m_type = IBIS_MATRIX_TYPE::UNDEFINED; + int m_dim = -5; + std::vector m_data; +}; + +class IBIS_MATRIX_BANDED : public IBIS_MATRIX +{ +public: + IBIS_MATRIX_BANDED( IBIS_REPORTER* aReporter ) : IBIS_MATRIX( aReporter ){}; + IBIS_MATRIX_TYPE m_type = IBIS_MATRIX_TYPE::BANDED; + int m_dim = -2; + int m_bandwidth = 0; + std::vector m_data; + + bool Check() override; +}; + +class IBIS_MATRIX_SPARSE : public IBIS_MATRIX +{ +public: + IBIS_MATRIX_SPARSE( IBIS_REPORTER* aReporter ) : IBIS_MATRIX( aReporter ){}; + IBIS_MATRIX_TYPE m_type = IBIS_MATRIX_TYPE::BANDED; + int m_dim = -3; + std::vector m_data; + + bool Check() override; +}; + + +class IBIS_MATRIX_FULL : public IBIS_MATRIX +{ +public: + IBIS_MATRIX_FULL( IBIS_REPORTER* aReporter ) : IBIS_MATRIX( aReporter ){}; + IBIS_MATRIX_TYPE m_type = IBIS_MATRIX_TYPE::FULL; + int m_dim = -4; + std::vector m_data; + + bool Check() override; +}; + + +class IBIS_SECTION : public IBIS_INPUT +{ +public: + IBIS_SECTION( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; +}; + + +class IbisHeader : IBIS_SECTION +{ +public: + IbisHeader( IBIS_REPORTER* aReporter ) : IBIS_SECTION( aReporter ){}; + double m_ibisVersion = -1; + double m_fileRevision = -1; + std::string m_fileName; + std::string m_source; + std::string m_date; + std::string m_notes; + std::string m_disclaimer; + std::string m_copyright; + + bool Check() override; +}; + + +class TypMinMaxValue : public IBIS_INPUT +{ +public: + TypMinMaxValue( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + double value[3]; + + bool Check() override; +}; + + +class IbisComponentPackage : public IBIS_INPUT +{ +public: + IbisComponentPackage( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ) + { + m_Rpkg = new TypMinMaxValue( m_reporter ); + m_Lpkg = new TypMinMaxValue( m_reporter ); + m_Cpkg = new TypMinMaxValue( m_reporter ); + }; + TypMinMaxValue* m_Rpkg; + TypMinMaxValue* m_Lpkg; + TypMinMaxValue* m_Cpkg; + + bool Check() override; +}; + + +class IbisComponentPin : public IBIS_INPUT +{ +public: + IbisComponentPin( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + + std::string m_pinName; + std::string m_signalName; + std::string m_modelName; + double m_Rpin = std::nan( NAN_NA ); + double m_Lpin = std::nan( NAN_NA ); + double m_Cpin = std::nan( NAN_NA ); + + int m_Rcol = 0; + int m_Lcol = 0; + int m_Ccol = 0; + + bool Check() override; + + bool m_dummy = false; +}; + + +class IbisComponentPinMapping : public IBIS_INPUT +{ +public: + IbisComponentPinMapping( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + std::string m_pinName; + std::string m_PDref; + std::string m_PUref; + std::string m_GNDClampRef; + std::string m_POWERClampRef; + std::string m_extRef; + + bool m_virtual; +}; + + +class IbisDiffPinEntry : public IBIS_INPUT +{ +public: + IbisDiffPinEntry( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ) + { + tdelay = new TypMinMaxValue( aReporter ); + }; + + std::string pinA; + std::string pinB; + double Vdiff = 0.2; // ignored for input + TypMinMaxValue* tdelay; // ignored for outputs +}; + + +class IbisDiffPin : IBIS_INPUT +{ +public: + IbisDiffPin( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + std::vector m_entries; +}; + +class IbisComponent : public IBIS_INPUT +{ +public: + IbisComponent( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ), m_package( aReporter ) + { + m_diffPin = new IbisDiffPin( m_reporter ); + }; + + std::string m_name = ""; + std::string m_manufacturer = ""; + IbisComponentPackage m_package; + std::vector m_pins; + std::vector m_pinMappings; + std::string m_packageModel; + std::string m_busLabel; + std::string m_dieSupplyPads; + IbisDiffPin* m_diffPin; + + bool Check() override; +}; + + +class IbisModelSelectorEntry +{ +public: + std::string m_modelName; + std::string m_modelDescription; +}; + + +class IbisModelSelector : public IBIS_INPUT +{ +public: + IbisModelSelector( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + std::string m_name; + std::vector m_models; + + bool Check() override; +}; + + +class IVtableEntry : public IBIS_INPUT +{ +public: + IVtableEntry( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ) + { + I = new TypMinMaxValue( m_reporter ); + }; + double V; + TypMinMaxValue* I; +}; + + +class IVtable : public IBIS_INPUT +{ +public: + IVtable( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + std::vector m_entries; + + bool Check() override; + + /** @brief Interpolate the IV table + * + * Linear interpolation to find the current for voltage aV + * + * @param aV voltage + * @param aCorner Power supply corner + * @return current + */ + double InterpolatedI( double aV, IBIS_CORNER aCorner ); + + /** @brief Interpolate the IV table + * + * Generate the spice directive needed to define a model defined by its IV table. + * The order of aPort1 and aPort2 is important. ( Inverting them will reverse the component ) + * + * @param aN Index of the 'a' device + * @param aPort1 Spice node + * @param aPort2 Spice node + * @param aPort2 Name of the generated model + * @param aCorner Power supply corner + * @return Multline spice directives + */ + std::string Spice( int aN, std::string aPort1, std::string aPort2, std::string aModelName, + IBIS_CORNER aCorner ); + +private: +}; + +class VTtableEntry : public IBIS_INPUT +{ +public: + VTtableEntry( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ) + { + V = new TypMinMaxValue( m_reporter ); + }; + double t; + TypMinMaxValue* V; +}; + +class VTtable : public IBIS_INPUT +{ +public: + VTtable( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + std::vector m_entries; +}; + +/* +Model_type must be one of the following: +Input, Output, I/O, 3-state, Open_drain, I/O_open_drain, Open_sink, I/O_open_sink, +Open_source, I/O_open_source, Input_ECL, Output_ECL, I/O_ECL, 3-state_ECL, Terminator, +Series, and Series_switch. +*/ + +enum class IBIS_MODEL_TYPE +{ + UNDEFINED, + INPUT, + OUTPUT, + IO, + THREE_STATE, + OPEN_DRAIN, + IO_OPEN_DRAIN, + OPEN_SINK, + IO_OPEN_SINK, + OPEN_SOURCE, + IO_OPEN_SOURCE, + INPUT_ECL, + OUTPUT_ECL, + IO_ECL, + THREE_STATE_ECL, + TERMINATOR, + SERIES, + SERIES_SWITCH +}; + +enum class IBIS_MODEL_ENABLE +{ + UNDEFINED, + ACTIVE_HIGH, + ACTIVE_LOW +}; + +class dvdt +{ +public: + double m_dv; + double m_dt; +}; + +class dvdtTypMinMax : public IBIS_INPUT +{ +public: + dvdtTypMinMax( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + dvdt value[3]; + + bool Check() override; +}; + + +class IbisRamp : public IBIS_INPUT +{ +public: + IbisRamp( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ) + { + m_falling = new dvdtTypMinMax( m_reporter ); + m_rising = new dvdtTypMinMax( m_reporter ); + }; + + dvdtTypMinMax* m_falling; + dvdtTypMinMax* m_rising; + double m_Rload = 50; // The R_load subparameter is optional if the default 50 ohm load is used + + bool Check() override; +}; + +enum class IBIS_WAVEFORM_TYPE +{ + RISING, + FALLING +}; + +class IbisWaveform : public IBIS_INPUT +{ +public: + IbisWaveform( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ) + { + m_table = new VTtable( m_reporter ); + }; + VTtable* m_table; + IBIS_WAVEFORM_TYPE m_type; + double m_R_dut; + double m_C_dut; + double m_L_dut; + double m_R_fixture = 0; + double m_C_fixture = 0; + double m_L_fixture = 0; + double m_V_fixture = 0; + double m_V_fixture_min = 0; + double m_V_fixture_max = 0; +}; + +enum class IBIS_MODEL_POLARITY +{ + UNDEFINED, + INVERTING, + NON_INVERTING +}; + +class IbisModel : IBIS_INPUT +{ +public: + IbisModel( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ) + { + m_C_comp = new TypMinMaxValue( m_reporter ); + m_voltageRange = new TypMinMaxValue( m_reporter ); + m_temperatureRange = new TypMinMaxValue( m_reporter ); + m_pullupReference = new TypMinMaxValue( m_reporter ); + m_pulldownReference = new TypMinMaxValue( m_reporter ); + m_GNDClampReference = new TypMinMaxValue( m_reporter ); + m_POWERClampReference = new TypMinMaxValue( m_reporter ); + m_Rgnd = new TypMinMaxValue( m_reporter ); + m_Rpower = new TypMinMaxValue( m_reporter ); + m_Rac = new TypMinMaxValue( m_reporter ); + m_Cac = new TypMinMaxValue( m_reporter ); + m_GNDClamp = new IVtable( m_reporter ); + m_POWERClamp = new IVtable( m_reporter ); + m_pullup = new IVtable( m_reporter ); + m_pulldown = new IVtable( m_reporter ); + m_ramp = new IbisRamp( m_reporter ); + }; + + std::string m_name; + IBIS_MODEL_TYPE m_type = IBIS_MODEL_TYPE::UNDEFINED; + /* The Polarity, Enable, Vinl, Vinh, Vmeas, Cref, Rref, and Vref subparameters are optional. */ + /* the default values of Vinl = 0.8 V and Vinh = 2.0 V are assumed. */ + double m_vinl = 0.8; + double m_vinh = 2; + double m_vref = 0; + double m_rref = 0; + double m_cref = 0; + double m_vmeas = 0; + IBIS_MODEL_ENABLE m_enable = IBIS_MODEL_ENABLE::UNDEFINED; + IBIS_MODEL_POLARITY m_polarity = IBIS_MODEL_POLARITY::UNDEFINED; + // End of optional subparameters + + TypMinMaxValue* m_C_comp; + TypMinMaxValue* m_voltageRange; + TypMinMaxValue* m_temperatureRange; + TypMinMaxValue* m_pullupReference; + TypMinMaxValue* m_pulldownReference; + TypMinMaxValue* m_GNDClampReference; + TypMinMaxValue* m_POWERClampReference; + TypMinMaxValue* m_Rgnd; + TypMinMaxValue* m_Rpower; + TypMinMaxValue* m_Rac; + TypMinMaxValue* m_Cac; + IVtable* m_GNDClamp; + IVtable* m_POWERClamp; + IVtable* m_pullup; + IVtable* m_pulldown; + std::vector m_risingWaveforms; + std::vector m_fallingWaveforms; + IbisRamp* m_ramp; + + bool Check() override; +}; + + +class IbisPackageModel : public IBIS_INPUT +{ +public: + IbisPackageModel( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ){}; + + std::string m_name; + std::string m_manufacturer; + std::string m_OEM; + std::string m_description; + int m_numberOfPins; + std::vector m_pins; + + std::shared_ptr m_resistanceMatrix; + std::shared_ptr m_capacitanceMatrix; + std::shared_ptr m_inductanceMatrix; + + bool Check() override; +}; + +class IbisFile : public IBIS_INPUT +{ +public: + IbisFile( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ) + { + m_header = new IbisHeader( m_reporter ); + }; + + IbisHeader* m_header; + std::vector m_components; + std::vector m_modelSelectors; + std::vector m_models; + std::vector m_packageModels; +}; + + +enum class IBIS_PARSER_CONTINUE +{ + NONE, + STRING, + COMPONENT_PACKAGE, + COMPONENT_PINMAPPING, + COMPONENT_DIFFPIN, + COMPONENT_DIESUPPLYPADS, + COMPONENT_PIN, + MATRIX, + MODELSELECTOR, + MODEL, + IV_TABLE, + VT_TABLE, + RAMP, + WAVEFORM, + PACKAGEMODEL_PINS +}; + +enum class IBIS_PARSER_CONTEXT +{ + HEADER, + COMPONENT, + MODELSELECTOR, + MODEL, + PACKAGEMODEL, + PACKAGEMODEL_MODELDATA, + END +}; + +class IbisParser : public IBIS_INPUT +{ +public: + IbisParser( IBIS_REPORTER* aReporter ) : IBIS_INPUT( aReporter ), m_ibisFile( aReporter ){}; + + bool m_parrot = true; // Write back all lines. + + long m_lineCounter; + char m_commentChar = '|'; + std::vector m_buffer; + int m_bufferIndex; + int m_lineOffset; + int m_lineIndex; + int m_lineLength; + + IbisFile m_ibisFile; + IbisComponent* m_currentComponent; + IbisModelSelector* m_currentModelSelector; + IbisModel* m_currentModel; + IbisPackageModel* m_currentPackageModel; + std::shared_ptr m_currentMatrix; + int m_currentMatrixRow; + int m_currentMatrixRowIndex; + IVtable* m_currentIVtable; + VTtable* m_currentVTtable; + IbisWaveform* m_currentWaveform; + + /** @brief Parse a file + * + * This is the entry point to parse a file + * + * @param aFileName input file name + * @return True in case of success + */ + bool ParseFile( std::string& aFileName ); + +private: + std::string* m_continuingString; + + /** @brief compare two strings without being case sensitive + * + * Ibis: "The content of the files is case sensitive, except for reserved words and keywords." + * + * @param a string to compare + * @param b string to compare + * @return true if the string are equal + */ + bool compareIbisWord( const std::string& a, const std::string& b ); + + /** @brief Parse a single keyword in the header context + * + * @param aKeyword Keyword + * @return True in case of success + */ + bool parseHeader( std::string& aKeyword ); + /** @brief Parse a single keyword in the component context + * + * @param aKeyword Keyword + * @return True in case of success + */ + bool parseComponent( std::string& aKeyword ); + /** @brief Parse a single keyword in the component context + * + * @param aKeyword Keyword + * @return True in case of success + */ + bool parseModelSelector( std::string& aKeyword ); + /** @brief Parse a single keyword in the model selector context + * + * @param aKeyword Keyword + * @return True in case of success + */ + bool parseModel( std::string& aKeyword ); + /** @brief Parse a single keyword in the model context + * + * @param aKeyword Keyword + * @return True in case of success + */ + bool parsePackageModel( std::string& aKeyword ); + /** @brief Parse a single keyword in the package model context + * + * @param aKeyword Keyword + * @return True in case of success + */ + bool parsePackageModelModelData( std::string& ); + /** @brief Parse a double according to the ibis standard + * + * @param aDest Where the double should be stored + * @param aStr The string to parse + * @param aAllowModifiers Allows modifiers ( p for pico, f for femto, k for kilo, ... ) + * @return True in case of success + */ + bool parseDouble( double& aDest, std::string& aStr, bool aAllowModifiers = false ); + + /** @brief Parse the current line + * + * @return True in case of success + */ + bool onNewLine(); // Gets rid of comments ( except on a comment character change command...) + /** @brief Load the next line + * + * @return True in case of success + */ + bool getNextLine(); + /** @brief Print the current line */ + void printLine(); + + void skipWhitespaces(); + bool checkEndofLine(); // To be used when there cannot be any character left on the line + bool isLineEmptyFromCursor(); + std::string getKeyword(); + + bool readInt( int& aDest ); + bool readDouble( double& aDest ); + bool readWord( std::string& aDest ); + bool readDvdt( std::string& aString, dvdt& aDest ); + bool readMatrix( std::shared_ptr aDest ); + bool readMatrixBanded( std::string, IBIS_MATRIX_BANDED& aDest ); + bool readMatrixFull( std::string, IBIS_MATRIX_FULL& aDest ); + bool readMatrixSparse( std::string, IBIS_MATRIX_SPARSE& aDest ); + bool readRampdvdt( dvdtTypMinMax& aDest ); + bool readRamp(); + bool readWaveform( IbisWaveform* aDest, IBIS_WAVEFORM_TYPE aType ); + bool readString( std::string& aDest ); + bool storeString( std::string& aDest, bool aMultiline ); + bool readTableLine( std::vector& aDest ); + + bool readNumericSubparam( std::string aSubparam, double& aDest ); + bool readIVtableEntry( IVtable& aTable ); + bool readVTtableEntry( VTtable& aTable ); + bool readTypMinMaxValue( TypMinMaxValue& aDest ); + bool readTypMinMaxValueSubparam( std::string aSubparam, TypMinMaxValue& aDest ); + //bool ReadDieSupplyPads(); + + bool readPackage(); + bool readPin(); + bool readPinMapping(); + bool readDiffPin(); + bool readModelSelector(); + bool readModel(); + bool readPackageModelPins(); + + /** @brief Ibis can change the character used for comments */ + bool changeCommentChar(); + bool changeContext( std::string& aKeyword ); + + IBIS_PARSER_CONTINUE m_continue = IBIS_PARSER_CONTINUE::NONE; + IBIS_PARSER_CONTEXT m_context = IBIS_PARSER_CONTEXT::HEADER; +}; + +#endif \ No newline at end of file diff --git a/pcbnew/signalIntegrity/kibis.cpp b/pcbnew/signalIntegrity/kibis.cpp new file mode 100644 index 0000000000..19803fd5fc --- /dev/null +++ b/pcbnew/signalIntegrity/kibis.cpp @@ -0,0 +1,1238 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Fabien Corona f.coronalaposte.net + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "kibis.h" +#include "ibisParser.h" +#include + +KIBIS_ANY::KIBIS_ANY( KIBIS* aTopLevel ) : IBIS_ANY( aTopLevel->m_reporter ) +{ + m_topLevel = aTopLevel; + m_valid = false; +} + + +IBIS_CORNER ReverseLogic( IBIS_CORNER aIn ) +{ + IBIS_CORNER out = IBIS_CORNER::TYP; + + if( aIn == IBIS_CORNER::MIN ) + { + out = IBIS_CORNER::MAX; + } + else if( aIn == IBIS_CORNER::MAX ) + { + out = IBIS_CORNER::MIN; + } + + return out; +} + +KIBIS::KIBIS( std::string aFileName ) : KIBIS_ANY( this ), m_file( this ) +{ + IBIS_REPORTER reporter; + IbisParser parser( &reporter ); + + parser.m_parrot = false; + parser.ParseFile( aFileName ); + + bool status = true; + + status &= m_file.Init( parser ); + + for( IbisModel& iModel : parser.m_ibisFile.m_models ) + { + KIBIS_MODEL kModel( this, iModel, parser ); + status &= kModel.m_valid; + m_models.push_back( kModel ); + } + + for( IbisComponent& iComponent : parser.m_ibisFile.m_components ) + { + KIBIS_COMPONENT kComponent( this, iComponent, parser ); + + status &= kComponent.m_valid; + m_components.push_back( kComponent ); + + for( KIBIS_PIN& pin : m_components.back().m_pins ) + { + pin.m_parent = &( m_components.back() ); + } + } + + m_valid = status; +} + + +KIBIS_FILE::KIBIS_FILE( KIBIS* aTopLevel ) : KIBIS_ANY( aTopLevel ) +{ +} + +bool KIBIS_FILE::Init( IbisParser& aParser ) +{ + bool status = true; + m_fileName = aParser.m_ibisFile.m_header->m_fileName; + m_fileRev = aParser.m_ibisFile.m_header->m_fileRevision; + m_ibisVersion = aParser.m_ibisFile.m_header->m_ibisVersion; + m_date = aParser.m_ibisFile.m_header->m_date; + m_notes = aParser.m_ibisFile.m_header->m_notes; + m_disclaimer = aParser.m_ibisFile.m_header->m_disclaimer; + m_copyright = aParser.m_ibisFile.m_header->m_copyright; + + m_valid = status; + + return status; +} + +KIBIS_PIN::KIBIS_PIN( KIBIS* aTopLevel, IbisComponentPin& aPin, IbisComponentPackage& aPackage, + IbisParser& aParser, KIBIS_COMPONENT* aParent, + std::vector& aModels ) : + KIBIS_ANY( aTopLevel ) +{ + m_signalName = aPin.m_signalName; + m_pinNumber = aPin.m_pinName; + m_parent = aParent; + + R_pin = aPackage.m_Rpkg; + L_pin = aPackage.m_Lpkg; + C_pin = aPackage.m_Cpkg; + + // The values listed in the [Pin] description section override the default + // values defined in [Package] + + // @TODO : Reading the IBIS standard, I can't figure out if we are supposed + // to replace typ, min, and max, or just the typ ? + + if( !std::isnan( aPin.m_Lpin ) ) + { + R_pin->value[IBIS_CORNER::TYP] = aPin.m_Rpin; + R_pin->value[IBIS_CORNER::MIN] = aPin.m_Rpin; + R_pin->value[IBIS_CORNER::MAX] = aPin.m_Rpin; + } + if( !std::isnan( aPin.m_Lpin ) ) + { + L_pin->value[IBIS_CORNER::TYP] = aPin.m_Lpin; + L_pin->value[IBIS_CORNER::MIN] = aPin.m_Lpin; + L_pin->value[IBIS_CORNER::MAX] = aPin.m_Lpin; + } + if( !std::isnan( aPin.m_Cpin ) ) + { + C_pin->value[IBIS_CORNER::TYP] = aPin.m_Cpin; + C_pin->value[IBIS_CORNER::MIN] = aPin.m_Cpin; + C_pin->value[IBIS_CORNER::MAX] = aPin.m_Cpin; + } + + bool modelSelected = false; + std::vector listOfModels; + + for( IbisModelSelector modelSelector : aParser.m_ibisFile.m_modelSelectors ) + { + if( !strcmp( modelSelector.m_name.c_str(), aPin.m_modelName.c_str() ) ) + { + for( IbisModelSelectorEntry model : modelSelector.m_models ) + { + listOfModels.push_back( model.m_modelName ); + } + modelSelected = true; + break; + } + } + + if( !modelSelected ) + { + listOfModels.push_back( aPin.m_modelName ); + } + + for( std::string modelName : listOfModels ) + { + for( KIBIS_MODEL& model : aModels ) + { + if( !strcmp( model.m_name.c_str(), modelName.c_str() ) ) + { + m_models.push_back( &model ); + } + } + } +} + +KIBIS_MODEL::KIBIS_MODEL( KIBIS* aTopLevel, IbisModel& aSource, IbisParser& aParser ) : + KIBIS_ANY( aTopLevel ) +{ + bool status = true; + + m_name = aSource.m_name; + m_type = aSource.m_type; + + m_description = std::string( "No description available." ); + + for( IbisModelSelector modelSelector : aParser.m_ibisFile.m_modelSelectors ) + { + for( IbisModelSelectorEntry entry : modelSelector.m_models ) + { + if( !strcmp( entry.m_modelName.c_str(), m_name.c_str() ) ) + { + m_description = entry.m_modelDescription; + } + } + } + + m_vinh = aSource.m_vinh; + m_vinl = aSource.m_vinl; + m_vref = aSource.m_vref; + m_rref = aSource.m_rref; + m_cref = aSource.m_cref; + m_vmeas = aSource.m_vmeas; + + m_enable = aSource.m_enable; + m_polarity = aSource.m_polarity; + + m_ramp = aSource.m_ramp; + m_risingWaveforms = aSource.m_risingWaveforms; + m_fallingWaveforms = aSource.m_fallingWaveforms; + m_GNDClamp = aSource.m_GNDClamp; + m_GNDClampReference = aSource.m_GNDClampReference; + m_POWERClamp = aSource.m_POWERClamp; + m_POWERClampReference = aSource.m_POWERClampReference; + + m_C_comp = aSource.m_C_comp; + m_voltageRange = aSource.m_voltageRange; + m_temperatureRange = aSource.m_temperatureRange; + m_pullupReference = aSource.m_pullupReference; + m_pulldownReference = aSource.m_pulldownReference; + + m_Rgnd = aSource.m_Rgnd; + m_Rpower = aSource.m_Rpower; + m_Rac = aSource.m_Rac; + m_Cac = aSource.m_Cac; + m_pullup = aSource.m_pullup; + m_pulldown = aSource.m_pulldown; + + m_valid = status; +} + +KIBIS_COMPONENT::KIBIS_COMPONENT( KIBIS* aTopLevel, IbisComponent& aSource, IbisParser& aParser ) : + KIBIS_ANY( aTopLevel ) +{ + bool status = true; + + m_name = aSource.m_name; + m_manufacturer = aSource.m_manufacturer; + m_topLevel = aTopLevel; + + for( IbisComponentPin& iPin : aSource.m_pins ) + { + if( iPin.m_dummy ) + { + continue; + } + + KIBIS_PIN kPin( aTopLevel, iPin, aSource.m_package, aParser, nullptr, + m_topLevel->m_models ); + status &= kPin.m_valid; + m_pins.push_back( kPin ); + } + + m_valid = status; +} + +KIBIS_PIN* KIBIS_COMPONENT::getPin( std::string aPinNumber ) +{ + for( KIBIS_PIN& pin : m_pins ) + { + if( pin.m_pinNumber == aPinNumber ) + { + return &pin; + } + } + + return nullptr; +} + +std::vector> KIBIS_MODEL::waveformPairs() +{ + std::vector> pairs; + IbisWaveform* wf1; + IbisWaveform* wf2; + + for( int i = 0; i < m_risingWaveforms.size(); i++ ) + { + for( int j = 0; j < m_fallingWaveforms.size(); j++ ) + { + wf1 = m_risingWaveforms.at( i ); + wf2 = m_fallingWaveforms.at( j ); + + if( wf1->m_R_fixture == wf2->m_R_fixture && wf1->m_L_fixture == wf2->m_L_fixture + && wf1->m_C_fixture == wf2->m_C_fixture && wf1->m_V_fixture == wf2->m_V_fixture + && wf1->m_V_fixture_min == wf2->m_V_fixture_min + && wf1->m_V_fixture_max == wf2->m_V_fixture_max ) + { + std::pair p; + p.first = wf1; + p.second = wf2; + pairs.push_back( p ); + } + } + } + + return pairs; +} + +std::string KIBIS_MODEL::SpiceDie( IBIS_CORNER aSupply, IBIS_CORNER aParasitics, int aIndex ) +{ + std::string result; + + std::string GC_GND = "GC_GND"; + std::string PC_PWR = "PC_PWR"; + std::string PU_PWR = "PU_PWR"; + std::string PD_GND = "PD_GND"; + std::string DIE = "DIE"; + std::string DIEBUFF = "DIEBUFF"; + + GC_GND += std::to_string( aIndex ); + PC_PWR += std::to_string( aIndex ); + PU_PWR += std::to_string( aIndex ); + PD_GND += std::to_string( aIndex ); + DIE += std::to_string( aIndex ); + DIEBUFF += std::to_string( aIndex ); + + + std::string GC = "GC"; + std::string PC = "PC"; + std::string PU = "PU"; + std::string PD = "PD"; + + GC += std::to_string( aIndex ); + PC += std::to_string( aIndex ); + PU += std::to_string( aIndex ); + PD += std::to_string( aIndex ); + + result = "\n"; + result += "VPWR POWER GND "; + result += doubleToString( m_voltageRange->value[aSupply] ); + result += "\n"; + result += "CCPOMP " + DIE + " GND "; + result += doubleToString( m_C_comp->value[aParasitics] ); + result += "\n"; + + if( HasGNDClamp() ) + { + result += m_GNDClamp->Spice( aIndex * 4 + 1, DIE, GC_GND, GC, aSupply ); + result += "VmeasGC GND " + GC_GND + " 0\n"; + } + + if( HasPOWERClamp() ) + { + result += m_POWERClamp->Spice( aIndex * 4 + 2, "POWER", DIE, PC, aSupply ); + result += "VmeasPC POWER " + PC_PWR + " 0\n"; + } + + if( HasPulldown() ) + { + result += m_pulldown->Spice( aIndex * 4 + 3, DIEBUFF, PD_GND, PD, aSupply ); + result += "VmeasPD GND " + PD_GND + " 0\n"; + result += "BKD GND " + DIE + " i=( -i(VmeasPU) * v(KU) )\n"; + } + + if( HasPullup() ) + { + result += m_pullup->Spice( aIndex * 4 + 4, PU_PWR, DIEBUFF, PU, aSupply ); + result += "VmeasPU POWER " + PU_PWR + " 0\n"; + result += "BKU POWER " + DIE + " i=( i(VmeasPD) * v(KD) )\n"; + } + + result += "BDIEBUFF " + DIEBUFF + " GND v=v(" + DIE + ")\n"; + + return result; +} + +IbisWaveform KIBIS_MODEL::TrimWaveform( IbisWaveform& aIn ) +{ + IbisWaveform out( aIn.m_reporter ); + + int nbPoints = aIn.m_table->m_entries.size(); + + if( nbPoints < 2 ) + { + Report( _( "waveform has less than two points" ), RPT_SEVERITY_ERROR ); + return out; + } + + double DCtyp = aIn.m_table->m_entries[0].V->value[IBIS_CORNER::TYP]; + double DCmin = aIn.m_table->m_entries[0].V->value[IBIS_CORNER::MIN]; + double DCmax = aIn.m_table->m_entries[0].V->value[IBIS_CORNER::MAX]; + + if( nbPoints == 2 ) + { + return out; + } + + out.m_table->m_entries.clear(); + bool kept = false; + + for( int i = 0; i < nbPoints; i++ ) + { + VTtableEntry entry( out.m_reporter ); + + entry.t = aIn.m_table->m_entries.at( i ).t; + entry.V->value[IBIS_CORNER::TYP] = + aIn.m_table->m_entries.at( i ).V->value[IBIS_CORNER::TYP]; + entry.V->value[IBIS_CORNER::MIN] = + aIn.m_table->m_entries.at( i ).V->value[IBIS_CORNER::MIN]; + entry.V->value[IBIS_CORNER::MAX] = + aIn.m_table->m_entries.at( i ).V->value[IBIS_CORNER::MAX]; + out.m_table->m_entries.push_back( entry ); + out.m_table->m_entries.at( i ).V->value[IBIS_CORNER::TYP] -= DCtyp; + out.m_table->m_entries.at( i ).V->value[IBIS_CORNER::MIN] -= DCmin; + out.m_table->m_entries.at( i ).V->value[IBIS_CORNER::MAX] -= DCmax; + } + + return out; +} + +bool KIBIS_MODEL::HasPulldown() +{ + return m_pulldown->m_entries.size() > 0; +} +bool KIBIS_MODEL::HasPullup() +{ + return m_pullup->m_entries.size() > 0; +} +bool KIBIS_MODEL::HasGNDClamp() +{ + return m_GNDClamp->m_entries.size() > 0; +} +bool KIBIS_MODEL::HasPOWERClamp() +{ + return m_POWERClamp->m_entries.size() > 0; +} + +std::string KIBIS_MODEL::generateSquareWave( std::string aNode1, std::string aNode2, + std::vector> aBits, + std::pair aPair, + IBIS_CORNER aSupply ) +{ + std::string simul; + + IbisWaveform risingWF = TrimWaveform( *( aPair.first ) ); + IbisWaveform fallingWF = TrimWaveform( *( aPair.second ) ); + + double deltaR = risingWF.m_table->m_entries.back().V->value[aSupply] + - risingWF.m_table->m_entries.at( 0 ).V->value[aSupply]; + double deltaF = fallingWF.m_table->m_entries.back().V->value[aSupply] + - fallingWF.m_table->m_entries.at( 0 ).V->value[aSupply]; + + // Ideally, delta should be equal to zero. + // It can be different from zero if the falling waveform does not start were the rising one ended. + double delta = deltaR + deltaF; + + int i = 0; + for( std::pair bit : aBits ) + { + IbisWaveform* WF; + double timing = bit.second; + + if( bit.first == 1 ) + WF = &risingWF; + else + WF = &fallingWF; + + simul += "Vstimuli"; + simul += std::to_string( i ); + simul += " stimuli"; + simul += std::to_string( i ); + simul += " "; + simul += aNode2; + simul += " pwl ( "; + + if( i != 0 ) + { + simul += "0 0 "; + VTtableEntry entry0 = WF->m_table->m_entries.at( 0 ); + VTtableEntry entry1 = WF->m_table->m_entries.at( 1 ); + double deltaT = entry1.t - entry0.t; + + simul += doubleToString( entry0.t + timing - deltaT ); + simul += " "; + simul += "0"; + simul += " "; + } + + for( VTtableEntry& entry : WF->m_table->m_entries ) + { + simul += doubleToString( entry.t + timing ); + simul += " "; + simul += doubleToString( entry.V->value[aSupply] - delta ); + simul += " "; + } + simul += ")\n"; + + i++; + } + + simul += "bin "; + simul += aNode1; + simul += " "; + simul += aNode2; + simul += " v=("; + + for( int i = 0; i < aBits.size(); i++ ) + { + simul += " v( stimuli"; + simul += std::to_string( i ); + simul += " ) + "; + } + + // Depending on the first bit, we add a different DC value + // The DC value we add is the first value of the first bit. + if( aBits[0].first == 0 ) + { + simul += doubleToString( aPair.second->m_table->m_entries.at( 0 ).V->value[aSupply] ); + } + else + { + simul += doubleToString( aPair.first->m_table->m_entries.at( 0 ).V->value[aSupply] ); + } + + simul += ")\n"; + return simul; +} + +std::string KIBIS_PIN::addDie( KIBIS_MODEL& aModel, IBIS_CORNER aSupply, int aIndex ) +{ + std::string simul; + + std::string GC_GND = "GC_GND"; + std::string PC_PWR = "PC_PWR"; + std::string PU_PWR = "PU_PWR"; + std::string PD_GND = "PD_GND"; + std::string DIE = "DIE"; + + GC_GND += std::to_string( aIndex ); + PC_PWR += std::to_string( aIndex ); + PU_PWR += std::to_string( aIndex ); + PD_GND += std::to_string( aIndex ); + DIE += std::to_string( aIndex ); + + + std::string GC = "GC"; + std::string PC = "PC"; + std::string PU = "PU"; + std::string PD = "PD"; + + GC += std::to_string( aIndex ); + PC += std::to_string( aIndex ); + PU += std::to_string( aIndex ); + PD += std::to_string( aIndex ); + + if( aModel.HasGNDClamp() ) + { + simul += aModel.m_GNDClamp->Spice( aIndex * 4 + 1, DIE, GC_GND, GC, aSupply ); + } + + if( aModel.HasPOWERClamp() ) + { + simul += aModel.m_POWERClamp->Spice( aIndex * 4 + 2, PC_PWR, DIE, PC, aSupply ); + } + + if( aModel.HasPulldown() ) + { + simul += aModel.m_pulldown->Spice( aIndex * 4 + 3, DIE, PD_GND, PD, aSupply ); + } + + if( aModel.HasPullup() ) + { + simul += aModel.m_pullup->Spice( aIndex * 4 + 4, PU_PWR, DIE, PU, aSupply ); + } + + return simul; +} + +void KIBIS_PIN::getKuKdFromFile( std::string* aSimul ) +{ + // @TODO + // that's not the best way to do, but ¯\_(ツ)_/¯ + std::ifstream in( "temp_input.spice" ); + + std::remove( "temp_input.spice" ); + std::remove( "temp_output.spice" ); + + std::ofstream file( "temp_input.spice" ); + + file << *aSimul; + + system( "ngspice temp_input.spice" ); + + + std::ifstream KuKdfile; + KuKdfile.open( "temp_output.spice" ); + + std::vector ku, kd, t; + if( KuKdfile ) + { + std::string line; + for( int i = 0; i < 11; i++ ) // number of line in the ngspice output header + { + std::getline( KuKdfile, line ); + } + int i = 0; + double t_v, ku_v, kd_v; + while( KuKdfile ) + { + std::getline( KuKdfile, line ); + + if( line.empty() ) + { + continue; + } + switch( i ) + { + case 0: + line = line.substr( line.find_first_of( "\t" ) + 1 ); + t_v = std::stod( line ); + break; + case 1: ku_v = std::stod( line ); break; + case 2: + kd_v = std::stod( line ); + ku.push_back( ku_v ); + kd.push_back( kd_v ); + t.push_back( t_v ); + break; + default: Report( _( "Error while reading temporary file" ), RPT_SEVERITY_ERROR ); + } + i = ( i + 1 ) % 3; + } + std::getline( KuKdfile, line ); + } + else + { + Report( _( "Error while creating temporary file" ), RPT_SEVERITY_ERROR ); + } + std::remove( "temp_input.spice" ); + std::remove( "temp_output.spice" ); + + // @TODO : this is the end of the dirty code + + m_Ku = ku; + m_Kd = kd; + m_t = t; +} + +std::string KIBIS_PIN::KuKdDriver( KIBIS_MODEL& aModel, + std::pair aPair, + KIBIS_WAVEFORM* aWave, IBIS_CORNER aSupply, IBIS_CORNER aParasitics, + int aIndex ) +{ + std::string simul = ""; + + simul += "*THIS IS NOT A VALID SPICE MODEL.\n"; + simul += "*This part is intended to be executed by Kibis internally.\n"; + simul += "*You should not be able to read this.\n\n"; + + simul += ".SUBCKT DRIVER"; + simul += std::to_string( aIndex ); + simul += " POWER GND PIN \n"; // 1: POWER, 2:GND, 3:PIN + + if( ( aPair.first->m_R_dut == 0 ) && ( aPair.first->m_L_dut == 0 ) + && ( aPair.first->m_C_dut == 0 ) + || true ) + { + simul += "Vdummy 2 PIN 0\n"; + } + { + /* + simul += "RPIN 1 PIN "; + simul << aPair.first->m_R_dut; + simul += "\n"; + simul += "LPIN 2 1 "; + simul << aPair.first->m_L_dut; + simul += "\n"; + simul += "CPIN PIN GND "; + simul << aPair.first->m_C_dut; + simul += "\n"; + */ + Report( _( "Kibis does not support DUT values yet. " + "https://ibis.org/summits/nov16a/chen.pdf" ), + RPT_SEVERITY_WARNING ); + } + + simul += "\n"; + simul += "CCPOMP 2 GND "; + simul += doubleToString( aModel.m_C_comp->value[aParasitics] ); //@TODO: Check the corner ? + simul += "\n"; + switch( aWave->GetType() ) + { + case KIBIS_WAVEFORM_TYPE::RECTANGULAR: + { + std::vector> bits; + KIBIS_WAVEFORM_RECTANGULAR* rectWave = static_cast( aWave ); + + IbisWaveform* risingWF = aPair.first; + IbisWaveform* fallingWF = aPair.second; + + if( rectWave->m_ton < risingWF->m_table->m_entries.back().t ) + { + Report( _( "Rising edge is longer than on time." ), RPT_SEVERITY_WARNING ); + } + + if( rectWave->m_toff < fallingWF->m_table->m_entries.back().t ) + { + Report( _( "Falling edge is longer than off time." ), RPT_SEVERITY_WARNING ); + } + + for( int i = 0; i < rectWave->m_cycles; i++ ) + { + std::pair bit; + bit.first = rectWave->inverted ? 0 : 1; + bit.second = ( rectWave->m_ton + rectWave->m_toff ) * i + rectWave->m_delay; + bits.push_back( bit ); + + bit.first = rectWave->inverted ? 1 : 0; + bit.second = ( rectWave->m_ton + rectWave->m_toff ) * i + rectWave->m_delay + + rectWave->m_ton; + bits.push_back( bit ); + } + + simul += aModel.generateSquareWave( "DIE0", "GND", bits, aPair, aSupply ); + break; + } + case KIBIS_WAVEFORM_TYPE::STUCK_HIGH: + { + IbisWaveform* fallingWF = aPair.second; + simul += "Vsig DIE0 GND "; + simul += doubleToString( fallingWF->m_table->m_entries.at( 0 ).V->value[aSupply] ); + simul += "\n"; + break; + } + case KIBIS_WAVEFORM_TYPE::STUCK_LOW: + { + IbisWaveform* risingWF = aPair.first; + simul += "Vsig DIE0 GND "; + simul += doubleToString( risingWF->m_table->m_entries.at( 0 ).V->value[aSupply] ); + simul += "\n"; + break; + } + case KIBIS_WAVEFORM_TYPE::NONE: + case KIBIS_WAVEFORM_TYPE::HIGH_Z: + default: break; + } + simul += addDie( aModel, aSupply, 0 ); + + simul += "\n.ENDS DRIVER\n\n"; + return simul; +} + +void KIBIS_PIN::getKuKdOneWaveform( KIBIS_MODEL& aModel, + std::pair aPair, + KIBIS_WAVEFORM* aWave, IBIS_CORNER aSupply, IBIS_CORNER aParasitics ) +{ + std::string simul = ""; + + if( aWave->GetType() == KIBIS_WAVEFORM_TYPE::NONE ) + { + //@TODO , there could be some current flowing through pullup / pulldown transistors, even when off + std::vector ku, kd, t; + ku.push_back( 0 ); + kd.push_back( 0 ); + t.push_back( 0 ); + m_Ku = ku; + m_Kd = kd; + m_t = t; + } + else + { + simul += KuKdDriver( aModel, aPair, aWave, aSupply, aParasitics, 0 ); + simul += "\n x1 3 0 1 DRIVER0 \n"; + + simul += "VCC 3 0 "; + simul += doubleToString( aModel.m_voltageRange->value[aSupply] ); + simul += "\n"; + //simul += "Vpin x1.DIE 0 1 \n" + simul += "Lfixture 1 4 "; + simul += doubleToString( aPair.first->m_L_fixture ); + simul += "\n"; + simul += "Rfixture 4 5 "; + simul += doubleToString( aPair.first->m_R_fixture ); + simul += "\n"; + simul += "Cfixture 4 0 "; + simul += doubleToString( aPair.first->m_C_fixture ); + simul += "\n"; + simul += "Vfixture 5 0 "; + simul += doubleToString( aPair.first->m_V_fixture ); + simul += "\n"; + simul += "VmeasIout x1.DIE0 x1.2 0\n"; + simul += "VmeasPD 0 x1.PD_GND0 0\n"; + simul += "VmeasPU x1.PU_PWR0 3 0\n"; + simul += "VmeasPC x1.PC_PWR0 3 0\n"; + simul += "VmeasGC 0 x1.GC_PWR0 0\n"; + + if( aModel.HasPullup() && aModel.HasPulldown() ) + { + Report( _( "Model has only one waveform pair, reduced accuracy" ), + RPT_SEVERITY_WARNING ); + simul += "Bku KU 0 v=( (i(VmeasIout)-i(VmeasPC)-i(VmeasGC)-i(VmeasPD) " + ")/(i(VmeasPU)-i(VmeasPD)))\n"; + simul += "Bkd KD 0 v=(1-v(KU))\n"; + } + + else if( !aModel.HasPullup() && aModel.HasPulldown() ) + { + simul += "Bku KD 0 v=( ( i(VmeasIout)+i(VmeasPC)+i(VmeasGC) )/(i(VmeasPD)))\n"; + simul += "Bkd KU 0 v=0\n"; + } + + else if( aModel.HasPullup() && !aModel.HasPulldown() ) + { + simul += "Bku KU 0 v=( ( i(VmeasIout)+i(VmeasPC)+i(VmeasGC) )/(i(VmeasPU)))\n"; + simul += "Bkd KD 0 v=0\n"; + } + else + { + Report( _( "Driver needs at least a pullup or a pulldown" ), RPT_SEVERITY_ERROR ); + } + + switch( aWave->GetType() ) + { + case KIBIS_WAVEFORM_TYPE::RECTANGULAR: simul += ".tran 0.1n 1000n \n"; break; + + case KIBIS_WAVEFORM_TYPE::HIGH_Z: + case KIBIS_WAVEFORM_TYPE::STUCK_LOW: + case KIBIS_WAVEFORM_TYPE::STUCK_HIGH: + default: simul += ".tran 0.5 1 \n"; // + } + //simul += ".dc Vpin -5 5 0.1\n"; + simul += ".control run \n"; + simul += "set filetype=ascii\n"; + simul += "run \n"; + simul += "plot v(x1.DIE0) i(VmeasIout) i(VmeasPD) i(VmeasPU) i(VmeasPC) i(VmeasGC)\n"; + simul += "plot v(KU) v(KD)\n"; + simul += "write temp_output.spice v(KU) v(KD)\n"; // @TODO we might want to remove this... + //simul += "quit\n"; + simul += ".endc \n"; + simul += ".end \n"; + + getKuKdFromFile( &simul ); + } +} + +void KIBIS_PIN::getKuKdNoWaveform( KIBIS_MODEL& aModel, KIBIS_WAVEFORM* aWave, IBIS_CORNER aSupply ) +{ + std::vector ku, kd, t; + + switch( aWave->GetType() ) + { + case KIBIS_WAVEFORM_TYPE::RECTANGULAR: + { + KIBIS_WAVEFORM_RECTANGULAR* rectWave = static_cast( aWave ); + for( int i = 0; i < rectWave->m_cycles; i++ ) + { + ku.push_back( 0 ); + kd.push_back( 1 ); + t.push_back( ( rectWave->m_ton + rectWave->m_toff ) * i ); + ku.push_back( 1 ); + kd.push_back( 0 ); + t.push_back( ( rectWave->m_ton + rectWave->m_toff ) * i + + aModel.m_ramp->m_rising->value[aSupply].m_dt + / 0.6 ); // 0.6 because ibis only gives 20%-80% time + ku.push_back( 1 ); + kd.push_back( 0 ); + t.push_back( ( rectWave->m_ton + rectWave->m_toff ) * i + rectWave->m_toff ); + ku.push_back( 0 ); + kd.push_back( 1 ); + t.push_back( ( rectWave->m_ton + rectWave->m_toff ) * i + rectWave->m_toff + + aModel.m_ramp->m_falling->value[aSupply].m_dt / 0.6 ); + } + break; + } + case KIBIS_WAVEFORM_TYPE::STUCK_HIGH: + { + ku.push_back( 1 ); + kd.push_back( 0 ); + t.push_back( 0 ); + break; + } + case KIBIS_WAVEFORM_TYPE::STUCK_LOW: + { + ku.push_back( 0 ); + kd.push_back( 1 ); + t.push_back( 0 ); + break; + } + case KIBIS_WAVEFORM_TYPE::HIGH_Z: + case KIBIS_WAVEFORM_TYPE::NONE: + default: + ku.push_back( 0 ); + kd.push_back( 0 ); + t.push_back( 0 ); + } + m_Ku = ku; + m_Kd = kd; + m_t = t; +} + +void KIBIS_PIN::getKuKdTwoWaveforms( KIBIS_MODEL& aModel, + std::pair aPair1, + std::pair aPair2, + KIBIS_WAVEFORM* aWave, IBIS_CORNER aSupply, + IBIS_CORNER aParasitics ) +{ + std::string simul = ""; + + if( aWave->GetType() == KIBIS_WAVEFORM_TYPE::NONE ) + { + //@TODO , there could be some current flowing through pullup / pulldown transistors, even when off + std::vector ku, kd, t; + ku.push_back( 0 ); + kd.push_back( 0 ); + t.push_back( 0 ); + m_Ku = ku; + m_Kd = kd; + m_t = t; + } + else + { + simul += KuKdDriver( aModel, aPair1, aWave, aSupply, aParasitics, 0 ); + simul += KuKdDriver( aModel, aPair2, aWave, aSupply, aParasitics, 1 ); + simul += "\n x1 3 0 1 DRIVER0 \n"; + + simul += "VCC 3 0 "; + simul += doubleToString( aModel.m_voltageRange->value[aSupply] ); + simul += "\n"; + //simul += "Vpin x1.DIE 0 1 \n" + simul += "Lfixture0 1 4 "; + simul += doubleToString( aPair1.first->m_L_fixture ); + simul += "\n"; + simul += "Rfixture0 4 5 "; + simul += doubleToString( aPair1.first->m_R_fixture ); + simul += "\n"; + simul += "Cfixture0 4 0 "; + simul += doubleToString( aPair1.first->m_C_fixture ); + simul += "\n"; + simul += "Vfixture0 5 0 "; + simul += doubleToString( aPair1.first->m_V_fixture ); + simul += "\n"; + simul += "VmeasIout0 x1.2 x1.DIE0 0\n"; + simul += "VmeasPD0 0 x1.PD_GND0 0\n"; + simul += "VmeasPU0 x1.PU_PWR0 3 0\n"; + simul += "VmeasPC0 x1.PC_PWR0 3 0\n"; + simul += "VmeasGC0 0 x1.GC_PWR0 0\n"; + + + simul += "\n x2 3 0 7 DRIVER1 \n"; + //simul += "Vpin x1.DIE 0 1 \n" + simul += "Lfixture1 7 8 "; + simul += doubleToString( aPair2.first->m_L_fixture ); + simul += "\n"; + simul += "Rfixture1 8 9 "; + simul += doubleToString( aPair2.first->m_R_fixture ); + simul += "\n"; + simul += "Cfixture1 8 0 "; + simul += doubleToString( aPair2.first->m_C_fixture ); + simul += "\n"; + simul += "Vfixture1 9 0 "; + simul += doubleToString( aPair2.first->m_V_fixture ); + simul += "\n"; + simul += "VmeasIout1 x2.2 x2.DIE0 0\n"; + simul += "VmeasPD1 0 x2.PD_GND0 0\n"; + simul += "VmeasPU1 x2.PU_PWR0 3 0\n"; + simul += "VmeasPC1 x2.PC_PWR0 3 0\n"; + simul += "VmeasGC1 0 x2.GC_PWR0 0\n"; + + if( aModel.HasPullup() && aModel.HasPulldown() ) + { + simul += + "Bku KU 0 v=( ( i(VmeasPD1) * ( i(VmeasIout0) + i(VmeasPC0) + i(VmeasGC0) ) - " + "i(VmeasPD0) * ( i(VmeasIout1) + i(VmeasPC1) + i(VmeasGC1) ) )/ ( i(VmeasPU1) " + "* " + "i(VmeasPD0) - i(VmeasPU0) * i(VmeasPD1) ) )\n"; + simul += + "Bkd KD 0 v=( ( i(VmeasPU1) * ( i(VmeasIout0) + i(VmeasPC0) + i(VmeasGC0) ) - " + "i(VmeasPU0) * ( i(VmeasIout1) + i(VmeasPC1) + i(VmeasGC1) ) )/ ( i(VmeasPD1) " + "* " + "i(VmeasPU0) - i(VmeasPD0) * i(VmeasPU1) ) )\n"; + //simul += "Bkd KD 0 v=(1-v(KU))\n"; + } + + else if( !aModel.HasPullup() && aModel.HasPulldown() ) + { + Report( _( "There are two waveform pairs, but only one transistor. More equations than " + "unknowns." ), + RPT_SEVERITY_WARNING ); + simul += "Bku KD 0 v=( ( i(VmeasIout0)+i(VmeasPC0)+i(VmeasGC0) )/(i(VmeasPD0)))\n"; + simul += "Bkd KU 0 v=0\n"; + } + + else if( aModel.HasPullup() && !aModel.HasPulldown() ) + { + Report( _( "There are two waveform pairs, but only one transistor. More equations than " + "unknowns." ), + RPT_SEVERITY_WARNING ); + simul += "Bku KU 0 v=( ( i(VmeasIout)+i(VmeasPC)+i(VmeasGC) )/(i(VmeasPU)))\n"; + simul += "Bkd KD 0 v=0\n"; + } + else + { + Report( _( "Driver needs at least a pullup or a pulldown" ), RPT_SEVERITY_ERROR ); + } + + simul += ".tran 0.1n 1000n \n"; + //simul += ".dc Vpin -5 5 0.1\n"; + simul += ".control run \n"; + simul += "set filetype=ascii\n"; + simul += "run \n"; + simul += "plot v(KU) v(KD)\n"; + simul += "plot v(x1.DIE0) \n"; + simul += "write temp_output.spice v(KU) v(KD)\n"; // @TODO we might want to remove this... + //simul += "quit\n"; + simul += ".endc \n"; + simul += ".end \n"; + + getKuKdFromFile( &simul ); + } +} + +bool KIBIS_PIN::writeSpiceDriver( std::string* aDest, std::string aName, KIBIS_MODEL& aModel, + IBIS_CORNER aSupply, IBIS_CORNER aParasitics, KIBIS_ACCURACY aAccuracy, + KIBIS_WAVEFORM* aWave ) +{ + bool status = true; + + switch( aModel.m_type ) + { + case IBIS_MODEL_TYPE::OUTPUT: + case IBIS_MODEL_TYPE::IO: + case IBIS_MODEL_TYPE::THREE_STATE: + case IBIS_MODEL_TYPE::OPEN_DRAIN: + case IBIS_MODEL_TYPE::IO_OPEN_DRAIN: + case IBIS_MODEL_TYPE::OPEN_SINK: + case IBIS_MODEL_TYPE::IO_OPEN_SINK: + case IBIS_MODEL_TYPE::OPEN_SOURCE: + case IBIS_MODEL_TYPE::IO_OPEN_SOURCE: + case IBIS_MODEL_TYPE::OUTPUT_ECL: + case IBIS_MODEL_TYPE::IO_ECL: + case IBIS_MODEL_TYPE::THREE_STATE_ECL: + { + std::string result; + std::string tmp; + + result = "\n*Driver model generated by Kicad using Ibis data. "; + result += "\n*Component: "; + + if( m_parent ) + { + result += m_parent->m_name; + } + result += "\n*Manufacturer: "; + + if( m_parent ) + { + result += m_parent->m_manufacturer; + } + result += "\n*Pin number: "; + result += m_pinNumber; + result += "\n*Signal name: "; + result += m_signalName; + result += "\n*Model: "; + result += aModel.m_name; + result += "\n.SUBCKT "; + result += aName; + result += " GND PIN \n"; + result += "\n"; + + result += "RPIN 1 PIN "; + result += doubleToString( R_pin->value[aParasitics] ); + result += "\n"; + result += "LPIN DIE0 1 "; + result += doubleToString( L_pin->value[aParasitics] ); + result += "\n"; + result += "CPIN PIN GND "; + result += doubleToString( C_pin->value[aParasitics] ); + result += "\n"; + + std::vector> wfPairs = aModel.waveformPairs(); + + if( wfPairs.size() < 1 || aAccuracy <= KIBIS_ACCURACY::LEVEL_0 ) + { + if( aAccuracy > KIBIS_ACCURACY::LEVEL_0 ) + { + Report( _( "Model has no waveform pair, using [Ramp] instead, poor accuracy" ), + RPT_SEVERITY_INFO ); + } + getKuKdNoWaveform( aModel, aWave, aSupply ); + } + else if( wfPairs.size() == 1 || aAccuracy <= KIBIS_ACCURACY::LEVEL_1 ) + { + getKuKdOneWaveform( aModel, wfPairs.at( 0 ), aWave, aSupply, aParasitics ); + } + else + { + if( wfPairs.size() > 2 || aAccuracy <= KIBIS_ACCURACY::LEVEL_2 ) + { + Report( _( "Model has more than 2 waveform pairs, using the first two." ), + RPT_SEVERITY_WARNING ); + } + getKuKdTwoWaveforms( aModel, wfPairs.at( 0 ), wfPairs.at( 1 ), aWave, aSupply, aParasitics ); + } + + result += "Vku KU GND pwl ( "; + for( int i = 0; i < m_t.size(); i++ ) + { + result += doubleToString( m_t.at( i ) ); + result += " "; + result += doubleToString( m_Ku.at( i ) ); + result += " "; + } + result += ") \n"; + + + result += "Vkd KD GND pwl ( "; + for( int i = 0; i < m_t.size(); i++ ) + { + result += doubleToString( m_t.at( i ) ); + result += " "; + result += doubleToString( m_Kd.at( i ) ); + result += " "; + } + + result += ") \n"; + + result += aModel.SpiceDie( aSupply, aParasitics, 0 ); + + result += "\n.ENDS DRIVER\n\n"; + + *aDest += result; + break; + } + default: Report( _( "Invalid model type for a driver." ), RPT_SEVERITY_ERROR ); status = false; + } + + return status; +} + + +bool KIBIS_PIN::writeSpiceDevice( std::string* aDest, std::string aName, KIBIS_MODEL& aModel, + IBIS_CORNER aSupply, IBIS_CORNER aParasitics ) +{ + bool status = true; + + switch( aModel.m_type ) + { + case IBIS_MODEL_TYPE::INPUT: + case IBIS_MODEL_TYPE::IO: + case IBIS_MODEL_TYPE::IO_OPEN_DRAIN: + case IBIS_MODEL_TYPE::IO_OPEN_SINK: + case IBIS_MODEL_TYPE::IO_OPEN_SOURCE: + case IBIS_MODEL_TYPE::IO_ECL: + { + std::string result; + std::string tmp; + + result += "\n"; + result = "*Device model generated by Kicad using Ibis data."; + result += "\n.SUBCKT "; + result += aName; + result += " GND PIN\n"; + result += "\n"; + result += "\n"; + result += "RPIN 1 PIN "; + result += doubleToString( R_pin->value[aParasitics] ); + result += "\n"; + result += "LPIN DIE 1 "; + result += doubleToString( L_pin->value[aParasitics] ); + result += "\n"; + result += "CPIN PIN GND "; + result += doubleToString( C_pin->value[aParasitics] ); + result += "\n"; + + + result += "Vku KU GND pwl ( 0 0 )\n"; + result += "Vkd KD GND pwl ( 0 0 )\n"; + + result += aModel.SpiceDie( aSupply, aParasitics, 0 ); + + result += "\n.ENDS DRIVER\n\n"; + + *aDest = result; + break; + } + default: Report( _( "Invalid model type for a device" ), RPT_SEVERITY_ERROR ); status = false; + } + + return status; +} + +bool KIBIS_PIN::writeSpiceDiffDriver( std::string* aDest, std::string aName, KIBIS_MODEL& aModel, + IBIS_CORNER aSupply, IBIS_CORNER aParasitics, + KIBIS_ACCURACY aAccuracy, KIBIS_WAVEFORM* aWave ) +{ + bool status = true; + + std::string result; + result = "\n*Differential driver model generated by Kicad using Ibis data. "; + result += "\n*Component: "; + + if( m_parent ) + { + result += m_parent->m_name; + } + result += "\n*Manufacturer: "; + + if( m_parent ) + { + result += m_parent->m_manufacturer; + } + + result += "\n.SUBCKT "; + result += aName; + result += " GND PIN_P PIN_N\n"; + result += "\n"; + + status &= writeSpiceDriver( &result, aName + "_P", aModel, aSupply, aParasitics, aAccuracy, + aWave ); + aWave->inverted = !aWave->inverted; + status &= writeSpiceDriver( &result, aName + "_N", aModel, aSupply, aParasitics, aAccuracy, + aWave ); + aWave->inverted = !aWave->inverted; + + + result += "\n"; + result += "x1 GND PIN_P " + aName + "_P \n"; + result += "x2 GND PIN_N " + aName + "_N \n"; + result += "\n"; + + result += "\n.ENDS " + aName + "\n\n"; + + if( status ) + { + *aDest += result; + } + + return status; +} \ No newline at end of file diff --git a/pcbnew/signalIntegrity/kibis.h b/pcbnew/signalIntegrity/kibis.h new file mode 100644 index 0000000000..f94fa12770 --- /dev/null +++ b/pcbnew/signalIntegrity/kibis.h @@ -0,0 +1,376 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Fabien Corona f.coronalaposte.net + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef KIBIS_H +#define KIBIS_H + +#include "ibisParser.h" + +class KIBIS_PIN; +class KIBIS_FILE; +class KIBIS_MODEL; +class KIBIS_COMPONENT; +class KIBIS; + +class KIBIS_ANY : public IBIS_ANY +{ +public: + KIBIS_ANY( KIBIS* aTopLevel ); + KIBIS* m_topLevel; + bool m_valid; +}; + +enum KIBIS_WAVEFORM_TYPE +{ + NONE = 0, // Used for three state + RECTANGULAR, + STUCK_HIGH, + STUCK_LOW, + HIGH_Z +}; + + +class KIBIS_WAVEFORM +{ +public: + KIBIS_WAVEFORM(){}; + KIBIS_WAVEFORM_TYPE GetType() { return m_type; }; + bool inverted = false; // Used for differential drivers + +protected: + KIBIS_WAVEFORM_TYPE m_type = KIBIS_WAVEFORM_TYPE::NONE; +}; + +class KIBIS_WAVEFORM_RECTANGULAR : public KIBIS_WAVEFORM +{ +public: + KIBIS_WAVEFORM_RECTANGULAR() : KIBIS_WAVEFORM() { m_type = KIBIS_WAVEFORM_TYPE::RECTANGULAR; }; + double m_ton; + double m_toff; + int m_cycles; + double m_delay = 0; +}; + +class KIBIS_WAVEFORM_STUCK_HIGH : public KIBIS_WAVEFORM +{ +public: + KIBIS_WAVEFORM_STUCK_HIGH() : KIBIS_WAVEFORM() { m_type = KIBIS_WAVEFORM_TYPE::STUCK_HIGH; }; +}; + +class KIBIS_WAVEFORM_STUCK_LOW : public KIBIS_WAVEFORM +{ +public: + KIBIS_WAVEFORM_STUCK_LOW() : KIBIS_WAVEFORM() { m_type = KIBIS_WAVEFORM_TYPE::STUCK_LOW; }; +}; + +class KIBIS_WAVEFORM_HIGH_Z : public KIBIS_WAVEFORM +{ +public: + KIBIS_WAVEFORM_HIGH_Z() : KIBIS_WAVEFORM() { m_type = KIBIS_WAVEFORM_TYPE::HIGH_Z; }; +}; + +/** Accuracy level. + * + * Level 0 is faster, but not as accurate + * + * Level 0 : + * - Driver: Don't use waveform + * - Driver: Don't use _DUT info + * Level 1 : + * _ Driver: Use up to one waveform + * _ Driver: Don't use _DUT info + * Level 2 : + * _ Driver: Use up to two waveforms + * _ Driver: Don't use _DUT info + * + * Level 3 : ( not implemented, fallback to level 2 ) + * _ Driver: Use up to two waveforms + * _ Driver: Use _DUT info if at least one waveform +*/ +enum class KIBIS_ACCURACY +{ + LEVEL_0, + LEVEL_1, + LEVEL_2, + LEVEL_3, +}; + + +class KIBIS_FILE : KIBIS_ANY +{ +public: + KIBIS_FILE( KIBIS* aTopLevel ); + + std::string m_fileName; + double m_fileRev; + double m_ibisVersion; + std::string m_date; + std::string m_source; + std::string m_notes; + std::string m_disclaimer; + std::string m_copyright; + + bool Init( IbisParser& aParser ); +}; + + +class KIBIS : public KIBIS_ANY +{ +public: + KIBIS( std::string aFileName ); + std::vector m_components; + std::vector m_models; + KIBIS_FILE m_file; +}; + +class KIBIS_MODEL : public KIBIS_ANY +{ +public: + KIBIS_MODEL( KIBIS* aTopLevel, IbisModel& aSource, IbisParser& aParser ); + + std::string m_name; + std::string m_description; + IBIS_MODEL_TYPE m_type = IBIS_MODEL_TYPE::UNDEFINED; + /* The Polarity, Enable, Vinl, Vinh, Vmeas, Cref, Rref, and Vref subparameters are optional. */ + /* the default values of Vinl = 0.8 V and Vinh = 2.0 V are assumed. */ + double m_vinl = 0.8; + double m_vinh = 2; + double m_vref = 0; + double m_rref = 0; + double m_cref = 0; + double m_vmeas = 0; + IBIS_MODEL_ENABLE m_enable = IBIS_MODEL_ENABLE::UNDEFINED; + IBIS_MODEL_POLARITY m_polarity = IBIS_MODEL_POLARITY::UNDEFINED; + // End of optional subparameters + + TypMinMaxValue* m_C_comp; + TypMinMaxValue* m_voltageRange; + TypMinMaxValue* m_temperatureRange; + TypMinMaxValue* m_pullupReference; + TypMinMaxValue* m_pulldownReference; + TypMinMaxValue* m_GNDClampReference; + TypMinMaxValue* m_POWERClampReference; + TypMinMaxValue* m_Rgnd; + TypMinMaxValue* m_Rpower; + TypMinMaxValue* m_Rac; + TypMinMaxValue* m_Cac; + IVtable* m_GNDClamp; + IVtable* m_POWERClamp; + IVtable* m_pullup; + IVtable* m_pulldown; + std::vector m_risingWaveforms; + std::vector m_fallingWaveforms; + IbisRamp* m_ramp; + + /** @brief Return true if the model has a pulldown transistor */ + bool HasPulldown(); + /** @brief Return true if the model has a pullup transistor */ + bool HasPullup(); + /** @brief Return true if the model has a clamp diode to the gnd net */ + bool HasGNDClamp(); + /** @brief Return true if the model has a clamp diode to the power net */ + bool HasPOWERClamp(); + + /** @brief Generate the spice directive to simulate the die + * + * @param aSupply Power supply corner + * @param aParasitics Parasitics corner + * @param aIndex Index used to offset spice nodes / directives + * @return A multiline string with spice directives + */ + std::string SpiceDie( IBIS_CORNER aSupply, IBIS_CORNER aParasitics, int aIndex ); + + /** @brief Create waveform pairs + * + * For maximum accuracy, we need a waveform pair. + * This function creates the pairs based on the fixture. + * The first element is the rising edge, the second is the falling edge. + * + * @return a vector of waveform pairs + */ + std::vector> waveformPairs(); + + + /** @brief Generate a square waveform + * + * For maximum accuracy, we need a waveform pair. + * This function creates the pairs based on the fixture. + * + * @param aNode1 node where the voltage is applied + * @param aNode2 Reference node + * @param aBits The first member is the bit value ( 1 or 0 ). + * The second member is the time of the transition edge. + * @param aPair @see waveformPairs() + * @param aSupply Power supply corner + * @return A multiline string with spice directives + */ + std::string generateSquareWave( std::string aNode1, std::string aNode2, + std::vector> aBits, + std::pair aPair, + IBIS_CORNER aSupply ); + + + /** @brief Copy a waveform, and substract the first value to all samples + * + * + * @param aIn Input waveform + * @return Output waveform + */ + IbisWaveform TrimWaveform( IbisWaveform& aIn ); +}; + +class KIBIS_PIN : public KIBIS_ANY +{ +public: + KIBIS_PIN( KIBIS* aTopLevel, IbisComponentPin& aPin, IbisComponentPackage& aPackage, + IbisParser& aParser, KIBIS_COMPONENT* aParent, std::vector& aModels ); + /** @brief Name of the pin + * Examples : "VCC", "GPIOA", "CLK", etc... + */ + std::string m_signalName; + /** @brief Pin Number + * Examples : 1, 2, 3 ( or for BGA ), A1, A2, A3, etc... + */ + std::string m_pinNumber; + + /** @brief Resistance from die to pin */ + TypMinMaxValue* R_pin; + /** @brief Inductance from die to pin */ + TypMinMaxValue* L_pin; + /** @brief Capacitance from pin to GND */ + TypMinMaxValue* C_pin; + + KIBIS_COMPONENT* m_parent; + + std::vector m_t, m_Ku, m_Kd; + + std::vector m_models; + + bool writeSpiceDriver( std::string* aDest, std::string aName, KIBIS_MODEL& aModel, + IBIS_CORNER aSupply, IBIS_CORNER aParasitics, KIBIS_ACCURACY aAccuracy, + KIBIS_WAVEFORM* aWave ); + bool writeSpiceDiffDriver( std::string* aDest, std::string aName, KIBIS_MODEL& aModel, + IBIS_CORNER aSupply, IBIS_CORNER aParasitics, + KIBIS_ACCURACY aAccuracy, KIBIS_WAVEFORM* aWave ); + bool writeSpiceDevice( std::string* aDest, std::string aName, KIBIS_MODEL& aModel, + IBIS_CORNER aSupply, IBIS_CORNER aParasitics ); + + /** @brief Update m_Ku, m_Kd using no falling / rising waveform inputs ( low accuracy ) + * @param aModel Model to be used + * @param aWave Waveform to generate + * @param aSupply Power supply corner + */ + void getKuKdNoWaveform( KIBIS_MODEL& aModel, KIBIS_WAVEFORM* aWave, IBIS_CORNER aSupply ); + + /** @brief Update m_Ku, m_Kd using with a single waveform input + * @param aModel Model to be used + * @param aPair @see waveformPairs() + * @param aWave Waveform to generate + * @param aSupply Power supply corner + * @param aParasitics Parasitics corner + */ + void getKuKdOneWaveform( KIBIS_MODEL& aModel, std::pair aPair, + KIBIS_WAVEFORM* aWave, IBIS_CORNER aSupply, IBIS_CORNER aParasitics ); + + /** @brief Update m_Ku, m_Kd using with two waveform inputs + * + * The order of aPair1 and aPair2 is not important. + * @param aModel Model to be used + * @param aPair1 @see waveformPairs() + * @param aPair2 @see waveformPairs() + * @param aWave Waveform to generate + * @param aSupply Power supply corner + * @param aParasitics Parasitics corner + */ + void getKuKdTwoWaveforms( KIBIS_MODEL& aModel, std::pair aPair1, + std::pair aPair2, KIBIS_WAVEFORM* aWave, + IBIS_CORNER aSupply, IBIS_CORNER aParasitics ); + + /** @brief Update m_Ku, m_Kd using with two waveform inputs + * + * The order of aPair1 and aPair2 is not important. + * @param aModel Model to be used + * @param aPair @see waveformPairs() + * @param aWave Waveform to generate + * @param aSupply Power supply corner + * @param aParasitics Parasitics corner + * @param aIndex Index for numbering spice .SUBCKT + * + * @return A multiline string with spice directives + */ + std::string KuKdDriver( KIBIS_MODEL& aModel, std::pair aPair, + KIBIS_WAVEFORM* aWave, IBIS_CORNER aSupply, IBIS_CORNER aParasitics, + int aIndex ); + + /** @brief Generate the spice directive to simulate the die for Ku/Kd estimation + * + * DO NOT use it in order to generate a model. + * It sole purpose is to run the internal simulation to get Ku/Kd + * + * @param aModel Model to be used + * @param aSupply Power supply corner + * @param aIndex Index for numbering ports + * @return A multiline string with spice directives + */ + std::string addDie( KIBIS_MODEL& aModel, IBIS_CORNER aSupply, int aIndex ); + + + /** @brief Update m_Ku, m_Kd using with two waveform inputs + * + * Runs a simulation. The simulation creates a specific file with Ku/Kd values + * This function then reads the output file and updates m_Ku / m_Kd. + * This function probably needs a rewrite. + * + * @param aSimul The simulation to run, multiline spice directives + */ + void getKuKdFromFile( std::string* aSimul ); +}; + +class KIBIS_COMPONENT : public KIBIS_ANY +{ +public: + KIBIS_COMPONENT( KIBIS* aToplevel, IbisComponent& aSource, IbisParser& aParser ); + /** @brief Name of the component */ + std::string m_name; + /** @brief Name of the manufacturer */ + std::string m_manufacturer; + + std::vector m_pins; + + /** @brief Get a pin by its number ( 1, 2, A1, A2, ... ) + * + * @param aPinNumber pin number + * @return pointer to a KIBIS_PIN, or nullptr if there is no matching pin + */ + KIBIS_PIN* getPin( std::string aPinNumber ); +}; + +#endif \ No newline at end of file diff --git a/qa/CMakeLists.txt b/qa/CMakeLists.txt index 2391673bba..86ceadeb43 100644 --- a/qa/CMakeLists.txt +++ b/qa/CMakeLists.txt @@ -47,3 +47,6 @@ add_subdirectory( unittests ) # Utility/debugging/profiling programs add_subdirectory( tools ) +if( KICAD_SIGNAL_INTEGRITY ) + add_subdirectory( signalIntegrity ) +endif() diff --git a/qa/signalIntegrity/CMakeLists.txt b/qa/signalIntegrity/CMakeLists.txt new file mode 100644 index 0000000000..0ede83834c --- /dev/null +++ b/qa/signalIntegrity/CMakeLists.txt @@ -0,0 +1,84 @@ +# +# This program source code file is part of KiCad, a free EDA CAD application. +# +# Copyright (C) 2017 CERN +# @author Alejandro García Montoro +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +find_package(Boost COMPONENTS unit_test_framework REQUIRED) +find_package( wxWidgets 3.0.0 COMPONENTS gl aui adv html core net base xml stc REQUIRED ) + + +add_definitions(-DBOOST_TEST_DYN_LINK -DPCBNEW -DDRC_PROTO -DTEST_APP_NO_MAIN) + +add_executable( ibis_proto + qaIbisParser.cpp + ../../pcbnew/signalIntegrity/ibisParser.cpp + ../../pcbnew/signalIntegrity/kibis.cpp +) + +add_dependencies( ibis_proto pnsrouter pcbcommon ${PCBNEW_IO_LIBRARIES} ) + +include_directories( BEFORE ${INC_BEFORE} ) +include_directories( + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/3d-viewer + ${CMAKE_SOURCE_DIR}/common + ${CMAKE_SOURCE_DIR}/pcbnew + ${CMAKE_SOURCE_DIR}/pcbnew/router + ${CMAKE_SOURCE_DIR}/pcbnew/tools + ${CMAKE_SOURCE_DIR}/pcbnew/dialogs + ${CMAKE_SOURCE_DIR}/polygon + ${CMAKE_SOURCE_DIR}/common/geometry + ${CMAKE_SOURCE_DIR}/libs/kimath/include/math + ${CMAKE_SOURCE_DIR}/qa/common + ${CMAKE_SOURCE_DIR}/qa + ${CMAKE_SOURCE_DIR}/qa/qa_utils + ${CMAKE_SOURCE_DIR}/qa/qa_utils/include + ${CMAKE_SOURCE_DIR}/qa/pcbnew_utils/include + ${Boost_INCLUDE_DIR} + ${INC_AFTER} +) + +target_link_libraries( ibis_proto + qa_pcbnew_utils + 3d-viewer + connectivity + pcbcommon + pnsrouter + gal + common + gal + qa_utils + dxflib_qcad + tinyspline_lib + nanosvg + idf3 + ${PCBNEW_IO_LIBRARIES} + ${wxWidgets_LIBRARIES} + ${GDI_PLUS_LIBRARIES} + ${PYTHON_LIBRARIES} + ${Boost_LIBRARIES} + ${PCBNEW_EXTRA_LIBS} # -lrt must follow Boost +) + +configure_file( "ibis_v1_1.ibs" "ibis_v1_1.ibs" COPYONLY ) +configure_file( "ibis_v2_1.ibs" "ibis_v2_1.ibs" COPYONLY ) +configure_file( "ibis_v2_1.pkg" "ibis_v2_1.pkg" COPYONLY ) \ No newline at end of file diff --git a/qa/signalIntegrity/ibis_v1_1.ibs b/qa/signalIntegrity/ibis_v1_1.ibs new file mode 100644 index 0000000000..ea0fc7b014 --- /dev/null +++ b/qa/signalIntegrity/ibis_v1_1.ibs @@ -0,0 +1,89 @@ +[IBIS Ver] 1.1 |Let's test a comment +[Comment char] #_char +[File name] ibis_v1_1.ibs +[File Rev] 1.0 #Let's test a comment +[Date] 26/08/2021 +[Source] This is the + source for the files +[Notes] We can have some + Notes +[Disclaimer] +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +[Component] Virtual +[Manufacturer] KiCad +[Package] +R_pkg 1m 0.8m NA +L_pkg 1m NA NA +C_pkg 1m NA NA + +[Pin] signal_name model_name R_pin L_pin C_pin + 1 VCC POWER 1m 0.8m NA + 2 GND GND 1m NA 2m + 3 X Input 1m NA NA + 4 Y Ouput 1m 0.8m 2m + +[Model] Input +Model_type Input +Polarity Non-Inverting +Enable Active-High +Vinl = 0.8V +Vinh = 2.0V +C_comp 10.0pF 8.0pF 15.0pF + +[Voltage range] 5.0V 4.5V 5.5V +[Pulldown] +# Voltage I(typ) I(min) I(max) +# + -5.0V -50.0m -40.0m -60.0m + 0.0V 0 0 0 + 5.0V 50.0m 40.0m 60.0m +[Pullup] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V 50.0m 40.0m 60.0m + 0.0V 0 0 0 + 5.0V -50.0m -40.0m -60.0m +[GND_clamp] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V -50.0m NA NA + 0.0V 0 NA NA + 5.0V 0 NA NA +[POWER_clamp] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V 50.0m NA NA + 0.0V 0 NA NA + 5.0V 0 NA NA + +[Ramp] +# variable typ min max +dV/dt_r 3.0/2n 2.8/3n NA +dV/dt_f 3.0/2n 2.8/3n 3.2/1n + +[END] \ No newline at end of file diff --git a/qa/signalIntegrity/ibis_v2_1.ibs b/qa/signalIntegrity/ibis_v2_1.ibs new file mode 100644 index 0000000000..af47d067f4 --- /dev/null +++ b/qa/signalIntegrity/ibis_v2_1.ibs @@ -0,0 +1,147 @@ +[IBIS Ver] 2.1 |Let's test a comment +[Comment char] #_char +[File name] ibis_v2_1.pkg +[File Rev] 1.0 #Let's test a comment +[Date] 26/08/2021 +[Source] This is the + source for the files +[Notes] We can have some + Notes +[Copyright] +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +[Disclaimer] This is NOT a valid component. +[Component] Virtual +[Manufacturer] KiCad +[Package] +R_pkg 1m 0.8m 2m +L_pkg 1m 0.8m 2m +C_pkg 1m 0.8m 2m + +[Pin] signal_name model_name R_pin L_pin C_pin + 1 VCC POWER 1m 0.8m 2m + 2 GND GND 1m 0.8m 2m + 3 X Input 1m 0.8m 2m + 4 Y Ouput 1m 0.8m 2m + 5 YN Ouput 1m 0.8m 2m + 6 Y Ouput 1m 0.8m 2m + 7 YN Ouput 1m 0.8m 2m + +[Package Model] QS-SMT-cer-8-pin-pkgs +[Pin Mapping] pulldown_ref pullup_ref gnd_clamp_ref power_clamp_ref +1 GNDBUS NC +2 NC PWRBUS +3 GNDBUS PWRBUS +4 GNDBUS PWRBUS GNDBUS PWRBUS +5 GNDBUS PWRBUS GNDBUS PWRBUS +6 GNDBUS PWRBUS GNDBUS PWRBUS +7 GNDBUS PWRBUS GNDBUS PWRBUS + +[Diff Pin] inv_pin vdiff tdelay_typ tdelay_min tdelay_max +# + 4 5 150mV -1ns 0ns -2ns + 6 7 150mV -1ns 0ns -2ns + + +[Model] Input +Model_type Input +Polarity Non-Inverting +Enable Active-High +Vinl = 0.8V +Vinh = 2.0V +Vmeas= 1.5V +Cref =50pF +Rref=500 +Vref = 0 + +C_comp 10.0pF 8.0pF 15.0pF + +[Voltage range] 5.0V 4.5V 5.5V +[Temperature Range] 27.0 -50 130.0 +[Pullup Reference] 5.0V 4.5V 5.5V +[Pulldown Reference] 0V 0V 0V +[POWER Clamp Reference] 5.0V 4.5V 5.5V +[GND Clamp Reference] 0V 0V 0V + +[Pulldown] +# Voltage I(typ) I(min) I(max) +# + -5.0V -50.0m -40.0m -60.0m + 0.0V 0 0 0 + 5.0V 50.0m 40.0m 60.0m +[Pullup] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V 50.0m 40.0m 60.0m + 0.0V 0 0 0 + 5.0V -50.0m -40.0m -60.0m +[GND_clamp] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V -50.0m NA NA + 0.0V 0 NA NA + 5.0V 0 NA NA +[POWER_clamp] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V 50.0m NA NA + 0.0V 0 NA NA + 5.0V 0 NA NA + +[Rgnd] 1500hm 100Ohm 3600 +[Rpower] 150Ohm 100Ohm NA +[Rac] 30Ohm NA NA +[Cac] 50pF NA NA + + +[Ramp] +# variable typ min max +dV/dt_r 3.0/2n 2.8/3n 3.2/1n +dV/dt_f 3.0/2n 2.8/3n 3.2/1n + +[Rising Waveform] +R_fixture = 500 +V_fixture = 5.0 +C_fixture = 10p +L_fixture = 2n +C_dut = 7p +R_dut = 1m +L_dut = 1n +#Time V(typ) V(min) V(max) + 0.0ns 0 0 0 + 1.0ns 1 0.5 1.5 + 2.0ns 2 1 3 +# +[Falling Waveform] +R_fixture = 50 +V_fixture = 0 +#Time V(typ) V(min) V(max) + 0.0ns 2 1 NA + 1.0ns 1 0.5 NA + 2.0ns 0 0 NA + + +[END] \ No newline at end of file diff --git a/qa/signalIntegrity/ibis_v2_1.pkg b/qa/signalIntegrity/ibis_v2_1.pkg new file mode 100644 index 0000000000..78fdf0fb6e --- /dev/null +++ b/qa/signalIntegrity/ibis_v2_1.pkg @@ -0,0 +1,110 @@ +[IBIS Ver] 2.1 |Let's test a comment +[Comment char] |_char +[File name] ibis_v2_1.pkg +[File Rev] 1.0 #Let's test a comment +[Date] 26/08/2021 +[Source] This is the + source for the files +[Notes] We can have some + Notes +[Copyright] +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +[Define Package Model] QFN4 +[Manufacturer] Kicad +[OEM] Kicad Packaging Co. +[Description] A 4 pin QFN, really ? +[Number of Pins] 4 +[Pin Numbers] +A1 +A2 +A3 +A4 + +[Model Data] +| +| The resistance matrix for this package has no coupling +| +[Resistance Matrix] Banded_matrix +[Bandwidth] 3 +[Row] 1 +1 2 1 +[Row] 2 +2 3 2 +[Row] 3 +4 5 4 +[Row] 4 +5 6 5 +| +| The inductance matrix has loads of coupling +| +[Inductance Matrix] Full_matrix +[Row] 1 +3.04859e-07 4.73185e-08 1.3428e-08 +1.74022e-07 +[Row] 2 +3.04859e-07 4.73185e-08 1.3428e-08 +[Row] 3 +3.04859e-07 4.73185e-08 +[Row] 4 +3.04859e-07 +| +| The capacitance matrix has sparse coupling +| +[Capacitance Matrix] Sparse_matrix +[Row] 1 +1 2.48227e-10 +2 -1.56651e-11 +5 -9.54158e-11 +6 -7.15684e-12 +[Row] 2 +2 2.51798e-10 +3 -1.56552e-11 +5 -6.85199e-12 +6 -9.0486e-11 +7 -6.82003e-12 +[Row] 3 +3 2.51798e-10 +4 -1.56651e-11 +6 -6.82003e-12 +7 -9.0486e-11 +8 -6.85199e-12 +[Row] 4 +4 2.48227e-10 +7 -7.15684e-12 +8 -9.54158e-11 +[Row] 5 +5 1.73542e-10 +6 -3.38247e-11 +[Row] 6 +6 1.86833e-10 +7 -3.27226e-11 +[Row] 7 +7 1.86833e-10 +8 -3.38247e-11 +[Row] 8 +8 1.73542e-10 +| +[End Model Data] +[End Package Model] +[END] \ No newline at end of file diff --git a/qa/signalIntegrity/ibis_v3_2.ibs b/qa/signalIntegrity/ibis_v3_2.ibs new file mode 100644 index 0000000000..8e86af1d7d --- /dev/null +++ b/qa/signalIntegrity/ibis_v3_2.ibs @@ -0,0 +1,419 @@ +[IBIS Ver] 3.2 |Let's test a comment +[Comment char] #_char +[File name] ibis_v2_1.pkg +[File Rev] 1.0 #Let's test a comment +[Date] 26/08/2021 +[Source] This is the + source for the files +[Notes] We can have some + Notes +[Copyright] +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +[Disclaimer] This is NOT a valid component. +[Component] Virtual +[Manufacturer] KiCad +[Package] +R_pkg 1m 0.8m 2m +L_pkg 1m 0.8m 2m +C_pkg 1m 0.8m 2m + +[Pin] signal_name model_name R_pin L_pin C_pin + 1 VCC POWER 1m 0.8m 2m + 2 GND GND 1m 0.8m 2m + 3 X Input 1m 0.8m 2m + 4 Y Ouput 1m 0.8m 2m + 5 YN Ouput 1m 0.8m 2m + 6 Y Ouput 1m 0.8m 2m + 7 YN Ouput 1m 0.8m 2m + +[Package Model] QS-SMT-cer-8-pin-pkgs +[Pin Mapping] pulldown_ref pullup_ref gnd_clamp_ref power_clamp_ref +1 GNDBUS NC +2 NC PWRBUS +3 GNDBUS PWRBUS +4 GNDBUS PWRBUS GNDBUS PWRBUS +5 GNDBUS PWRBUS GNDBUS PWRBUS +6 GNDBUS PWRBUS GNDBUS PWRBUS +7 GNDBUS PWRBUS GNDBUS PWRBUS + +[Diff Pin] inv_pin vdiff tdelay_typ tdelay_min tdelay_max +# + 4 5 150mV -1ns 0ns -2ns + 6 7 150mV -1ns 0ns -2ns + +[Series Pin Mapping] pin_2 model_name function_table_group +| + 4 5 PinSeries 1 + 6 7 PinSeries 2 + +[Series Switch Groups] +On 4 5 / +Off 4 5 / +On 4 5 6 7 / +Off 4 5 6 7 / + + +[Model] Input +Model_type Input +Polarity Non-Inverting +Enable Active-High +Vinl = 0.8V +Vinh = 2.0V +Vmeas= 1.5V +Cref =50pF +Rref=500 +Vref = 0 + +C_comp 10.0pF 8.0pF 15.0pF + +[Model Spec] +| Subparameter typ min max +Vinh 3.5 3.15 3.85 +Vinl 1.5 1.35 1.65 +Vinh+ 2.0 NA NA | Overrides the +Vinh- 1.6 NA NA | thresholds +Vinl+ 1.1 NA NA +Vinl- 0.6 NA NA | All 4 are required +S_overshoot_high 5.5 5.0 6.0 | Static overshoot +S_overshoot_low -0.5 NA NA +D_overshoot_high 6.0 5.5 6.5 | Dynamic overshoot +D_overshoot_low -1.0 -1.0 -1.0 +D_overshoot_time 20n 20n 20n | & static overshoot +Pulse_high 3V NA NA | Pulse immunity +Pulse_low 0 NA NA | requires +Pulse_time 3n NA NA | Pulse_time +Vmeas 3.68 3.18 4.68 | A 5 volt PECL + +[Add Submodel] +| Submodel_name Mode +Bus_Hold_1 Non-Driving +Dynamic_clamp_1 All + +[Driver Schedule] +| Model_name Rise_on_dly Rise_off_dly Fall_on_dly Fall_off_dly + MODEL_OUT 0.0ns NA 0.0ns NA + M_O_SOURCE1 0.5ns NA 0.5ns NA +| low (high-Z) to high high to low (high-Z) + M_O_SOURCE2 0.5n 1.5n NA NA +| low to high to low low (high-Z) + M_O_DRAIN1 1.0n NA 1.5n NA +| low to high (high-Z) high (high-Z) to low + M_O_DRAIN2 NA NA 1.5n 2.0n +| high (high-Z) high to low to high + +[Voltage range] 5.0V 4.5V 5.5V +[Temperature Range] 27.0 -50 130.0 +[Pullup Reference] 5.0V 4.5V 5.5V +[Pulldown Reference] 0V 0V 0V +[POWER Clamp Reference] 5.0V 4.5V 5.5V +[GND Clamp Reference] 0V 0V 0V + + +[TTgnd] 10n 12n 9n +[TTpower] 12n NA NA + +[Pulldown] +# Voltage I(typ) I(min) I(max) +# + -5.0V -50.0m -40.0m -60.0m + 0.0V 0 0 0 + 5.0V 50.0m 40.0m 60.0m +[Pullup] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V 50.0m 40.0m 60.0m + 0.0V 0 0 0 + 5.0V -50.0m -40.0m -60.0m +[GND_clamp] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V -50.0m NA NA + 0.0V 0 NA NA + 5.0V 0 NA NA +[POWER_clamp] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V 50.0m NA NA + 0.0V 0 NA NA + 5.0V 0 NA NA + +[Rgnd] 1500hm 100Ohm 3600 +[Rpower] 150Ohm 100Ohm NA +[Rac] 30Ohm NA NA +[Cac] 50pF NA NA + +[On] +| variable R(typ) R(min) R(max) +[R Series] 8ohm 6ohm 12ohm +| +| variable L(typ) L(min) L(max) +[L Series] 5nH NA NA +| variable R(typ) R(min) R(max) +[Rl Series] 4ohm NA NA +| +| variable C(typ) C(min) C(max) | The other elements +[C Series] 50pF NA NA | are 0 impedance +[Off] +| variable R(typ) R(min) R(max) +[R Series] 8ohm 6ohm 12ohm +| +| variable L(typ) L(min) L(max) +[L Series] 5nH NA NA +| variable R(typ) R(min) R(max) +[Rl Series] 4ohm NA NA +| +| variable C(typ) C(min) C(max) | The other elements +[C Series] 50pF NA NA | are 0 impedance + +[Series Current] +| Voltage I(typ) I(min) I(max) + -5.0V -3900.0m -3800.0m -4000.0m + -0.7V -80.0m -75.0m -85.0m + -0.6V -22.0m -20.0m -25.0m + -0.5V -2.4m -2.0m -2.9m + -0.4V 0.0m 0.0m 0.0m + 5.0V 0.0m 0.0m 0.0m + +[Series MOSFET] +Vds = 1.0 +| Voltage I(typ) I(min) I(max) + 5.0V 257.9m 153.3m 399.5m | Defines the Ids current as a + 4.0V 203.0m 119.4m 317.3m | function of Vtable, for Vds = 1.0 + 3.0V 129.8m 74.7m 205.6m + 2.0V 31.2m 16.6m 51.0m + 1.0V 52.7p 46.7p 56.7p + 0.0V 0.0p 0.0p 0.0p + + +[Off] +| variable R(typ) R(min) R(max) +[R Series] 8ohm 6ohm 12ohm +| +| variable L(typ) L(min) L(max) +[L Series] 5nH NA NA +| variable R(typ) R(min) R(max) +[Rl Series] 4ohm NA NA +| +| variable C(typ) C(min) C(max) | The other elements +[C Series] 50pF NA NA | are 0 impedance +[Off] +| variable R(typ) R(min) R(max) +[R Series] 8ohm 6ohm 12ohm +| +| variable L(typ) L(min) L(max) +[L Series] 5nH NA NA +| variable R(typ) R(min) R(max) +[Rl Series] 4ohm NA NA +| +| variable C(typ) C(min) C(max) | The other elements +[C Series] 50pF NA NA | are 0 impedance + +[Series Current] +| Voltage I(typ) I(min) I(max) + -5.0V -3900.0m -3800.0m -4000.0m + -0.7V -80.0m -75.0m -85.0m + -0.6V -22.0m -20.0m -25.0m + -0.5V -2.4m -2.0m -2.9m + -0.4V 0.0m 0.0m 0.0m + 5.0V 0.0m 0.0m 0.0m + +[Ramp] +# variable typ min max +dV/dt_r 3.0/2n 2.8/3n 3.2/1n +dV/dt_f 3.0/2n 2.8/3n 3.2/1n + +[Rising Waveform] +R_fixture = 500 +V_fixture = 5.0 +C_fixture = 10p +L_fixture = 2n +C_dut = 7p +R_dut = 1m +L_dut = 1n +#Time V(typ) V(min) V(max) + 0.0ns 0 0 0 + 1.0ns 1 0.5 1.5 + 2.0ns 2 1 3 +# +[Falling Waveform] +R_fixture = 50 +V_fixture = 0 +#Time V(typ) V(min) V(max) + 0.0ns 2 1 NA + 1.0ns 1 0.5 NA + 2.0ns 0 0 NA + +[Submodel] DMySubmodel + +Submodel_type Dynamic_clamp + +[Pulldown] +# Voltage I(typ) I(min) I(max) +# + -5.0V -50.0m -40.0m -60.0m + 0.0V 0 0 0 + 5.0V 50.0m 40.0m 60.0m +[Pullup] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V 50.0m 40.0m 60.0m + 0.0V 0 0 0 + 5.0V -50.0m -40.0m -60.0m +[GND_clamp] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V -50.0m NA NA + 0.0V 0 NA NA + 5.0V 0 NA NA +[POWER_clamp] +# +# Voltage I(typ) I(min) I(max) +# + -5.0V 50.0m NA NA + 0.0V 0 NA NA + 5.0V 0 NA NA + +[Ramp] +# variable typ min max +dV/dt_r 3.0/2n 2.8/3n 3.2/1n +dV/dt_f 3.0/2n 2.8/3n 3.2/1n + +[Rising Waveform] +R_fixture = 500 +V_fixture = 5.0 +C_fixture = 10p +L_fixture = 2n +C_dut = 7p +R_dut = 1m +L_dut = 1n +#Time V(typ) V(min) V(max) + 0.0ns 0 0 0 + 1.0ns 1 0.5 1.5 + 2.0ns 2 1 3 +# +[Falling Waveform] +R_fixture = 50 +V_fixture = 0 +#Time V(typ) V(min) V(max) + 0.0ns 2 1 NA + 1.0ns 1 0.5 NA + 2.0ns 0 0 NA + + +[Submodel Spec] +| Subparameter typ min max +V_trigger_r 3.1 2.4 3.7 | Low to high transition + | triggers the turn on + | process of the pullup +V_trigger_f -10.0 -10.0 -10.0 | Not used, so trigger + | voltages are set out + | of range +Off_delay 5n 6n 4n | Time from rising edge + | trigger at which the + | pullup turned off + +[GND Pulse Table] | GND Clamp offset table +| Time V(typ) V(min) V(max) +| + 0 0 0 0 + 1e-9 0 0 0 + 2e-9 0.9 0.8 1.0 + 10e-9 0.9 0.8 1.0 + 11e-9 0 0 0 + +[GND Clamp] | Table to be offset +| +| Voltage I(typ) I(min) I(max) +| + -5.000 -3.300e+01 -3.000e+01 -3.500e+01 + -4.000 -2.300e+01 -2.200e+01 -2.400e+01 + -3.000 -1.300e+01 -1.200e+01 -1.400e+01 + -2.000 -3.000e+00 -2.300e+00 -3.700e+00 + -1.900 -2.100e+00 -1.500e+00 -2.800e+00 + -1.800 -1.300e+00 -8.600e-01 -1.900e+00 + -1.700 -6.800e-01 -4.000e-01 -1.100e+00 + -1.600 -2.800e-01 -1.800e-01 -5.100e-01 + -1.500 -1.200e-01 -9.800e-02 -1.800e-01 + -1.400 -7.500e-02 -7.100e-02 -8.300e-02 + -1.300 -5.750e-02 -5.700e-02 -5.900e-02 + -1.200 -4.600e-02 -4.650e-02 -4.550e-02 + -1.100 -3.550e-02 -3.700e-02 -3.450e-02 + -1.000 -2.650e-02 -2.850e-02 -2.500e-02 + -0.900 -1.850e-02 -2.100e-02 -1.650e-02 + -0.800 -1.200e-02 -1.400e-02 -9.750e-03 + -0.700 -6.700e-03 -8.800e-03 -4.700e-03 + -0.600 -3.000e-03 -4.650e-03 -1.600e-03 + -0.500 -9.450e-04 -1.950e-03 -3.650e-04 + -0.400 -5.700e-05 -2.700e-04 -5.550e-06 + -0.300 -1.200e-06 -1.200e-05 -5.500e-08 + -0.200 -3.000e-08 -5.000e-07 0.000e+00 + -0.100 0.000e+00 0.000e+00 0.000e+00 + 0.000 0.000e+00 0.000e+00 0.000e+00 + 5.000 0.000e+00 0.000e+00 0.000e+00 +| +[POWER Pulse Table] | POWER Clamp offset table | +| Time V(typ) V(min) V(max) +| + 0 0 0 0 + 1e-9 0 0 0 + 2e-9 -0.9 -1.0 -0.8 + 10e-9 -0.9 -1.0 -0.8 + 11e-9 0 0 0 +| +[POWER Clamp] | Table to be offset +| +| Voltage I(typ) I(min) I(max) +| + -5.000 1.150e+01 1.100e+01 1.150e+01 + -4.000 7.800e+00 7.500e+00 8.150e+00 + -3.000 4.350e+00 4.100e+00 4.700e+00 + -2.000 1.100e+00 8.750e-01 1.300e+00 + -1.900 8.000e-01 6.050e-01 1.000e+00 + -1.800 5.300e-01 3.700e-01 7.250e-01 + -1.700 2.900e-01 1.800e-01 4.500e-01 + -1.600 1.200e-01 6.850e-02 2.200e-01 + -1.500 3.650e-02 2.400e-02 6.900e-02 + -1.400 1.200e-02 1.100e-02 1.600e-02 + -1.300 6.300e-03 6.650e-03 6.100e-03 + -1.200 4.200e-03 4.750e-03 3.650e-03 + -1.100 2.900e-03 3.500e-03 2.350e-03 + -1.000 1.900e-03 2.450e-03 1.400e-03 + -0.900 1.150e-03 1.600e-03 7.100e-04 + -0.800 5.500e-04 9.150e-04 2.600e-04 + -0.700 1.200e-04 4.400e-04 5.600e-05 + -0.600 5.400e-05 1.550e-04 1.200e-05 + -0.500 1.350e-05 5.400e-05 1.300e-06 + -0.400 8.650e-07 7.450e-06 4.950e-08 + -0.300 6.250e-08 7.550e-07 0.000e+00 + -0.200 0.000e+00 8.400e-08 0.000e+00 + -0.100 0.000e+00 0.000e-08 0.000e+00 + 0.000 0.000e+00 0.000e+00 0.000e+00 + + +[END] \ No newline at end of file diff --git a/qa/signalIntegrity/ibis_v3_2.pkg b/qa/signalIntegrity/ibis_v3_2.pkg new file mode 100644 index 0000000000..486e56f0af --- /dev/null +++ b/qa/signalIntegrity/ibis_v3_2.pkg @@ -0,0 +1,46 @@ +[IBIS Ver] 3.2 |Let's test a comment +[Comment char] |_char +[File name] ibis_v2_1.pkg +[File Rev] 1.0 #Let's test a comment +[Date] 26/08/2021 +[Source] This is the + source for the files +[Notes] We can have some + Notes +[Copyright] +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +[Define Package Model] QFN4 +[Manufacturer] Kicad +[OEM] Kicad Packaging Co. +[Description] A 4 pin QFN, really ? +[Number Of Sections] 2 +[Number of Pins] 4 +[Pin Numbers] +A1 +A2 +A3 +A4 + +[End Package Model] +[END] \ No newline at end of file diff --git a/qa/signalIntegrity/qaIbisParser.cpp b/qa/signalIntegrity/qaIbisParser.cpp new file mode 100644 index 0000000000..794631e564 --- /dev/null +++ b/qa/signalIntegrity/qaIbisParser.cpp @@ -0,0 +1,89 @@ +#include "../../pcbnew/signalIntegrity/kibis.h" +#include + +int main( void ) +{ + //KIBIS* k1 = new KIBIS( "ibis_v1_1.ibs" ); + //KIBIS* k2 = new KIBIS( "ibis_v2_1.ibs" ); + //KIBIS* k4 = new KIBIS( "sn74lvc541a.ibs" ); + KIBIS* k4 = new KIBIS( "ibis_lan8670_1_2_1v3.ibs" ); + + KIBIS_COMPONENT& comp = k4->m_components.at( 0 ); + + std::cout << "Component: " << comp.m_name << std::endl; + + for( KIBIS_PIN& pin : comp.m_pins ) + { + std::cout << "--Pin: " << pin.m_pinNumber << " " << pin.m_signalName << std::endl; + } + + + //KIBIS_PIN* pin1 = comp.getPin( "11" ); + //KIBIS_PIN* pin2 = comp.getPin( "2" ); + KIBIS_PIN* pin1 = comp.getPin( "30" ); + KIBIS_PIN* pin2 = comp.getPin( "31" ); + + + std::cout << "pin1 name: " << pin1->m_signalName << std::endl; + std::cout << "pin2 name: " << pin2->m_signalName << std::endl; + std::cout << "pin1 model length: " << pin1->m_models.size()<< std::endl; + std::cout << "pin2 model length: " << pin2->m_models.size()<< std::endl; + std::string* tmp1 = new std::string(); + std::string* tmp2 = new std::string(); + std::string* tmp3 = new std::string(); + std::string* tmp4 = new std::string(); + + KIBIS_WAVEFORM_RECTANGULAR* wave = new KIBIS_WAVEFORM_RECTANGULAR(); + wave->m_ton = 80e-9; + wave->m_toff = 80e-9; + wave->m_cycles = 10; + wave->m_delay = 0; + //KIBIS_WAVEFORM_STUCK_HIGH* wave = new KIBIS_WAVEFORM_STUCK_HIGH(); + + std::cout << "WAVEFORM TYPE IN QA: " << wave->GetType() << std::endl; + std::cout << pin2->m_models.at(0)->m_name << std::endl; + pin2->writeSpiceDevice( tmp4, "device_typ", *( pin2->m_models.at( 0 ) ), IBIS_CORNER::TYP, + IBIS_CORNER::TYP ); + + KIBIS_MODEL* model1 = pin1->m_models.at( 0 ); + std::cout << "Model used for driver: " << model1->m_name << std::endl; + pin1->writeSpiceDiffDriver( tmp1, "driver_typ", *( model1 ), IBIS_CORNER::TYP, IBIS_CORNER::TYP, + KIBIS_ACCURACY::LEVEL_2, wave ); + + wxTextFile file( "output.sp" ); + if( file.Exists() ) + { + file.Clear(); + } + else + { + file.Create(); + } + file.AddLine( *tmp1 ); + file.AddLine( *tmp2 ); + file.AddLine( *tmp3 ); + file.AddLine( *tmp4 ); + + wxString simul = ""; + simul += "x1 0 OUT_1 OUT_2 driver_typ \n"; + simul += "R1 OUT_1 COM 25\n"; + simul += "R2 OUT_2 COM 25\n"; + simul += "V1 COM 0 1.25\n"; + simul += ".tran 0.1n 1000n \n"; + simul += ".control run \n"; + simul += "run \n"; + simul += "plot v(OUT_1) v(OUT_2)\n"; + //simul += "plot v(x1.KU) v(x1.KD) v(1) v(x1.DIEBUFF) \n"; + simul += ".endc \n"; + simul += ".end \n"; + + + file.AddLine( simul ); + + file.Write(); + + + std::cout << "Done" << std::endl; + + return 1; +} \ No newline at end of file