/* * 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 "ibis_parser.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 PRBS, RECTANGULAR, STUCK_HIGH, STUCK_LOW, HIGH_Z }; class KIBIS_WAVEFORM { public: KIBIS_WAVEFORM(){}; KIBIS_WAVEFORM_TYPE GetType() { return m_type; }; virtual double GetDuration() { return 1; }; bool inverted = false; // Used for differential drivers virtual ~KIBIS_WAVEFORM(){}; 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 = 100e-9; double m_toff = 100e-9; int m_cycles = 1; double m_delay = 0; double GetDuration() override { return ( m_ton + m_toff ) * m_cycles; }; }; // For now, we only support PRBS7 class KIBIS_WAVEFORM_PRBS : public KIBIS_WAVEFORM { public: KIBIS_WAVEFORM_PRBS() : KIBIS_WAVEFORM() { m_type = KIBIS_WAVEFORM_TYPE::PRBS; }; double m_bitrate = 10e6; double m_delay = 0; double m_bits = 10; std::vector> GenerateBitSequence(); double GetDuration() override { return m_bits / m_bitrate ; }; }; 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_PARAMETER { public: IBIS_CORNER m_Rpin = IBIS_CORNER::TYP; IBIS_CORNER m_Lpin = IBIS_CORNER::TYP; IBIS_CORNER m_Cpin = IBIS_CORNER::TYP; IBIS_CORNER m_Ccomp = IBIS_CORNER::TYP; IBIS_CORNER m_supply = IBIS_CORNER::TYP; KIBIS_WAVEFORM* m_waveform; KIBIS_ACCURACY m_accuracy = KIBIS_ACCURACY::LEVEL_2; void SetCornerFromString( IBIS_CORNER& aCorner, std::string aString ); }; 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() : KIBIS_ANY( this ), m_file( this ) { m_valid = false; }; // Constructor for unitialized KIBIS members KIBIS( std::string aFileName ); std::vector m_components; std::vector m_models; KIBIS_FILE m_file; /** @brief Return the model with name aName . Nullptr if not found */ KIBIS_MODEL* GetModel( std::string aName ); /** @brief Return the component with name aName . Nullptr if not found */ KIBIS_COMPONENT* GetComponent( std::string aName ); }; 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 aParam Parameters * @param aIndex Index used to offset spice nodes / directives * @return A multiline string with spice directives */ std::string SpiceDie( KIBIS_PARAMETER& aParam, 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 aParam Parameters * @return A multiline string with spice directives */ std::string generateSquareWave( std::string aNode1, std::string aNode2, std::vector> aBits, std::pair aPair, KIBIS_PARAMETER& aParam ); /** @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 m_Rpin; /** @brief Inductance from die to pin */ TypMinMaxValue m_Lpin; /** @brief Capacitance from pin to GND */ TypMinMaxValue m_Cpin; 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, KIBIS_PARAMETER& aParam ); bool writeSpiceDiffDriver( std::string* aDest, std::string aName, KIBIS_MODEL& aModel, KIBIS_PARAMETER& aParam ); bool writeSpiceDevice( std::string* aDest, std::string aName, KIBIS_MODEL& aModel, KIBIS_PARAMETER& aParam ); bool writeSpiceDiffDevice( std::string* aDest, std::string aName, KIBIS_MODEL& aModel, KIBIS_PARAMETER& aParam ); /** @brief Update m_Ku, m_Kd using no falling / rising waveform inputs ( low accuracy ) * @param aModel Model to be used * @param aParam Parameters */ void getKuKdNoWaveform( KIBIS_MODEL& aModel, KIBIS_PARAMETER& aParam ); /** @brief Update m_Ku, m_Kd using with a single waveform input * @param aModel Model to be used * @param aPair @see waveformPairs() * @param aParam Parameters */ void getKuKdOneWaveform( KIBIS_MODEL& aModel, std::pair aPair, KIBIS_PARAMETER& aParam ); /** @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 aParam Parameters * @param aIndex Index for numbering spice .SUBCKT */ void getKuKdTwoWaveforms( KIBIS_MODEL& aModel, std::pair aPair1, std::pair aPair2, KIBIS_PARAMETER& aParam ); /** @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 aParam Parameters * @param aIndex Index for numbering spice .SUBCKT * * @return A multiline string with spice directives */ std::string KuKdDriver( KIBIS_MODEL& aModel, std::pair aPair, KIBIS_PARAMETER& aParam, 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 aParam Parameters * @param aIndex Index for numbering ports * @return A multiline string with spice directives */ std::string addDie( KIBIS_MODEL& aModel, KIBIS_PARAMETER& aParam, 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