// license:GPL-2.0+ // copyright-holders:Couriersud /*************************************************************************** nltool.c Simple tool to debug netlists outside MAME. ****************************************************************************/ #include #ifdef PSTANDALONE #if (PSTANDALONE) #define PSTANDALONE_PROVIDED #endif #endif #include "plib/poptions.h" #include "plib/pstring.h" #include "plib/plists.h" #include "plib/ptypes.h" #include "nl_setup.h" #include "nl_factory.h" #include "nl_parser.h" #include "devices/net_lib.h" #include "tools/nl_convert.h" #ifdef PSTANDALONE_PROVIDED #include #define osd_ticks_t clock_t inline osd_ticks_t osd_ticks_per_second() { return CLOCKS_PER_SEC; } osd_ticks_t osd_ticks(void) { return clock(); } #else #endif /*************************************************************************** * MAME COMPATIBILITY ... * * These are needed if we link without libutil ***************************************************************************/ #if 0 void ATTR_PRINTF(1,2) osd_printf_warning(const char *format, ...) { va_list argptr; /* do the output */ va_start(argptr, format); vprintf(format, argptr); va_end(argptr); } void *malloc_file_line(size_t size, const char *file, int line) { // allocate the memory and fail if we can't void *ret = osd_malloc(size); memset(ret, 0, size); return ret; } void *malloc_array_file_line(size_t size, const char *file, int line) { // allocate the memory and fail if we can't void *ret = osd_malloc_array(size); memset(ret, 0, size); return ret; } void free_file_line( void *memory, const char *file, int line ) { osd_free( memory ); } void CLIB_DECL logerror(const char *format, ...) { va_list arg; va_start(arg, format); vprintf(format, arg); va_end(arg); } void report_bad_cast(const std::type_info &src_type, const std::type_info &dst_type) { printf("Error: bad downcast<> or device<>. Tried to convert a %s to a %s, which are incompatible.\n", src_type.name(), dst_type.name()); throw; } #endif class tool_options_t : public poptions { public: tool_options_t() : poptions(), opt_ttr ("t", "time_to_run", 1.0, "time to run the emulation (seconds)", this), opt_name("n", "name", "", "netlist in file to run; default is first one", this), opt_logs("l", "logs", "", "colon separated list of terminals to log", this), opt_file("f", "file", "-", "file to process (default is stdin)", this), opt_type("y", "type", "spice", "spice:eagle", "type of file to be converted: spice,eagle", this), opt_cmd ("c", "cmd", "run", "run|convert|listdevices", this), opt_inp( "i", "input", "", "input file to process (default is none)", this), opt_verb("v", "verbose", "be verbose - this produces lots of output", this), opt_quiet("q", "quiet", "be quiet - no warnings", this), opt_help("h", "help", "display help", this) {} poption_double opt_ttr; poption_str opt_name; poption_str opt_logs; poption_str opt_file; poption_str_limit opt_type; poption_str opt_cmd; poption_str opt_inp; poption_bool opt_verb; poption_bool opt_quiet; poption_bool opt_help; }; pstdout pout_strm; pstderr perr_strm; pstream_fmt_writer_t pout(pout_strm); pstream_fmt_writer_t perr(perr_strm); NETLIST_START(dummy) /* Standard stuff */ CLOCK(clk, 1000) // 1000 Hz SOLVER(Solver, 48000) NETLIST_END() /*************************************************************************** CORE IMPLEMENTATION ***************************************************************************/ class netlist_tool_t : public netlist::netlist_t { public: netlist_tool_t() : netlist::netlist_t(), m_opts(NULL), m_setup(NULL) { } ~netlist_tool_t() { if (m_setup != NULL) pfree(m_setup); }; void init() { m_setup = palloc(netlist::setup_t(this)); this->init_object(*this, "netlist"); m_setup->init(); } void read_netlist(const pstring &filename, const pstring &name) { // read the netlist ... m_setup->register_source(palloc(netlist::source_file_t(filename))); m_setup->include(name); log_setup(); // start devices m_setup->start_devices(); m_setup->resolve_inputs(); // reset this->reset(); } void log_setup() { log().debug("Creating dynamic logs ...\n"); pstring_list_t ll(m_opts ? m_opts->opt_logs() : "" , ":"); for (unsigned i=0; i < ll.size(); i++) { pstring name = "log_" + ll[i]; /*netlist_device_t *nc = */ m_setup->register_dev("LOG", name); m_setup->register_link(name + ".I", ll[i]); } } tool_options_t *m_opts; protected: void vlog(const plog_level &l, const pstring &ls) const { pout("{}: {}\n", l.name().cstr(), ls.cstr()); if (l == plog_level::FATAL) throw; } private: netlist::setup_t *m_setup; }; void usage(tool_options_t &opts) { perr("{}", "Usage:\n" " nltool -help\n" " nltool [options]\n" "\n" "Where:\n" ); perr("{}\n", opts.help().cstr()); } struct input_t { input_t() : m_param(NULL), m_value(0.0) { } input_t(netlist::netlist_t *netlist, const pstring &line) { char buf[400]; double t; int e = sscanf(line.cstr(), "%lf,%[^,],%lf", &t, buf, &m_value); if ( e!= 3) throw netlist::fatalerror_e(pfmt("error {1} scanning line {2}\n")(e)(line)); m_time = netlist::netlist_time::from_double(t); m_param = netlist->setup().find_param(buf, true); } void setparam() { switch (m_param->param_type()) { case netlist::param_t::MODEL: case netlist::param_t::STRING: throw netlist::fatalerror_e(pfmt("param {1} is not numeric\n")(m_param->name())); case netlist::param_t::DOUBLE: static_cast(m_param)->setTo(m_value); break; case netlist::param_t::INTEGER: static_cast(m_param)->setTo((int)m_value); break; case netlist::param_t::LOGIC: static_cast(m_param)->setTo((int) m_value); break; } } netlist::netlist_time m_time; netlist::param_t *m_param; double m_value; }; plist_t *read_input(netlist::netlist_t *netlist, pstring fname) { plist_t *ret = palloc(plist_t()); if (fname != "") { pifilestream f(fname); pstring l; while (f.readline(l)) { if (l != "") { input_t inp(netlist, l); ret->add(inp); } } } return ret; } static void run(tool_options_t &opts) { netlist_tool_t nt; osd_ticks_t t = osd_ticks(); nt.m_opts = &opts; nt.init(); if (!opts.opt_verb()) nt.log().verbose.set_enabled(false); if (opts.opt_quiet()) nt.log().warning.set_enabled(false); nt.read_netlist(opts.opt_file(), opts.opt_name()); plist_t *inps = read_input(&nt, opts.opt_inp()); double ttr = opts.opt_ttr(); pout("startup time ==> {1:5.3f}\n", (double) (osd_ticks() - t) / (double) osd_ticks_per_second() ); pout("runnning ...\n"); t = osd_ticks(); unsigned pos = 0; netlist::netlist_time nlt = netlist::netlist_time::zero; while (pos < inps->size() && (*inps)[pos].m_time < netlist::netlist_time::from_double(ttr)) { nt.process_queue((*inps)[pos].m_time - nlt); (*inps)[pos].setparam(); nlt = (*inps)[pos].m_time; pos++; } nt.process_queue(netlist::netlist_time::from_double(ttr) - nlt); nt.stop(); pfree(inps); double emutime = (double) (osd_ticks() - t) / (double) osd_ticks_per_second(); pout("{1:f} seconds emulation took {2:f} real time ==> {3:5.2f}%\n", ttr, emutime, ttr/emutime*100.0); } /*------------------------------------------------- listdevices - list all known devices -------------------------------------------------*/ static void listdevices() { netlist_tool_t nt; nt.init(); const netlist::factory_list_t &list = nt.setup().factory(); nt.setup().register_source(palloc(netlist::source_proc_t("dummy", &netlist_dummy))); nt.setup().include("dummy"); nt.setup().start_devices(); nt.setup().resolve_inputs(); for (unsigned i=0; i < list.size(); i++) { netlist::base_factory_t *f = list.value_at(i); pstring out = pfmt("{1} {2}(")(f->classname(),"-20")(f->name()); pstring terms(""); netlist::device_t *d = f->Create(); d->init(nt, pfmt("dummy{1}")(i)); d->start_dev(); // get the list of terminals ... for (unsigned j=0; j < d->m_terminals.size(); j++) { pstring inp = d->m_terminals[j]; if (inp.startsWith(d->name() + ".")) inp = inp.substr(d->name().len() + 1); terms += "," + inp; } if (f->param_desc().startsWith("+")) { out += "," + f->param_desc().substr(1); terms = ""; } else if (f->param_desc() == "-") { /* no params at all */ } else { out += "," + f->param_desc(); } out += ")"; printf("%s\n", out.cstr()); if (terms != "") printf("Terminals: %s\n", terms.substr(1).cstr()); } } /*------------------------------------------------- main - primary entry point -------------------------------------------------*/ #if (!PSTANDALONE) #include "corealloc.h" #endif static const char *pmf_verbose[] = { "NL_PMF_TYPE_VIRTUAL", "NL_PMF_TYPE_GNUC_PMF", "NL_PMF_TYPE_GNUC_PMF_CONV", "NL_PMF_TYPE_INTERNAL" }; int main(int argc, char *argv[]) { #if (!PSTANDALONE) track_memory(true); { #endif tool_options_t opts; int ret; perr("{}", "WARNING: This is Work In Progress! - It may fail anytime\n"); perr("Update dispatching using method {}\n", pmf_verbose[NL_PMF_TYPE]); if ((ret = opts.parse(argc, argv)) != argc) { perr("Error parsing {}\n", argv[ret]); usage(opts); return 1; } if (opts.opt_help()) { usage(opts); return 1; } pstring cmd = opts.opt_cmd(); if (cmd == "listdevices") listdevices(); else if (cmd == "run") run(opts); else if (cmd == "convert") { pstring contents; postringstream ostrm; if (opts.opt_file() == "-") { pstdin f; ostrm.write(f); } else { pifilestream f(opts.opt_file()); ostrm.write(f); } contents = ostrm.str(); pstring result; if (opts.opt_type().equals("spice")) { nl_convert_spice_t c; c.convert(contents); result = c.result(); } else { nl_convert_eagle_t c; c.convert(contents); result = c.result(); } /* present result */ pout_strm.write(result.cstr()); } else { perr("Unknown command {}\n", cmd.cstr()); usage(opts); return 1; } pstring::resetmem(); #if (!PSTANDALONE) } dump_unfreed_mem(); #endif return 0; }