klayout/src/db/db/dbNetlistSpiceReader.h

254 lines
7.5 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2021 Matthias Koefferlein
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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_dbNetlistSpiceReader
#define HDR_dbNetlistSpiceReader
#include "dbCommon.h"
#include "dbNetlistReader.h"
#include "tlStream.h"
#include <string>
#include <set>
#include <map>
#include <memory>
#include <list>
namespace db
{
class Netlist;
class Net;
class Circuit;
class DeviceClass;
class Device;
/**
* @brief A specialized exception class to handle netlist reader delegate errors
*/
class DB_PUBLIC NetlistSpiceReaderDelegateError
: public tl::Exception
{
public:
NetlistSpiceReaderDelegateError (const std::string &msg)
: tl::Exception (msg)
{ }
};
/**
* @brief A delegate to handle various forms of devices and translates them
*
* The reader delegate can be configured to receive subcircuit elements too.
* In this case, parameters are allowed.
* For receiving subcircuit elements, the delegate needs to indicate
* this by returning true upon "wants_subcircuit".
*/
class DB_PUBLIC NetlistSpiceReaderDelegate
: public tl::Object
{
public:
NetlistSpiceReaderDelegate ();
virtual ~NetlistSpiceReaderDelegate ();
/**
* @brief Called when the netlist reading starts
*/
virtual void start (db::Netlist *netlist);
/**
* @brief Called when the netlist reading ends
*/
virtual void finish (db::Netlist *netlist);
/**
* @brief Called when an unknown control statement is encountered
*
* Returns true if the statement is understood.
*/
virtual bool control_statement (const std::string &line);
/**
* @brief Returns true, if the delegate wants subcircuit elements with this name
*
* The name is always upper case.
*/
virtual bool wants_subcircuit (const std::string &circuit_name);
/**
* @brief This method translates a raw net name to a valid net name
*
* The default implementation will unescape backslash sequences into plain characters.
*/
virtual std::string translate_net_name (const std::string &nn);
/**
* @brief Makes a device from an element line
*
* @param circuit The circuit that is currently read.
* @param element The upper-case element code ("M", "R", ...).
* @param name The element's name.
* @param model The upper-case model name (may be empty).
* @param value The default value (e.g. resistance for resistors) and may be zero.
* @param nets The nets given in the element line.
* @param parameters The parameters of the element statement (parameter names are upper case).
*
* The default implementation will create corresponding devices for
* some known elements using the Spice writer's parameter conventions.
*
* This method returns true, if the element was read.
*/
virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params);
/**
* @brief Parses an element from a line
*
* @param s The line to parse (the part after the element and name)
* @param model Out parameter: the model name if given
* @param value Out parameter: the value if given (for R, L, C)
* @param nn Out parameter: the net names
* @param pv Out parameter: the parameter values (key/value pairs)
*/
virtual void parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector<std::string> &nn, std::map<std::string, double> &pv);
/**
* @brief Produces an error with the given message
*/
virtual void error (const std::string &msg);
/**
* @brief Reads a set of string components and parameters from the string
* A special key "param:" is recognized for starting a parameter list.
*/
void parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, double> &pv);
/**
* @brief Reads a value from the extractor (with formula evaluation)
*/
double read_value (tl::Extractor &ex);
/**
* @brief Tries to read a value from the extractor (with formula evaluation)
*/
bool try_read_value (const std::string &s, double &v);
private:
double read_atomic_value (tl::Extractor &ex);
double read_dot_expr (tl::Extractor &ex);
double read_bar_expr (tl::Extractor &ex);
};
/**
* @brief A SPICE format reader for netlists
*/
class DB_PUBLIC NetlistSpiceReader
: public NetlistReader
{
public:
NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate = 0);
virtual ~NetlistSpiceReader ();
virtual void read (tl::InputStream &stream, db::Netlist &netlist);
private:
class SpiceReaderStream
{
public:
SpiceReaderStream ();
~SpiceReaderStream ();
void set_stream (tl::InputStream &stream);
void set_stream (tl::InputStream *stream);
void close ();
std::pair<std::string, bool> get_line();
int line_number () const;
std::string source () const;
bool at_end () const;
void swap (SpiceReaderStream &other)
{
std::swap (mp_stream, other.mp_stream);
std::swap (m_owns_stream, other.m_owns_stream);
std::swap (mp_text_stream, other.mp_text_stream);
std::swap (m_line_number, other.m_line_number);
std::swap (m_stored_line, other.m_stored_line);
std::swap (m_has_stored_line, other.m_has_stored_line);
}
private:
tl::InputStream *mp_stream;
bool m_owns_stream;
tl::TextInputStream *mp_text_stream;
int m_line_number;
std::string m_stored_line;
bool m_has_stored_line;
};
db::Netlist *mp_netlist;
db::Circuit *mp_circuit;
db::Circuit *mp_anonymous_top_circuit;
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
std::list<SpiceReaderStream> m_streams;
SpiceReaderStream m_stream;
std::unique_ptr<std::map<std::string, db::Net *> > mp_nets_by_name;
std::map<std::string, bool> m_captured;
std::vector<std::string> m_global_nets;
std::set<std::string> m_global_net_names;
std::set<const db::Circuit *> m_circuits_read;
void push_stream (const std::string &path);
void pop_stream ();
bool at_end ();
bool read_element (tl::Extractor &ex, const std::string &element, const std::string &name);
void read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector<db::Net *> &nets);
void read_circuit (tl::Extractor &ex, const std::string &name);
void skip_circuit (tl::Extractor &ex);
bool read_card ();
std::string read_name (tl::Extractor &ex);
std::string get_line ();
void error (const std::string &msg);
void warn (const std::string &msg);
void finish ();
db::Net *make_net (const std::string &name);
void ensure_circuit ();
bool subcircuit_captured (const std::string &nc_name);
void build_global_nets ();
};
}
namespace tl
{
template <>
struct type_traits<db::NetlistSpiceReader>
: public tl::type_traits<void>
{
typedef tl::false_tag has_default_constructor;
typedef tl::false_tag has_copy_constructor;
};
}
#endif