summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/core/nets.h
blob: 2d17ebd8e047cd95409882ffdd069ceaf3bbdb06 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
// license:GPL-2.0+
// copyright-holders:Couriersud

///
/// \file nets.h
///

#ifndef NL_CORE_NETS_H_
#define NL_CORE_NETS_H_

#include "base_objects.h"
#include "core_device.h"
#include "exec.h"
#include "state_var.h"

#include "../nltypes.h"
#include "../plib/plists.h"
#include "../plib/pstring.h"

// Enable the setting below to avoid queue pushes were at execution
// no action will be taken. This is academically cleaner, but slower than
// allowing this to happen and filter it during during "process".

#define AVOID_NOOP_QUEUE_PUSHES	(0)

namespace netlist
{

	namespace detail {

		// -----------------------------------------------------------------------------
		// net_t
		// -----------------------------------------------------------------------------

		class net_t : public netlist_object_t
		{
		public:

			enum class queue_status
			{
				DELAYED_DUE_TO_INACTIVE = 0,
				QUEUED,
				DELIVERED
			};

			net_t(netlist_state_t &nl, const pstring &aname, core_terminal_t *railterminal = nullptr);

			PCOPYASSIGNMOVE(net_t, delete)

			virtual ~net_t() noexcept = default;

			virtual void reset() noexcept;

			// -----------------------------------------------------------------------------
			// Hot section
			//
			// Any changes below will impact performance.
			// -----------------------------------------------------------------------------

			void toggle_new_Q() noexcept { m_new_Q = (m_cur_Q ^ 1);   }

			void toggle_and_push_to_queue(const netlist_time &delay) noexcept
			{
				toggle_new_Q();
				push_to_queue(delay);
			}

			void push_to_queue(const netlist_time &delay) noexcept
			{
				if (has_connections())
				{
					if (!!is_queued())
						exec().qremove(this);

					const auto nst(exec().time() + delay);
					m_next_scheduled_time = nst;
#if (AVOID_NOOP_QUEUE_PUSHES)
					m_in_queue = (m_list_active.empty() ? queue_status::DELAYED_DUE_TO_INACTIVE
						: (m_new_Q != m_cur_Q ? queue_status::QUEUED : queue_status::DELIVERED));
					if (m_in_queue == queue_status::QUEUED)
						exec().qpush(nst, this);
					else
						update_inputs();
#else
					if (!m_list_active.empty())
					{
						m_in_queue = queue_status::QUEUED;
						exec().qpush(nst, this);
					}
					else
					{
						m_in_queue = queue_status::DELAYED_DUE_TO_INACTIVE;
						update_inputs();
					}
#endif
				}
			}
			NVCC_CONSTEXPR bool is_queued() const noexcept { return m_in_queue == queue_status::QUEUED; }

			template <bool KEEP_STATS>
			void update_devs() noexcept
			{
				nl_assert(this->is_rail_net());

				m_in_queue = queue_status::DELIVERED; // mark as taken ...

#if (!AVOID_NOOP_QUEUE_PUSHES)
				if (m_new_Q ^ m_cur_Q)
#endif
				{
					process<KEEP_STATS>((m_new_Q << core_terminal_t::INP_LH_SHIFT)
						| (m_cur_Q << core_terminal_t::INP_HL_SHIFT), m_new_Q);
				}
			}

			netlist_time_ext next_scheduled_time() const noexcept { return m_next_scheduled_time; }
			void set_next_scheduled_time(netlist_time_ext ntime) noexcept { m_next_scheduled_time = ntime; }

			NVCC_CONSTEXPR bool is_rail_net() const noexcept { return !(m_railterminal == nullptr); }
			core_terminal_t & railterminal() const noexcept { return *m_railterminal; }

			bool has_connections() const noexcept { return !m_core_terms.empty(); }

			void add_to_active_list(core_terminal_t &term) noexcept
			{
				if (!m_list_active.empty())
				{
					term.set_copied_input(m_cur_Q);
					m_list_active.push_front(&term);
				}
				else
				{
					m_list_active.push_front(&term);
					railterminal().device().do_inc_active();
					if (m_in_queue == queue_status::DELAYED_DUE_TO_INACTIVE)
					{
#if (AVOID_NOOP_QUEUE_PUSHES)
						if (m_next_scheduled_time > exec().time() 
							&& (m_cur_Q != m_new_Q))
#else
						if (m_next_scheduled_time > exec().time())
#endif
						{
							m_in_queue = queue_status::QUEUED;     // pending
							exec().qpush(m_next_scheduled_time, this);
						}
						else
						{
							m_in_queue = queue_status::DELIVERED;
							m_cur_Q = m_new_Q;
						}
						update_inputs();
					}
					else
						term.set_copied_input(m_cur_Q);
				}
			}

