kicad/eeschema/sim/sim_model_subckt.cpp

173 lines
6.4 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mikolaj Wielgus
* Copyright (C) 2022 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 3
* 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:
* https://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <sim/sim_model_subckt.h>
#include <sim/spice_grammar.h>
#include <fmt/core.h>
#include <pegtl.hpp>
#include <pegtl/contrib/parse_tree.hpp>
namespace SIM_MODEL_SUBCKT_SPICE_PARSER
{
using namespace SPICE_GRAMMAR;
template <typename Rule> struct spiceUnitSelector : std::false_type {};
template <> struct spiceUnitSelector<dotSubckt> : std::true_type {};
template <> struct spiceUnitSelector<modelName> : std::true_type {};
template <> struct spiceUnitSelector<dotSubcktPinName> : std::true_type {};
template <> struct spiceUnitSelector<dotSubcktParams> : std::true_type {};
template <> struct spiceUnitSelector<param> : std::true_type {};
template <> struct spiceUnitSelector<number<SIM_VALUE::TYPE_INT, NOTATION::SPICE>>
: std::true_type {};
template <> struct spiceUnitSelector<number<SIM_VALUE::TYPE_FLOAT, NOTATION::SPICE>>
: std::true_type {};
}
std::string SPICE_GENERATOR_SUBCKT::ModelLine( const SPICE_ITEM& aItem ) const
{
return "";
}
std::vector<std::string> SPICE_GENERATOR_SUBCKT::CurrentNames( const SPICE_ITEM& aItem ) const
{
std::vector<std::string> currentNames;
for( const SIM_MODEL::PIN& pin : GetPins() )
currentNames.push_back( fmt::format( "I({}:{})", ItemName( aItem ), pin.name ) );
return currentNames;
}
void SPICE_MODEL_PARSER_SUBCKT::ReadModel( const SIM_LIBRARY_SPICE& aLibrary,
const std::string& aSpiceCode )
{
tao::pegtl::string_input<> in( aSpiceCode, "from_content" );
std::unique_ptr<tao::pegtl::parse_tree::node> root;
try
{
root = tao::pegtl::parse_tree::parse<SIM_MODEL_SUBCKT_SPICE_PARSER::spiceUnitGrammar,
SIM_MODEL_SUBCKT_SPICE_PARSER::spiceUnitSelector,
tao::pegtl::nothing,
SIM_MODEL_SUBCKT_SPICE_PARSER::control>( in );
}
catch( const tao::pegtl::parse_error& e )
{
THROW_IO_ERROR( e.what() );
}
SIM_MODEL_SUBCKT& model = static_cast<SIM_MODEL_SUBCKT&>( m_model );
for( const auto& node : root->children )
{
if( node->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubckt>() )
{
for( const auto& subnode : node->children )
{
if( subnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::modelName>() )
{
}
else if( subnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubcktPinName>() )
{
model.AddPin( { subnode->string(), fmt::format( "{}", model.GetPinCount() + 1 ) } );
}
else if( subnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubcktParams>() )
{
for( const auto& subsubnode : subnode->children )
{
if( subsubnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::param>() )
{
model.m_paramInfos.push_back( std::make_unique<SIM_MODEL::PARAM::INFO>() );
model.m_paramInfos.back()->name = subsubnode->string();
model.m_paramInfos.back()->isInstanceParam = true;
model.m_paramInfos.back()->isSpiceInstanceParam = true;
model.AddParam( *model.m_paramInfos.back() );
}
else
{
wxFAIL_MSG( "Unhandled parse tree subsubnode" );
}
}
}
else if( subnode->is_type<
SIM_MODEL_SUBCKT_SPICE_PARSER::number<SIM_VALUE::TYPE_INT,
SIM_MODEL_SUBCKT_SPICE_PARSER::NOTATION::SPICE>>()
|| subnode->is_type<
SIM_MODEL_SUBCKT_SPICE_PARSER::number<SIM_VALUE::TYPE_FLOAT,
SIM_MODEL_SUBCKT_SPICE_PARSER::NOTATION::SPICE>>() )
{
wxASSERT( model.m_paramInfos.size() > 0 );
model.m_paramInfos.back()->defaultValue = subnode->string();
}
}
}
else
{
wxFAIL_MSG( "Unhandled parse tree node" );
}
}
model.m_spiceCode = aSpiceCode;
}
SIM_MODEL_SUBCKT::SIM_MODEL_SUBCKT() :
SIM_MODEL_SPICE( TYPE::SUBCKT,
std::make_unique<SPICE_GENERATOR_SUBCKT>( *this ),
std::make_unique<SPICE_MODEL_PARSER_SUBCKT>( *this ) )
{
}
void SIM_MODEL_SUBCKT::SetBaseModel( const SIM_MODEL& aBaseModel )
{
SIM_MODEL::SetBaseModel( aBaseModel );
// Pins aren't constant for subcircuits, so they need to be copied from the base model.
for( const PIN& pin : GetBaseModel()->GetPins() )
AddPin( pin );
// Same for parameters.
for( const PARAM& param : GetBaseModel()->GetParams() )
AddParam( param.info );
}
void SIM_MODEL_SUBCKT::CreatePins( unsigned aSymbolPinCount )
{
SIM_MODEL::CreatePins( aSymbolPinCount );
// Reset the pins to Not Connected. Linear order is not as common, and reordering the pins is
// more effort in the GUI than assigning them from scratch.
for( int pinIndex = 0; pinIndex < GetPinCount(); ++pinIndex )
SetPinSymbolPinNumber( pinIndex, "" );
}