xref: /oneTBB/examples/graph/logic_sim/basics.hpp (revision b15aabb3)
1 /*
2     Copyright (c) 2005-2021 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 TBB_examples_logic_sim_basic_H
18 #define TBB_examples_logic_sim_basic_H
19 
20 #include <cstdio>
21 
22 #include <string>
23 #include <tuple>
24 
25 #include "oneapi/tbb/tick_count.h"
26 #include "oneapi/tbb/flow_graph.h"
27 #include "common/utility/utility.hpp"
28 
29 #ifndef _WIN32
30 #include <sys/time.h>
31 #include <unistd.h>
32 
rt_sleep(int msec)33 void rt_sleep(int msec) {
34     usleep(msec * 1000);
35 }
36 
37 #else //_WIN32
38 
39 #undef OLDUNIXTIME
40 #undef STDTIME
41 
42 #include <windows.h>
43 
rt_sleep(int msec)44 void rt_sleep(int msec) {
45     Sleep(msec);
46 }
47 #endif /*  _WIN32  */
48 
49 using oneapi::tbb::flow::make_edge;
50 using oneapi::tbb::flow::cast_to;
51 using oneapi::tbb::flow::input_port;
52 using oneapi::tbb::flow::output_port;
53 
54 typedef enum { low = 0, high, undefined } signal_t;
55 
56 template <int N>
57 class gate;
58 
59 template <>
60 class gate<1>
61         : public oneapi::tbb::flow::composite_node<std::tuple<signal_t>, std::tuple<signal_t>> {
62 protected:
63     typedef oneapi::tbb::flow::indexer_node<signal_t> input_port_t;
64     typedef oneapi::tbb::flow::multifunction_node<input_port_t::output_type, std::tuple<signal_t>>
65         gate_fn_t;
66     typedef gate_fn_t::output_ports_type ports_type;
67     typedef oneapi::tbb::flow::composite_node<std::tuple<signal_t>, std::tuple<signal_t>> base_type;
68 
69 public:
70     template <typename Body>
gate(oneapi::tbb::flow::graph & g,Body b)71     gate(oneapi::tbb::flow::graph& g, Body b)
72             : base_type(g),
73               my_graph(g),
74               in_ports(g),
75               gate_fn(g, 1, b) {
76         make_edge(in_ports, gate_fn);
77         base_type::input_ports_type input_tuple(input_port<0>(in_ports));
78         base_type::output_ports_type output_tuple(output_port<0>(gate_fn));
79         base_type::set_external_ports(input_tuple, output_tuple);
80         base_type::add_visible_nodes(in_ports, gate_fn);
81     }
~gate()82     virtual ~gate() {}
operator =(const gate & src)83     gate& operator=(const gate& src) {
84         return *this;
85     }
86 
87 protected:
88     oneapi::tbb::flow::graph& my_graph;
89 
90 private:
91     input_port_t in_ports;
92     gate_fn_t gate_fn;
93 };
94 
95 template <>
96 class gate<2> : public oneapi::tbb::flow::composite_node<std::tuple<signal_t, signal_t>,
97                                                          std::tuple<signal_t>> {
98 protected:
99     typedef oneapi::tbb::flow::indexer_node<signal_t, signal_t> input_port_t;
100     typedef oneapi::tbb::flow::multifunction_node<input_port_t::output_type, std::tuple<signal_t>>
101         gate_fn_t;
102     typedef gate_fn_t::output_ports_type ports_type;
103     typedef oneapi::tbb::flow::composite_node<std::tuple<signal_t, signal_t>, std::tuple<signal_t>>
104         base_type;
105 
106 public:
107     template <typename Body>
gate(oneapi::tbb::flow::graph & g,Body b)108     gate(oneapi::tbb::flow::graph& g, Body b)
109             : base_type(g),
110               my_graph(g),
111               in_ports(g),
112               gate_fn(g, 1, b) {
113         make_edge(in_ports, gate_fn);
114         base_type::input_ports_type input_tuple(input_port<0>(in_ports), input_port<1>(in_ports));
115         base_type::output_ports_type output_tuple(output_port<0>(gate_fn));
116         base_type::set_external_ports(input_tuple, output_tuple);
117         base_type::add_visible_nodes(in_ports, gate_fn);
118     }
~gate()119     virtual ~gate() {}
operator =(const gate & src)120     gate& operator=(const gate& src) {
121         return *this;
122     }
123 
124 protected:
125     oneapi::tbb::flow::graph& my_graph;
126 
127 private:
128     input_port_t in_ports;
129     gate_fn_t gate_fn;
130 };
131 
132 template <>
133 class gate<3> : public oneapi::tbb::flow::composite_node<std::tuple<signal_t, signal_t, signal_t>,
134                                                          std::tuple<signal_t>> {
135 protected:
136     typedef oneapi::tbb::flow::indexer_node<signal_t, signal_t, signal_t> input_port_t;
137     typedef oneapi::tbb::flow::multifunction_node<input_port_t::output_type, std::tuple<signal_t>>
138         gate_fn_t;
139     typedef gate_fn_t::output_ports_type ports_type;
140     typedef oneapi::tbb::flow::composite_node<std::tuple<signal_t, signal_t, signal_t>,
141                                               std::tuple<signal_t>>
142         base_type;
143 
144 public:
145     template <typename Body>
gate(oneapi::tbb::flow::graph & g,Body b)146     gate(oneapi::tbb::flow::graph& g, Body b)
147             : base_type(g),
148               my_graph(g),
149               in_ports(g),
150               gate_fn(g, 1, b) {
151         make_edge(in_ports, gate_fn);
152         base_type::input_ports_type input_tuple(
153             input_port<0>(in_ports), input_port<1>(in_ports), input_port<2>(in_ports));
154         base_type::output_ports_type output_tuple(output_port<0>(gate_fn));
155         base_type::set_external_ports(input_tuple, output_tuple);
156         base_type::add_visible_nodes(in_ports, gate_fn);
157     }
~gate()158     virtual ~gate() {}
operator =(const gate & src)159     gate& operator=(const gate& src) {
160         return *this;
161     }
162 
163 protected:
164     oneapi::tbb::flow::graph& my_graph;
165 
166 private:
167     input_port_t in_ports;
168     gate_fn_t gate_fn;
169 };
170 
171 template <>
172 class gate<4> : public oneapi::tbb::flow::composite_node<
173                     std::tuple<signal_t, signal_t, signal_t, signal_t>,
174                     std::tuple<signal_t>> {
175 protected:
176     typedef oneapi::tbb::flow::indexer_node<signal_t, signal_t, signal_t, signal_t> input_port_t;
177     typedef oneapi::tbb::flow::multifunction_node<input_port_t::output_type, std::tuple<signal_t>>
178         gate_fn_t;
179     typedef gate_fn_t::output_ports_type ports_type;
180     typedef oneapi::tbb::flow::composite_node<std::tuple<signal_t, signal_t, signal_t, signal_t>,
181                                               std::tuple<signal_t>>
182         base_type;
183 
184 public:
185     template <typename Body>
gate(oneapi::tbb::flow::graph & g,Body b)186     gate(oneapi::tbb::flow::graph& g, Body b)
187             : base_type(g),
188               my_graph(g),
189               in_ports(g),
190               gate_fn(g, 1, b) {
191         make_edge(in_ports, gate_fn);
192         base_type::input_ports_type input_tuple(input_port<0>(in_ports),
193                                                 input_port<1>(in_ports),
194                                                 input_port<2>(in_ports),
195                                                 input_port<3>(in_ports));
196         base_type::output_ports_type output_tuple(output_port<0>(gate_fn));
197         base_type::set_external_ports(input_tuple, output_tuple);
198         base_type::add_visible_nodes(in_ports, gate_fn);
199     }
~gate()200     virtual ~gate() {}
operator =(const gate & src)201     gate& operator=(const gate& src) {
202         return *this;
203     }
204 
205 protected:
206     oneapi::tbb::flow::graph& my_graph;
207 
208 private:
209     input_port_t in_ports;
210     gate_fn_t gate_fn;
211 };
212 
213 // Input devices
214 class steady_signal {
215     oneapi::tbb::flow::graph& my_graph;
216     signal_t init_signal;
217     oneapi::tbb::flow::write_once_node<signal_t> signal_node;
218 
219 public:
steady_signal(oneapi::tbb::flow::graph & g,signal_t v)220     steady_signal(oneapi::tbb::flow::graph& g, signal_t v)
221             : my_graph(g),
222               init_signal(v),
223               signal_node(g) {}
steady_signal(const steady_signal & src)224     steady_signal(const steady_signal& src)
225             : my_graph(src.my_graph),
226               init_signal(src.init_signal),
227               signal_node(src.my_graph) {}
~steady_signal()228     ~steady_signal() {}
229     // Assignment is ignored
operator =(const steady_signal & src)230     steady_signal& operator=(const steady_signal& src) {
231         return *this;
232     }
get_out()233     oneapi::tbb::flow::write_once_node<signal_t>& get_out() {
234         return signal_node;
235     }
activate()236     void activate() {
237         signal_node.try_put(init_signal);
238     }
239 };
240 
241 class pulse {
242     class clock_body {
243         std::size_t& ms;
244         int& reps;
245         signal_t val;
246 
247     public:
clock_body(std::size_t & _ms,int & _reps)248         clock_body(std::size_t& _ms, int& _reps) : ms(_ms), reps(_reps), val(low) {}
operator ()(oneapi::tbb::flow_control & fc)249         signal_t operator()(oneapi::tbb::flow_control& fc) {
250             rt_sleep((int)ms);
251 
252             if (reps > 0)
253                 --reps;
254             if (val == low)
255                 val = high;
256             else
257                 val = low;
258 
259             if (!(reps > 0 || reps == -1))
260                 fc.stop();
261             return val;
262         }
263     };
264     oneapi::tbb::flow::graph& my_graph;
265     std::size_t ms, init_ms;
266     int reps, init_reps;
267     oneapi::tbb::flow::input_node<signal_t> clock_node;
268 
269 public:
pulse(oneapi::tbb::flow::graph & g,std::size_t _ms=1000,int _reps=-1)270     pulse(oneapi::tbb::flow::graph& g, std::size_t _ms = 1000, int _reps = -1)
271             : my_graph(g),
272               ms(_ms),
273               init_ms(_ms),
274               reps(_reps),
275               init_reps(_reps),
276               clock_node(g, clock_body(ms, reps)) {}
pulse(const pulse & src)277     pulse(const pulse& src)
278             : my_graph(src.my_graph),
279               ms(src.init_ms),
280               init_ms(src.init_ms),
281               reps(src.init_reps),
282               init_reps(src.init_reps),
283               clock_node(src.my_graph, clock_body(ms, reps)) {}
~pulse()284     ~pulse() {}
285     // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph
operator =(const pulse & src)286     pulse& operator=(const pulse& src) {
287         ms = src.ms;
288         init_ms = src.init_ms;
289         reps = src.reps;
290         init_reps = src.init_reps;
291         return *this;
292     }
get_out()293     oneapi::tbb::flow::input_node<signal_t>& get_out() {
294         return clock_node;
295     }
activate()296     void activate() {
297         clock_node.activate();
298     }
reset()299     void reset() {
300         reps = init_reps;
301     }
302 };
303 
304 class push_button {
305     oneapi::tbb::flow::graph& my_graph;
306     oneapi::tbb::flow::overwrite_node<signal_t> push_button_node;
307 
308 public:
push_button(oneapi::tbb::flow::graph & g)309     push_button(oneapi::tbb::flow::graph& g) : my_graph(g), push_button_node(g) {
310         push_button_node.try_put(low);
311     }
push_button(const push_button & src)312     push_button(const push_button& src) : my_graph(src.my_graph), push_button_node(src.my_graph) {
313         push_button_node.try_put(low);
314     }
~push_button()315     ~push_button() {}
316     // Assignment is ignored
operator =(const push_button & src)317     push_button& operator=(const push_button& src) {
318         return *this;
319     }
get_out()320     oneapi::tbb::flow::overwrite_node<signal_t>& get_out() {
321         return push_button_node;
322     }
press()323     void press() {
324         push_button_node.try_put(high);
325     }
release()326     void release() {
327         push_button_node.try_put(low);
328     }
329 };
330 
331 class toggle {
332     oneapi::tbb::flow::graph& my_graph;
333     signal_t state;
334     oneapi::tbb::flow::overwrite_node<signal_t> toggle_node;
335 
336 public:
toggle(oneapi::tbb::flow::graph & g)337     toggle(oneapi::tbb::flow::graph& g) : my_graph(g), state(undefined), toggle_node(g) {}
toggle(const toggle & src)338     toggle(const toggle& src)
339             : my_graph(src.my_graph),
340               state(undefined),
341               toggle_node(src.my_graph) {}
~toggle()342     ~toggle() {}
343     // Assignment ignored
operator =(const toggle & src)344     toggle& operator=(const toggle& src) {
345         return *this;
346     }
get_out()347     oneapi::tbb::flow::overwrite_node<signal_t>& get_out() {
348         return toggle_node;
349     }
flip()350     void flip() {
351         if (state == high)
352             state = low;
353         else
354             state = high;
355         toggle_node.try_put(state);
356     }
activate()357     void activate() {
358         state = low;
359         toggle_node.try_put(state);
360     }
361 };
362 
363 // Basic gates
364 class buffer : public gate<1> {
365     using gate<1>::my_graph;
366     typedef gate<1>::ports_type ports_type;
367     class buffer_body {
368         signal_t state;
369         bool touched;
370 
371     public:
buffer_body()372         buffer_body() : state(undefined), touched(false) {}
operator ()(const input_port_t::output_type & v,ports_type & p)373         void operator()(const input_port_t::output_type& v, ports_type& p) {
374             if (!touched || state != cast_to<signal_t>(v)) {
375                 state = cast_to<signal_t>(v);
376                 std::get<0>(p).try_put(state);
377                 touched = true;
378             }
379         }
380     };
381 
382 public:
buffer(oneapi::tbb::flow::graph & g)383     buffer(oneapi::tbb::flow::graph& g) : gate<1>(g, buffer_body()) {}
buffer(const buffer & src)384     buffer(const buffer& src) : gate<1>(src.my_graph, buffer_body()) {}
~buffer()385     ~buffer() {}
386 };
387 
388 class not_gate : public gate<1> {
389     using gate<1>::my_graph;
390     typedef gate<1>::ports_type ports_type;
391     class not_body {
392         signal_t port;
393         bool touched;
394 
395     public:
not_body()396         not_body() : port(undefined), touched(false) {}
operator ()(const input_port_t::output_type & v,ports_type & p)397         void operator()(const input_port_t::output_type& v, ports_type& p) {
398             if (!touched || port != cast_to<signal_t>(v)) {
399                 port = cast_to<signal_t>(v);
400                 signal_t state = low;
401                 if (port == low)
402                     state = high;
403                 std::get<0>(p).try_put(state);
404                 touched = true;
405             }
406         }
407     };
408 
409 public:
not_gate(oneapi::tbb::flow::graph & g)410     not_gate(oneapi::tbb::flow::graph& g) : gate<1>(g, not_body()) {}
not_gate(const not_gate & src)411     not_gate(const not_gate& src) : gate<1>(src.my_graph, not_body()) {}
~not_gate()412     ~not_gate() {}
413 };
414 
415 template <int N>
416 class and_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::output_type from_input;
420     class and_body {
421         signal_t* ports;
422         signal_t state;
423         bool touched;
424 
425     public:
and_body()426         and_body() : state(undefined), touched(false) {
427             ports = new signal_t[N];
428             for (int i = 0; i < N; ++i)
429                 ports[i] = undefined;
430         }
operator ()(const from_input & v,ports_type & p)431         void operator()(const from_input& v, ports_type& p) {
432             ports[v.tag()] = cast_to<signal_t>(v);
433             signal_t new_state = high;
434             std::size_t i = 0;
435             while (i < N) {
436                 if (ports[i] == low) {
437                     new_state = low;
438                     break;
439                 }
440                 else if (ports[i] == undefined && new_state != low) {
441                     new_state = undefined;
442                 }
443                 ++i;
444             }
445             if (!touched || state != new_state) {
446                 state = new_state;
447                 std::get<0>(p).try_put(state);
448                 touched = true;
449             }
450         }
451     };
452 
453 public:
and_gate(oneapi::tbb::flow::graph & g)454     and_gate(oneapi::tbb::flow::graph& g) : gate<N>(g, and_body()) {}
and_gate(const and_gate<N> & src)455     and_gate(const and_gate<N>& src) : gate<N>(src.my_graph, and_body()) {}
~and_gate()456     ~and_gate() {}
457 };
458 
459 template <int N>
460 class or_gate : public gate<N> {
461     using gate<N>::my_graph;
462     typedef typename gate<N>::ports_type ports_type;
463     typedef typename gate<N>::input_port_t::output_type from_input;
464     class or_body {
465         signal_t* ports;
466         signal_t state;
467         bool touched;
468 
469     public:
or_body()470         or_body() : state(undefined), touched(false) {
471             ports = new signal_t[N];
472             for (int i = 0; i < N; ++i)
473                 ports[i] = undefined;
474         }
operator ()(const from_input & v,ports_type & p)475         void operator()(const from_input& v, ports_type& p) {
476             ports[v.tag()] = cast_to<signal_t>(v);
477             signal_t new_state = low;
478             std::size_t i = 0;
479             while (i < N) {
480                 if (ports[i] == high) {
481                     new_state = high;
482                     break;
483                 }
484                 else if (ports[i] == undefined && new_state != high) {
485                     new_state = undefined;
486                 }
487                 ++i;
488             }
489             if (!touched || state != new_state) {
490                 state = new_state;
491                 std::get<0>(p).try_put(state);
492                 touched = true;
493             }
494         }
495     };
496 
497 public:
or_gate(oneapi::tbb::flow::graph & g)498     or_gate(oneapi::tbb::flow::graph& g) : gate<N>(g, or_body()) {}
or_gate(const or_gate & src)499     or_gate(const or_gate& src) : gate<N>(src.my_graph, or_body()) {}
~or_gate()500     ~or_gate() {}
501 };
502 
503 template <int N>
504 class xor_gate : public gate<N> {
505     using gate<N>::my_graph;
506     typedef typename gate<N>::ports_type ports_type;
507     typedef typename gate<N>::input_port_t input_port_t;
508     class xor_body {
509         signal_t* ports;
510         signal_t state;
511         bool touched;
512 
513     public:
xor_body()514         xor_body() : state(undefined), touched(false) {
515             ports = new signal_t[N];
516             for (int i = 0; i < N; ++i)
517                 ports[i] = undefined;
518         }
operator ()(const typename input_port_t::output_type & v,ports_type & p)519         void operator()(const typename input_port_t::output_type& v, ports_type& p) {
520             ports[v.tag()] = cast_to<signal_t>(v);
521             signal_t new_state = low;
522             std::size_t i = 0, highs = 0;
523             while (i < N) {
524                 if (ports[i] == undefined) {
525                     new_state = undefined;
526                 }
527                 else if (ports[i] == high && new_state == low) {
528                     new_state = high;
529                     ++highs;
530                 }
531                 else if (ports[i] == high && highs > 0) {
532                     new_state = low;
533                     break;
534                 }
535                 else if (ports[i] == high) {
536                     ++highs;
537                 }
538                 ++i;
539             }
540             if (!touched || state != new_state) {
541                 state = new_state;
542                 std::get<0>(p).try_put(state);
543                 touched = true;
544             }
545         }
546     };
547 
548 public:
xor_gate(oneapi::tbb::flow::graph & g)549     xor_gate(oneapi::tbb::flow::graph& g) : gate<N>(g, xor_body()) {}
xor_gate(const xor_gate & src)550     xor_gate(const xor_gate& src) : gate<N>(src.my_graph, xor_body()) {}
~xor_gate()551     ~xor_gate() {}
552 };
553 
554 template <int N>
555 class nor_gate : public gate<N> {
556     using gate<N>::my_graph;
557     typedef typename gate<N>::ports_type ports_type;
558     typedef typename gate<N>::input_port_t input_port_t;
559     class nor_body {
560         signal_t* ports;
561         signal_t state;
562         bool touched;
563 
564     public:
nor_body()565         nor_body() : state(undefined), touched(false) {
566             ports = new signal_t[N];
567             for (int i = 0; i < N; ++i)
568                 ports[i] = undefined;
569         }
operator ()(const typename input_port_t::output_type & v,ports_type & p)570         void operator()(const typename input_port_t::output_type& v, ports_type& p) {
571             ports[v.tag()] = cast_to<signal_t>(v);
572             signal_t new_state = low;
573             std::size_t i = 0;
574             while (i < N) {
575                 if (ports[i] == high) {
576                     new_state = high;
577                     break;
578                 }
579                 else if (ports[i] == undefined && new_state != high) {
580                     new_state = undefined;
581                 }
582                 ++i;
583             }
584             if (new_state == high)
585                 new_state = low;
586             else if (new_state == low)
587                 new_state = high;
588             if (!touched || state != new_state) {
589                 state = new_state;
590                 std::get<0>(p).try_put(state);
591                 touched = true;
592             }
593         }
594     };
595 
596 public:
nor_gate(oneapi::tbb::flow::graph & g)597     nor_gate(oneapi::tbb::flow::graph& g) : gate<N>(g, nor_body()) {}
nor_gate(const nor_gate & src)598     nor_gate(const nor_gate& src) : gate<N>(src.my_graph, nor_body()) {}
~nor_gate()599     ~nor_gate() {}
600 };
601 
602 // Output devices
603 class led {
604     class led_body {
605         signal_t& state;
606         std::string& label;
607         bool report_changes;
608         bool touched;
609 
610     public:
led_body(signal_t & s,std::string & l,bool r)611         led_body(signal_t& s, std::string& l, bool r)
612                 : state(s),
613                   label(l),
614                   report_changes(r),
615                   touched(false) {}
operator ()(signal_t b)616         oneapi::tbb::flow::continue_msg operator()(signal_t b) {
617             if (!touched || b != state) {
618                 state = b;
619                 if (state != undefined && report_changes) {
620                     if (state)
621                         printf("%s: (*)\n", label.c_str());
622                     else
623                         printf("%s: ( )\n", label.c_str());
624                 }
625                 touched = false;
626             }
627             return oneapi::tbb::flow::continue_msg();
628         }
629     };
630     oneapi::tbb::flow::graph& my_graph;
631     std::string label;
632     signal_t state;
633     bool report_changes;
634     oneapi::tbb::flow::function_node<signal_t, oneapi::tbb::flow::continue_msg> led_node;
635 
636 public:
led(oneapi::tbb::flow::graph & g,std::string l,bool rc=false)637     led(oneapi::tbb::flow::graph& g, std::string l, bool rc = false)
638             : my_graph(g),
639               label(l),
640               state(undefined),
641               report_changes(rc),
642               led_node(g, 1, led_body(state, label, report_changes)) {}
led(const led & src)643     led(const led& src)
644             : my_graph(src.my_graph),
645               label(src.label),
646               state(undefined),
647               report_changes(src.report_changes),
648               led_node(src.my_graph, 1, led_body(state, label, report_changes)) {}
~led()649     ~led() {}
650     // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph
651     // state is set to undefined so that next signal changes it
operator =(const led & src)652     led& operator=(const led& src) {
653         label = src.label;
654         state = undefined;
655         report_changes = src.report_changes;
656         return *this;
657     }
get_in()658     oneapi::tbb::flow::function_node<signal_t, oneapi::tbb::flow::continue_msg>& get_in() {
659         return led_node;
660     }
display()661     void display() {
662         if (state == high)
663             printf("%s: (*)\n", label.c_str());
664         else if (state == low)
665             printf("%s: ( )\n", label.c_str());
666         else
667             printf("%s: (u)\n", label.c_str());
668     }
get_value()669     signal_t get_value() {
670         return state;
671     }
672 };
673 
674 class digit : public gate<4> {
675     using gate<4>::my_graph;
676     typedef gate<4>::ports_type ports_type;
677     typedef gate<4>::input_port_t input_port_t;
678     class digit_body {
679         signal_t ports[4];
680         static const int N = 4;
681         unsigned int& state;
682         std::string& label;
683         bool& report_changes;
684 
685     public:
digit_body(unsigned int & s,std::string & l,bool & r)686         digit_body(unsigned int& s, std::string& l, bool& r)
687                 : state(s),
688                   label(l),
689                   report_changes(r) {
690             for (int i = 0; i < N; ++i)
691                 ports[i] = undefined;
692         }
operator ()(const input_port_t::output_type & v,ports_type & p)693         void operator()(const input_port_t::output_type& v, ports_type& p) {
694             unsigned int new_state = 0;
695             ports[v.tag()] = cast_to<signal_t>(v);
696             if (ports[0] == high)
697                 ++new_state;
698             if (ports[1] == high)
699                 new_state += 2;
700             if (ports[2] == high)
701                 new_state += 4;
702             if (ports[3] == high)
703                 new_state += 8;
704             if (state != new_state) {
705                 state = new_state;
706                 if (report_changes) {
707                     printf("%s: %x\n", label.c_str(), state);
708                 }
709             }
710         }
711     };
712     std::string label;
713     unsigned int state;
714     bool report_changes;
715 
716 public:
digit(oneapi::tbb::flow::graph & g,std::string l,bool rc=false)717     digit(oneapi::tbb::flow::graph& g, std::string l, bool rc = false)
718             : gate<4>(g, digit_body(state, label, report_changes)),
719               label(l),
720               state(0),
721               report_changes(rc) {}
digit(const digit & src)722     digit(const digit& src)
723             : gate<4>(src.my_graph, digit_body(state, label, report_changes)),
724               label(src.label),
725               state(0),
726               report_changes(src.report_changes) {}
~digit()727     ~digit() {}
728     // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph.
729     // state is reset as in constructors
operator =(const digit & src)730     digit& operator=(const digit& src) {
731         label = src.label;
732         state = 0;
733         report_changes = src.report_changes;
734         return *this;
735     }
display()736     void display() {
737         printf("%s: %x\n", label.c_str(), state);
738     }
get_value()739     unsigned int get_value() {
740         return state;
741     }
742 };
743 
744 #endif /* TBB_examples_logic_sim_basic_H */
745