			void remove_from_active_list(core_terminal_t &term) noexcept
			{
				gsl_Expects(!m_list_active.empty());
				m_list_active.remove(&term);
				if (m_list_active.empty())
				{
#if (AVOID_NOOP_QUEUE_PUSHES)
					if (!!is_queued())
					{
						exec().qremove(this);
						m_in_queue = queue_status::DELAYED_DUE_TO_INACTIVE;
					}
#endif
					railterminal().device().do_dec_active();
				}
			}

			// -----------------------------------------------------------------------------
			// setup stuff - cold
			// -----------------------------------------------------------------------------

			bool is_logic() const noexcept;
			bool is_analog() const noexcept;

			void rebuild_list();     // rebuild m_list after a load

			std::vector<core_terminal_t *> &core_terms() noexcept { return m_core_terms; }

			void update_inputs() noexcept
			{
#if NL_USE_COPY_INSTEAD_OF_REFERENCE
				for (auto & term : m_core_terms)
					term->m_Q = m_cur_Q;
#endif
				// nothing needs to be done if define not set
			}

		protected:

			// only used for logic nets
			NVCC_CONSTEXPR const netlist_sig_t &Q() const noexcept { return m_cur_Q; }

			// only used for logic nets
			void initial(netlist_sig_t val) noexcept
			{
				m_cur_Q = m_new_Q = val;
				update_inputs();
			}

			// only used for logic nets
			void set_Q_and_push(const netlist_sig_t &newQ, const netlist_time &delay) noexcept
			{
				if (newQ != m_new_Q)
				{
					m_new_Q = newQ;
					push_to_queue(delay);
				}
			}

			// only used for logic nets
			void set_Q_time(const netlist_sig_t &newQ, const netlist_time_ext &at) noexcept
			{
				if (newQ != m_new_Q)
				{
					m_in_queue = queue_status::DELAYED_DUE_TO_INACTIVE;
					m_next_scheduled_time = at;
					m_cur_Q = m_new_Q = newQ;
					update_inputs();
				}
				else
				{
					m_cur_Q = newQ;
					update_inputs();
				}
			}

		private:
			state_var<netlist_sig_t>     m_new_Q;
			state_var<netlist_sig_t>     m_cur_Q;
			state_var<queue_status>      m_in_queue;
			state_var<netlist_time_ext>  m_next_scheduled_time;

			core_terminal_t * m_railterminal;
			plib::linkedlist_t<core_terminal_t> m_list_active;
			std::vector<core_terminal_t *> m_core_terms; // save post-start m_list ...

			// -----------------------------------------------------------------------------
			// Very hot
			// -----------------------------------------------------------------------------

			template <bool KEEP_STATS, typename T, typename S>
			void process(const T &mask, const S &sig) noexcept
			{
				m_cur_Q = sig;

				if (KEEP_STATS)
				{
					for (auto & p : m_list_active)
					{
						p.set_copied_input(sig);
						auto *stats(p.device().stats());
						stats->m_stat_call_count.inc();
						if ((p.terminal_state() & mask))
						{
							auto g(stats->m_stat_total_time.guard());
							p.run_delegate();
						}
					}
				}
				else
				{
					for (auto &p : m_list_active)
					{
						p.set_copied_input(sig);
						if ((p.terminal_state() & mask) != 0)
							p.run_delegate();
					}
				}
			}
		};
	} // namespace detail

	class analog_net_t : public detail::net_t
	{
	public:

		analog_net_t(netlist_state_t &nl, const pstring &aname, detail::core_terminal_t *railterminal = nullptr);

		void reset() noexcept override;

		nl_fptype Q_Analog() const noexcept { return m_cur_Analog; }
		void set_Q_Analog(nl_fptype v) noexcept { m_cur_Analog = v; }
		// used by solver code ...
		nl_fptype *Q_Analog_state_ptr() noexcept { return &m_cur_Analog(); }

		//FIXME: needed by current solver code
		solver::matrix_solver_t *solver() const noexcept { return m_solver; }
		void set_solver(solver::matrix_solver_t *solver) noexcept { m_solver = solver; }

		friend constexpr bool operator==(const analog_net_t &lhs, const analog_net_t &rhs) noexcept
		{
			return &lhs == &rhs;
		}

	private:
		state_var<nl_fptype>     m_cur_Analog;
		solver::matrix_solver_t *m_solver;
	};

	class logic_net_t : public detail::net_t
	{
	public:

		logic_net_t(netlist_state_t &nl, const pstring &aname, detail::core_terminal_t *railterminal = nullptr);

		using detail::net_t::Q;
		using detail::net_t::initial;
		using detail::net_t::set_Q_and_push;
		using detail::net_t::set_Q_time;
	};


} // namespace netlist


#endif // NL_CORE_NETS_H_