1 // license:GPL-2.0+
2 // copyright-holders:Couriersud
3
4 #include "plib/palloc.h"
5 #include "core/setup.h"
6 #include "devices/nlid_proxy.h"
7 #include "devices/nlid_truthtable.h"
8 #include "nl_base.h"
9 #include "nl_errstr.h"
10 #include "nl_factory.h"
11 #include "nl_parser.h"
12 #include "nl_setup.h"
13 #include "plib/penum.h"
14 #include "plib/pstonum.h"
15 #include "plib/pstrutil.h"
16 #include "plib/putil.h"
17
18 #include "solver/nld_solver.h"
19
20 #include <sstream>
21
22 namespace netlist
23 {
24 // ----------------------------------------------------------------------------------------
25 // nl_parse_t
26 // ----------------------------------------------------------------------------------------
27
nlparse_t(log_type & log,detail::abstract_t & abstract)28 nlparse_t::nlparse_t(log_type &log, detail::abstract_t &abstract)
29 : m_abstract(abstract)
30 , m_log(log)
31 , m_frontier_cnt(0)
32 { }
33
register_alias(const pstring & alias,const pstring & out)34 void nlparse_t::register_alias(const pstring &alias, const pstring &out)
35 {
36 pstring alias_fqn = build_fqn(alias);
37 pstring out_fqn = build_fqn(out);
38 register_alias_nofqn(alias_fqn, out_fqn);
39 }
40
register_dip_alias_arr(const pstring & terms)41 void nlparse_t::register_dip_alias_arr(const pstring &terms)
42 {
43 const auto list(plib::psplit(terms,pstring(", ")));
44 if (list.empty() || (list.size() % 2) == 1)
45 {
46 log().fatal(MF_DIP_PINS_MUST_BE_AN_EQUAL_NUMBER_OF_PINS_1(build_fqn("")));
47 throw nl_exception(MF_DIP_PINS_MUST_BE_AN_EQUAL_NUMBER_OF_PINS_1(build_fqn("")));
48 }
49 std::size_t n = list.size();
50 for (std::size_t i = 0; i < n / 2; i++)
51 {
52 register_alias(plib::pfmt("{1}")(i+1), list[i * 2]);
53 register_alias(plib::pfmt("{1}")(n-i), list[i * 2 + 1]);
54 }
55 }
56
register_dev(const pstring & classname,std::initializer_list<const char * > more_parameters)57 void nlparse_t::register_dev(const pstring &classname,
58 std::initializer_list<const char *> more_parameters)
59 {
60 std::vector<pstring> params;
61 const auto *i(more_parameters.begin());
62 pstring name(*i);
63 ++i;
64 for (; i != more_parameters.end(); ++i)
65 {
66 params.emplace_back(*i);
67 }
68 register_dev(classname, name, params);
69 }
70
register_dev(const pstring & classname,const pstring & name,const std::vector<pstring> & params_and_connections,factory::element_t ** felem)71 void nlparse_t::register_dev(const pstring &classname, const pstring &name,
72 const std::vector<pstring> ¶ms_and_connections,
73 factory::element_t **felem)
74 {
75
76 auto *f = factory().factory_by_name(classname);
77
78 // make sure we parse macro library entries
79 // FIXME: this could be done here if e.g. f
80 // would have an indication that this is macro element.
81 if (f->type() == factory::element_type::MACRO)
82 {
83 namespace_push(name);
84 include(f->name());
85 namespace_pop();
86 }
87
88 pstring key = build_fqn(name);
89 if (device_exists(key))
90 {
91 log().fatal(MF_DEVICE_ALREADY_EXISTS_1(key));
92 throw nl_exception(MF_DEVICE_ALREADY_EXISTS_1(key));
93 }
94
95 m_abstract.m_device_factory.insert(m_abstract.m_device_factory.end(), {key, f});
96
97 auto paramlist = plib::psplit(f->param_desc(), ',');
98
99 if (!params_and_connections.empty())
100 {
101 auto ptok(params_and_connections.begin());
102 auto ptok_end(params_and_connections.end());
103
104 for (const pstring &tp : paramlist)
105 {
106 if (plib::startsWith(tp, "+"))
107 {
108 if (ptok == ptok_end)
109 {
110 auto err = MF_PARAM_COUNT_MISMATCH_2(name, params_and_connections.size());
111 log().fatal(err);
112 throw nl_exception(err);
113 //break;
114 }
115 pstring output_name = *ptok;
116 log().debug("Link: {1} {2}", tp, output_name);
117
118 register_link(name + "." + tp.substr(1), output_name);
119 ++ptok;
120 }
121 else if (plib::startsWith(tp, "@"))
122 {
123 pstring term = tp.substr(1);
124 log().debug("Link: {1} {2}", tp, term);
125
126 register_link(name + "." + term, term);
127 }
128 else
129 {
130 if (ptok == params_and_connections.end())
131 {
132 auto err = MF_PARAM_COUNT_MISMATCH_2(name, params_and_connections.size());
133 log().fatal(err);
134 throw nl_exception(err);
135 }
136 pstring paramfq = name + "." + tp;
137
138 log().debug("Defparam: {1}\n", paramfq);
139
140 register_param(paramfq, *ptok);
141
142 ++ptok;
143 }
144 }
145 if (ptok != params_and_connections.end())
146 {
147 MF_PARAM_COUNT_EXCEEDED_2 err(name, params_and_connections.size());
148 log().fatal(err);
149 throw nl_exception(err);
150 }
151 }
152 if (felem != nullptr)
153 *felem = f;
154 }
155
register_hint(const pstring & objname,const pstring & hintname)156 void nlparse_t::register_hint(const pstring &objname, const pstring &hintname)
157 {
158 const auto name = build_fqn(objname) + hintname;
159 if (!m_abstract.m_hints.insert({name, false}).second)
160 {
161 log().fatal(MF_ADDING_HINT_1(name));
162 throw nl_exception(MF_ADDING_HINT_1(name));
163 }
164 }
165
register_link(const pstring & sin,const pstring & sout)166 void nlparse_t::register_link(const pstring &sin, const pstring &sout)
167 {
168 register_link_fqn(build_fqn(plib::trim(sin)), build_fqn(plib::trim(sout)));
169 }
170
register_link_arr(const pstring & terms)171 void nlparse_t::register_link_arr(const pstring &terms)
172 {
173 const auto list(plib::psplit(terms,pstring(", ")));
174 if (list.size() < 2)
175 {
176 log().fatal(MF_NET_C_NEEDS_AT_LEAST_2_TERMINAL());
177 throw nl_exception(MF_NET_C_NEEDS_AT_LEAST_2_TERMINAL());
178 }
179 for (std::size_t i = 1; i < list.size(); i++)
180 {
181 register_link(list[0], list[i]);
182 }
183 }
184
include(const pstring & netlist_name)185 void nlparse_t::include(const pstring &netlist_name)
186 {
187 if (m_sources.for_all<source_netlist_t>([this, &netlist_name] (source_netlist_t *src)
188 {
189 return src->parse(*this, netlist_name);
190 }))
191 return;
192 log().fatal(MF_NOT_FOUND_IN_SOURCE_COLLECTION(netlist_name));
193 throw nl_exception(MF_NOT_FOUND_IN_SOURCE_COLLECTION(netlist_name));
194 }
195
namespace_push(const pstring & aname)196 void nlparse_t::namespace_push(const pstring &aname)
197 {
198 if (m_namespace_stack.empty())
199 //m_namespace_stack.push(netlist().name() + "." + aname);
200 m_namespace_stack.push(aname);
201 else
202 m_namespace_stack.push(m_namespace_stack.top() + "." + aname);
203 }
204
namespace_pop()205 void nlparse_t::namespace_pop()
206 {
207 m_namespace_stack.pop();
208 }
209
register_param_fp(const pstring & param,const nl_fptype value)210 void nlparse_t::register_param_fp(const pstring ¶m, const nl_fptype value)
211 {
212 if (plib::abs(value - plib::floor(value)) > nlconst::magic(1e-30)
213 || plib::abs(value) > nlconst::magic(1e9))
214 register_param(param, plib::pfmt("{1:.9}").e(value));
215 else
216 register_param(param, plib::pfmt("{1}")(gsl::narrow<long>(value)));
217 }
218
register_param(const pstring & param,const pstring & value)219 void nlparse_t::register_param(const pstring ¶m, const pstring &value)
220 {
221 pstring fqn = build_fqn(param);
222 pstring val(value);
223
224 // strip " from stringified strings
225 if (plib::startsWith(value, "\"") && plib::endsWith(value, "\""))
226 val = value.substr(1, value.length() - 2);
227
228 // Replace "@." with the current namespace
229 val = plib::replace_all(val, "@.", namespace_prefix());
230 auto idx = m_abstract.m_param_values.find(fqn);
231 if (idx == m_abstract.m_param_values.end())
232 {
233 if (!m_abstract.m_param_values.insert({fqn, val}).second)
234 {
235 log().fatal(MF_ADDING_PARAMETER_1_TO_PARAMETER_LIST(param));
236 throw nl_exception(MF_ADDING_PARAMETER_1_TO_PARAMETER_LIST(param));
237 }
238 }
239 else
240 {
241 if (idx->second.find("$(") == pstring::npos)
242 {
243 //* There may be reason ... so make it an INFO
244 log().info(MI_OVERWRITING_PARAM_1_OLD_2_NEW_3(fqn,
245 idx->second, val));
246 }
247 m_abstract.m_param_values[fqn] = val;
248 }
249 }
250
register_defparam(const pstring & name,const pstring & def)251 void nlparse_t::register_defparam(const pstring &name, const pstring &def)
252 {
253 // strip " from stringified strings
254 pstring val(def);
255 if (plib::startsWith(def, "\"") && plib::endsWith(def, "\""))
256 val = def.substr(1, def.length() - 2);
257 // Replace "@." with the current namespace
258 val = plib::replace_all(val, "@.", namespace_prefix());
259 m_abstract.m_defparams.emplace_back(namespace_prefix() + name, val);
260 }
261
factory()262 factory::list_t &nlparse_t::factory() noexcept
263 {
264 return m_abstract.m_factory;
265 }
266
factory() const267 const factory::list_t &nlparse_t::factory() const noexcept
268 {
269 return m_abstract.m_factory;
270 }
271
272
register_lib_entry(const pstring & name,const pstring & def_params,plib::source_location && loc)273 void nlparse_t::register_lib_entry(const pstring &name, const pstring &def_params, plib::source_location &&loc)
274 {
275 factory().add(plib::make_unique<factory::library_element_t, host_arena>(name, factory::properties(def_params, std::move(loc))));
276 }
277
register_frontier(const pstring & attach,const pstring & r_IN,const pstring & r_OUT)278 void nlparse_t::register_frontier(const pstring &attach, const pstring &r_IN,
279 const pstring &r_OUT)
280 {
281 pstring frontier_name = plib::pfmt("frontier_{1}")(m_frontier_cnt);
282 m_frontier_cnt++;
283 register_dev("FRONTIER_DEV", frontier_name);
284 register_param(frontier_name + ".RIN", r_IN);
285 register_param(frontier_name + ".ROUT", r_OUT);
286 register_link(frontier_name + ".G", "GND");
287 pstring attfn = build_fqn(attach);
288 pstring front_fqn = build_fqn(frontier_name);
289 bool found = false;
290 for (auto & link : m_abstract.m_links)
291 {
292 if (link.first == attfn)
293 {
294 link.first = front_fqn + ".I";
295 found = true;
296 }
297 else if (link.second == attfn)
298 {
299 link.second = front_fqn + ".I";
300 found = true;
301 }
302 }
303 if (!found)
304 {
305 log().fatal(MF_FOUND_NO_OCCURRENCE_OF_1(attach));
306 throw nl_exception(MF_FOUND_NO_OCCURRENCE_OF_1(attach));
307 }
308 register_link(attach, frontier_name + ".Q");
309 }
310
register_source_proc(const pstring & name,nlsetup_func func)311 void nlparse_t::register_source_proc(const pstring &name, nlsetup_func func)
312 {
313 register_source<netlist::source_proc_t>(name, func);
314 }
315
truthtable_create(tt_desc & desc,const pstring & def_params,plib::source_location && loc)316 void nlparse_t::truthtable_create(tt_desc &desc, const pstring &def_params, plib::source_location &&loc)
317 {
318 auto fac = factory::truthtable_create(desc, netlist::factory::properties(def_params, std::move(loc)));
319 factory().add(std::move(fac));
320 }
321
namespace_prefix() const322 pstring nlparse_t::namespace_prefix() const
323 {
324 return (m_namespace_stack.empty() ? "" : m_namespace_stack.top() + ".");
325 }
326
build_fqn(const pstring & obj_name) const327 pstring nlparse_t::build_fqn(const pstring &obj_name) const
328 {
329 return namespace_prefix() + obj_name;
330 }
331
register_alias_nofqn(const pstring & alias,const pstring & out)332 void nlparse_t::register_alias_nofqn(const pstring &alias, const pstring &out)
333 {
334 if (!m_abstract.m_alias.insert({alias, out}).second)
335 {
336 log().fatal(MF_ALIAS_ALREAD_EXISTS_1(alias));
337 throw nl_exception(MF_ALIAS_ALREAD_EXISTS_1(alias));
338 }
339 }
340
register_link_fqn(const pstring & sin,const pstring & sout)341 void nlparse_t::register_link_fqn(const pstring &sin, const pstring &sout)
342 {
343 detail::abstract_t::link_t temp(sin, sout);
344 log().debug("link {1} <== {2}", sin, sout);
345 m_abstract.m_links.push_back(temp);
346 }
347
device_exists(const pstring & name) const348 bool nlparse_t::device_exists(const pstring &name) const
349 {
350 for (const auto &d : m_abstract.m_device_factory)
351 if (d.first == name)
352 return true;
353 return false;
354 }
355
parse_tokens(const plib::detail::token_store & tokens,const pstring & name)356 bool nlparse_t::parse_tokens(const plib::detail::token_store &tokens, const pstring &name)
357 {
358 parser_t parser(*this);
359 return parser.parse(tokens, name);
360 }
361
parse_stream(plib::istream_uptr && istrm,const pstring & name)362 bool nlparse_t::parse_stream(plib::istream_uptr &&istrm, const pstring &name)
363 {
364 #if 0
365 auto key = istrm.filename();
366
367 if (m_source_cache.find(key) != m_source_cache.end())
368 {
369 return parser_t(*this).parse(m_source_cache[key], name);
370 }
371 else
372 {
373 auto preprocessed = std::make_unique<std::stringstream>(
374 plib::ppreprocessor(m_includes, &m_defines).process(std::move(istrm), istrm.filename()));
375
376 parser_t::token_store &st = m_source_cache[key];
377 parser_t parser(*this);
378 parser.parse_tokens(plib::istream_uptr(std::move(preprocessed), key), st);
379 return parser.parse(st, name);
380 }
381 #else
382 const auto filename = istrm.filename();
383 auto preprocessed = std::make_unique<std::stringstream>(putf8string(
384 plib::ppreprocessor(m_includes, &m_defines).process(std::move(istrm), filename)));
385
386 parser_t::token_store st;
387 parser_t parser(*this);
388 parser.parse_tokens(plib::istream_uptr(std::move(preprocessed), filename), st);
389 return parser.parse(st, name);
390 #endif
391 }
392
add_define(const pstring & defstr)393 void nlparse_t::add_define(const pstring &defstr)
394 {
395 auto p = defstr.find('=');
396 if (p != pstring::npos)
397 add_define(plib::left(defstr, p), defstr.substr(p+1));
398 else
399 add_define(defstr, "1");
400 }
401
register_dynamic_log_devices(const std::vector<pstring> & loglist)402 void nlparse_t::register_dynamic_log_devices(const std::vector<pstring> &loglist)
403 {
404 log().debug("Creating dynamic logs ...");
405 for (const pstring &ll : loglist)
406 {
407 pstring name = "log_" + ll;
408
409 register_dev("LOG", name);
410 register_link(name + ".I", ll);
411 }
412 }
413
remove_connections(const pstring & pin)414 void nlparse_t::remove_connections(const pstring &pin)
415 {
416 // FIXME: check use_cases - remove_connections may be dead
417 pstring pinfn = build_fqn(pin);
418 bool found = false;
419
420 for (auto link = m_abstract.m_links.begin(); link != m_abstract.m_links.end(); )
421 {
422 if ((link->first == pinfn) || (link->second == pinfn))
423 {
424 log().verbose("removing connection: {1} <==> {2}\n", link->first, link->second);
425 link = m_abstract.m_links.erase(link);
426 found = true;
427 }
428 else
429 link++;
430 }
431 if (!found)
432 {
433 log().fatal(MF_FOUND_NO_OCCURRENCE_OF_1(pin));
434 throw nl_exception(MF_FOUND_NO_OCCURRENCE_OF_1(pin));
435 }
436 }
437
register_model(const pstring & model_in)438 void nlparse_t::register_model(const pstring &model_in)
439 {
440 auto pos = model_in.find(' ');
441 if (pos == pstring::npos)
442 throw nl_exception(MF_UNABLE_TO_PARSE_MODEL_1(model_in));
443 pstring model = plib::ucase(plib::trim(plib::left(model_in, pos)));
444 pstring def = plib::trim(model_in.substr(pos + 1));
445 if (!m_abstract.m_models.insert({model, def}).second)
446 {
447 // FIXME: Add an directive MODEL_OVERWRITE to netlist language
448 //throw nl_exception(MF_MODEL_ALREADY_EXISTS_1(model_in));
449 log().info(MI_MODEL_OVERWRITE_1(model, model_in));
450 m_abstract.m_models[model] = def;
451 }
452 }
453
454
455 // ----------------------------------------------------------------------------------------
456 // Sources
457 // ----------------------------------------------------------------------------------------
458
get_data_stream(const pstring & name)459 plib::istream_uptr nlparse_t::get_data_stream(const pstring &name)
460 {
461 auto strm = m_sources.get_stream<source_data_t>(name);
462 if (!strm.empty())
463 return strm;
464 log().warning(MW_DATA_1_NOT_FOUND(name));
465 return plib::istream_uptr();
466 }
467
468 // ----------------------------------------------------------------------------------------
469 // setup_t
470 // ----------------------------------------------------------------------------------------
471
472
setup_t(netlist_state_t & nlstate)473 setup_t::setup_t(netlist_state_t &nlstate)
474 : m_abstract(nlstate.log())
475 , m_parser(nlstate.log(), m_abstract)
476 , m_nlstate(nlstate)
477 , m_models(m_abstract.m_models) // FIXME : parse abstract_t only
478 , m_netlist_params(nullptr)
479 , m_proxy_cnt(0)
480 {
481 }
482
termtype_as_str(detail::core_terminal_t & in)483 pstring setup_t::termtype_as_str(detail::core_terminal_t &in)
484 {
485 switch (in.type())
486 {
487 case detail::terminal_type::TERMINAL:
488 return "TERMINAL";
489 case detail::terminal_type::INPUT:
490 return "INPUT";
491 case detail::terminal_type::OUTPUT:
492 return "OUTPUT";
493 }
494 return "Error"; // Tease gcc
495 }
496
get_initial_param_val(const pstring & name,const pstring & def) const497 pstring setup_t::get_initial_param_val(const pstring &name, const pstring &def) const
498 {
499 // when get_intial_param_val is called the parameter <name> is already registered
500 // and the value (valstr()) is set to the default value, e.g. "74XX"
501 // If thus $(IC5E.A.MODEL) is given for name=="IC5E.A.MODEL" valstr() below
502 // will return the default.
503 // FIXME: It may be more explicit and stable to test if pattern==name and return
504 // def in this case.
505
506 auto i = m_abstract.m_param_values.find(name);
507 auto found_pat(false);
508 pstring v = (i == m_abstract.m_param_values.end()) ? def : i->second;
509
510 do
511 {
512 found_pat = false;
513 auto sp(plib::psplit(v, std::vector<pstring>({"$(", ")"})));
514 std::size_t p(0);
515 v = "";
516 while (p < sp.size())
517 {
518 if (sp[p] == "$(")
519 {
520 p++;
521 pstring r;
522 while (p < sp.size() && sp[p] != ")")
523 r += sp[p++];
524 p++;
525 auto k = m_params.find(r);
526 if (k != m_params.end())
527 {
528 v = v + k->second.param().valstr();
529 found_pat = true;
530 }
531 else
532 {
533 // pass - on
534 v = v + "$(" + r + ")";
535 }
536 }
537 else
538 v += sp[p++];
539 }
540 } while (found_pat);
541
542 return v;
543 }
544
register_term(detail::core_terminal_t & term)545 void setup_t::register_term(detail::core_terminal_t &term)
546 {
547 log().debug("{1} {2}\n", termtype_as_str(term), term.name());
548 if (!m_terminals.insert({term.name(), &term}).second)
549 {
550 log().fatal(MF_ADDING_1_2_TO_TERMINAL_LIST(termtype_as_str(term), term.name()));
551 throw nl_exception(MF_ADDING_1_2_TO_TERMINAL_LIST(termtype_as_str(term), term.name()));
552 }
553 }
554
register_term(terminal_t & term,terminal_t * other_term,const std::array<terminal_t *,2> & splitter_terms)555 void setup_t::register_term(terminal_t &term, terminal_t *other_term, const std::array<terminal_t *, 2> &splitter_terms)
556 {
557 this->register_term(term);
558 m_connected_terminals.insert({&term, {other_term, splitter_terms[0], splitter_terms[1], nullptr}});
559 }
560
register_param_t(param_t & param)561 void setup_t::register_param_t(param_t ¶m)
562 {
563 if (!m_params.insert({param.name(), param_ref_t(param.device(), param)}).second)
564 {
565 log().fatal(MF_ADDING_PARAMETER_1_TO_PARAMETER_LIST(param.name()));
566 throw nl_exception(MF_ADDING_PARAMETER_1_TO_PARAMETER_LIST(param.name()));
567 }
568 }
569
resolve_alias(const pstring & name) const570 pstring setup_t::resolve_alias(const pstring &name) const
571 {
572 pstring temp = name;
573 pstring ret;
574
575 // FIXME: Detect endless loop
576 do {
577 ret = temp;
578 auto p = m_abstract.m_alias.find(ret);
579 temp = (p != m_abstract.m_alias.end() ? p->second : "");
580 } while (!temp.empty() && temp != ret);
581
582 log().debug("{1}==>{2}\n", name, ret);
583 return ret;
584 }
585
de_alias(const pstring & alias) const586 pstring setup_t::de_alias(const pstring &alias) const
587 {
588 pstring temp = alias;
589 pstring ret;
590
591 // FIXME: Detect endless loop
592 do {
593 ret = temp;
594 temp = "";
595 for (const auto &e : m_abstract.m_alias)
596 {
597 // FIXME: this will resolve first one found
598 if (e.second == ret)
599 {
600 temp = e.first;
601 break;
602 }
603 }
604 } while (!temp.empty() && temp != ret);
605
606 log().debug("{1}==>{2}\n", alias, ret);
607 return ret;
608 }
609
get_terminals_for_device_name(const pstring & devname) const610 std::vector<pstring> setup_t::get_terminals_for_device_name(const pstring &devname) const
611 {
612 std::vector<pstring> terms;
613 for (const auto & t : m_terminals)
614 {
615 if (plib::startsWith(t.second->name(), devname))
616 {
617 pstring tn(t.second->name().substr(devname.length()+1));
618 if (tn.find('.') == pstring::npos)
619 terms.push_back(tn);
620 }
621 }
622
623 for (const auto & t : m_abstract.m_alias)
624 {
625 if (plib::startsWith(t.first, devname))
626 {
627 pstring tn(t.first.substr(devname.length()+1));
628 if (tn.find('.') == pstring::npos)
629 {
630 terms.push_back(tn);
631 pstring resolved = resolve_alias(t.first);
632 if (resolved != t.first)
633 {
634 auto found = std::find(terms.begin(), terms.end(), resolved.substr(devname.length()+1));
635 if (found!=terms.end())
636 terms.erase(found);
637 }
638 }
639 }
640 }
641 return terms;
642 }
643
find_terminal(const pstring & terminal_in,bool required) const644 detail::core_terminal_t *setup_t::find_terminal(const pstring &terminal_in, bool required) const
645 {
646 const pstring &tname = resolve_alias(terminal_in);
647 auto ret = m_terminals.find(tname);
648 // look for default
649 if (ret == m_terminals.end())
650 {
651 // look for ".Q" std output
652 ret = m_terminals.find(tname + ".Q");
653 }
654
655 detail::core_terminal_t *term = (ret == m_terminals.end() ? nullptr : ret->second);
656
657 if (term == nullptr && required)
658 {
659 log().fatal(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname));
660 throw nl_exception(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname));
661 }
662 if (term != nullptr)
663 {
664 log().debug("Found input {1}\n", tname);
665 }
666
667 return term;
668 }
669
find_terminal(const pstring & terminal_in,detail::terminal_type atype,bool required) const670 detail::core_terminal_t *setup_t::find_terminal(const pstring &terminal_in,
671 detail::terminal_type atype, bool required) const
672 {
673 const pstring &tname = resolve_alias(terminal_in);
674 auto ret = m_terminals.find(tname);
675 // look for default
676 if (ret == m_terminals.end() && atype == detail::terminal_type::OUTPUT)
677 {
678 // look for ".Q" std output
679 ret = m_terminals.find(tname + ".Q");
680 }
681 if (ret == m_terminals.end() && required)
682 {
683 log().fatal(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname));
684 throw nl_exception(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname));
685 }
686 detail::core_terminal_t *term = (ret == m_terminals.end() ? nullptr : ret->second);
687
688 if (term != nullptr && term->type() != atype)
689 {
690 if (required)
691 {
692 log().fatal(MF_OBJECT_1_2_WRONG_TYPE(terminal_in, tname));
693 throw nl_exception(MF_OBJECT_1_2_WRONG_TYPE(terminal_in, tname));
694 }
695
696 term = nullptr;
697 }
698 if (term != nullptr)
699 log().debug("Found input {1}\n", tname);
700
701 return term;
702 }
703
find_param(const pstring & param_in) const704 param_ref_t setup_t::find_param(const pstring ¶m_in) const
705 {
706 const pstring outname(resolve_alias(param_in));
707 auto ret(m_params.find(outname));
708 if (ret == m_params.end())
709 {
710 log().fatal(MF_PARAMETER_1_2_NOT_FOUND(param_in, outname));
711 throw nl_exception(MF_PARAMETER_1_2_NOT_FOUND(param_in, outname));
712 }
713 return ret->second;
714 }
715
716
717 //NOLINTNEXTLINE(misc-no-recursion)
get_d_a_proxy(const detail::core_terminal_t & out)718 devices::nld_base_proxy *setup_t::get_d_a_proxy(const detail::core_terminal_t &out)
719 {
720 gsl_Expects(out.is_logic());
721
722 const auto &out_cast = dynamic_cast<const logic_output_t &>(out);
723 auto iter_proxy(m_proxies.find(&out));
724
725 if (iter_proxy != m_proxies.end())
726 return iter_proxy->second;
727
728 // create a new one ...
729 pstring x = plib::pfmt("proxy_da_{1}_{2}")(out.name())(m_proxy_cnt);
730 auto new_proxy =
731 out_cast.logic_family()->create_d_a_proxy(m_nlstate, x, &out_cast);
732 m_proxy_cnt++;
733 // connect all existing terminals to new net
734
735 for (auto & p : nlstate().core_terms(out.net()))
736 {
737 p->clear_net(); // de-link from all nets ...
738 if (!connect(new_proxy->proxy_term(), *p))
739 {
740 log().fatal(MF_CONNECTING_1_TO_2(
741 new_proxy->proxy_term().name(), (*p).name()));
742 throw nl_exception(MF_CONNECTING_1_TO_2(
743 new_proxy->proxy_term().name(), (*p).name()));
744 }
745 }
746 nlstate().core_terms(out.net()).clear();
747
748 add_terminal(out.net(), new_proxy->in());
749
750 auto *proxy(new_proxy.get());
751 if (!m_proxies.insert({&out, proxy}).second)
752 throw nl_exception(MF_DUPLICATE_PROXY_1(out.name()));
753
754 m_nlstate.register_device(new_proxy->name(), std::move(new_proxy));
755 return proxy;
756
757 }
758
759
760 //NOLINTNEXTLINE(misc-no-recursion)
get_a_d_proxy(detail::core_terminal_t & inp)761 devices::nld_base_proxy *setup_t::get_a_d_proxy(detail::core_terminal_t &inp)
762 {
763 gsl_Expects(inp.is_logic());
764
765 const auto &incast = dynamic_cast<const logic_input_t &>(inp);
766
767 auto iter_proxy(m_proxies.find(&inp));
768
769 if (iter_proxy != m_proxies.end())
770 return iter_proxy->second;
771
772 log().debug("connect_terminal_input: connecting proxy\n");
773 auto new_proxy = incast.logic_family()->create_a_d_proxy(m_nlstate,
774 plib::pfmt("proxy_ad_{1}_{2}")(inp.name())(m_proxy_cnt), &incast);
775
776 auto *ret(new_proxy.get());
777
778 if (!m_proxies.insert({&inp, ret}).second)
779 throw nl_exception(MF_DUPLICATE_PROXY_1(inp.name()));
780
781 m_proxy_cnt++;
782
783 // connect all existing terminals to new net
784
785 if (inp.has_net())
786 {
787 for (detail::core_terminal_t * p : nlstate().core_terms(inp.net()))
788 {
789 // inp may already belongs to the logic net. Thus skip it here.
790 // It will be removed by the clear further down.
791 if (p != &inp)
792 {
793 p->clear_net(); // de-link from all nets ...
794 if (!connect(ret->proxy_term(), *p))
795 {
796 log().fatal(MF_CONNECTING_1_TO_2(
797 ret->proxy_term().name(), (*p).name()));
798 throw nl_exception(MF_CONNECTING_1_TO_2(
799 ret->proxy_term().name(), (*p).name()));
800 }
801 }
802 }
803 nlstate().core_terms(inp.net()).clear(); // clear the list
804 }
805 inp.clear_net();
806 add_terminal(ret->out().net(), inp);
807 m_nlstate.register_device(new_proxy->name(), std::move(new_proxy));
808 return ret;
809 }
810
resolve_proxy(detail::core_terminal_t & term)811 detail::core_terminal_t &setup_t::resolve_proxy(detail::core_terminal_t &term)
812 {
813 if (term.is_logic())
814 {
815 const auto &out = dynamic_cast<const logic_t &>(term);
816 auto iter_proxy(m_proxies.find(&out));
817 if (iter_proxy != m_proxies.end())
818 return iter_proxy->second->proxy_term();
819 }
820 return term;
821 }
822
823
824 //NOLINTNEXTLINE(misc-no-recursion)
merge_nets(detail::net_t & thisnet,detail::net_t & othernet)825 void setup_t::merge_nets(detail::net_t &thisnet, detail::net_t &othernet)
826 {
827 log().debug("merging nets ...\n");
828 if (&othernet == &thisnet)
829 {
830 log().warning(MW_CONNECTING_1_TO_ITSELF(thisnet.name()));
831 return; // Nothing to do
832 }
833
834 if (thisnet.is_rail_net() && othernet.is_rail_net())
835 {
836 log().fatal(MF_MERGE_RAIL_NETS_1_AND_2(thisnet.name(), othernet.name()));
837 throw nl_exception(MF_MERGE_RAIL_NETS_1_AND_2(thisnet.name(), othernet.name()));
838 }
839
840 if (othernet.is_rail_net())
841 {
842 log().debug("othernet is railnet\n");
843 merge_nets(othernet, thisnet);
844 }
845 else
846 {
847 move_connections(othernet, thisnet);
848 }
849 }
850
851
852 //NOLINTNEXTLINE(misc-no-recursion)
connect_input_output(detail::core_terminal_t & in,detail::core_terminal_t & out)853 void setup_t::connect_input_output(detail::core_terminal_t &in, detail::core_terminal_t &out)
854 {
855 if (out.is_analog() && in.is_logic())
856 {
857 auto *proxy = get_a_d_proxy(in);
858
859 add_terminal(out.net(), proxy->proxy_term());
860 }
861 else if (out.is_logic() && in.is_analog())
862 {
863 devices::nld_base_proxy *proxy = get_d_a_proxy(out);
864
865 connect_terminals(proxy->proxy_term(), in);
866 //proxy->out().net().register_con(in);
867 }
868 else
869 {
870 if (in.has_net())
871 merge_nets(out.net(), in.net());
872 else
873 add_terminal(out.net(), in);
874 }
875 }
876
877
878 //NOLINTNEXTLINE(misc-no-recursion)
connect_terminal_input(terminal_t & term,detail::core_terminal_t & inp)879 void setup_t::connect_terminal_input(terminal_t &term, detail::core_terminal_t &inp)
880 {
881 if (inp.is_analog())
882 {
883 connect_terminals(inp, term);
884 }
885 else if (inp.is_logic())
886 {
887 log().verbose("connect terminal {1} (in, {2}) to {3}\n", inp.name(),
888 inp.is_analog() ? "analog" : inp.is_logic() ? "logic" : "?", term.name());
889 auto *proxy = get_a_d_proxy(inp);
890
891 //out.net().register_con(proxy->proxy_term());
892 connect_terminals(term, proxy->proxy_term());
893
894 }
895 else
896 {
897 log().fatal(MF_OBJECT_INPUT_TYPE_1(inp.name()));
898 throw nl_exception(MF_OBJECT_INPUT_TYPE_1(inp.name()));
899 }
900 }
901
902
903 //NOLINTNEXTLINE(misc-no-recursion)
connect_terminal_output(terminal_t & in,detail::core_terminal_t & out)904 void setup_t::connect_terminal_output(terminal_t &in, detail::core_terminal_t &out)
905 {
906 if (out.is_analog())
907 {
908 log().debug("connect_terminal_output: {1} {2}\n", in.name(), out.name());
909 // no proxy needed, just merge existing terminal net
910 if (in.has_net())
911 {
912 if (&out.net() != &in.net())
913 merge_nets(out.net(), in.net());
914 else
915 // Only an info - some ICs (CD4538) connect pins internally to GND
916 // and the schematics again externally. This will cause this warning.
917 // FIXME: Add a hint to suppress the warning.
918 log().info(MI_CONNECTING_1_TO_2_SAME_NET(in.name(), out.name(), in.net().name()));
919 }
920 else
921 add_terminal(out.net(), in);
922 }
923 else if (out.is_logic())
924 {
925 log().debug("connect_terminal_output: connecting proxy\n");
926 devices::nld_base_proxy *proxy = get_d_a_proxy(out);
927
928 connect_terminals(proxy->proxy_term(), in);
929 }
930 else
931 {
932 log().fatal(MF_OBJECT_OUTPUT_TYPE_1(out.name()));
933 throw nl_exception(MF_OBJECT_OUTPUT_TYPE_1(out.name()));
934 }
935 }
936
connect_terminals(detail::core_terminal_t & t1,detail::core_terminal_t & t2)937 void setup_t::connect_terminals(detail::core_terminal_t &t1,detail::core_terminal_t &t2)
938 {
939 if (t1.has_net() && t2.has_net())
940 {
941 log().debug("T2 and T1 have net\n");
942 merge_nets(t1.net(), t2.net());
943 }
944 else if (t2.has_net())
945 {
946 log().debug("T2 has net\n");
947 add_terminal(t2.net(), t1);
948 }
949 else if (t1.has_net())
950 {
951 log().debug("T1 has net\n");
952 add_terminal(t1.net(), t2);
953 }
954 else
955 {
956 log().debug("adding analog net ...\n");
957 // FIXME: Nets should have a unique name
958 auto anet = plib::make_owned<analog_net_t>(nlstate().pool(), m_nlstate,"net." + t1.name());
959 auto *anetp = anet.get();
960 m_nlstate.register_net(std::move(anet));
961 t1.set_net(anetp);
962 add_terminal(*anetp, t2);
963 add_terminal(*anetp, t1);
964 }
965 }
966
967 //NOLINTNEXTLINE(misc-no-recursion)
connect_input_input(detail::core_terminal_t & t1,detail::core_terminal_t & t2)968 bool setup_t::connect_input_input(detail::core_terminal_t &t1, detail::core_terminal_t &t2)
969 {
970 bool ret = false;
971 if (t1.has_net())
972 {
973 if (t1.net().is_rail_net())
974 ret = connect(t2, t1.net().railterminal());
975 if (!ret)
976 {
977 for (auto & t : nlstate().core_terms(t1.net()))
978 {
979 if (t->is_type(detail::terminal_type::TERMINAL))
980 ret = connect(t2, *t);
981 if (ret)
982 break;
983 }
984 }
985 }
986 if (!ret && t2.has_net())
987 {
988 if (t2.net().is_rail_net())
989 ret = connect(t1, t2.net().railterminal());
990 if (!ret)
991 {
992 for (auto & t : nlstate().core_terms(t2.net()))
993 {
994 if (t->is_type(detail::terminal_type::TERMINAL))
995 ret = connect(t1, *t);
996 if (ret)
997 break;
998 }
999 }
1000 }
1001 return ret;
1002 }
1003
1004
1005 //NOLINTNEXTLINE(misc-no-recursion)
connect(detail::core_terminal_t & t1_in,detail::core_terminal_t & t2_in)1006 bool setup_t::connect(detail::core_terminal_t &t1_in, detail::core_terminal_t &t2_in)
1007 {
1008 log().debug("Connecting {1} to {2}\n", t1_in.name(), t2_in.name());
1009 detail::core_terminal_t &t1 = resolve_proxy(t1_in);
1010 detail::core_terminal_t &t2 = resolve_proxy(t2_in);
1011 bool ret = true;
1012
1013 if (t1.is_type(detail::terminal_type::OUTPUT) && t2.is_type(detail::terminal_type::INPUT))
1014 {
1015 if (t2.has_net() && t2.net().is_rail_net())
1016 {
1017 log().fatal(MF_INPUT_1_ALREADY_CONNECTED(t2.name()));
1018 throw nl_exception(MF_INPUT_1_ALREADY_CONNECTED(t2.name()));
1019 }
1020 connect_input_output(t2, t1);
1021 }
1022 else if (t1.is_type(detail::terminal_type::INPUT) && t2.is_type(detail::terminal_type::OUTPUT))
1023 {
1024 if (t1.has_net() && t1.net().is_rail_net())
1025 {
1026 log().fatal(MF_INPUT_1_ALREADY_CONNECTED(t1.name()));
1027 throw nl_exception(MF_INPUT_1_ALREADY_CONNECTED(t1.name()));
1028 }
1029 connect_input_output(t1, t2);
1030 }
1031 else if (t1.is_type(detail::terminal_type::OUTPUT) && t2.is_type(detail::terminal_type::TERMINAL))
1032 {
1033 connect_terminal_output(dynamic_cast<terminal_t &>(t2), t1);
1034 }
1035 else if (t1.is_type(detail::terminal_type::TERMINAL) && t2.is_type(detail::terminal_type::OUTPUT))
1036 {
1037 connect_terminal_output(dynamic_cast<terminal_t &>(t1), t2);
1038 }
1039 else if (t1.is_type(detail::terminal_type::INPUT) && t2.is_type(detail::terminal_type::TERMINAL))
1040 {
1041 connect_terminal_input(dynamic_cast<terminal_t &>(t2), t1);
1042 }
1043 else if (t1.is_type(detail::terminal_type::TERMINAL) && t2.is_type(detail::terminal_type::INPUT))
1044 {
1045 connect_terminal_input(dynamic_cast<terminal_t &>(t1), t2);
1046 }
1047 else if (t1.is_type(detail::terminal_type::TERMINAL) && t2.is_type(detail::terminal_type::TERMINAL))
1048 {
1049 connect_terminals(dynamic_cast<terminal_t &>(t1), dynamic_cast<terminal_t &>(t2));
1050 }
1051 else if (t1.is_type(detail::terminal_type::INPUT) && t2.is_type(detail::terminal_type::INPUT))
1052 {
1053 ret = connect_input_input(t1, t2);
1054 }
1055 else
1056 {
1057 ret = false;
1058 }
1059 return ret;
1060 }
1061
resolve_inputs()1062 void setup_t::resolve_inputs()
1063 {
1064 log().verbose("Resolving inputs ...");
1065
1066 // Netlist can directly connect input to input.
1067 // We therefore first park connecting inputs and retry
1068 // after all other terminals were connected.
1069
1070 unsigned tries = m_netlist_params->m_max_link_loops();
1071 #if 0
1072 // This code fails for some netlists when the element at position 0
1073 // is deleted. It will fail somewhere deep in std::pair releasing
1074 // std::string called from erase.
1075 //
1076 // One example is the this netlist:
1077 //
1078 // #include "netlist/devices/net_lib.h"
1079 // NETLIST_START(charge_discharge)
1080 // SOLVER(solver, 48000) // Fixed frequency solver
1081 // CLOCK(I, 200) // 200 Hz clock as input, TTL logic output
1082 // RES(R, RES_K(1))
1083 // CAP(C, CAP_U(1))
1084 //
1085 // NET_C(I.Q, R.1)
1086 // NET_C(R.2, C.1)
1087 // NET_C(C.2, GND)
1088 //
1089 // ALIAS(O, R.2) // Output O == C.1 == R.2
1090 // // NETLIST_END()
1091 //
1092 // Just save the net list as /tmp/test1.cpp, run
1093 // ./nltool --cmd=run -t 0.05 -l O -l I /tmp/test1.cpp
1094 // and see it crash with this code enabled.
1095 //
1096 // g++-7 (Ubuntu 7.4.0-1ubuntu1~16.04~ppa1) 7.4.0
1097 //
1098 while (!m_links.empty() && tries > 0)
1099 {
1100 auto li = m_links.begin();
1101 while (li != m_links.end())
1102 {
1103 const pstring t1s = li->first;
1104 const pstring t2s = li->second;
1105 detail::core_terminal_t *t1 = find_terminal(t1s);
1106 detail::core_terminal_t *t2 = find_terminal(t2s);
1107
1108 if (connect(*t1, *t2))
1109 li = m_links.erase(li);
1110 else
1111 li++;
1112 }
1113 tries--;
1114 }
1115 #else
1116 while (!m_abstract.m_links.empty() && tries > 0)
1117 {
1118 for (std::size_t i = 0; i < m_abstract.m_links.size(); )
1119 {
1120 const pstring t1s(m_abstract.m_links[i].first);
1121 const pstring t2s(m_abstract.m_links[i].second);
1122 detail::core_terminal_t *t1 = find_terminal(t1s);
1123 detail::core_terminal_t *t2 = find_terminal(t2s);
1124 if (connect(*t1, *t2))
1125 m_abstract.m_links.erase(m_abstract.m_links.begin() + plib::narrow_cast<std::ptrdiff_t>(i));
1126 else
1127 i++;
1128 }
1129 tries--;
1130 }
1131 #endif
1132 if (tries == 0)
1133 {
1134 for (auto & link : m_abstract.m_links)
1135 log().warning(MF_CONNECTING_1_TO_2(de_alias(link.first), de_alias(link.second)));
1136
1137 log().fatal(MF_LINK_TRIES_EXCEEDED(m_netlist_params->m_max_link_loops()));
1138 throw nl_exception(MF_LINK_TRIES_EXCEEDED(m_netlist_params->m_max_link_loops()));
1139 }
1140
1141 log().verbose("deleting empty nets ...");
1142
1143 // delete empty nets
1144
1145 delete_empty_nets();
1146
1147 bool err(false);
1148
1149 log().verbose("looking for terminals not connected ...");
1150 for (auto & i : m_terminals)
1151 {
1152 detail::core_terminal_t *term = i.second;
1153 const pstring name_da = de_alias(term->name());
1154 bool is_nc_pin(dynamic_cast< devices::NETLIB_NAME(nc_pin) *>(&term->device()) != nullptr);
1155 bool is_nc_flagged(false);
1156
1157 auto hnc = m_abstract.m_hints.find(name_da + sHINT_NC);
1158 if (hnc != m_abstract.m_hints.end())
1159 {
1160 hnc->second = true; // mark as used
1161 is_nc_flagged = true;
1162 }
1163
1164 if (term->has_net() && is_nc_pin)
1165 {
1166 log().error(ME_NC_PIN_1_WITH_CONNECTIONS(name_da));
1167 err = true;
1168 }
1169 else if (is_nc_pin)
1170 {
1171 /* ignore */
1172 }
1173 else if (!term->has_net())
1174 {
1175 log().error(ME_TERMINAL_1_WITHOUT_NET(name_da));
1176 err = true;
1177 }
1178 else if (nlstate().core_terms(term->net()).empty())
1179 {
1180 if (term->is_logic_input())
1181 log().warning(MW_LOGIC_INPUT_1_WITHOUT_CONNECTIONS(name_da));
1182 else if (term->is_logic_output())
1183 {
1184 if (!is_nc_flagged)
1185 log().info(MI_LOGIC_OUTPUT_1_WITHOUT_CONNECTIONS(name_da));
1186 }
1187 else if (term->is_analog_output())
1188 {
1189 if (!is_nc_flagged)
1190 log().info(MI_ANALOG_OUTPUT_1_WITHOUT_CONNECTIONS(name_da));
1191 }
1192 else
1193 log().warning(MW_TERMINAL_1_WITHOUT_CONNECTIONS(name_da));
1194 }
1195 }
1196
1197 log().verbose("checking tristate consistency ...");
1198 for (auto & i : m_terminals)
1199 {
1200 detail::core_terminal_t *term = i.second;
1201 if (term->is_tristate_output())
1202 {
1203 const auto &tri(dynamic_cast<tristate_output_t &>(*term));
1204 // check if we are connected to a proxy
1205 const auto iter_proxy(m_proxies.find(&tri));
1206
1207 if (iter_proxy == m_proxies.end() && !tri.is_force_logic())
1208 {
1209 log().error(ME_TRISTATE_NO_PROXY_FOUND_2(term->name(), term->device().name()));
1210 err = true;
1211 }
1212 else if (iter_proxy != m_proxies.end() && tri.is_force_logic())
1213 {
1214 log().error(ME_TRISTATE_PROXY_FOUND_2(term->name(), term->device().name()));
1215 err = true;
1216 }
1217 }
1218 }
1219 if (err)
1220 {
1221 log().fatal(MF_TERMINALS_WITHOUT_NET());
1222 throw nl_exception(MF_TERMINALS_WITHOUT_NET());
1223 }
1224
1225 }
1226
add_terminal(detail::net_t & net,detail::core_terminal_t & terminal)1227 void setup_t::add_terminal(detail::net_t &net, detail::core_terminal_t &terminal) noexcept(false)
1228 {
1229 for (auto &t : nlstate().core_terms(net))
1230 if (t == &terminal)
1231 {
1232 log().fatal(MF_NET_1_DUPLICATE_TERMINAL_2(net.name(), t->name()));
1233 throw nl_exception(MF_NET_1_DUPLICATE_TERMINAL_2(net.name(), t->name()));
1234 }
1235
1236 terminal.set_net(&net);
1237
1238 nlstate().core_terms(net).push_back(&terminal);
1239 }
1240
remove_terminal(detail::net_t & net,detail::core_terminal_t & terminal)1241 void setup_t::remove_terminal(detail::net_t &net, detail::core_terminal_t &terminal) noexcept(false)
1242 {
1243 if (plib::container::contains(nlstate().core_terms(net), &terminal))
1244 {
1245 terminal.set_net(nullptr);
1246 plib::container::remove(nlstate().core_terms(net), &terminal);
1247 }
1248 else
1249 {
1250 log().fatal(MF_REMOVE_TERMINAL_1_FROM_NET_2(terminal.name(), net.name()));
1251 throw nl_exception(MF_REMOVE_TERMINAL_1_FROM_NET_2(terminal.name(), net.name()));
1252 }
1253 }
1254
move_connections(detail::net_t & net,detail::net_t & dest_net)1255 void setup_t::move_connections(detail::net_t &net, detail::net_t &dest_net)
1256 {
1257 for (auto &ct : nlstate().core_terms(net))
1258 add_terminal(dest_net, *ct);
1259 nlstate().core_terms(net).clear();
1260 }
1261
1262
1263
log()1264 log_type &setup_t::log() noexcept { return m_nlstate.log(); }
log() const1265 const log_type &setup_t::log() const noexcept { return m_nlstate.log(); }
1266
1267 // ----------------------------------------------------------------------------------------
1268 // Models
1269 // ----------------------------------------------------------------------------------------
1270
1271
1272 //NOLINTNEXTLINE(misc-no-recursion)
model_parse(const pstring & model_in,map_t & map)1273 void models_t::model_parse(const pstring &model_in, map_t &map)
1274 {
1275 pstring model = model_in;
1276 std::size_t pos = 0;
1277 pstring key;
1278
1279 while (true)
1280 {
1281 pos = model.find('(');
1282 if (pos != pstring::npos) break;
1283
1284 key = plib::ucase(model);
1285 auto i = m_models.find(key);
1286 if (i == m_models.end())
1287 {
1288 throw nl_exception(MF_MODEL_NOT_FOUND(pstring("xx") + model));
1289 }
1290
1291 model = i->second;
1292 }
1293 pstring xmodel = plib::left(model, pos);
1294
1295 if (xmodel == "_")
1296 map["COREMODEL"] = key;
1297 else
1298 {
1299 auto i = m_models.find(xmodel);
1300 if (i != m_models.end())
1301 model_parse(xmodel, map);
1302 else
1303 throw nl_exception(MF_MODEL_NOT_FOUND(model_in));
1304 }
1305
1306 pstring remainder = plib::trim(model.substr(pos + 1));
1307 if (!plib::endsWith(remainder, ")"))
1308 throw nl_exception(MF_MODEL_ERROR_1(model));
1309 // FIMXE: Not optimal
1310 remainder = plib::left(remainder, remainder.length() - 1);
1311
1312 const auto pairs(plib::psplit(remainder,' ', true));
1313 for (const pstring &pe : pairs)
1314 {
1315 auto pose = pe.find('=');
1316 if (pose == pstring::npos)
1317 throw nl_exception(MF_MODEL_ERROR_ON_PAIR_1(model));
1318 map[plib::ucase(plib::left(pe, pose))] = pe.substr(pose + 1);
1319 }
1320 }
1321
model_string(const map_t & map)1322 pstring models_t::model_t::model_string(const map_t &map)
1323 {
1324 // operator [] has no const implementation
1325 pstring ret = map.at("COREMODEL") + "(";
1326 for (const auto & i : map)
1327 ret += (i.first + '=' + i.second + ' ');
1328
1329 return ret + ")";
1330 }
1331
get_model(const pstring & model)1332 models_t::model_t models_t::get_model(const pstring &model)
1333 {
1334 map_t &map = m_cache[model];
1335
1336 if (map.empty())
1337 model_parse(model , map);
1338
1339 return model_t(model, map);
1340 }
1341
value_str(const pstring & entity) const1342 pstring models_t::model_t::value_str(const pstring &entity) const
1343 {
1344 if (entity != plib::ucase(entity))
1345 throw nl_exception(MF_MODEL_PARAMETERS_NOT_UPPERCASE_1_2(entity, model_string(m_map)));
1346 const auto it(m_map.find(entity));
1347 if ( it == m_map.end())
1348 throw nl_exception(MF_ENTITY_1_NOT_FOUND_IN_MODEL_2(entity, model_string(m_map)));
1349
1350 return it->second;
1351 }
1352
value(const pstring & entity) const1353 nl_fptype models_t::model_t::value(const pstring &entity) const
1354 {
1355 pstring tmp = value_str(entity);
1356
1357 nl_fptype factor = nlconst::one();
1358 auto p = std::next(tmp.begin(), plib::narrow_cast<pstring::difference_type>(tmp.length() - 1));
1359 switch (*p)
1360 {
1361 case 'M': factor = nlconst::magic(1e6); break; // NOLINT
1362 case 'k':
1363 case 'K': factor = nlconst::magic(1e3); break; // NOLINT
1364 case 'm': factor = nlconst::magic(1e-3); break; // NOLINT
1365 case 'u': factor = nlconst::magic(1e-6); break; // NOLINT
1366 case 'n': factor = nlconst::magic(1e-9); break; // NOLINT
1367 case 'p': factor = nlconst::magic(1e-12); break; // NOLINT
1368 case 'f': factor = nlconst::magic(1e-15); break; // NOLINT
1369 case 'a': factor = nlconst::magic(1e-18); break; // NOLINT
1370 default:
1371 if (*p < '0' || *p > '9')
1372 throw nl_exception(MF_UNKNOWN_NUMBER_FACTOR_IN_2(m_model, entity));
1373 }
1374 if (factor != nlconst::one())
1375 tmp = plib::left(tmp, tmp.length() - 1);
1376 // FIXME: check for errors
1377 bool err(false);
1378 auto val = plib::pstonum_ne<nl_fptype>(tmp, err);
1379 if (err)
1380 throw nl_exception(MF_MODEL_NUMBER_CONVERSION_ERROR(entity, tmp, "double", m_model));
1381 return val * factor;
1382 }
1383
1384
1385 // FIXME: all this belongs elsewhere
1386
1387 PENUM(family_type,
1388 CUSTOM,
1389 TTL,
1390 MOS,
1391 CMOS,
1392 NMOS,
1393 PMOS)
1394
1395 class logic_family_std_proxy_t : public logic_family_desc_t
1396 {
1397 public:
logic_family_std_proxy_t(family_type ft)1398 logic_family_std_proxy_t(family_type ft)
1399 : m_family_type(ft)
1400 {
1401 }
1402
1403 // FIXME: create proxies based on family type (far future)
create_d_a_proxy(netlist_state_t & anetlist,const pstring & name,const logic_output_t * proxied) const1404 device_arena::unique_ptr<devices::nld_base_d_to_a_proxy> create_d_a_proxy(netlist_state_t &anetlist,
1405 const pstring &name, const logic_output_t *proxied) const override
1406 {
1407 switch(m_family_type)
1408 {
1409 case family_type::CUSTOM:
1410 case family_type::TTL:
1411 case family_type::MOS:
1412 case family_type::CMOS:
1413 case family_type::NMOS:
1414 case family_type::PMOS:
1415 return anetlist.make_pool_object<devices::nld_d_to_a_proxy>(anetlist, name, proxied);
1416 }
1417 return anetlist.make_pool_object<devices::nld_d_to_a_proxy>(anetlist, name, proxied);
1418 }
1419
create_a_d_proxy(netlist_state_t & anetlist,const pstring & name,const logic_input_t * proxied) const1420 device_arena::unique_ptr<devices::nld_base_a_to_d_proxy> create_a_d_proxy(netlist_state_t &anetlist, const pstring &name, const logic_input_t *proxied) const override
1421 {
1422 switch(m_family_type)
1423 {
1424 case family_type::CUSTOM:
1425 case family_type::TTL:
1426 case family_type::MOS:
1427 case family_type::CMOS:
1428 case family_type::NMOS:
1429 case family_type::PMOS:
1430 return anetlist.make_pool_object<devices::nld_a_to_d_proxy>(anetlist, name, proxied);
1431 }
1432 return anetlist.make_pool_object<devices::nld_a_to_d_proxy>(anetlist, name, proxied);
1433 }
1434 private:
1435 family_type m_family_type;
1436 };
1437
1438
1439 /// \brief Class representing the logic families.
1440 ///
1441 /// This is the model representation of the logic families. This is a
1442 /// netlist specific model. Examples give values for TTL family
1443 ///
1444 //
1445 /// |NL? |name |parameter |units| TTL |
1446 /// |:--:|:-----|:----------------------------------------------------------|:----|------:|
1447 /// | Y |IVL |Input voltage low threshold relative to supply voltage | |1.0e-14|
1448 /// | Y |IVH |Input voltage high threshold relative to supply voltage | | 0|
1449 /// | Y |OVL |Output voltage minimum voltage relative to supply voltage | |1.0e-14|
1450 /// | Y |OVL |Output voltage maximum voltage relative to supply voltage | |1.0e-14|
1451 /// | Y |ORL |Output output resistance for logic 0 | | 0|
1452 /// | Y |ORH |Output output resistance for logic 1 | | 0|
1453 ///
1454 class family_model_t
1455 {
1456 public:
1457 template <typename P>
family_model_t(P & model)1458 family_model_t(P &model)
1459 : m_TYPE(model, "TYPE")
1460 , m_IVL(model, "IVL")
1461 , m_IVH(model, "IVH")
1462 , m_OVL(model, "OVL")
1463 , m_OVH(model, "OVH")
1464 , m_ORL(model, "ORL")
1465 , m_ORH(model, "ORH")
1466 {}
1467
1468 param_model_t::value_str_t m_TYPE; //!< Family type (TTL, CMOS, ...)
1469 param_model_t::value_t m_IVL; //!< Input voltage low threshold relative to supply voltage
1470 param_model_t::value_t m_IVH; //!< Input voltage high threshold relative to supply voltage
1471 param_model_t::value_t m_OVL; //!< Output voltage minimum voltage relative to supply voltage
1472 param_model_t::value_t m_OVH; //!< Output voltage maximum voltage relative to supply voltage
1473 param_model_t::value_t m_ORL; //!< Output output resistance for logic 0
1474 param_model_t::value_t m_ORH; //!< Output output resistance for logic 1
1475 };
1476
1477
family_from_model(const pstring & model)1478 const logic_family_desc_t *setup_t::family_from_model(const pstring &model)
1479 {
1480 family_type ft(family_type::CUSTOM);
1481
1482 auto mod(m_models.get_model(model));
1483 family_model_t modv(mod);
1484
1485 if (!ft.set_from_string(modv.m_TYPE()))
1486 throw nl_exception(MF_UNKNOWN_FAMILY_TYPE_1(modv.m_TYPE(), model));
1487
1488 auto it = m_nlstate.family_cache().find(model);
1489 if (it != m_nlstate.family_cache().end())
1490 return it->second.get();
1491
1492 auto ret = plib::make_unique<logic_family_std_proxy_t, host_arena>(ft);
1493
1494 ret->m_low_thresh_PCNT = modv.m_IVL();
1495 ret->m_high_thresh_PCNT = modv.m_IVH();
1496 ret->m_low_VO = modv.m_OVL();
1497 ret->m_high_VO = modv.m_OVH();
1498 ret->m_R_low = modv.m_ORL();
1499 ret->m_R_high = modv.m_ORH();
1500
1501 switch (ft)
1502 {
1503 case family_type::CUSTOM:
1504 case family_type::TTL:
1505 case family_type::NMOS:
1506 ret->m_vcc = "VCC";
1507 ret->m_gnd = "GND";
1508 break;
1509 case family_type::MOS:
1510 case family_type::CMOS:
1511 case family_type::PMOS:
1512 ret->m_vcc = "VDD";
1513 ret->m_gnd = "VSS";
1514 break;
1515 }
1516
1517 auto *retp = ret.get();
1518
1519 m_nlstate.family_cache().emplace(model, std::move(ret));
1520
1521 return retp;
1522 }
1523
1524 // ----------------------------------------------------------------------------------------
1525 // Device handling
1526 // ----------------------------------------------------------------------------------------
1527
delete_empty_nets()1528 void setup_t::delete_empty_nets()
1529 {
1530 m_nlstate.nets().erase(
1531 std::remove_if(m_nlstate.nets().begin(), m_nlstate.nets().end(),
1532 [](device_arena::owned_ptr<detail::net_t> &net)
1533 {
1534 if (net->state().core_terms(*net).empty())
1535 {
1536 // FIXME: need to remove from state->m_core_terms as well.
1537 net->state().log().verbose("Deleting net {1} ...", net->name());
1538 net->state().run_state_manager().remove_save_items(net.get());
1539 return true;
1540 }
1541 return false;
1542 }), m_nlstate.nets().end());
1543 }
1544
1545 // ----------------------------------------------------------------------------------------
1546 // Run preparation
1547 // ----------------------------------------------------------------------------------------
1548
prepare_to_run()1549 void setup_t::prepare_to_run()
1550 {
1551 pstring envlog = plib::util::environment("NL_LOGS", "");
1552
1553 if (!envlog.empty())
1554 {
1555 const auto loglist(plib::psplit(envlog, ':'));
1556 m_parser.register_dynamic_log_devices(loglist);
1557 }
1558
1559 // create defparams first!
1560
1561 for (auto & e : m_abstract.m_defparams)
1562 {
1563 auto param(plib::make_unique<param_str_t, host_arena>(nlstate(), e.first, e.second));
1564 register_param_t(*param);
1565 m_defparam_lifetime.push_back(std::move(param));
1566 }
1567
1568 // make sure the solver and parameters are started first!
1569
1570 for (auto & e : m_abstract.m_device_factory)
1571 {
1572 if ( m_parser.factory().is_class<devices::NETLIB_NAME(solver)>(e.second)
1573 || m_parser.factory().is_class<devices::NETLIB_NAME(netlistparams)>(e.second))
1574 {
1575 m_nlstate.register_device(e.first, e.second->make_device(nlstate().pool(), m_nlstate, e.first));
1576 }
1577 }
1578
1579 log().debug("Searching for solver and parameters ...\n");
1580
1581 auto *solver = m_nlstate.get_single_device<devices::NETLIB_NAME(solver)>("solver");
1582 m_netlist_params = m_nlstate.get_single_device<devices::NETLIB_NAME(netlistparams)>("parameter");
1583
1584 // set default model parameters
1585
1586 // FIXME: this is not optimal
1587 m_parser.register_model(plib::pfmt("NMOS_DEFAULT _(CAPMOD={1})")(m_netlist_params->m_mos_capmodel()));
1588 m_parser.register_model(plib::pfmt("PMOS_DEFAULT _(CAPMOD={1})")(m_netlist_params->m_mos_capmodel()));
1589
1590 // create devices
1591
1592 log().debug("Creating devices ...\n");
1593 for (auto & e : m_abstract.m_device_factory)
1594 {
1595 if ( !m_parser.factory().is_class<devices::NETLIB_NAME(solver)>(e.second)
1596 && !m_parser.factory().is_class<devices::NETLIB_NAME(netlistparams)>(e.second))
1597 {
1598 auto dev = e.second->make_device(m_nlstate.pool(), m_nlstate, e.first);
1599 m_nlstate.register_device(dev->name(), std::move(dev));
1600 }
1601 }
1602
1603 int errcnt(0);
1604
1605 const bool use_deactivate = m_netlist_params->m_use_deactivate();
1606
1607 for (auto &d : m_nlstate.devices())
1608 {
1609 auto p = m_abstract.m_hints.find(d.second->name() + sHINT_NO_DEACTIVATE);
1610 if (p != m_abstract.m_hints.end())
1611 {
1612 p->second = true; // mark as used
1613 d.second->set_hint_deactivate(false);
1614 }
1615 else
1616 d.second->set_hint_deactivate(use_deactivate);
1617 }
1618
1619 if (errcnt > 0)
1620 {
1621 log().fatal(MF_ERRORS_FOUND(errcnt));
1622 throw nl_exception(MF_ERRORS_FOUND(errcnt));
1623 }
1624
1625 // resolve inputs
1626 resolve_inputs();
1627
1628 log().verbose("looking for two terms connected to rail nets ...");
1629 for (auto & t : m_nlstate.get_device_list<analog::NETLIB_NAME(twoterm)>())
1630 {
1631 if (t->N().net().is_rail_net() && t->P().net().is_rail_net())
1632 {
1633 log().info(MI_REMOVE_DEVICE_1_CONNECTED_ONLY_TO_RAILS_2_3(
1634 t->name(), t->N().net().name(), t->P().net().name()));
1635 // The following would remove internal devices in e.g. MOSFETs as well.
1636 #if 0
1637 remove_terminal(t->setup_N().net(), t->setup_N());
1638 remove_terminal(t->setup_P().net(), t->setup_P());
1639 m_nlstate.remove_device(t);
1640 #endif
1641 }
1642 }
1643
1644 log().verbose("looking for unused hints ...");
1645 for (auto &h : m_abstract.m_hints)
1646 {
1647 if (!h.second)
1648 {
1649 log().fatal(MF_UNUSED_HINT_1(h.first));
1650 throw nl_exception(MF_UNUSED_HINT_1(h.first));
1651 }
1652 }
1653
1654 log().verbose("initialize solver ...\n");
1655
1656 if (solver == nullptr)
1657 {
1658 for (auto &p : m_nlstate.nets())
1659 if (p->is_analog())
1660 {
1661 log().fatal(MF_NO_SOLVER());
1662 throw nl_exception(MF_NO_SOLVER());
1663 }
1664 }
1665 else
1666 solver->post_start();
1667
1668 errcnt = 0;
1669 log().debug("Looking for unknown parameters ...\n");
1670 for (auto &p : m_abstract.m_param_values)
1671 {
1672 auto f = m_params.find(p.first);
1673 if (f == m_params.end())
1674 {
1675 log().error(ME_UNKNOWN_PARAMETER(p.first));
1676 errcnt++;
1677 }
1678 }
1679 if (errcnt > 0)
1680 {
1681 log().fatal(MF_ERRORS_FOUND(errcnt));
1682 throw nl_exception(MF_ERRORS_FOUND(errcnt));
1683 }
1684
1685 for (auto &n : m_nlstate.nets())
1686 {
1687 for (auto & term : m_nlstate.core_terms(*n))
1688 if (!term->delegate())
1689 {
1690 log().fatal(MF_DELEGATE_NOT_SET_1(term->name()));
1691 throw nl_exception(MF_DELEGATE_NOT_SET_1(term->name()));
1692 }
1693 n->rebuild_list();
1694 }
1695 }
1696
1697 // ----------------------------------------------------------------------------------------
1698 // base sources
1699 // ----------------------------------------------------------------------------------------
1700
parse(nlparse_t & setup,const pstring & name)1701 bool source_netlist_t::parse(nlparse_t &setup, const pstring &name)
1702 {
1703 auto strm(stream(name));
1704 return (!strm.empty()) ? setup.parse_stream(std::move(strm), name) : false;
1705 }
1706
stream(const pstring & name)1707 plib::istream_uptr source_string_t::stream(const pstring &name)
1708 {
1709 plib::unused_var(name);
1710 plib::istream_uptr ret(std::make_unique<std::istringstream>(putf8string(m_str)), name);
1711 ret->imbue(std::locale::classic());
1712 return ret;
1713 }
1714
stream(const pstring & name)1715 plib::istream_uptr source_mem_t::stream(const pstring &name)
1716 {
1717 plib::unused_var(name);
1718 plib::istream_uptr ret(std::make_unique<std::istringstream>(m_str, std::ios_base::binary), name);
1719 ret->imbue(std::locale::classic());
1720 return ret;
1721 }
1722
stream(const pstring & name)1723 plib::istream_uptr source_file_t::stream(const pstring &name)
1724 {
1725 plib::unused_var(name);
1726 auto f = std::make_unique<plib::ifstream>(plib::filesystem::u8path(m_filename));
1727 if (f->is_open())
1728 {
1729 return plib::istream_uptr(std::move(f), m_filename);
1730 }
1731
1732 return plib::istream_uptr();
1733 }
1734
stream(const pstring & name)1735 plib::istream_uptr source_pattern_t::stream(const pstring &name)
1736 {
1737 pstring filename = plib::pfmt(m_pattern)(m_force_lowercase ? plib::lcase(name) : name);
1738 auto f = std::make_unique<plib::ifstream>(plib::filesystem::u8path(filename));
1739 if (f->is_open())
1740 {
1741 return plib::istream_uptr(std::move(f), filename);
1742 }
1743
1744 return plib::istream_uptr();
1745 }
1746
1747
parse(nlparse_t & setup,const pstring & name)1748 bool source_proc_t::parse(nlparse_t &setup, const pstring &name)
1749 {
1750 if (name == m_setup_func_name)
1751 {
1752 m_setup_func(setup);
1753 return true;
1754 }
1755
1756 return false;
1757 }
1758
stream(const pstring & name)1759 plib::istream_uptr source_proc_t::stream(const pstring &name)
1760 {
1761 plib::unused_var(name);
1762 return plib::istream_uptr();
1763 }
1764
1765 } // namespace netlist
1766
1767