// license:GPL-2.0+
// copyright-holders:Couriersud
// ***************************************************************************
//
// nltool.cpp
//
// Simple tool to debug netlists outside MAME.
//
// ***************************************************************************
#include "netlist/plib/pmain.h"
#include "netlist/devices/net_lib.h"
#include "netlist/nl_errstr.h"
#include "netlist/nl_parser.h"
#include "netlist/nl_setup.h"
#include "netlist/plib/pstrutil.h"
#include "netlist/solver/nld_solver.h"
#include "netlist/tools/nl_convert.h"
#include <cstdio> // scanf
#include <iomanip> // scanf
#include <ios>
#include <iostream> // scanf
#define NLTOOL_VERSION 20190420
class tool_app_t : public plib::app
{
public:
tool_app_t() :
plib::app(),
opt_grp1(*this, "General options", "The following options apply to all commands."),
opt_cmd (*this, "c", "cmd", 0, std::vector<pstring>({"run","validate","convert","listdevices","static","header","docheader"}), "run|validate|convert|listdevices|static|header|docheader"),
opt_file(*this, "f", "file", "-", "file to process (default is stdin)"),
opt_includes(*this, "I", "include", "Add the directory to the list of directories to be searched for header files. This option may be specified repeatedly."),
opt_defines(*this, "D", "define", "predefine value as macro, e.g. -Dname=value. If '=value' is omitted predefine it as 1. This option may be specified repeatedly."),
opt_rfolders(*this, "r", "rom", "where to look for data files"),
opt_verb(*this, "v", "verbose", "be verbose - this produces lots of output"),
opt_quiet(*this, "q", "quiet", "be quiet - no warnings"),
opt_prepro(*this, "", "prepro", "output preprocessor output to stderr"),
opt_version(*this, "", "version", "display version and exit"),
opt_help(*this, "h", "help", "display help and exit"),
opt_grp2(*this, "Options for run and static commands", "These options apply to run and static commands."),
opt_name(*this, "n", "name", "", "the netlist in file specified by ""-f"" option to run; default is first one"),
opt_grp3(*this, "Options for static command", "These options apply to static command."),
opt_dir(*this, "d", "dir", "", "output directory for the generated files"),
opt_grp4(*this, "Options for run command", "These options are only used by the run command."),
opt_ttr (*this, "t", "time_to_run", 1, "time to run the emulation (seconds)\n\n abc def\n\n xyz"),
opt_stats(*this, "s", "statistics", "gather runtime statistics"),
opt_logs(*this, "l", "log" , "define terminal to log. This option may be specified repeatedly."),
opt_inp(*this, "i", "input", "", "input file to process (default is none)"),
opt_loadstate(*this,"", "loadstate", "", "load state from file and continue from there"),
opt_savestate(*this,"", "savestate", "", "save state to file at end of run"),
opt_fperr(*this, "", "fperr",
"raise exception on floating point errors. This is intended to be used during debugging."),
opt_grp5(*this, "Options for convert command", "These options are only used by the convert command."),
opt_type(*this, "y", "type", 0, std::vector<pstring>({"spice","eagle","rinf"}), "type of file to be converted: spice,eagle,rinf"),
opt_grp6(*this, "Options for validate command", "These options are only used by the validate command."),
opt_extended_validation(*this, "", "extended", "Identify issues with power terminals."),
opt_grp7(*this, "Options for header command", "These options are only used by the header command."),
opt_tabwidth(*this, "", "tab-width", 4, "Tab width for output."),
opt_linewidth(*this,"", "line-width", 72, "Line width for output."),
opt_ex1(*this, "nltool -c run -t 3.5 -f nl_examples/cdelay.c -n cap_delay",
"Run netlist \"cap_delay\" from file nl_examples/cdelay.c for 3.5 seconds"),
opt_ex2(*this, "nltool --cmd=listdevices",
"List all known devices."),
opt_ex3(*this, "nltool --cmd=header --tab-width=8 --line-width=80",
"Create the header file needed for including netlists as code."),
m_warnings(0),
m_errors(0)
{}
plib::option_group opt_grp1;
plib::option_str_limit<unsigned> opt_cmd;
plib::option_str opt_file;
plib::option_vec opt_includes;
plib::option_vec opt_defines;
plib::option_vec opt_rfolders;
plib::option_bool opt_verb;
plib::option_bool opt_quiet;
plib::option_bool opt_prepro;
plib::option_bool opt_version;
plib::option_bool opt_help;
plib::option_group opt_grp2;
plib::option_str opt_name;
plib::option_group opt_grp3;
plib::option_str opt_dir;
plib::option_group opt_grp4;
plib::option_num<nl_fptype> opt_ttr;
plib::option_bool opt_stats;
plib::option_vec opt_logs;
plib::option_str opt_inp;
plib::option_str opt_loadstate;
plib::option_str opt_savestate;
plib::option_bool opt_fperr;
plib::option_group opt_grp5;
plib::option_str_limit<unsigned> opt_type;
plib::option_group opt_grp6;
plib::option_bool opt_extended_validation;
plib::option_group opt_grp7;
plib::option_num<unsigned> opt_tabwidth;
plib::option_num<unsigned> opt_linewidth;
plib::option_example opt_ex1;
plib::option_example opt_ex2;
plib::option_example opt_ex3;
int execute() override;
pstring usage() override;
template<typename... ARGS>
void poutprefix(const pstring &prefix, const pstring &fmt, ARGS&&... args)
{
pstring res = plib::pfmt(fmt)(std::forward<ARGS>(args)...);
auto lines(plib::psplit(res, "\n", false));
if (lines.size() == 0)
pout(prefix + "\n");
else
for (auto &l : lines)
pout(prefix + l + "\n");
}
int m_warnings;
int m_errors;
private:
void run();
void validate();
void convert();
void static_compile();
void mac_out(const pstring &s, const bool cont = true);
void header_entry(const netlist::factory::element_t *e);
void mac(const netlist::factory::element_t *e);
void create_header();
void create_docheader();
void listdevices();
std::vector<pstring> m_defines;
};
static NETLIST_START(dummy)
// Standard stuff
CLOCK(clk, 1000) // 1000 Hz
SOLVER(Solver, 48000)
NETLIST_END()
// **************************************************************************
// CORE IMPLEMENTATION
// **************************************************************************
class netlist_data_folder_t : public netlist::source_data_t
{
public:
explicit netlist_data_folder_t(const pstring &folder)
: netlist::source_data_t()
, m_folder(folder)
{
}
stream_ptr stream(const pstring &file) override
{
pstring name = m_folder + "/" + file;
auto strm(plib::make_unique<std::ifstream>(plib::filesystem::u8path(name)));
if (strm->fail())
return stream_ptr(nullptr);
else
{
strm->imbue(std::locale::classic());
return std::move(strm);
}
}
private:
pstring m_folder;
};
class netlist_tool_callbacks_t : public netlist::callbacks_t
{
public:
explicit netlist_tool_callbacks_t(tool_app_t &app)
: netlist::callbacks_t()
, m_app(app)
{ }
void vlog(const plib::plog_level &l, const pstring &ls) const noexcept override;
private:
tool_app_t &m_app;
};
class netlist_tool_t : public netlist::netlist_t
{
public:
netlist_tool_t(tool_app_t &app, const pstring &aname)
: netlist::netlist_t(aname, plib::make_unique<netlist_tool_callbacks_t>(app))
{
}
netlist::setup_t &setup() { return nlstate().setup(); }
void read_netlist(const pstring &filename, const pstring &name,
const std::vector<pstring> &logs,
const std::vector<pstring> &defines,
const std::vector<pstring> &roms,
const std::vector<pstring> &includes)
{
// read the netlist ...
for (auto & d : defines)
setup().add_define(d);
for (auto & r : roms)
setup().register_source(plib::make_unique<netlist_data_folder_t>(r));
using a = plib::psource_str_t<plib::psource_t>;
setup().add_include(plib::make_unique<a>("netlist/devices/net_lib.h",""));
for (auto & i : includes)
setup().add_include(plib::make_unique<netlist_data_folder_t>(i));
setup().register_source(plib::make_unique<netlist::source_file_t>(filename));
setup().include(name);
create_dynamic_logs(logs);
// start devices
setup().prepare_to_run();
}
void create_dynamic_logs(const std::vector<pstring> &logs)
{
log().debug("Creating dynamic logs ...\n");
for (auto & log : logs)
{
pstring name = "log_" + log;
setup().register_link(name + ".I", log);
}
}
std::vector<char> save_state()
{
run_state_manager().pre_save();
std::size_t size = 0;
for (auto const & s : run_state_manager().save_list())
size += s->m_dt.size * s->m_count;
std::vector<char> buf(size);
char *p = buf.data();
for (auto const & s : run_state_manager().save_list())
{
std::size_t sz = s->m_dt.size * s->m_count;
if (s->m_dt.is_float || s->m_dt.is_integral)
std::copy(static_cast<char *>(s->m_ptr),
static_cast<char *>(s->m_ptr) + sz, p);
else
log().fatal("found unsupported save element {1}\n", s->m_name);
p += sz;
}
return buf;
}
void load_state(std::vector<char> &buf)
{
std::size_t size = 0;
for (auto const & s : run_state_manager().save_list())
size += s->m_dt.size * s->m_count;
if (buf.size() != size)
plib::pthrow<netlist::nl_exception>("Size different during load state.");
char *p = buf.data();
for (auto const & s : run_state_manager().save_list())
{
std::size_t sz = s->m_dt.size * s->m_count;
if (s->m_dt.is_float || s->m_dt.is_integral)
std::copy(p, p + sz, static_cast<char *>(s->m_ptr));
else
log().fatal("found unsupported save element {1}\n", s->m_name);
p += sz;
}
run_state_manager().post_load();
nlstate().rebuild_lists();
}
protected:
private:
};
void netlist_tool_callbacks_t::vlog(const plib::plog_level &l, const pstring &ls) const noexcept
{
pstring err = plib::pfmt("{}: {}\n")(l.name())(ls.c_str());
if (l == plib::plog_level::WARNING)
m_app.m_warnings++;
if (l == plib::plog_level::ERROR)
m_app.m_errors++;
if (l == plib::plog_level::FATAL)
m_app.m_errors++;
m_app.pout("{}", err);
}
struct input_t
{
input_t(const netlist::setup_t &setup, const pstring &line)
: m_value(netlist::nlconst::zero())
{
std::array<char, 400> buf; // NOLINT(cppcoreguidelines-pro-type-member-init)
double t(0);
double val(0);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
int e = std::sscanf(line.c_str(), "%lf,%[^,],%lf", &t, buf.data(), &val);
if (e != 3)
plib::pthrow<netlist::nl_exception>(plib::pfmt("error {1} scanning line {2}\n")(e)(line));
m_value = static_cast<nl_fptype>(val);
m_time = netlist::netlist_time::from_fp(t);
m_param = setup.find_param(pstring(buf.data()), true);
}
void setparam()
{
switch (m_param->param_type())
{
case netlist::param_t::STRING:
case netlist::param_t::POINTER:
plib::pthrow<netlist::nl_exception>(plib::pfmt("param {1} is not numeric\n")(m_param->name()));
case netlist::param_t::DOUBLE:
static_cast<netlist::param_fp_t*>(m_param)->setTo(m_value);
break;
case netlist::param_t::INTEGER:
static_cast<netlist::param_int_t*>(m_param)->setTo(static_cast<int>(m_value));
break;
case netlist::param_t::LOGIC:
static_cast<netlist::param_logic_t*>(m_param)->setTo(static_cast<bool>(m_value));
break;
}
}
netlist::netlist_time m_time;
netlist::param_t *m_param;
nl_fptype m_value;
};
static std::vector<input_t> read_input(const netlist::setup_t &setup, const pstring &fname)
{
std::vector<input_t> ret;
if (fname != "")
{
plib::putf8_reader r = plib::putf8_reader(std::ifstream(plib::filesystem::u8path(fname)));
if (r.stream().fail())
plib::pthrow<netlist::nl_exception>(netlist::MF_FILE_OPEN_ERROR(fname));
r.stream().imbue(std::locale::classic());
pstring l;
while (r.readline(l))
{
if (l != "")
{
input_t inp(setup, l);
ret.push_back(inp);
}
}
}
return ret;
}
void tool_app_t::run()
{
plib::chrono::timer<plib::chrono::system_ticks> t;
std::vector<input_t> inps;
netlist::netlist_time ttr;
netlist_tool_t nt(*this, "netlist");
{
auto t_guard(t.guard());
//plib::perftime_t<plib::exact_ticks> t;
nt.enable_stats(opt_stats());
if (!opt_verb())
nt.log().verbose.set_enabled(false);
if (opt_quiet())
nt.log().info.set_enabled(false);
nt.read_netlist(opt_file(), opt_name(),
opt_logs(),
m_defines, opt_rfolders(), opt_includes());
nt.reset();
inps = read_input(nt.setup(), opt_inp());
ttr = netlist::netlist_time::from_fp(opt_ttr());
}
pout("startup time ==> {1:5.3f}\n", t.as_seconds<nl_fptype>() );
t.reset();
netlist::netlist_time nlt = nt.time();
{
auto t_guard(t.guard());
// FIXME: error handling
if (opt_loadstate.was_specified())
{
std::ifstream strm(plib::filesystem::u8path(opt_loadstate()));
if (strm.fail())
plib::pthrow<netlist::nl_exception>(netlist::MF_FILE_OPEN_ERROR(opt_loadstate()));
strm.imbue(std::locale::classic());
plib::pbinary_reader reader(strm);
std::vector<char> loadstate;
reader.read(loadstate);
nt.load_state(loadstate);
pout("Loaded state, run will continue at {1:.6f}\n", nt.time().as_double());
}
unsigned pos = 0;
while (pos < inps.size()
&& inps[pos].m_time < ttr
&& inps[pos].m_time >= nlt)
{
nt.process_queue(inps[pos].m_time - nlt);
inps[pos].setparam();
nlt = inps[pos].m_time;
pos++;
}
pout("runnning ...\n");
if (ttr > nlt)
nt.process_queue(ttr - nlt);
else
{
pout("end time {1:.6f} less than saved time {2:.6f}\n",
ttr.as_double(), nlt.as_double());
ttr = nlt;
}
if (opt_savestate.was_specified())
{
auto savestate = nt.save_state();
std::ofstream strm(plib::filesystem::u8path(opt_savestate()), std::ios_base::binary);
if (strm.fail())
plib::pthrow<plib::file_open_e>(opt_savestate());
strm.imbue(std::locale::classic());
plib::pbinary_writer writer(strm);
writer.write(savestate);
}
nt.stop();
}
auto emutime(t.as_seconds<nl_fptype>());
pout("{1:f} seconds emulation took {2:f} real time ==> {3:5.2f}%\n",
(ttr - nlt).as_fp<nl_fptype>(), emutime,
(ttr - nlt).as_fp<nl_fptype>() / emutime * netlist::nlconst::magic(100.0));
}
void tool_app_t::validate()
{
netlist_tool_t nt(*this, "netlist");
if (!opt_verb())
nt.log().verbose.set_enabled(false);
if (opt_quiet())
nt.log().info.set_enabled(false);
m_errors = 0;
m_warnings = 0;
nt.setup().set_extended_validation(opt_extended_validation());
try
{
nt.read_netlist(opt_file(), opt_name(),
opt_logs(),
m_defines, opt_rfolders(), opt_includes());
}
catch (netlist::nl_exception &e)
{
pout("Netlist exception caught: {}\n", e.text());
}
catch (plib::pexception &e)
{
pout("plib exception caught: {}\n", e.text());
}
//pout("Validation warnings: {}\n", m_warnings);
//pout("Validation errors: {}\n", m_errors);
if (m_warnings + m_errors > 0)
plib::pthrow<netlist::nl_exception>("validation: {1} errors {2} warnings", m_errors, m_warnings);
}
void tool_app_t::static_compile()
{
if (!opt_dir.was_specified())
plib::pthrow<netlist::nl_exception>("--dir option needs to be specified");
netlist_tool_t nt(*this, "netlist");
nt.log().verbose.set_enabled(false);
nt.log().info.set_enabled(false);
nt.log().warning.set_enabled(false);
nt.read_netlist(opt_file(), opt_name(),
opt_logs(),
m_defines, opt_rfolders(), opt_includes());
// need to reset ...
nt.reset();
std::map<pstring, pstring> mp;
nt.solver()->create_solver_code(mp);
for (auto &e : mp)
{
auto sout(std::ofstream(opt_dir() + "/" + e.first + ".c" ));
sout << e.second;
}
nt.stop();
}
// "Description: The Swiss army knife for timing purposes\n"
// " which has a ton of applications.\n"
// "DipAlias: GND,TRIG,OUT,RESET,VCC,DISCH,THRES,CONT\n"
// "Package: DIP\n"
// "NamingConvention: Naming conventions follow Texas Instruments datasheet\n"
// "Limitations: Internal resistor network currently fixed to 5k\n"
// " more limitations\n"
// "FunctionTable:\n"
//
struct doc_ext
{
pstring id;
pstring title;
pstring description;
std::vector<pstring> pinalias;
pstring package;
pstring namingconventions;
pstring limitations;
pstring functiontable;
std::vector<pstring> example;
};
static doc_ext read_docsrc(const pstring &fname, const pstring &id)
{
//printf("file %s\n", fname.c_str());
plib::putf8_reader r = plib::putf8_reader(std::ifstream(plib::filesystem::u8path(fname)));
if (r.stream().fail())
plib::pthrow<netlist::nl_exception>(netlist::MF_FILE_OPEN_ERROR(fname));
r.stream().imbue(std::locale::classic());
doc_ext ret;
pstring l;
if (!r.readline(l))
return ret;
do
{
l = plib::trim(l);
if (plib::startsWith(l, "//-"))
{
l = plib::trim(l.substr(3));
if (l != "")
{
auto a(plib::psplit(l, ":", true));
if ((a.size() < 1) || (a.size() > 2))
plib::pthrow<netlist::nl_exception>(l+" size mismatch");
pstring n(plib::trim(a[0]));
pstring v(a.size() < 2 ? "" : plib::trim(a[1]));
if (n == "Identifier")
{
ret.id = v;
if (!r.readline(l))
return (ret.id == id ? ret : doc_ext());
}
else
{
while (r.readline(l))
{
l = plib::ltrim(l);
if (!(plib::startsWith(l, "//- ") || plib::startsWith(l, "//-\t"))
&& !(plib::rtrim(l) == "//-"))
break;
v = v + "\n" + l.substr(3);
}
if (n == "Title")
ret.title = plib::trim(v);
else if (n == "Pinalias")
ret.pinalias = plib::psplit(plib::trim(v),",",true);
else if (n == "Description")
ret.description = v;
else if (n == "Package")
ret.package = plib::trim(v);
else if (n == "NamingConvention")
ret.namingconventions = v;
else if (n == "Limitations")
ret.limitations = v;
else if (n == "FunctionTable")
ret.functiontable = v;
else if (n == "Example")
{
ret.example = plib::psplit(plib::trim(v),",",true);
if (ret.example.size() != 2 && ret.example.size() != 0)
plib::pthrow<netlist::nl_exception>("Example requires 2 parameters, but found {1}", ret.example.size());
}
else
plib::pthrow<netlist::nl_exception>(n);
}
}
}
else if (!r.readline(l))
return (ret.id == id ? ret : doc_ext());
} while (true);
//return ret;
}
void tool_app_t::mac_out(const pstring &s, const bool cont)
{
if (cont)
{
unsigned pos = 0;
pstring r;
for (const auto &x : s)
{
if (x == '\t')
{
auto pos_mod_4 = pos % opt_tabwidth();
auto tab_adj = opt_tabwidth() - pos_mod_4;
r += plib::rpad(pstring(""), pstring(" "), tab_adj);
pos += tab_adj;
}
else
{
r += x;
pos++;
}
}
pout("{1}\\\n", plib::rpad(r, pstring(" "), opt_linewidth()-1));
}
else
pout("{1}\n", s);
}
void tool_app_t::header_entry(const netlist::factory::element_t *e)
{
auto v = plib::psplit(e->param_desc(), ",");
pstring vs;
for (const auto &s : v)
if (!plib::startsWith(s, "@"))
vs += ", p" + plib::replace_all(plib::replace_all(s, "+", ""), ".", "_");
mac_out("#define " + e->name() + "(name" + vs + ")");
vs = "";
for (const auto &s : v)
{
pstring r(plib::replace_all(plib::replace_all(plib::replace_all(s, "+", ""), ".", "_"), "@",""));
if (plib::startsWith(s, "+"))
vs += ", p" + r;
else if (plib::startsWith(s, "@"))
{
// automatically connected
//mac_out("\tNET_CONNECT(name, " + r + ", " + r + ")");
}
else
vs += ", p" + r;
}
mac_out("\tNET_REGISTER_DEVEXT(" + e->name() +", name" + vs + ")", false);
mac_out("", false);
}
void tool_app_t::mac(const netlist::factory::element_t *e)
{
auto v = plib::psplit(e->param_desc(), ",");
pstring vs;
for (const auto &s : v)
if (!plib::startsWith(s, "@"))
vs += ", " + plib::replace_all(plib::replace_all(s, "+", ""), ".", "_");
pout("{1}(name{2})\n", e->name(), vs);
if (v.size() > 0)
{
pout("/*\n");
for (const auto &s : v)
{
pstring r(plib::replace_all(plib::replace_all(plib::replace_all(s, "+", ""), ".", "_"), "@",""));
if (plib::startsWith(s, "+"))
pout("{1:10}: Terminal\n",r);
else if (plib::startsWith(s, "@"))
pout("{1:10}: Power terminal - automatically connected\n", r);
else
pout("{1:10}: Parameter\n", r);
}
pout("*/\n");
}
}
void tool_app_t::create_header()
{
netlist_tool_t nt(*this, "netlist");
nt.log().verbose.set_enabled(false);
nt.log().info.set_enabled(false);
nt.setup().register_source(plib::make_unique<netlist::source_proc_t>("dummy", &netlist_dummy));
nt.setup().include("dummy");
pout("// license:GPL-2.0+\n");
pout("// copyright-holders:Couriersud\n");
pout("#ifndef NLD_DEVINC_H\n");
pout("#define NLD_DEVINC_H\n");
pout("\n");
pout("#ifndef __PLIB_PREPROCESSOR__\n");
pout("\n");
pout("// ----------------------------------------------------------------------------\n");
pout("// Netlist Macros\n");
pout("// ---------------------------------------------------------------------------\n");
pout("\n");
pstring last_source("");
for (auto &e : nt.setup().factory())
{
if (last_source != e->sourcefile())
{
last_source = e->sourcefile();
pout("{1}\n", plib::rpad(pstring("// "), pstring("-"), opt_linewidth()));
pout("{1}{2}\n", "// Source: ", plib::replace_all(e->sourcefile(), "../", ""));
pout("{1}\n", plib::rpad(pstring("// "), pstring("-"), opt_linewidth()));
}
header_entry(e.get());
}
pout("#endif // __PLIB_PREPROCESSOR__\n");
pout("#endif\n");
nt.stop();
}
void tool_app_t::create_docheader()
{
netlist_tool_t nt(*this, "netlist");
nt.log().verbose.set_enabled(false);
nt.log().info.set_enabled(false);
nt.setup().register_source(plib::make_unique<netlist::source_proc_t>("dummy", &netlist_dummy));
nt.setup().include("dummy");
std::vector<pstring> devs;
for (auto &e : nt.setup().factory())
devs.push_back(e->name());
std::sort(devs.begin(), devs.end(), [&](pstring &a, pstring &b) { return a < b; });
pout("// license:GPL-2.0+\n");
pout("// copyright-holders:Couriersud\n");
pout("\n");
pout("// ----------------------------------------------------------------------------\n");
pout("// Automatically created file. DO NOT MODIFY.\n");
pout("// ---------------------------------------------------------------------------\n");
pout("///\n");
pout("/// \\page devices Devices\n");
pout("///\n");
pout("/// Below is a list of all the devices currently known to the system ...\n");
pout("///\n");
for (auto &s : devs)
pout("/// - @subpage {1}\n", s);
pout("\n");
for (auto &e : nt.setup().factory())
{
pout("//! [{1} csynopsis]\n", e->name());
header_entry(e.get());
pout("//! [{1} csynopsis]\n", e->name());
pout("//! [{1} synopsis]\n", e->name());
mac(e.get());
pout("//! [{1} synopsis]\n", e->name());
}
poutprefix("", "");
poutprefix("///", "");
//poutprefix("///", " @file ");
poutprefix("///", " @page '' "); // FIXME: snippets and pages need to be separate files
poutprefix("", "");
for (auto &e : nt.setup().factory())
{
auto d(read_docsrc(e->sourcefile(), e->name()));
if (d.id != "")
{
poutprefix("///", "");
//poutprefix("///", " @file {}", e->sourcefile());
poutprefix("///", "");
poutprefix("///", " @page {} {}", d.id, d.title);
poutprefix("///", "");
poutprefix("///", " {}", d.description);
poutprefix("///", "");
poutprefix("///", " @section {}_1 Synopsis", d.id);
poutprefix("///", "");
poutprefix("///", " @snippet devsyn.dox.h {} synopsis", d.id);
poutprefix("///", "");
poutprefix("///", " @section {}_11 C Synopsis", d.id);
poutprefix("///", "");
poutprefix("///", " @snippet devsyn.dox.h {} csynopsis", d.id);
poutprefix("///", "");
poutprefix("///", " @section {}_2 Connection Diagram", d.id);
poutprefix("///", "");
if (d.pinalias.size() > 0)
{
poutprefix("///", " <pre>");
if (d.package == "DIP")
{
auto & pins = d.pinalias;
//const int w = 8;
poutprefix("///", " {1:10} +--------+", " ");
for (std::size_t i=0; i<pins.size()/2; i++)
{
poutprefix("///", " {1:10} |{2:-2} {3:2}| {4:-10}",
pins[i], i+1, pins.size()-i, pins[pins.size()-i-1]);
}
poutprefix("///", " {1:10} +--------+", " ");
}
poutprefix("///", " </pre>");
}
poutprefix("///", "");
poutprefix("///", " {}", d.namingconventions);
poutprefix("///", "");
poutprefix("///", " @section {}_3 Function Table", d.id);
poutprefix("///", "");
if (d.functiontable == "")
poutprefix("///", " Please refer to the datasheet.");
else
poutprefix("///", " {}", d.functiontable);
poutprefix("///", "");
poutprefix("///", " @section {}_4 Limitations", d.id);
poutprefix("///", "");
poutprefix("///", " {}", d.limitations);
if (d.example.size() > 0)
{
poutprefix("///", "");
poutprefix("///", " @section {}_5 Example", d.id);
poutprefix("///", " @snippet {1} {2}", d.example[0], d.example[1]);
poutprefix("///", "");
poutprefix("", "");
}
}
}
nt.stop();
}
// -------------------------------------------------
// listdevices - list all known devices
// -------------------------------------------------
void tool_app_t::listdevices()
{
netlist_tool_t nt(*this, "netlist");
nt.log().verbose.set_enabled(false);
nt.log().info.set_enabled(false);
nt.log().warning.set_enabled(false);
netlist::factory::list_t &list = nt.setup().factory();
nt.setup().register_source(plib::make_unique<netlist::source_proc_t>("dummy", &netlist_dummy));
nt.setup().include("dummy");
nt.setup().prepare_to_run();
std::vector<netlist::unique_pool_ptr<netlist::core_device_t>> devs;
for (auto & f : list)
{
pstring out = plib::pfmt("{1:-20} {2}(<id>")(f->classname())(f->name());
f->macro_actions(nt.setup(), f->name() + "_lc");
auto d = f->Create(nt.nlstate(), f->name() + "_lc");
// get the list of terminals ...
std::vector<pstring> terms(nt.setup().get_terminals_for_device_name(d->name()));
out += "," + f->param_desc();
for (const auto &p : plib::psplit(f->param_desc(),",") )
{
if (plib::startsWith(p, "+"))
{
plib::container::remove(terms, p.substr(1));
}
}
out += ")";
pout("{}\n", out);
if (terms.size() > 0)
{
pstring t = "";
for (auto & j : terms)
t += "," + j;
pout("\tTerminals: {}\n", t.substr(1));
}
devs.emplace_back(std::move(d));
}
}
// -------------------------------------------------
// convert - convert spice et al to netlist
// -------------------------------------------------
void tool_app_t::convert()
{
pstring contents;
std::stringstream ostrm;
ostrm.imbue(std::locale::classic());
if (opt_file() == "-")
{
plib::copystream(ostrm, std::cin);
}
else
{
std::ifstream strm(plib::filesystem::u8path(opt_file()));
if (strm.fail())
plib::pthrow<netlist::nl_exception>(netlist::MF_FILE_OPEN_ERROR(opt_file()));
strm.imbue(std::locale::classic());
plib::copystream(ostrm, strm);
}
contents = pstring(ostrm.str());
pstring result;
if (opt_type.as_string() == "spice")
{
nl_convert_spice_t c;
c.convert(contents);
result = c.result();
}
else if (opt_type.as_string() == "eagle")
{
nl_convert_eagle_t c;
c.convert(contents);
result = c.result();
}
else if (opt_type.as_string() == "rinf")
{
nl_convert_rinf_t c;
c.convert(contents);
result = c.result();
}
// present result
pout.write(result);
}
// -------------------------------------------------
// main - primary entry point
// -------------------------------------------------
#if 0
static const pstring pmf_verbose[] =
{
"NL_PMF_TYPE_VIRTUAL",
"NL_PMF_TYPE_GNUC_PMF",
"NL_PMF_TYPE_GNUC_PMF_CONV",
"NL_PMF_TYPE_INTERNAL"
};
#endif
pstring tool_app_t::usage()
{
return help(
"nltool serves as the Swiss Army knife to run, test and convert netlists.",
"nltool [options]");
}
int tool_app_t::execute()
{
tool_app_t opts;
if (opt_help())
{
pout(usage());
return 0;
}
if (opt_version())
{
pout(
"nltool (netlist) " PSTRINGIFY(NLTOOL_VERSION) "\n"
"Copyright (C) 2019 Couriersud\n"
"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n\n"
"Written by Couriersud.\n");
if (opt_verb())
{
std::vector<std::pair<pstring, pstring>> defs;
netlist::netlist_state_t::compile_defines(defs);
pout("\nCompile defines:\n");
for (auto &x : defs)
pout("{1:-30} = {2}\n", x.first, x.second);
}
return 0;
}
m_defines = opt_defines();
m_defines.emplace_back("NLTOOL_VERSION=" PSTRINGIFY(NLTOOL_VERSION));
if (opt_prepro())
m_defines.emplace_back("__PREPROCESSOR_DEBUG__=1");
try
{
plib::fpsignalenabler::global_enable(opt_fperr());
plib::fpsignalenabler fpprotect(plib::FP_DIVBYZERO | plib::FP_UNDERFLOW | plib::FP_OVERFLOW | plib::FP_INVALID);
pstring cmd = opt_cmd.as_string();
if (cmd == "listdevices")
listdevices();
else if (cmd == "run")
run();
else if (cmd == "validate")
validate();
else if (cmd == "static")
static_compile();
else if (cmd == "header")
create_header();
else if (cmd == "docheader")
create_docheader();
else if (cmd == "convert")
convert();
else
{
perr("Unknown command {}\n", cmd.c_str());
//FIXME: usage_short
perr(usage());
return 1;
}
}
catch (netlist::nl_exception &e)
{
perr("Netlist exception caught: {}\n", e.text());
return 2;
}
catch (plib::pexception &e)
{
perr("plib exception caught: {}\n", e.text());
return 2;
}
#if 0
std::cout.imbue(std::locale("de_DE.utf8"));
std::cout.imbue(std::locale("C.UTF-8"));
std::cout << std::fixed << 20.003 << "\n";
std::cout << std::setw(20) << std::left << "01234567890" << "|" << "\n";
std::cout << std::setw(20) << "Общая ком" << "|" << "\n";
std::cout << "Общая ком" << pstring(20 - pstring("Общая ком").length(), ' ') << "|" << "\n";
std::cout << plib::pfmt("{:20}")("Общая ком") << "|" << "\n";
//char x = 'a';
//auto b= U'щ';
auto b= U'\U00000449';
std::cout << "b: <" << b << ">";
#endif
return 0;
}
PMAIN(tool_app_t)