1/*
2 Copyright (c) 2005-2019 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17#ifndef __TBBexample_graph_logicsim_basics_H
18#define __TBBexample_graph_logicsim_basics_H 1
19
20#include <cstdio>
21#include <string>
22#include "tbb/atomic.h"
23#include "tbb/task_scheduler_init.h"
24#include "tbb/tick_count.h"
25#include "tbb/flow_graph.h"
26#include "../../common/utility/utility.h"
27
28#ifndef _WIN32
29#include <sys/time.h>
30#include <unistd.h>
31
32void rt_sleep(int msec) {
33 usleep(msec*1000);
34}
35
36#else //_WIN32
37
38#undef OLDUNIXTIME
39#undef STDTIME
40
41#include <windows.h>
42
43void rt_sleep(int msec) {
44 Sleep(msec);
45}
46#endif /* _WIN32 */
47
48using namespace std;
49using namespace tbb;
50using namespace tbb::flow;
51
52typedef enum { low=0, high, undefined } signal_t;
53
54template<int N> class gate;
55
56template<>
57class gate<1> : public composite_node< tuple< signal_t >, tuple< signal_t > > {
58protected:
59 typedef indexer_node<signal_t> input_port_t;
60 typedef multifunction_node< input_port_t::output_type, tuple<signal_t> > gate_fn_t;
61 typedef gate_fn_t::output_ports_type ports_type;
62 typedef composite_node< tuple< signal_t >, tuple< signal_t > > base_type;
63public:
64 template <typename Body>
65 gate(graph& g, Body b) : base_type(g), my_graph(g), in_ports(g), gate_fn(g, 1, b) {
66 make_edge(in_ports, gate_fn);
67 base_type::input_ports_type input_tuple(input_port<0>(in_ports));
68 base_type::output_ports_type output_tuple(output_port<0>(gate_fn));
69 base_type::set_external_ports(input_tuple, output_tuple);
70 base_type::add_visible_nodes(in_ports, gate_fn);
71 }
72 virtual ~gate() {}
73 gate& operator=(const gate& src) { return *this; }
74protected:
75 graph& my_graph;
76private:
77 input_port_t in_ports;
78 gate_fn_t gate_fn;
79};
80
81template<>
82class gate<2> : public composite_node< tuple< signal_t, signal_t >, tuple< signal_t > > {
83protected:
84 typedef indexer_node<signal_t,signal_t> input_port_t;
85 typedef multifunction_node< input_port_t::output_type, tuple<signal_t> > gate_fn_t;
86 typedef gate_fn_t::output_ports_type ports_type;
87 typedef composite_node< tuple< signal_t, signal_t >, tuple< signal_t > > base_type;
88public:
89 template <typename Body>
90 gate(graph& g, Body b) : base_type(g), my_graph(g), in_ports(g), gate_fn(g, 1, b) {
91 make_edge(in_ports, gate_fn);
92 base_type::input_ports_type input_tuple(input_port<0>(in_ports),input_port<1>(in_ports));
93 base_type::output_ports_type output_tuple(output_port<0>(gate_fn));
94 base_type::set_external_ports(input_tuple, output_tuple);
95 base_type::add_visible_nodes(in_ports, gate_fn);
96 }
97 virtual ~gate() {}
98 gate& operator=(const gate& src) { return *this; }
99protected:
100 graph& my_graph;
101private:
102 input_port_t in_ports;
103 gate_fn_t gate_fn;
104};
105
106template<>
107class gate<3> : public composite_node< tuple< signal_t, signal_t, signal_t >, tuple< signal_t > > {
108protected:
109 typedef indexer_node<signal_t, signal_t, signal_t> input_port_t;
110 typedef multifunction_node< input_port_t::output_type, tuple<signal_t> > gate_fn_t;
111 typedef gate_fn_t::output_ports_type ports_type;
112 typedef composite_node< tuple< signal_t, signal_t, signal_t >, tuple< signal_t > > base_type;
113public:
114 template <typename Body>
115 gate(graph& g, Body b) : base_type(g), my_graph(g), in_ports(g), gate_fn(g, 1, b) {
116 make_edge(in_ports, gate_fn);
117 base_type::input_ports_type input_tuple(input_port<0>(in_ports),input_port<1>(in_ports),input_port<2>(in_ports));
118 base_type::output_ports_type output_tuple(output_port<0>(gate_fn));
119 base_type::set_external_ports(input_tuple, output_tuple);
120 base_type::add_visible_nodes(in_ports, gate_fn);
121 }
122 virtual ~gate() {}
123 gate& operator=(const gate& src) { return *this; }
124protected:
125 graph& my_graph;
126private:
127 input_port_t in_ports;
128 gate_fn_t gate_fn;
129};
130
131template<>
132class gate<4> : public composite_node< tuple< signal_t, signal_t, signal_t, signal_t >, tuple< signal_t > > {
133protected:
134 typedef indexer_node<signal_t, signal_t, signal_t, signal_t> input_port_t;
135 typedef multifunction_node< input_port_t::output_type, tuple<signal_t> > gate_fn_t;
136 typedef gate_fn_t::output_ports_type ports_type;
137 typedef composite_node< tuple< signal_t, signal_t, signal_t, signal_t >, tuple< signal_t > > base_type;
138public:
139 template <typename Body>
140 gate(graph& g, Body b) : base_type(g), my_graph(g), in_ports(g), gate_fn(g, 1, b) {
141 make_edge(in_ports, gate_fn);
142 base_type::input_ports_type input_tuple(input_port<0>(in_ports),input_port<1>(in_ports),input_port<2>(in_ports), input_port<3>(in_ports));
143 base_type::output_ports_type output_tuple(output_port<0>(gate_fn));
144 base_type::set_external_ports(input_tuple, output_tuple);
145 base_type::add_visible_nodes(in_ports, gate_fn);
146 }
147 virtual ~gate() {}
148 gate& operator=(const gate& src) { return *this; }
149protected:
150 graph& my_graph;
151private:
152 input_port_t in_ports;
153 gate_fn_t gate_fn;
154};
155
156// Input devices
157class steady_signal {
158 graph& my_graph;
159 signal_t init_signal;
160 write_once_node<signal_t> signal_node;
161 public:
162 steady_signal(graph& g, signal_t v) :
163 my_graph(g), init_signal(v), signal_node(g) {}
164 steady_signal(const steady_signal& src) :
165 my_graph(src.my_graph), init_signal(src.init_signal),
166 signal_node(src.my_graph) {}
167 ~steady_signal() {}
168 // Assignment is ignored
169 steady_signal& operator=(const steady_signal& src) { return *this; }
170 sender<signal_t>& get_out() { return signal_node; }
171 void activate() { signal_node.try_put(init_signal); }
172};
173
174class pulse {
175 class clock_body {
176 size_t& ms;
177 int& reps;
178 signal_t val;
179 public:
180 clock_body(size_t& _ms, int& _reps) : ms(_ms), reps(_reps), val(low) {}
181 bool operator()(signal_t& out) {
182 rt_sleep((int)ms);
183 if (reps>0) --reps;
184 if (val==low) val = high;
185 else val = low;
186 out = val;
187 return reps>0 || reps == -1;
188 }
189 };
190 graph& my_graph;
191 size_t ms, init_ms;
192 int reps, init_reps;
193 source_node<signal_t> clock_node;
194
195public:
196 pulse(graph& g, size_t _ms=1000, int _reps=-1) :
197 my_graph(g), ms(_ms), init_ms(_ms), reps(_reps), init_reps(_reps),
198 clock_node(g, clock_body(ms, reps), false)
199 {}
200 pulse(const pulse& src) :
201 my_graph(src.my_graph), ms(src.init_ms), init_ms(src.init_ms),
202 reps(src.init_reps), init_reps(src.init_reps),
203 clock_node(src.my_graph, clock_body(ms, reps), false)
204 {}
205 ~pulse() {}
206 // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph
207 pulse& operator=(const pulse& src) {
208 ms = src.ms; init_ms = src.init_ms; reps = src.reps; init_reps = src.init_reps;
209 return *this;
210 }
211 sender<signal_t>& get_out() { return clock_node; }
212 void activate() { clock_node.activate(); }
213 void reset() { reps = init_reps; }
214};
215
216class push_button {
217 graph& my_graph;
218 overwrite_node<signal_t> push_button_node;
219 public:
220 push_button(graph& g) : my_graph(g), push_button_node(g) {
221 push_button_node.try_put(low);
222 }
223 push_button(const push_button& src) :
224 my_graph(src.my_graph), push_button_node(src.my_graph) {
225 push_button_node.try_put(low);
226 }
227 ~push_button() {}
228 // Assignment is ignored
229 push_button& operator=(const push_button& src) { return *this; }
230 sender<signal_t>& get_out() { return push_button_node; }
231 void press() { push_button_node.try_put(high); }
232 void release() { push_button_node.try_put(low); }
233};
234
235class toggle {
236 graph& my_graph;
237 signal_t state;
238 overwrite_node<signal_t> toggle_node;
239 public:
240 toggle(graph& g) : my_graph(g), state(undefined), toggle_node(g) {}
241 toggle(const toggle& src) : my_graph(src.my_graph), state(undefined),
242 toggle_node(src.my_graph) {}
243 ~toggle() {}
244 // Assignment ignored
245 toggle& operator=(const toggle& src) { return *this; }
246 sender<signal_t>& get_out() { return toggle_node; }
247 void flip() {
248 if (state==high) state = low;
249 else state = high;
250 toggle_node.try_put(state);
251 }
252 void activate() {
253 state = low;
254 toggle_node.try_put(state);
255 }
256};
257
258// Basic gates
259class buffer : public gate<1> {
260 using gate<1>::my_graph;
261 typedef gate<1>::ports_type ports_type;
262 class buffer_body {
263 signal_t state;
264 bool touched;
265 public:
266 buffer_body() : state(undefined), touched(false) {}
267 void operator()(const input_port_t::output_type &v, ports_type& p) {
268 if (!touched || state != cast_to<signal_t>(v)) {
269 state = cast_to<signal_t>(v);
270 tbb::flow::get<0>(p).try_put(state);
271 touched = true;
272 }
273 }
274 };
275public:
276 buffer(graph& g) : gate<1>(g, buffer_body()) {}
277 buffer(const buffer& src) : gate<1>(src.my_graph, buffer_body()) {}
278 ~buffer() {}
279};
280
281class not_gate : public gate<1> {
282 using gate<1>::my_graph;
283 typedef gate<1>::ports_type ports_type;
284 class not_body {
285 signal_t port;
286 bool touched;
287 public:
288 not_body() : port(undefined), touched(false) {}
289 void operator()(const input_port_t::output_type &v, ports_type& p) {
290 if (!touched || port != cast_to<signal_t>(v)) {
291 port = cast_to<signal_t>(v);
292 signal_t state = low;
293 if (port==low) state = high;
294 tbb::flow::get<0>(p).try_put(state);
295 touched = true;
296 }
297 }
298 };
299 public:
300 not_gate(graph& g) : gate<1>(g, not_body()) {}
301 not_gate(const not_gate& src) : gate<1>(src.my_graph, not_body()) {}
302 ~not_gate() {}
303};
304
305template <int N>
306class and_gate : public gate<N> {
307 using gate<N>::my_graph;
308 typedef typename gate<N>::ports_type ports_type;
309 typedef typename gate<N>::input_port_t::output_type from_input;
310 class and_body {
311 signal_t *ports;
312 signal_t state;
313 bool touched;
314 public:
315 and_body() : state(undefined), touched(false) {
316 ports = new signal_t[N];
317 for (int i=0; i<N; ++i) ports[i] = undefined;
318 }
319 void operator()(const from_input& v, ports_type& p) {
320 ports[v.tag()] = cast_to<signal_t>(v);
321 signal_t new_state=high;
322 size_t i=0;
323 while (i<N) {
324 if (ports[i] == low) { new_state = low; break; }
325 else if (ports[i] == undefined && new_state != low) { new_state = undefined; }
326 ++i;
327 }
328 if (!touched || state != new_state) {
329 state = new_state;
330 tbb::flow::get<0>(p).try_put(state);
331 touched = true;
332 }
333 }
334 };
335 public:
336 and_gate(graph& g) : gate<N>(g, and_body()) {}
337 and_gate(const and_gate<N>& src) : gate<N>(src.my_graph, and_body()) {}
338 ~and_gate() {}
339};
340
341template<int N>
342class or_gate : public gate<N> {
343 using gate<N>::my_graph;
344 typedef typename gate<N>::ports_type ports_type;
345 typedef typename gate<N>::input_port_t::output_type from_input;
346 class or_body {
347 signal_t *ports;
348 signal_t state;
349 bool touched;
350 public:
351 or_body() : state(undefined), touched(false) {
352 ports = new signal_t[N];
353 for (int i=0; i<N; ++i) ports[i] = undefined;
354 }
355 void operator()(const from_input& v, ports_type& p) {
356 ports[v.tag()] = cast_to<signal_t>(v);
357 signal_t new_state=low;
358 size_t i=0;
359 while (i<N) {
360 if (ports[i] == high) { new_state = high; break; }
361 else if (ports[i] == undefined && new_state != high) { new_state = undefined; }
362 ++i;
363 }
364 if (!touched || state != new_state) {
365 state = new_state;
366 tbb::flow::get<0>(p).try_put(state);
367 touched = true;
368 }
369 }
370 };
371public:
372 or_gate(graph& g) : gate<N>(g, or_body()) {}
373 or_gate(const or_gate& src) : gate<N>(src.my_graph, or_body()) {}
374 ~or_gate() {}
375};
376
377template <int N>
378class xor_gate : public gate<N> {
379 using gate<N>::my_graph;
380 typedef typename gate<N>::ports_type ports_type;
381 typedef typename gate<N>::input_port_t input_port_t;
382 class xor_body {
383 signal_t *ports;
384 signal_t state;
385 bool touched;
386 public:
387 xor_body() : state(undefined), touched(false) {
388 ports = new signal_t[N];
389 for (int i=0; i<N; ++i) ports[i] = undefined;
390 }
391 void operator()(const typename input_port_t::output_type &v, ports_type& p) {
392 ports[v.tag()] = cast_to<signal_t>(v);
393 signal_t new_state=low;
394 size_t i=0, highs=0;
395 while (i<N) {
396 if (ports[i] == undefined) { new_state = undefined; }
397 else if (ports[i] == high && new_state == low) { new_state = high; ++highs; }
398 else if (ports[i] == high && highs > 0) { new_state = low; break; }
399 else if (ports[i] == high ) { ++highs; }
400 ++i;
401 }
402 if (!touched || state != new_state) {
403 state = new_state;
404 tbb::flow::get<0>(p).try_put(state);
405 touched = true;
406 }
407 }
408 };
409 public:
410 xor_gate(graph& g) : gate<N>(g, xor_body()) {}
411 xor_gate(const xor_gate& src) : gate<N>(src.my_graph, xor_body()) {}
412 ~xor_gate() {}
413};
414
415template <int N>
416class nor_gate : public gate<N> {
417 using gate<N>::my_graph;
418 typedef typename gate<N>::ports_type ports_type;
419 typedef typename gate<N>::input_port_t input_port_t;
420 class nor_body {
421 signal_t *ports;
422 signal_t state;
423 bool touched;
424 public:
425 nor_body() : state(undefined), touched(false) {
426 ports = new signal_t[N];
427 for (int i=0; i<N; ++i) ports[i] = undefined;
428 }
429 void operator()(const typename input_port_t::output_type &v, ports_type& p) {
430 ports[v.tag()] = cast_to<signal_t>(v);
431 signal_t new_state=low;
432 size_t i=0;
433 while (i<N) {
434 if (ports[i] == high) { new_state = high; break; }
435 else if (ports[i] == undefined && new_state != high) { new_state = undefined; }
436 ++i;
437 }
438 if (new_state == high) new_state = low;
439 else if (new_state == low) new_state = high;
440 if (!touched || state != new_state) {
441 state = new_state;
442 tbb::flow::get<0>(p).try_put(state);
443 touched = true;
444 }
445 }
446 };
447 public:
448 nor_gate(graph& g) : gate<N>(g, nor_body()) {}
449 nor_gate(const nor_gate& src) : gate<N>(src.my_graph, nor_body()) {}
450 ~nor_gate() {}
451};
452
453// Output devices
454class led {
455 class led_body {
456 signal_t &state;
457 string &label;
458 bool report_changes;
459 bool touched;
460 public:
461 led_body(signal_t &s, string &l, bool r) :
462 state(s), label(l), report_changes(r), touched(false)
463 {}
464 continue_msg operator()(signal_t b) {
465 if (!touched || b!=state) {
466 state = b;
467 if (state != undefined && report_changes) {
468 if (state) printf("%s: (*)\n", label.c_str());
469 else printf("%s: ( )\n", label.c_str());
470 }
471 touched = false;
472 }
473 return continue_msg();
474 }
475 };
476 graph& my_graph;
477 string label;
478 signal_t state;
479 bool report_changes;
480 function_node<signal_t, continue_msg> led_node;
481 public:
482 led(graph& g, string l, bool rc=false) : my_graph(g), label(l), state(undefined),
483 report_changes(rc),
484 led_node(g, 1, led_body(state, label, report_changes))
485 {}
486 led(const led& src) : my_graph(src.my_graph), label(src.label), state(undefined),
487 report_changes(src.report_changes),
488 led_node(src.my_graph, 1, led_body(state, label, report_changes))
489 {}
490 ~led() {}
491 // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph
492 // state is set to undefined so that next signal changes it
493 led& operator=(const led& src) {
494 label = src.label; state = undefined; report_changes = src.report_changes;
495 return *this;
496 }
497 receiver<signal_t>& get_in() { return led_node; }
498 void display() {
499 if (state == high) printf("%s: (*)\n", label.c_str());
500 else if (state == low) printf("%s: ( )\n", label.c_str());
501 else printf("%s: (u)\n", label.c_str());
502 }
503 signal_t get_value() { return state; }
504};
505
506class digit : public gate<4> {
507 using gate<4>::my_graph;
508 typedef gate<4>::ports_type ports_type;
509 typedef gate<4>::input_port_t input_port_t;
510 class digit_body {
511 signal_t ports[4];
512 static const int N = 4;
513 unsigned int &state;
514 string &label;
515 bool& report_changes;
516 public:
517 digit_body(unsigned int &s, string &l, bool& r) : state(s), label(l), report_changes(r) {
518 for (int i=0; i<N; ++i) ports[i] = undefined;
519 }
520 void operator()(const input_port_t::output_type& v, ports_type& p) {
521 unsigned int new_state = 0;
522 ports[v.tag()] = cast_to<signal_t>(v);
523 if (ports[0] == high) ++new_state;
524 if (ports[1] == high) new_state += 2;
525 if (ports[2] == high) new_state += 4;
526 if (ports[3] == high) new_state += 8;
527 if (state != new_state) {
528 state = new_state;
529 if (report_changes) {
530 printf("%s: %x\n", label.c_str(), state);
531 }
532 }
533 }
534 };
535 string label;
536 unsigned int state;
537 bool report_changes;
538 public:
539 digit(graph& g, string l, bool rc=false) :
540 gate<4>(g, digit_body(state, label, report_changes)),
541 label(l), state(0), report_changes(rc) {}
542 digit(const digit& src) :
543 gate<4>(src.my_graph, digit_body(state, label, report_changes)),
544 label(src.label), state(0), report_changes(src.report_changes) {}
545 ~digit() {}
546 // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph.
547 // state is reset as in constructors
548 digit& operator=(const digit& src) {
549 label = src.label; state = 0; report_changes = src.report_changes;
550 return *this;
551 }
552 void display() { printf("%s: %x\n", label.c_str(), state); }
553 unsigned int get_value() { return state; }
554};
555
556#endif /* __TBBexample_graph_logicsim_basics_H */
557