/* Copyright (c) 2005-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef TBB_examples_logic_sim_basic_H #define TBB_examples_logic_sim_basic_H #include #include #include #include "oneapi/tbb/tick_count.h" #include "oneapi/tbb/flow_graph.h" #include "common/utility/utility.hpp" #ifndef _WIN32 #include #include void rt_sleep(int msec) { usleep(msec * 1000); } #else //_WIN32 #undef OLDUNIXTIME #undef STDTIME #include void rt_sleep(int msec) { Sleep(msec); } #endif /* _WIN32 */ using oneapi::tbb::flow::make_edge; using oneapi::tbb::flow::cast_to; using oneapi::tbb::flow::input_port; using oneapi::tbb::flow::output_port; typedef enum { low = 0, high, undefined } signal_t; template class gate; template <> class gate<1> : public oneapi::tbb::flow::composite_node, std::tuple> { protected: typedef oneapi::tbb::flow::indexer_node input_port_t; typedef oneapi::tbb::flow::multifunction_node> gate_fn_t; typedef gate_fn_t::output_ports_type ports_type; typedef oneapi::tbb::flow::composite_node, std::tuple> base_type; public: template gate(oneapi::tbb::flow::graph& g, Body b) : base_type(g), my_graph(g), in_ports(g), gate_fn(g, 1, b) { make_edge(in_ports, gate_fn); base_type::input_ports_type input_tuple(input_port<0>(in_ports)); base_type::output_ports_type output_tuple(output_port<0>(gate_fn)); base_type::set_external_ports(input_tuple, output_tuple); base_type::add_visible_nodes(in_ports, gate_fn); } virtual ~gate() {} gate& operator=(const gate& src) { return *this; } protected: oneapi::tbb::flow::graph& my_graph; private: input_port_t in_ports; gate_fn_t gate_fn; }; template <> class gate<2> : public oneapi::tbb::flow::composite_node, std::tuple> { protected: typedef oneapi::tbb::flow::indexer_node input_port_t; typedef oneapi::tbb::flow::multifunction_node> gate_fn_t; typedef gate_fn_t::output_ports_type ports_type; typedef oneapi::tbb::flow::composite_node, std::tuple> base_type; public: template gate(oneapi::tbb::flow::graph& g, Body b) : base_type(g), my_graph(g), in_ports(g), gate_fn(g, 1, b) { make_edge(in_ports, gate_fn); base_type::input_ports_type input_tuple(input_port<0>(in_ports), input_port<1>(in_ports)); base_type::output_ports_type output_tuple(output_port<0>(gate_fn)); base_type::set_external_ports(input_tuple, output_tuple); base_type::add_visible_nodes(in_ports, gate_fn); } virtual ~gate() {} gate& operator=(const gate& src) { return *this; } protected: oneapi::tbb::flow::graph& my_graph; private: input_port_t in_ports; gate_fn_t gate_fn; }; template <> class gate<3> : public oneapi::tbb::flow::composite_node, std::tuple> { protected: typedef oneapi::tbb::flow::indexer_node input_port_t; typedef oneapi::tbb::flow::multifunction_node> gate_fn_t; typedef gate_fn_t::output_ports_type ports_type; typedef oneapi::tbb::flow::composite_node, std::tuple> base_type; public: template gate(oneapi::tbb::flow::graph& g, Body b) : base_type(g), my_graph(g), in_ports(g), gate_fn(g, 1, b) { make_edge(in_ports, gate_fn); base_type::input_ports_type input_tuple( input_port<0>(in_ports), input_port<1>(in_ports), input_port<2>(in_ports)); base_type::output_ports_type output_tuple(output_port<0>(gate_fn)); base_type::set_external_ports(input_tuple, output_tuple); base_type::add_visible_nodes(in_ports, gate_fn); } virtual ~gate() {} gate& operator=(const gate& src) { return *this; } protected: oneapi::tbb::flow::graph& my_graph; private: input_port_t in_ports; gate_fn_t gate_fn; }; template <> class gate<4> : public oneapi::tbb::flow::composite_node< std::tuple, std::tuple> { protected: typedef oneapi::tbb::flow::indexer_node input_port_t; typedef oneapi::tbb::flow::multifunction_node> gate_fn_t; typedef gate_fn_t::output_ports_type ports_type; typedef oneapi::tbb::flow::composite_node, std::tuple> base_type; public: template gate(oneapi::tbb::flow::graph& g, Body b) : base_type(g), my_graph(g), in_ports(g), gate_fn(g, 1, b) { make_edge(in_ports, gate_fn); 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)); base_type::output_ports_type output_tuple(output_port<0>(gate_fn)); base_type::set_external_ports(input_tuple, output_tuple); base_type::add_visible_nodes(in_ports, gate_fn); } virtual ~gate() {} gate& operator=(const gate& src) { return *this; } protected: oneapi::tbb::flow::graph& my_graph; private: input_port_t in_ports; gate_fn_t gate_fn; }; // Input devices class steady_signal { oneapi::tbb::flow::graph& my_graph; signal_t init_signal; oneapi::tbb::flow::write_once_node signal_node; public: steady_signal(oneapi::tbb::flow::graph& g, signal_t v) : my_graph(g), init_signal(v), signal_node(g) {} steady_signal(const steady_signal& src) : my_graph(src.my_graph), init_signal(src.init_signal), signal_node(src.my_graph) {} ~steady_signal() {} // Assignment is ignored steady_signal& operator=(const steady_signal& src) { return *this; } oneapi::tbb::flow::write_once_node& get_out() { return signal_node; } void activate() { signal_node.try_put(init_signal); } }; class pulse { class clock_body { std::size_t& ms; int& reps; signal_t val; public: clock_body(std::size_t& _ms, int& _reps) : ms(_ms), reps(_reps), val(low) {} signal_t operator()(oneapi::tbb::flow_control& fc) { rt_sleep((int)ms); if (reps > 0) --reps; if (val == low) val = high; else val = low; if (!(reps > 0 || reps == -1)) fc.stop(); return val; } }; oneapi::tbb::flow::graph& my_graph; std::size_t ms, init_ms; int reps, init_reps; oneapi::tbb::flow::input_node clock_node; public: pulse(oneapi::tbb::flow::graph& g, std::size_t _ms = 1000, int _reps = -1) : my_graph(g), ms(_ms), init_ms(_ms), reps(_reps), init_reps(_reps), clock_node(g, clock_body(ms, reps)) {} pulse(const pulse& src) : my_graph(src.my_graph), ms(src.init_ms), init_ms(src.init_ms), reps(src.init_reps), init_reps(src.init_reps), clock_node(src.my_graph, clock_body(ms, reps)) {} ~pulse() {} // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph pulse& operator=(const pulse& src) { ms = src.ms; init_ms = src.init_ms; reps = src.reps; init_reps = src.init_reps; return *this; } oneapi::tbb::flow::input_node& get_out() { return clock_node; } void activate() { clock_node.activate(); } void reset() { reps = init_reps; } }; class push_button { oneapi::tbb::flow::graph& my_graph; oneapi::tbb::flow::overwrite_node push_button_node; public: push_button(oneapi::tbb::flow::graph& g) : my_graph(g), push_button_node(g) { push_button_node.try_put(low); } push_button(const push_button& src) : my_graph(src.my_graph), push_button_node(src.my_graph) { push_button_node.try_put(low); } ~push_button() {} // Assignment is ignored push_button& operator=(const push_button& src) { return *this; } oneapi::tbb::flow::overwrite_node& get_out() { return push_button_node; } void press() { push_button_node.try_put(high); } void release() { push_button_node.try_put(low); } }; class toggle { oneapi::tbb::flow::graph& my_graph; signal_t state; oneapi::tbb::flow::overwrite_node toggle_node; public: toggle(oneapi::tbb::flow::graph& g) : my_graph(g), state(undefined), toggle_node(g) {} toggle(const toggle& src) : my_graph(src.my_graph), state(undefined), toggle_node(src.my_graph) {} ~toggle() {} // Assignment ignored toggle& operator=(const toggle& src) { return *this; } oneapi::tbb::flow::overwrite_node& get_out() { return toggle_node; } void flip() { if (state == high) state = low; else state = high; toggle_node.try_put(state); } void activate() { state = low; toggle_node.try_put(state); } }; // Basic gates class buffer : public gate<1> { using gate<1>::my_graph; typedef gate<1>::ports_type ports_type; class buffer_body { signal_t state; bool touched; public: buffer_body() : state(undefined), touched(false) {} void operator()(const input_port_t::output_type& v, ports_type& p) { if (!touched || state != cast_to(v)) { state = cast_to(v); std::get<0>(p).try_put(state); touched = true; } } }; public: buffer(oneapi::tbb::flow::graph& g) : gate<1>(g, buffer_body()) {} buffer(const buffer& src) : gate<1>(src.my_graph, buffer_body()) {} ~buffer() {} }; class not_gate : public gate<1> { using gate<1>::my_graph; typedef gate<1>::ports_type ports_type; class not_body { signal_t port; bool touched; public: not_body() : port(undefined), touched(false) {} void operator()(const input_port_t::output_type& v, ports_type& p) { if (!touched || port != cast_to(v)) { port = cast_to(v); signal_t state = low; if (port == low) state = high; std::get<0>(p).try_put(state); touched = true; } } }; public: not_gate(oneapi::tbb::flow::graph& g) : gate<1>(g, not_body()) {} not_gate(const not_gate& src) : gate<1>(src.my_graph, not_body()) {} ~not_gate() {} }; template class and_gate : public gate { using gate::my_graph; typedef typename gate::ports_type ports_type; typedef typename gate::input_port_t::output_type from_input; class and_body { signal_t* ports; signal_t state; bool touched; public: and_body() : state(undefined), touched(false) { ports = new signal_t[N]; for (int i = 0; i < N; ++i) ports[i] = undefined; } void operator()(const from_input& v, ports_type& p) { ports[v.tag()] = cast_to(v); signal_t new_state = high; std::size_t i = 0; while (i < N) { if (ports[i] == low) { new_state = low; break; } else if (ports[i] == undefined && new_state != low) { new_state = undefined; } ++i; } if (!touched || state != new_state) { state = new_state; std::get<0>(p).try_put(state); touched = true; } } }; public: and_gate(oneapi::tbb::flow::graph& g) : gate(g, and_body()) {} and_gate(const and_gate& src) : gate(src.my_graph, and_body()) {} ~and_gate() {} }; template class or_gate : public gate { using gate::my_graph; typedef typename gate::ports_type ports_type; typedef typename gate::input_port_t::output_type from_input; class or_body { signal_t* ports; signal_t state; bool touched; public: or_body() : state(undefined), touched(false) { ports = new signal_t[N]; for (int i = 0; i < N; ++i) ports[i] = undefined; } void operator()(const from_input& v, ports_type& p) { ports[v.tag()] = cast_to(v); signal_t new_state = low; std::size_t i = 0; while (i < N) { if (ports[i] == high) { new_state = high; break; } else if (ports[i] == undefined && new_state != high) { new_state = undefined; } ++i; } if (!touched || state != new_state) { state = new_state; std::get<0>(p).try_put(state); touched = true; } } }; public: or_gate(oneapi::tbb::flow::graph& g) : gate(g, or_body()) {} or_gate(const or_gate& src) : gate(src.my_graph, or_body()) {} ~or_gate() {} }; template class xor_gate : public gate { using gate::my_graph; typedef typename gate::ports_type ports_type; typedef typename gate::input_port_t input_port_t; class xor_body { signal_t* ports; signal_t state; bool touched; public: xor_body() : state(undefined), touched(false) { ports = new signal_t[N]; for (int i = 0; i < N; ++i) ports[i] = undefined; } void operator()(const typename input_port_t::output_type& v, ports_type& p) { ports[v.tag()] = cast_to(v); signal_t new_state = low; std::size_t i = 0, highs = 0; while (i < N) { if (ports[i] == undefined) { new_state = undefined; } else if (ports[i] == high && new_state == low) { new_state = high; ++highs; } else if (ports[i] == high && highs > 0) { new_state = low; break; } else if (ports[i] == high) { ++highs; } ++i; } if (!touched || state != new_state) { state = new_state; std::get<0>(p).try_put(state); touched = true; } } }; public: xor_gate(oneapi::tbb::flow::graph& g) : gate(g, xor_body()) {} xor_gate(const xor_gate& src) : gate(src.my_graph, xor_body()) {} ~xor_gate() {} }; template class nor_gate : public gate { using gate::my_graph; typedef typename gate::ports_type ports_type; typedef typename gate::input_port_t input_port_t; class nor_body { signal_t* ports; signal_t state; bool touched; public: nor_body() : state(undefined), touched(false) { ports = new signal_t[N]; for (int i = 0; i < N; ++i) ports[i] = undefined; } void operator()(const typename input_port_t::output_type& v, ports_type& p) { ports[v.tag()] = cast_to(v); signal_t new_state = low; std::size_t i = 0; while (i < N) { if (ports[i] == high) { new_state = high; break; } else if (ports[i] == undefined && new_state != high) { new_state = undefined; } ++i; } if (new_state == high) new_state = low; else if (new_state == low) new_state = high; if (!touched || state != new_state) { state = new_state; std::get<0>(p).try_put(state); touched = true; } } }; public: nor_gate(oneapi::tbb::flow::graph& g) : gate(g, nor_body()) {} nor_gate(const nor_gate& src) : gate(src.my_graph, nor_body()) {} ~nor_gate() {} }; // Output devices class led { class led_body { signal_t& state; std::string& label; bool report_changes; bool touched; public: led_body(signal_t& s, std::string& l, bool r) : state(s), label(l), report_changes(r), touched(false) {} oneapi::tbb::flow::continue_msg operator()(signal_t b) { if (!touched || b != state) { state = b; if (state != undefined && report_changes) { if (state) printf("%s: (*)\n", label.c_str()); else printf("%s: ( )\n", label.c_str()); } touched = false; } return oneapi::tbb::flow::continue_msg(); } }; oneapi::tbb::flow::graph& my_graph; std::string label; signal_t state; bool report_changes; oneapi::tbb::flow::function_node led_node; public: led(oneapi::tbb::flow::graph& g, std::string l, bool rc = false) : my_graph(g), label(l), state(undefined), report_changes(rc), led_node(g, 1, led_body(state, label, report_changes)) {} led(const led& src) : my_graph(src.my_graph), label(src.label), state(undefined), report_changes(src.report_changes), led_node(src.my_graph, 1, led_body(state, label, report_changes)) {} ~led() {} // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph // state is set to undefined so that next signal changes it led& operator=(const led& src) { label = src.label; state = undefined; report_changes = src.report_changes; return *this; } oneapi::tbb::flow::function_node& get_in() { return led_node; } void display() { if (state == high) printf("%s: (*)\n", label.c_str()); else if (state == low) printf("%s: ( )\n", label.c_str()); else printf("%s: (u)\n", label.c_str()); } signal_t get_value() { return state; } }; class digit : public gate<4> { using gate<4>::my_graph; typedef gate<4>::ports_type ports_type; typedef gate<4>::input_port_t input_port_t; class digit_body { signal_t ports[4]; static const int N = 4; unsigned int& state; std::string& label; bool& report_changes; public: digit_body(unsigned int& s, std::string& l, bool& r) : state(s), label(l), report_changes(r) { for (int i = 0; i < N; ++i) ports[i] = undefined; } void operator()(const input_port_t::output_type& v, ports_type& p) { unsigned int new_state = 0; ports[v.tag()] = cast_to(v); if (ports[0] == high) ++new_state; if (ports[1] == high) new_state += 2; if (ports[2] == high) new_state += 4; if (ports[3] == high) new_state += 8; if (state != new_state) { state = new_state; if (report_changes) { printf("%s: %x\n", label.c_str(), state); } } } }; std::string label; unsigned int state; bool report_changes; public: digit(oneapi::tbb::flow::graph& g, std::string l, bool rc = false) : gate<4>(g, digit_body(state, label, report_changes)), label(l), state(0), report_changes(rc) {} digit(const digit& src) : gate<4>(src.my_graph, digit_body(state, label, report_changes)), label(src.label), state(0), report_changes(src.report_changes) {} ~digit() {} // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph. // state is reset as in constructors digit& operator=(const digit& src) { label = src.label; state = 0; report_changes = src.report_changes; return *this; } void display() { printf("%s: %x\n", label.c_str(), state); } unsigned int get_value() { return state; } }; #endif /* TBB_examples_logic_sim_basic_H */