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> &params_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 &param, 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 &param, 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 &param)
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 &param_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