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 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 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> 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 } 82 virtual ~gate() {} 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> 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 } 119 virtual ~gate() {} 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> 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 } 158 virtual ~gate() {} 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> 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 } 200 virtual ~gate() {} 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: 220 steady_signal(oneapi::tbb::flow::graph& g, signal_t v) 221 : my_graph(g), 222 init_signal(v), 223 signal_node(g) {} 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) {} 228 ~steady_signal() {} 229 // Assignment is ignored 230 steady_signal& operator=(const steady_signal& src) { 231 return *this; 232 } 233 oneapi::tbb::flow::write_once_node<signal_t>& get_out() { 234 return signal_node; 235 } 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: 248 clock_body(std::size_t& _ms, int& _reps) : ms(_ms), reps(_reps), val(low) {} 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: 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)) {} 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)) {} 284 ~pulse() {} 285 // Assignment changes the behavior of LHS to that of the RHS, but doesn't change owning graph 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 } 293 oneapi::tbb::flow::input_node<signal_t>& get_out() { 294 return clock_node; 295 } 296 void activate() { 297 clock_node.activate(); 298 } 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: 309 push_button(oneapi::tbb::flow::graph& g) : my_graph(g), push_button_node(g) { 310 push_button_node.try_put(low); 311 } 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 } 315 ~push_button() {} 316 // Assignment is ignored 317 push_button& operator=(const push_button& src) { 318 return *this; 319 } 320 oneapi::tbb::flow::overwrite_node<signal_t>& get_out() { 321 return push_button_node; 322 } 323 void press() { 324 push_button_node.try_put(high); 325 } 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: 337 toggle(oneapi::tbb::flow::graph& g) : my_graph(g), state(undefined), toggle_node(g) {} 338 toggle(const toggle& src) 339 : my_graph(src.my_graph), 340 state(undefined), 341 toggle_node(src.my_graph) {} 342 ~toggle() {} 343 // Assignment ignored 344 toggle& operator=(const toggle& src) { 345 return *this; 346 } 347 oneapi::tbb::flow::overwrite_node<signal_t>& get_out() { 348 return toggle_node; 349 } 350 void flip() { 351 if (state == high) 352 state = low; 353 else 354 state = high; 355 toggle_node.try_put(state); 356 } 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: 372 buffer_body() : state(undefined), touched(false) {} 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: 383 buffer(oneapi::tbb::flow::graph& g) : gate<1>(g, buffer_body()) {} 384 buffer(const buffer& src) : gate<1>(src.my_graph, buffer_body()) {} 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: 396 not_body() : port(undefined), touched(false) {} 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: 410 not_gate(oneapi::tbb::flow::graph& g) : gate<1>(g, not_body()) {} 411 not_gate(const not_gate& src) : gate<1>(src.my_graph, not_body()) {} 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: 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 } 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: 454 and_gate(oneapi::tbb::flow::graph& g) : gate<N>(g, and_body()) {} 455 and_gate(const and_gate<N>& src) : gate<N>(src.my_graph, and_body()) {} 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: 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 } 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: 498 or_gate(oneapi::tbb::flow::graph& g) : gate<N>(g, or_body()) {} 499 or_gate(const or_gate& src) : gate<N>(src.my_graph, or_body()) {} 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: 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 } 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: 549 xor_gate(oneapi::tbb::flow::graph& g) : gate<N>(g, xor_body()) {} 550 xor_gate(const xor_gate& src) : gate<N>(src.my_graph, xor_body()) {} 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: 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 } 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: 597 nor_gate(oneapi::tbb::flow::graph& g) : gate<N>(g, nor_body()) {} 598 nor_gate(const nor_gate& src) : gate<N>(src.my_graph, nor_body()) {} 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: 611 led_body(signal_t& s, std::string& l, bool r) 612 : state(s), 613 label(l), 614 report_changes(r), 615 touched(false) {} 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: 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)) {} 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)) {} 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 652 led& operator=(const led& src) { 653 label = src.label; 654 state = undefined; 655 report_changes = src.report_changes; 656 return *this; 657 } 658 oneapi::tbb::flow::function_node<signal_t, oneapi::tbb::flow::continue_msg>& get_in() { 659 return led_node; 660 } 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 } 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: 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 } 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: 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) {} 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) {} 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 730 digit& operator=(const digit& src) { 731 label = src.label; 732 state = 0; 733 report_changes = src.report_changes; 734 return *this; 735 } 736 void display() { 737 printf("%s: %x\n", label.c_str(), state); 738 } 739 unsigned int get_value() { 740 return state; 741 } 742 }; 743 744 #endif /* TBB_examples_logic_sim_basic_H */ 745