/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2022 Fabien Corona f.coronalaposte.net * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. * * 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 "ibis_parser.h" #include #include //for memcmp #include #include // KiCad header // _() is used here to mark translatable strings in IBIS_REPORTER::Report() // However, currently non ASCII7 chars are nor correctly handled when printing messages // So we disable translations #if 0 #include // for _() macro and wxGetTranslation() #else #undef _ #define _( x ) x #endif bool IbisParser::compareIbisWord( const std::string& stra, const std::string& strb ) { return std::equal( stra.begin(), stra.end(), strb.begin(), strb.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( _( "Bandwidth of banded matrices should be >= 1." ), RPT_SEVERITY_ERROR ); status = false; } for( int i = 0; i < m_bandwidth * m_dim; i++ ) { if( 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( 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( isnan( m_data[i] ) ) { Report( _( "There are NaN elements in a matrix." ), RPT_SEVERITY_ERROR ); status = false; } } return status; } bool isNumberNA( double aNumber ) { double NA = nan( NAN_NA ); return std::memcmp( &aNumber, &NA, sizeof NA ) == 0; } bool TypMinMaxValue::Check() { bool status = true; if( isnan( value[IBIS_CORNER::TYP] ) ) status = false; if( isnan( value[IBIS_CORNER::MIN] ) && !isNumberNA( value[IBIS_CORNER::MIN] ) ) status = false; if( 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 ) { std::stringstream message; message << _( "Checking pin " ) << m_pinName; if( m_pinName.empty() ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Pin name cannot be empty." ), RPT_SEVERITY_ERROR ); status = false; } if( m_signalName.empty() ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Signal name cannot be empty." ), RPT_SEVERITY_ERROR ); status = false; } if( m_modelName.empty() ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Model name cannot be empty." ), RPT_SEVERITY_ERROR ); status = false; } if( isnan( m_Rpin ) && !isNumberNA( m_Rpin ) ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Rpin is not valid." ), RPT_SEVERITY_ERROR ); status = false; } if( isnan( m_Lpin )&& !isNumberNA( m_Lpin ) ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Lpin is not valid." ), RPT_SEVERITY_ERROR ); status = false; } if( isnan( m_Cpin )&& !isNumberNA( m_Cpin ) ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Cpin is not valid." ), RPT_SEVERITY_ERROR ); status = false; } } return status; } bool IbisComponent::Check() { bool status = true; std::stringstream message; message << _( "Checking component " ) << m_name; if( m_name.empty() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Component name cannot be empty." ), RPT_SEVERITY_ERROR ); status = false; } if( m_manufacturer.empty() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Component: manufacturer cannot be empty." ), RPT_SEVERITY_ERROR ); status = false; } if( !m_package.Check() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Component: Invalid Package." ), RPT_SEVERITY_ERROR ); status = false; } if( m_pins.size() < 1 ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); 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 += "\n+"; } result += "]\n+ y_array=["; for( IVtableEntry& entry : m_entries ) { result += doubleToString( entry.I.value[aCorner] ); result += "\n+"; } result += "]\n+ input_domain=0.05 fraction=TRUE)\n\n"; } return result; } double IVtable::InterpolatedI( double aV, IBIS_CORNER aCorner ) { // @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( size_t 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 nan( "" ); } else { // exiting the function here would mean the IV table is reversed. return nan( "" ); } // @TODO prefer another method such as a dichotomy } bool IVtable::Check() { bool status = true; for( IVtableEntry& entry : m_entries ) { if( 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( isnan( value[IBIS_CORNER::TYP].m_dv ) ) status = false; if( isnan( value[IBIS_CORNER::TYP].m_dt ) ) status = false; if( isnan( value[IBIS_CORNER::MIN].m_dv ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dv ) ) status = false; if( isnan( value[IBIS_CORNER::MIN].m_dt ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dt ) ) status = false; if( isnan( value[IBIS_CORNER::MIN].m_dv ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dv ) ) status = false; if( 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( 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; } std::stringstream message; message << _( "Checking model " ) << m_name; if(m_type == IBIS_MODEL_TYPE::UNDEFINED) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Undefined model type" ), RPT_SEVERITY_ERROR ); status = false; } if( isnan( m_vinh ) && !isNumberNA( m_vinh ) ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Invalid Vinh value." ), RPT_SEVERITY_ERROR ); status = false; } if( isnan( m_vinl ) && !isNumberNA( m_vinl ) ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Invalid Vinl value." ), RPT_SEVERITY_ERROR ); status = false; } if( isnan( m_rref ) && !isNumberNA( m_rref ) ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Invalid R_ref value." ), RPT_SEVERITY_ERROR ); status = false; } if( isnan( m_cref ) && !isNumberNA( m_cref ) ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Invalid C_ref value." ), RPT_SEVERITY_ERROR ); status = false; } if( isnan( m_vref ) && !isNumberNA( m_vref ) ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "invalid V_ref value." ), RPT_SEVERITY_ERROR ); status = false; } if( isnan( m_vmeas ) && !isNumberNA( m_vmeas ) ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Invalid V_meas value." ), RPT_SEVERITY_ERROR ); status = false; } if( !m_C_comp.Check() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "C_comp is invalid." ), RPT_SEVERITY_ERROR ); status = false; } if( !m_temperatureRange.Check() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); 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_STD && 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; std::stringstream message; message << _( "Checking Header... " ); if( m_ibisVersion == -1 ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Missing [IBIS Ver]" ), RPT_SEVERITY_ERROR ); status = false; } if( m_ibisVersion > IBIS_MAX_VERSION ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "The parser does not handle this IBIS version" ), RPT_SEVERITY_ERROR ); status = false; } if( m_fileRevision == -1 ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Missing [File Rev]" ), RPT_SEVERITY_ERROR ); status = false; } if( m_fileName.empty() ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); 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" ) ) ) { if(status ) Report( message.str(), RPT_SEVERITY_ACTION ); 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; } std::stringstream message; message << _( "Checking package model " ) << m_name; if( m_manufacturer.empty() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Manufacturer cannot be empty." ), RPT_SEVERITY_ERROR ); status = false; } if( m_OEM.empty() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "OEM cannot be empty." ), RPT_SEVERITY_ERROR ); status = false; } if( m_numberOfPins < 0 ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Negative number of pins." ), RPT_SEVERITY_ERROR ); status = false; } if( (int)m_pins.size() != m_numberOfPins ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( "Number of pins does not match [Pin Numbers] size", RPT_SEVERITY_ERROR ); status = false; } for( size_t i = 0; i < m_pins.size(); i++ ) { if( m_pins.at( i ).empty() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Empty pin number" ), RPT_SEVERITY_ERROR ); status = false; } } // resistance matrix is not required if( !( m_resistanceMatrix )->Check() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Resistance matrix is incorrect" ), RPT_SEVERITY_ERROR ); status = false; } if( m_capacitanceMatrix != nullptr ) { if( m_capacitanceMatrix->m_type == IBIS_MATRIX_TYPE::UNDEFINED ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Capacitance matrix is undefined" ), RPT_SEVERITY_ERROR ); status = false; } if( !m_capacitanceMatrix->Check() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Capacitance matrix is incorrect" ), RPT_SEVERITY_ERROR ); status = false; } } else { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Capacitance matrix is nullptr" ), RPT_SEVERITY_ERROR ); status = false; } if( m_inductanceMatrix != nullptr ) { if( m_inductanceMatrix->m_type == IBIS_MATRIX_TYPE::UNDEFINED ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Inductance matrix is undefined" ), RPT_SEVERITY_ERROR ); status = false; } if( !m_inductanceMatrix->Check() ) { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Inductance matrix is incorrect" ), RPT_SEVERITY_ERROR ); status = false; } } else { if( status ) Report( message.str(), RPT_SEVERITY_ACTION ); Report( _( "Inductance matrix is nullptr" ), RPT_SEVERITY_ERROR ); status = false; } return status; } bool IbisParser::ParseFile( std::string& aFileName ) { std::stringstream err_msg; std::ifstream ibisFile; ibisFile.open( aFileName ); if( !ibisFile.is_open() ) { err_msg << _( "Cannot open file " ) << aFileName; Report( err_msg.str(), RPT_SEVERITY_ERROR ); return false; } std::ostringstream ss; ss << ibisFile.rdbuf(); const std::string& s = ss.str(); m_buffer = std::vector( s.begin(), s.end() ); m_buffer.push_back( 0 ); 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() ) { err_msg.clear(); err_msg << _( "Error on line " ) << std::to_string( m_lineCounter ); Report( err_msg.str(), RPT_SEVERITY_ERROR ); 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; if( aString == "NA" ) { aDest.m_dv = nan( NAN_NA ); aDest.m_dt = nan( NAN_NA ); return status; } int i = 0; for( i = 1; i < (int)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 + 1, aString.size() - i - 1 ); if( !parseDouble( aDest.m_dv, str1, true ) || !parseDouble( aDest.m_dt, str2, true ) ) { 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; if( str == "NA" ) { result = nan( NAN_NA ); } else { try { result = std::stod( str, &size ); converted = true; } catch( ... ) { result = 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; if( m_bufferIndex >= (int)m_buffer.size() ) return false; 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; if( readWord( str ) ) { double result; size_t size = 0; try { result = std::stoi( str, &size ); } catch( ... ) { if( str == "NA" ) { result = nan( NAN_NA ); } else { result = 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 ) { bool status = true; skipWhitespaces(); status &= readString( aDest ); m_continue = aMultiline ? IBIS_PARSER_CONTINUE::STRING : IBIS_PARSER_CONTINUE::NONE; m_continuingString = &aDest; status &= checkEndofLine(); return status; } bool IbisParser::changeCommentChar() { skipWhitespaces(); std::string strChar = ""; // We cannot stop at m_lineLength here, because lineLength could stop before |_char // if the char 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( strChar != "_char" ) { Report( _( "Invalid syntax. Should be |_char or &_char, etc..." ), RPT_SEVERITY_ERROR ); return false; } 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; 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" ) ) { m_ibisFile.m_components.push_back( IbisComponent( m_reporter ) ); m_currentComponent = &( m_ibisFile.m_components.back() ); status &= storeString( m_currentComponent->m_name, false ); m_context = IBIS_PARSER_CONTEXT::COMPONENT; } else if( compareIbisWord( aKeyword.c_str(), "Model_Selector" ) ) { IbisModelSelector MS( m_reporter ); status &= 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; status &= 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; status &= 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; } std::stringstream message; message << _( "Unknown keyword in " ) << context_string << _( " context: " ) << aKeyword; Report( message.str(), 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; 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" ) ) { status &= readRampdvdt( m_currentModel->m_ramp.m_rising ); } else if( !strcmp( str.c_str(), "dV/dt_f" ) ) { status &= 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 < (int)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 ) ) { #if 0 // Currently not used int index = subindex + m_currentMatrixRow * aDest.m_dim + m_currentMatrixRow; #endif } 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 ) ) return false; status &= readString( model.m_modelDescription ); m_currentModelSelector->m_models.push_back( model ); return status; } bool IbisParser::readNumericSubparam( std::string aSubparam, double& aDest ) { std::string paramName; bool status = true; if( aSubparam.size() >= (size_t)m_lineLength ) { // Continuing would result in an overflow return false; } int old_index = m_lineIndex; m_lineIndex = 0; for( size_t 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() < (size_t)m_lineLength ) { for( size_t 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; 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_STD; 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 { std::stringstream message; message << _( "Unknown Model_type " ) << subparam; Report( message.str(), RPT_SEVERITY_ERROR ); 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 { std::stringstream message; message << _( "Unknown Enable: " ) << subparam; Report( message.str(), RPT_SEVERITY_ERROR ); 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 { std::stringstream message; message << _( "Unknown polarity " ) << subparam; Report( message.str(), RPT_SEVERITY_ERROR ); 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" ) ) { status &= changeCommentChar(); } else if( compareIbisWord( aKeyword.c_str(), "File_Name" ) ) { status &= 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" ) ) { status &= storeString( m_ibisFile.m_header.m_source, true ); } else if( compareIbisWord( aKeyword.c_str(), "Notes" ) ) { status &= storeString( m_ibisFile.m_header.m_notes, true ); } else if( compareIbisWord( aKeyword.c_str(), "Disclaimer" ) ) { status &= storeString( m_ibisFile.m_header.m_disclaimer, true ); } else if( compareIbisWord( aKeyword.c_str(), "Copyright" ) ) { status &= storeString( m_ibisFile.m_header.m_copyright, true ); } else if( compareIbisWord( aKeyword.c_str(), "Date" ) ) { status &= 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( (int)fields.size() == ( 4 + extraArg ) ) { TypMinMaxValue* cValue; if( fields.at( 0 ) == "R_pkg" ) cValue = R; else if( fields.at( 0 ) == "L_pkg" ) cValue = L; else if( fields.at( 0 ) == "C_pkg" ) cValue = C; else { Report( "Invalid field in [Package]" ); return false; } status &= parseDouble( cValue->value[IBIS_CORNER::TYP], fields.at( 1 ), true ); // Min / max values are optional, so don't update the status parseDouble( cValue->value[IBIS_CORNER::MIN], fields.at( 2 ), true ); parseDouble( cValue->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 status; } 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; m_lineIndex = 0; // rewind 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 ) { 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; } else { Report( _( "Internal error detected, a waveform should exist" ), RPT_SEVERITY_ERROR ); return false; } } 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 continuation of the previous one ! switch( m_continue ) { case IBIS_PARSER_CONTINUE::STRING: skipWhitespaces(); *m_continuingString += '\n'; status &= 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; }