457 lines
15 KiB
C++
457 lines
15 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2022 Fabien Corona f.corona<at>laposte.net
|
|
* Copyright (C) 2022-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.
|
|
*/
|
|
|
|
|
|
#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_ANY
|
|
{
|
|
public:
|
|
KIBIS_WAVEFORM( KIBIS* aTopLevel ) : KIBIS_ANY{ aTopLevel } { m_valid = true; };
|
|
KIBIS_WAVEFORM_TYPE GetType() { return m_type; };
|
|
virtual double GetDuration() { return 1; };
|
|
bool inverted = false; // Used for differential drivers
|
|
virtual ~KIBIS_WAVEFORM(){};
|
|
|
|
virtual std::vector<std::pair<int, double>> GenerateBitSequence()
|
|
{
|
|
std::vector<std::pair<int, double>> bits;
|
|
return bits;
|
|
};
|
|
|
|
// Check fuction if using waveform data
|
|
virtual bool Check( IbisWaveform* aRisingWf, IbisWaveform* aFallingWf ) { return true; };
|
|
// Check fuction if using ramp data
|
|
virtual bool Check( dvdtTypMinMax aRisingRp, dvdtTypMinMax aFallingRp ) { return true; };
|
|
|
|
protected:
|
|
KIBIS_WAVEFORM_TYPE m_type = KIBIS_WAVEFORM_TYPE::NONE;
|
|
};
|
|
|
|
class KIBIS_WAVEFORM_RECTANGULAR : public KIBIS_WAVEFORM
|
|
{
|
|
public:
|
|
KIBIS_WAVEFORM_RECTANGULAR( KIBIS* aTopLevel ) : KIBIS_WAVEFORM( aTopLevel )
|
|
{
|
|
m_type = KIBIS_WAVEFORM_TYPE::RECTANGULAR;
|
|
};
|
|
double m_ton = 100e-9;
|
|
double m_toff = 100e-9;
|
|
double m_delay = 0;
|
|
int m_cycles = 1;
|
|
|
|
|
|
std::vector<std::pair<int, double>> GenerateBitSequence() override;
|
|
bool Check( IbisWaveform* aRisingWf, IbisWaveform* aFallingWf ) override;
|
|
bool Check( dvdtTypMinMax aRisingRp, dvdtTypMinMax aFallingRp ) override;
|
|
|
|
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* aTopLevel ) : KIBIS_WAVEFORM( aTopLevel )
|
|
{
|
|
m_type = KIBIS_WAVEFORM_TYPE::PRBS;
|
|
};
|
|
double m_bitrate = 10e6;
|
|
double m_delay = 0;
|
|
int m_bits = 10;
|
|
|
|
std::vector<std::pair<int, double>> GenerateBitSequence() override;
|
|
bool Check( IbisWaveform* aRisingWf, IbisWaveform* aFallingWf ) override;
|
|
bool Check( dvdtTypMinMax aRisingRp, dvdtTypMinMax aFallingRp ) override;
|
|
|
|
double GetDuration() override { return m_bits / m_bitrate ; };
|
|
};
|
|
|
|
class KIBIS_WAVEFORM_STUCK_HIGH : public KIBIS_WAVEFORM
|
|
{
|
|
public:
|
|
KIBIS_WAVEFORM_STUCK_HIGH( KIBIS* aTopLevel ) : KIBIS_WAVEFORM( aTopLevel )
|
|
{
|
|
m_type = KIBIS_WAVEFORM_TYPE::STUCK_HIGH;
|
|
};
|
|
std::vector<std::pair<int, double>> GenerateBitSequence() override;
|
|
};
|
|
|
|
class KIBIS_WAVEFORM_STUCK_LOW : public KIBIS_WAVEFORM
|
|
{
|
|
public:
|
|
KIBIS_WAVEFORM_STUCK_LOW( KIBIS* aTopLevel ) : KIBIS_WAVEFORM( aTopLevel )
|
|
{
|
|
m_type = KIBIS_WAVEFORM_TYPE::STUCK_LOW;
|
|
};
|
|
std::vector<std::pair<int, double>> GenerateBitSequence() override;
|
|
};
|
|
|
|
class KIBIS_WAVEFORM_HIGH_Z : public KIBIS_WAVEFORM
|
|
{
|
|
public:
|
|
KIBIS_WAVEFORM_HIGH_Z( KIBIS* aTopLevel ) : KIBIS_WAVEFORM( aTopLevel )
|
|
{
|
|
m_type = KIBIS_WAVEFORM_TYPE::HIGH_Z;
|
|
};
|
|
std::vector<std::pair<int, double>> GenerateBitSequence() override;
|
|
};
|
|
|
|
/** 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 = nullptr;
|
|
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_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<IbisWaveform*> m_risingWaveforms;
|
|
std::vector<IbisWaveform*> 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<std::pair<IbisWaveform*, IbisWaveform*>> 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<std::pair<int, double>> aBits,
|
|
std::pair<IbisWaveform*, IbisWaveform*> 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<KIBIS_MODEL>& 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<double> m_t, m_Ku, m_Kd;
|
|
|
|
std::vector<KIBIS_MODEL*> 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<IbisWaveform*, IbisWaveform*> 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<IbisWaveform*, IbisWaveform*> aPair1,
|
|
std::pair<IbisWaveform*, IbisWaveform*> 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<IbisWaveform*, IbisWaveform*> 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 );
|
|
|
|
KIBIS_PIN* m_complementaryPin = nullptr;
|
|
|
|
bool isDiffPin() { return m_complementaryPin != nullptr; };
|
|
};
|
|
|
|
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<KIBIS_PIN> 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 );
|
|
};
|
|
|
|
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, REPORTER* aReporter = nullptr );
|
|
|
|
REPORTER* m_reporter;
|
|
std::vector<KIBIS_COMPONENT> m_components;
|
|
std::vector<KIBIS_MODEL> m_models;
|
|
KIBIS_FILE m_file;
|
|
|
|
/** @brief Absolute path of the directory that will be used for caching. */
|
|
std::string m_cacheDir = "";
|
|
|
|
/** @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 );
|
|
};
|
|
|
|
#endif
|