├── .gitignore ├── Cargo.toml ├── Readme.md ├── asm ├── countdown.asm ├── high_ipc.asm ├── junk.asm ├── load_store.asm ├── many_mispredictions.asm ├── nested_countdown.asm ├── prnd.asm ├── program1.asm ├── program2.asm └── subroutine.asm ├── build.rs ├── cpu.yaml ├── src ├── backend │ ├── backend.rs │ ├── execution_unit.rs │ ├── mod.rs │ ├── physical_register.rs │ ├── register_alias_table.rs │ ├── reorder_buffer.rs │ └── reservation_station.rs ├── cpu.rs ├── cpu_tests.rs ├── frontend │ ├── frontend.rs │ └── mod.rs ├── instructions │ ├── instructions.rs │ └── mod.rs ├── loader │ ├── assembly.lalrpop │ ├── ast.rs │ ├── loader.rs │ └── mod.rs ├── main.rs └── memory_subsystem │ ├── memory_subsystem.rs │ ├── mod.rs │ └── store_buffer.rs └── todo.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | Cargo.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust_cpu_emulator" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [build-dependencies] 7 | lalrpop = "0.20.2" 8 | 9 | [dependencies] 10 | regex = "1.10.4" 11 | lalrpop-util = { version = "0.20.2", features = ["lexer", "unicode"] } 12 | 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_yaml = "0.8.26" 15 | 16 | structopt = "0.3.26" 17 | structopt-derive = "0.4.18" 18 | log = "0.4.22" -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Rust based ARM CPU emulator 2 | 3 | ## About The Project 4 | The primary aim of this project is to implement modern ARM processors in software using Rust. 5 | 6 | The goal is to offer insights into how modern processors might work. 7 | 8 | This project does not aim to provide a fast implementation; for that, code generation 9 | using binary translation would be significantly faster. 10 | 11 | ## Warning 12 | 13 | This is a toy project. I want to upgrade my Rust skills and I needed a serious 14 | enough challenge to work on. Also, the challenge should initially be without the need 15 | for concurrency control so that I get a better understanding of ownership. 16 | 17 | ### CPU features 18 | 19 | * Pipelined Execution 20 | * Super Scalar Execution 21 | * Out of Order Execution using Tomasulo's algorithm. So only RAW dependencies are preserved. 22 | * Speculative Execution 23 | * Branch prediction (static only ATM) 24 | * Store Buffer 25 | * Performance monitor although not exposed through model specific registers. 26 | 27 | ### Planned CPU features 28 | * Support for different data types (currently only dword) 29 | * One-way fences like LDAR, STLR, LDAPR. 30 | * Two-way fences like DMB 31 | * Serializing instructions like DSB 32 | * Exclusive access instructions like LDXR, STXR, LDAXR, STLXR 33 | * SMT (aka hyper-threading) 34 | * CMP (aka multicore) 35 | * Working cache (MESI based) 36 | * Write coalescing 37 | * Store buffer out-of-order commit to the cache 38 | * SVE (SIMD) 39 | * NEON (SIMD) 40 | 41 | ## Supported instructions 42 | 43 | ### Arithmetic instructions: 44 | * ADD 45 | * SUB 46 | * MUL 47 | * SDIV 48 | * NEG 49 | * RSB 50 | 51 | ### Bitwise logical instructions: 52 | * AND 53 | * ORR 54 | * EOR 55 | * MVN 56 | 57 | ### Memory access instructions: 58 | * LDR 59 | * STR 60 | 61 | ### Miscellaneous instructions: 62 | * MOV 63 | 64 | ### Synchronization instructions; 65 | * NOP 66 | * DSB 67 | 68 | ### Branch & control instructions: 69 | * CMP 70 | * TST 71 | * TEQ 72 | * B 73 | * BX 74 | * BL 75 | * RET 76 | * CBZ 77 | * CBNZ 78 | * BEQ 79 | * BNE 80 | * BLE 81 | * BLT 82 | * BGE 83 | * BGT 84 | 85 | ### Unofficial instructions 86 | * PRINTR: prints the value of a register. 87 | 88 | More instructions will be added over time. 89 | 90 | ## How to run 91 | 92 | ```bash 93 | cargo run -- --file asm/high_ipc.asm --config cpu.yaml 94 | ``` 95 | 96 | -------------------------------------------------------------------------------- /asm/countdown.asm: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | _start: 5 | MOV r1, #10; 6 | PRINTR r1; 7 | _again: 8 | SUB r1, r1, #1; 9 | PRINTR r1; 10 | CBNZ r1, _again; 11 | MOV r1, #10000; 12 | PRINTR r1; -------------------------------------------------------------------------------- /asm/high_ipc.asm: -------------------------------------------------------------------------------- 1 | .data 2 | .text 3 | .global _start 4 | _start: 5 | NOP; 6 | NOP; 7 | NOP; 8 | NOP; 9 | NOP; 10 | NOP; 11 | NOP; 12 | NOP; 13 | NOP; 14 | NOP; 15 | NOP; 16 | NOP; 17 | NOP; 18 | NOP; 19 | NOP; 20 | NOP; 21 | NOP; 22 | NOP; 23 | NOP; 24 | NOP; 25 | NOP; 26 | NOP; 27 | NOP; 28 | NOP; 29 | NOP; 30 | NOP; 31 | NOP; 32 | NOP; 33 | NOP; 34 | NOP; 35 | NOP; 36 | NOP; 37 | NOP; 38 | NOP; 39 | NOP; 40 | NOP; 41 | NOP; 42 | NOP; 43 | NOP; 44 | NOP; 45 | NOP; 46 | NOP; 47 | NOP; 48 | NOP; 49 | NOP; 50 | NOP; 51 | NOP; 52 | NOP; 53 | NOP; 54 | NOP; 55 | NOP; 56 | NOP; 57 | NOP; 58 | NOP; 59 | NOP; 60 | NOP; 61 | NOP; 62 | NOP; 63 | NOP; 64 | NOP; 65 | NOP; 66 | NOP; 67 | NOP; 68 | NOP; 69 | NOP; 70 | NOP; 71 | NOP; 72 | NOP; 73 | NOP; 74 | NOP; 75 | NOP; 76 | NOP; 77 | NOP; 78 | NOP; 79 | NOP; 80 | NOP; 81 | NOP; 82 | NOP; 83 | NOP; 84 | NOP; 85 | NOP; 86 | NOP; 87 | NOP; 88 | NOP; 89 | NOP; 90 | NOP; 91 | NOP; 92 | NOP; 93 | NOP; 94 | NOP; 95 | NOP; 96 | NOP; 97 | NOP; 98 | NOP; 99 | NOP; 100 | NOP; 101 | NOP; 102 | NOP; 103 | NOP; 104 | NOP; 105 | NOP; 106 | NOP; 107 | NOP; 108 | NOP; 109 | NOP; 110 | NOP; 111 | NOP; 112 | NOP; 113 | NOP; 114 | NOP; 115 | NOP; 116 | NOP; 117 | NOP; 118 | NOP; 119 | NOP; 120 | NOP; 121 | NOP; 122 | NOP; 123 | NOP; 124 | NOP; 125 | NOP; 126 | NOP; 127 | NOP; 128 | NOP; 129 | NOP; 130 | NOP; 131 | NOP; 132 | NOP; 133 | NOP; 134 | NOP; 135 | NOP; 136 | NOP; 137 | NOP; 138 | NOP; 139 | NOP; 140 | NOP; 141 | NOP; 142 | NOP; 143 | NOP; 144 | NOP; 145 | NOP; 146 | NOP; 147 | NOP; 148 | NOP; 149 | NOP; 150 | NOP; 151 | NOP; 152 | NOP; 153 | NOP; 154 | NOP; 155 | NOP; 156 | NOP; 157 | NOP; 158 | NOP; 159 | NOP; 160 | NOP; 161 | NOP; 162 | NOP; 163 | NOP; 164 | NOP; 165 | NOP; 166 | NOP; 167 | NOP; 168 | NOP; 169 | NOP; 170 | NOP; 171 | NOP; 172 | NOP; 173 | NOP; 174 | NOP; 175 | NOP; 176 | NOP; 177 | NOP; 178 | NOP; 179 | NOP; 180 | NOP; 181 | NOP; 182 | NOP; 183 | NOP; 184 | NOP; 185 | NOP; 186 | NOP; 187 | NOP; 188 | NOP; 189 | NOP; 190 | NOP; 191 | NOP; 192 | NOP; 193 | NOP; 194 | NOP; 195 | NOP; 196 | NOP; 197 | NOP; 198 | NOP; 199 | NOP; 200 | NOP; 201 | NOP; 202 | NOP; 203 | NOP; 204 | NOP; 205 | NOP; 206 | NOP; 207 | NOP; 208 | NOP; 209 | NOP; 210 | NOP; 211 | NOP; 212 | NOP; 213 | NOP; 214 | NOP; 215 | NOP; 216 | NOP; 217 | NOP; 218 | NOP; 219 | NOP; 220 | NOP; 221 | NOP; 222 | NOP; 223 | NOP; 224 | NOP; 225 | NOP; 226 | NOP; 227 | NOP; 228 | NOP; 229 | NOP; 230 | NOP; 231 | NOP; 232 | NOP; 233 | NOP; 234 | NOP; 235 | NOP; 236 | NOP; 237 | NOP; 238 | NOP; 239 | NOP; 240 | NOP; 241 | NOP; 242 | NOP; 243 | NOP; 244 | NOP; 245 | NOP; 246 | NOP; 247 | NOP; 248 | NOP; 249 | NOP; 250 | NOP; 251 | NOP; 252 | NOP; 253 | NOP; 254 | NOP; 255 | NOP; 256 | NOP; 257 | NOP; 258 | NOP; 259 | NOP; 260 | NOP; 261 | NOP; 262 | NOP; 263 | NOP; 264 | NOP; 265 | NOP; 266 | NOP; 267 | NOP; 268 | NOP; 269 | NOP; 270 | NOP; 271 | NOP; 272 | NOP; 273 | NOP; 274 | NOP; 275 | NOP; 276 | NOP; 277 | NOP; 278 | NOP; 279 | NOP; 280 | NOP; 281 | NOP; 282 | NOP; 283 | NOP; 284 | NOP; 285 | NOP; 286 | NOP; 287 | NOP; 288 | NOP; 289 | NOP; 290 | NOP; 291 | NOP; 292 | NOP; 293 | NOP; 294 | NOP; 295 | NOP; 296 | NOP; 297 | NOP; 298 | NOP; 299 | NOP; 300 | NOP; 301 | NOP; 302 | NOP; 303 | NOP; 304 | NOP; 305 | NOP; 306 | NOP; 307 | NOP; 308 | NOP; 309 | NOP; 310 | NOP; 311 | NOP; 312 | NOP; 313 | NOP; 314 | NOP; 315 | NOP; 316 | NOP; 317 | NOP; 318 | NOP; 319 | NOP; 320 | NOP; 321 | NOP; 322 | NOP; 323 | NOP; 324 | NOP; 325 | NOP; 326 | NOP; 327 | NOP; 328 | NOP; 329 | NOP; 330 | NOP; 331 | NOP; 332 | NOP; 333 | NOP; 334 | NOP; 335 | NOP; 336 | NOP; 337 | NOP; 338 | NOP; 339 | NOP; 340 | NOP; 341 | NOP; 342 | NOP; 343 | NOP; 344 | NOP; 345 | NOP; 346 | NOP; 347 | NOP; 348 | NOP; 349 | NOP; 350 | NOP; 351 | NOP; 352 | NOP; 353 | NOP; 354 | NOP; 355 | NOP; 356 | NOP; 357 | NOP; 358 | NOP; 359 | NOP; 360 | NOP; 361 | NOP; 362 | NOP; 363 | NOP; 364 | NOP; 365 | NOP; 366 | NOP; 367 | NOP; 368 | NOP; 369 | NOP; 370 | NOP; 371 | NOP; 372 | NOP; 373 | NOP; 374 | NOP; 375 | NOP; 376 | NOP; 377 | NOP; 378 | NOP; 379 | NOP; 380 | NOP; 381 | NOP; 382 | NOP; 383 | NOP; 384 | NOP; 385 | NOP; 386 | NOP; 387 | NOP; 388 | NOP; 389 | NOP; 390 | NOP; 391 | NOP; 392 | NOP; 393 | NOP; 394 | NOP; 395 | NOP; 396 | NOP; 397 | NOP; 398 | NOP; 399 | NOP; 400 | NOP; 401 | NOP; 402 | NOP; 403 | NOP; 404 | NOP; 405 | NOP; 406 | NOP; 407 | NOP; 408 | NOP; 409 | NOP; 410 | NOP; 411 | NOP; 412 | NOP; 413 | NOP; 414 | NOP; 415 | NOP; 416 | NOP; 417 | NOP; 418 | NOP; 419 | NOP; 420 | NOP; 421 | NOP; 422 | NOP; 423 | NOP; 424 | NOP; 425 | NOP; 426 | NOP; 427 | NOP; 428 | NOP; 429 | NOP; 430 | NOP; 431 | NOP; 432 | NOP; 433 | NOP; 434 | NOP; 435 | NOP; 436 | NOP; 437 | NOP; 438 | NOP; 439 | NOP; 440 | NOP; 441 | NOP; 442 | NOP; 443 | NOP; 444 | NOP; 445 | NOP; 446 | NOP; 447 | NOP; 448 | NOP; 449 | NOP; 450 | NOP; 451 | NOP; 452 | NOP; 453 | NOP; 454 | NOP; 455 | NOP; 456 | NOP; 457 | NOP; 458 | NOP; 459 | NOP; 460 | NOP; 461 | NOP; 462 | NOP; 463 | NOP; 464 | NOP; 465 | NOP; 466 | NOP; 467 | NOP; 468 | NOP; 469 | NOP; 470 | NOP; 471 | NOP; 472 | NOP; 473 | NOP; 474 | NOP; 475 | NOP; 476 | NOP; 477 | NOP; 478 | NOP; 479 | NOP; 480 | NOP; 481 | NOP; 482 | NOP; 483 | NOP; 484 | NOP; 485 | NOP; 486 | NOP; 487 | NOP; 488 | NOP; 489 | NOP; 490 | NOP; 491 | NOP; 492 | NOP; 493 | NOP; 494 | NOP; 495 | NOP; 496 | NOP; 497 | NOP; 498 | NOP; 499 | NOP; 500 | NOP; 501 | NOP; 502 | NOP; 503 | NOP; 504 | NOP; 505 | NOP; 506 | NOP; 507 | NOP; 508 | NOP; 509 | NOP; 510 | NOP; 511 | NOP; 512 | NOP; 513 | NOP; 514 | NOP; 515 | NOP; 516 | NOP; 517 | NOP; 518 | NOP; 519 | NOP; 520 | NOP; 521 | NOP; 522 | NOP; 523 | NOP; 524 | NOP; 525 | NOP; 526 | NOP; 527 | NOP; 528 | NOP; 529 | NOP; 530 | NOP; 531 | NOP; 532 | NOP; 533 | NOP; 534 | NOP; 535 | NOP; 536 | NOP; 537 | NOP; 538 | NOP; 539 | NOP; 540 | NOP; 541 | NOP; 542 | NOP; 543 | NOP; 544 | NOP; 545 | NOP; 546 | NOP; 547 | NOP; 548 | NOP; 549 | NOP; 550 | NOP; 551 | NOP; 552 | NOP; 553 | NOP; 554 | NOP; 555 | NOP; 556 | NOP; 557 | NOP; 558 | NOP; 559 | NOP; 560 | NOP; 561 | NOP; 562 | NOP; 563 | NOP; 564 | NOP; 565 | NOP; 566 | NOP; 567 | NOP; 568 | NOP; 569 | NOP; 570 | NOP; 571 | NOP; 572 | NOP; 573 | NOP; 574 | NOP; 575 | NOP; 576 | NOP; 577 | NOP; 578 | NOP; 579 | NOP; 580 | NOP; 581 | NOP; 582 | NOP; 583 | NOP; 584 | NOP; 585 | NOP; 586 | NOP; 587 | NOP; 588 | NOP; 589 | NOP; 590 | NOP; 591 | NOP; 592 | NOP; 593 | NOP; 594 | NOP; 595 | NOP; 596 | NOP; 597 | NOP; 598 | NOP; 599 | NOP; 600 | NOP; 601 | NOP; 602 | NOP; 603 | NOP; 604 | NOP; 605 | NOP; 606 | NOP; 607 | NOP; 608 | NOP; 609 | NOP; 610 | NOP; 611 | NOP; 612 | NOP; 613 | NOP; 614 | NOP; 615 | NOP; 616 | NOP; 617 | NOP; 618 | NOP; 619 | NOP; 620 | NOP; 621 | NOP; 622 | NOP; 623 | NOP; 624 | NOP; 625 | NOP; 626 | NOP; 627 | NOP; 628 | NOP; 629 | NOP; 630 | NOP; 631 | NOP; 632 | NOP; 633 | NOP; 634 | NOP; 635 | NOP; 636 | NOP; 637 | NOP; 638 | NOP; 639 | NOP; 640 | NOP; 641 | NOP; 642 | NOP; 643 | NOP; 644 | NOP; 645 | NOP; 646 | NOP; 647 | NOP; 648 | NOP; 649 | NOP; 650 | NOP; 651 | NOP; 652 | NOP; 653 | NOP; 654 | NOP; 655 | NOP; 656 | NOP; 657 | NOP; 658 | NOP; 659 | NOP; 660 | NOP; 661 | NOP; 662 | NOP; 663 | NOP; 664 | NOP; 665 | NOP; 666 | NOP; 667 | NOP; 668 | NOP; 669 | NOP; 670 | NOP; 671 | NOP; 672 | NOP; 673 | NOP; 674 | NOP; 675 | NOP; 676 | NOP; 677 | NOP; 678 | NOP; 679 | NOP; 680 | NOP; 681 | NOP; 682 | NOP; 683 | NOP; 684 | NOP; 685 | NOP; 686 | NOP; 687 | NOP; 688 | NOP; 689 | NOP; 690 | NOP; 691 | NOP; 692 | NOP; 693 | NOP; 694 | NOP; 695 | NOP; 696 | NOP; 697 | NOP; 698 | NOP; 699 | NOP; 700 | NOP; 701 | NOP; 702 | NOP; 703 | NOP; 704 | NOP; 705 | NOP; 706 | NOP; 707 | NOP; 708 | NOP; 709 | NOP; 710 | NOP; 711 | NOP; 712 | NOP; 713 | NOP; 714 | NOP; 715 | NOP; 716 | NOP; 717 | NOP; 718 | NOP; 719 | NOP; 720 | NOP; 721 | NOP; 722 | NOP; 723 | NOP; 724 | NOP; 725 | NOP; 726 | NOP; 727 | NOP; 728 | NOP; 729 | NOP; 730 | NOP; 731 | NOP; 732 | NOP; 733 | NOP; 734 | NOP; 735 | NOP; 736 | NOP; 737 | NOP; 738 | NOP; 739 | NOP; 740 | NOP; 741 | NOP; 742 | NOP; 743 | NOP; 744 | NOP; 745 | NOP; 746 | NOP; 747 | NOP; 748 | NOP; 749 | NOP; 750 | NOP; 751 | NOP; 752 | NOP; 753 | NOP; 754 | NOP; 755 | NOP; 756 | NOP; 757 | NOP; 758 | NOP; 759 | NOP; 760 | NOP; 761 | NOP; 762 | NOP; 763 | NOP; 764 | NOP; 765 | NOP; 766 | NOP; 767 | NOP; 768 | NOP; 769 | NOP; 770 | NOP; 771 | NOP; 772 | NOP; 773 | NOP; 774 | NOP; 775 | NOP; 776 | NOP; 777 | NOP; 778 | NOP; 779 | NOP; 780 | NOP; 781 | NOP; 782 | NOP; 783 | NOP; 784 | NOP; 785 | NOP; 786 | NOP; 787 | NOP; 788 | NOP; 789 | NOP; 790 | NOP; 791 | NOP; 792 | NOP; 793 | NOP; 794 | NOP; 795 | NOP; 796 | NOP; 797 | NOP; 798 | NOP; 799 | NOP; 800 | NOP; 801 | NOP; 802 | NOP; 803 | NOP; 804 | NOP; 805 | NOP; 806 | NOP; 807 | NOP; 808 | NOP; 809 | NOP; 810 | NOP; 811 | NOP; 812 | NOP; 813 | NOP; 814 | NOP; 815 | NOP; 816 | NOP; 817 | NOP; 818 | NOP; 819 | NOP; 820 | NOP; 821 | NOP; 822 | NOP; 823 | NOP; 824 | NOP; 825 | NOP; 826 | NOP; 827 | NOP; 828 | NOP; 829 | NOP; 830 | NOP; 831 | NOP; 832 | NOP; 833 | NOP; 834 | NOP; 835 | NOP; 836 | NOP; 837 | NOP; 838 | NOP; 839 | NOP; 840 | NOP; 841 | NOP; 842 | NOP; 843 | NOP; 844 | NOP; 845 | NOP; 846 | NOP; 847 | NOP; 848 | NOP; 849 | NOP; 850 | NOP; 851 | NOP; 852 | NOP; 853 | NOP; 854 | NOP; 855 | NOP; 856 | NOP; 857 | NOP; 858 | NOP; 859 | NOP; 860 | NOP; 861 | NOP; 862 | NOP; 863 | NOP; 864 | NOP; 865 | NOP; 866 | NOP; 867 | NOP; 868 | NOP; 869 | NOP; 870 | NOP; 871 | NOP; 872 | NOP; 873 | NOP; 874 | NOP; 875 | NOP; 876 | NOP; 877 | NOP; 878 | NOP; 879 | NOP; 880 | NOP; 881 | NOP; 882 | NOP; 883 | NOP; 884 | NOP; 885 | NOP; 886 | NOP; 887 | NOP; 888 | NOP; 889 | NOP; 890 | NOP; 891 | NOP; 892 | NOP; 893 | NOP; 894 | NOP; 895 | NOP; 896 | NOP; 897 | NOP; 898 | NOP; 899 | NOP; 900 | NOP; 901 | NOP; 902 | NOP; 903 | NOP; 904 | NOP; 905 | NOP; 906 | NOP; 907 | NOP; 908 | NOP; 909 | NOP; 910 | NOP; 911 | NOP; 912 | NOP; 913 | NOP; 914 | NOP; 915 | NOP; 916 | NOP; 917 | NOP; 918 | NOP; 919 | NOP; 920 | NOP; 921 | NOP; 922 | NOP; 923 | NOP; 924 | NOP; 925 | NOP; 926 | NOP; 927 | NOP; 928 | NOP; 929 | NOP; 930 | NOP; 931 | NOP; 932 | NOP; 933 | NOP; 934 | NOP; 935 | NOP; 936 | NOP; 937 | NOP; 938 | NOP; 939 | NOP; 940 | NOP; 941 | NOP; 942 | NOP; 943 | NOP; 944 | NOP; 945 | NOP; 946 | NOP; 947 | NOP; 948 | NOP; 949 | NOP; 950 | NOP; 951 | NOP; 952 | NOP; 953 | NOP; 954 | NOP; 955 | NOP; 956 | NOP; 957 | NOP; 958 | NOP; 959 | NOP; 960 | NOP; 961 | NOP; 962 | NOP; 963 | NOP; 964 | NOP; 965 | NOP; 966 | NOP; 967 | NOP; 968 | NOP; 969 | NOP; 970 | NOP; 971 | NOP; 972 | NOP; 973 | NOP; 974 | NOP; 975 | NOP; 976 | NOP; 977 | NOP; 978 | NOP; 979 | NOP; 980 | NOP; 981 | NOP; 982 | NOP; 983 | NOP; 984 | NOP; 985 | NOP; 986 | NOP; 987 | NOP; 988 | NOP; 989 | NOP; 990 | NOP; 991 | NOP; 992 | NOP; 993 | NOP; 994 | NOP; 995 | NOP; 996 | NOP; 997 | NOP; 998 | NOP; 999 | NOP; 1000 | NOP; 1001 | NOP; 1002 | NOP; 1003 | NOP; 1004 | NOP; 1005 | NOP; 1006 | NOP; 1007 | NOP; 1008 | NOP; 1009 | NOP; 1010 | NOP; 1011 | NOP; 1012 | NOP; 1013 | NOP; 1014 | NOP; 1015 | NOP; 1016 | NOP; 1017 | NOP; 1018 | NOP; 1019 | NOP; 1020 | NOP; 1021 | NOP; 1022 | NOP; 1023 | NOP; 1024 | NOP; 1025 | NOP; 1026 | NOP; 1027 | NOP; 1028 | NOP; 1029 | NOP; 1030 | NOP; 1031 | NOP; 1032 | NOP; 1033 | NOP; 1034 | NOP; 1035 | NOP; 1036 | NOP; 1037 | NOP; 1038 | NOP; 1039 | NOP; 1040 | NOP; 1041 | NOP; 1042 | NOP; 1043 | NOP; 1044 | NOP; 1045 | NOP; 1046 | NOP; 1047 | NOP; 1048 | NOP; 1049 | NOP; 1050 | NOP; 1051 | NOP; 1052 | NOP; 1053 | NOP; 1054 | NOP; 1055 | NOP; 1056 | NOP; 1057 | NOP; 1058 | NOP; 1059 | NOP; 1060 | NOP; 1061 | NOP; 1062 | NOP; 1063 | NOP; 1064 | NOP; 1065 | NOP; 1066 | NOP; 1067 | NOP; 1068 | NOP; 1069 | NOP; 1070 | NOP; 1071 | NOP; 1072 | NOP; 1073 | NOP; 1074 | NOP; 1075 | NOP; 1076 | NOP; 1077 | NOP; 1078 | NOP; 1079 | NOP; 1080 | NOP; 1081 | NOP; 1082 | NOP; 1083 | NOP; 1084 | NOP; 1085 | NOP; 1086 | NOP; 1087 | NOP; 1088 | NOP; 1089 | NOP; 1090 | NOP; 1091 | NOP; 1092 | NOP; 1093 | NOP; 1094 | NOP; 1095 | NOP; 1096 | NOP; 1097 | NOP; 1098 | NOP; 1099 | NOP; 1100 | NOP; 1101 | NOP; 1102 | NOP; 1103 | NOP; 1104 | NOP; 1105 | NOP; 1106 | NOP; 1107 | NOP; 1108 | NOP; 1109 | NOP; 1110 | NOP; 1111 | NOP; 1112 | NOP; 1113 | NOP; 1114 | NOP; 1115 | NOP; 1116 | NOP; 1117 | NOP; 1118 | NOP; 1119 | NOP; 1120 | NOP; 1121 | NOP; 1122 | NOP; 1123 | NOP; 1124 | NOP; 1125 | NOP; 1126 | NOP; 1127 | NOP; 1128 | NOP; 1129 | NOP; 1130 | NOP; 1131 | NOP; 1132 | NOP; 1133 | NOP; 1134 | NOP; 1135 | NOP; 1136 | NOP; 1137 | NOP; 1138 | NOP; 1139 | NOP; 1140 | NOP; 1141 | NOP; 1142 | NOP; 1143 | NOP; 1144 | NOP; 1145 | NOP; 1146 | NOP; 1147 | NOP; 1148 | NOP; 1149 | NOP; 1150 | NOP; 1151 | NOP; 1152 | NOP; 1153 | NOP; 1154 | NOP; 1155 | NOP; 1156 | NOP; 1157 | NOP; 1158 | NOP; 1159 | NOP; 1160 | NOP; 1161 | NOP; 1162 | NOP; 1163 | NOP; 1164 | NOP; 1165 | B _start; -------------------------------------------------------------------------------- /asm/junk.asm: -------------------------------------------------------------------------------- 1 | .text 2 | MOV r1, #999999999999999999999999999999999999999; 3 | -------------------------------------------------------------------------------- /asm/load_store.asm: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .data 4 | var_a: .dword 10 5 | var_b: .dword 20 6 | var_c: .dword 0 7 | 8 | .text 9 | 10 | _start: 11 | MOV r0, =var_a; 12 | LDR r0, [r0]; 13 | MOV r1, =var_b; 14 | LDR r1, [r1]; 15 | ADD r2, r0, r1; 16 | MOV r0, =var_c; 17 | STR r2, [r0]; 18 | PRINTR r0; -------------------------------------------------------------------------------- /asm/many_mispredictions.asm: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .section .text 4 | 5 | _print_one: 6 | MOV r3, #1; 7 | PRINTR r3; 8 | BX lr; 9 | 10 | _print_zero: 11 | MOV r3, #0; 12 | PRINTR r3; 13 | BX lr; 14 | 15 | _start: 16 | MOV r0, #10; 17 | _loop: 18 | SUB r0, r0, #1; 19 | MOV r1, r1, #2; 20 | CBZ r1, _print_zero; 21 | CBNZ r1, _print_one; 22 | CBNZ r0, _loop; 23 | -------------------------------------------------------------------------------- /asm/nested_countdown.asm: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | _start: 5 | MOV r0, #3; 6 | 7 | _again_outer: 8 | MOV r1, #3; 9 | 10 | _again_inner: 11 | SUB r1, r1, #1; 12 | PRINTR r1; 13 | ADD r4, r4, #1; 14 | PRINTR r4; 15 | CBNZ r1, _again_inner; 16 | 17 | SUB r0, r0, #1; 18 | PRINTR r0; 19 | CBNZ r0, _again_outer; 20 | -------------------------------------------------------------------------------- /asm/prnd.asm: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .text 4 | 5 | generate_random: 6 | MUL r0, r0, r1; 7 | ADD r0, r0, r2; 8 | AND r0, r0, r3; 9 | BX lr; 10 | 11 | _start: 12 | MOV r0, #12345; 13 | MOV r1, #1103515245; 14 | MOV r2, #12345; 15 | MOV r3, #80000000; 16 | 17 | BL generate_random; 18 | PRINTR r0; 19 | BL generate_random; 20 | PRINTR r0; 21 | BL generate_random; 22 | PRINTR r0; 23 | -------------------------------------------------------------------------------- /asm/program1.asm: -------------------------------------------------------------------------------- 1 | .global start 2 | .data 3 | var_a: .dword 40 4 | var_b: .dword 0 5 | .text 6 | start: 7 | MOV r1, =var_a; 8 | LDR r1, [r1]; 9 | PRINTR r1; 10 | again: 11 | PRINTR r1; 12 | SUB r1, r1, #1; 13 | PRINTR r1; 14 | MOV r2, r1; 15 | MOV r3, =var_b; 16 | STR r2, [r3]; 17 | CBNZ r1, again; 18 | MOV r1, #100; 19 | PRINTR r1; 20 | -------------------------------------------------------------------------------- /asm/program2.asm: -------------------------------------------------------------------------------- 1 | .global start 2 | .section .data 3 | a: .dword 10 4 | .section .text 5 | 6 | start: 7 | ADD r0, r0, #1; 8 | PRINTR r0; 9 | PRINTR pc; 10 | B start; -------------------------------------------------------------------------------- /asm/subroutine.asm: -------------------------------------------------------------------------------- 1 | .global _start 2 | 3 | .section .data 4 | a1: .dword 1 5 | a2: .dword 1 6 | loop_count: .dword 10 7 | .section .text 8 | 9 | _add_numbers: 10 | ADD r2, r0, r1; 11 | BX lr; 12 | 13 | _start: 14 | MOV r0, =a1; 15 | LDR r0, [r0]; 16 | 17 | MOV r1, =a2; 18 | LDR r1, [r1]; 19 | 20 | MOV r3, =loop_count; 21 | LDR r3, [r3]; 22 | _loop: 23 | BL _add_numbers; 24 | PRINTR r2; 25 | MOV r0, r2; 26 | 27 | PRINTR r3; 28 | SUB r3, r3, #1; 29 | CBNZ r3, _loop; 30 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | lalrpop::process_root().unwrap(); 3 | } -------------------------------------------------------------------------------- /cpu.yaml: -------------------------------------------------------------------------------- 1 | # The number of physical registers 2 | phys_reg_count: 64 3 | # The number of instructions the frontend can fetch/decode per clock cycle. 4 | frontend_n_wide: 4 5 | # The size of the instruction queue between frontend and backend 6 | instr_queue_capacity: 64 7 | # The frequency of the CPU in Hz. 8 | frequency_hz: 40 9 | # The number of reservation stations 10 | rs_count: 100 11 | # The size of the memory in DWords 12 | memory_size: 128 13 | # The capacity of the store buffer 14 | sb_capacity: 16 15 | # The number of line fill buffers; currently there are no line fill buffers 16 | # it is just a limit of the number of stores that can commit to memory 17 | # per clock cycle (there is also no cache) 18 | lfb_count: 4 19 | # The capacity of the reorder buffer 20 | rob_capacity: 32 21 | # The number of execution units 22 | eu_count: 10 23 | # Various trace flags that helps to see what happens to individual instructions 24 | trace: 25 | decode: false 26 | issue: false 27 | allocate_rs: false 28 | dispatch: true 29 | execute: true 30 | retire: true 31 | pipeline_flush: true 32 | # The number of instructions that can retire per clock cycle 33 | retire_n_wide: 2 34 | # The number of instructions that can be dispatched (sent to execution units) every clock cycle 35 | dispatch_n_wide: 2 36 | # The number of instructions that can be issued to the ROB or finding reservation stations, every clock cycle 37 | issue_n_wide: 2 38 | # The delay between writing the CPU stats. A value of 0 means that stats are disabled. 39 | stats_seconds: 1 -------------------------------------------------------------------------------- /src/backend/backend.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | 4 | use crate::backend::execution_unit::{EUState, EUTable}; 5 | use crate::backend::physical_register::PhysRegFile; 6 | use crate::backend::register_alias_table::RAT; 7 | use crate::backend::reorder_buffer::{ROB, ROBSlotState}; 8 | use crate::backend::reservation_station::{RenamedRegister, RS, RSBranch, RSBranchTarget, RSDataProcessing, RSInstr, RSLoadStore, RSOperand2, RSPrintr, RSState, RSTable}; 9 | use crate::cpu::{ArgRegFile, CPSR, CPUConfig, LR, PC, PerfCounters, Trace}; 10 | use crate::frontend::frontend::FrontendControl; 11 | use crate::instructions::instructions::{BranchTarget, ConditionCode, DWordType, Instr, InstrQueue, Opcode, Operand2, RegisterType}; 12 | use crate::memory_subsystem::memory_subsystem::MemorySubsystem; 13 | 14 | pub struct CDBBroadcast { 15 | pub phys_reg: RegisterType, 16 | pub value: DWordType, 17 | } 18 | 19 | pub(crate) struct Backend { 20 | instr_queue: Rc>, 21 | arch_reg_file: Rc>, 22 | memory_subsystem: Rc>, 23 | frontend_control: Rc>, 24 | rs_table: RSTable, 25 | phys_reg_file: Rc>, 26 | rat: RAT, 27 | rob: Rc>, 28 | eu_table: EUTable, 29 | trace: Trace, 30 | retire_n_wide: u8, 31 | dispatch_n_wide: u8, 32 | issue_n_wide: u8, 33 | cdb_broadcast_buffer: Rc>>, 34 | pub(crate) exit: bool, 35 | perf_counters: Rc>, 36 | } 37 | 38 | impl Backend { 39 | pub(crate) fn new( 40 | cpu_config: &CPUConfig, 41 | instr_queue: &Rc>, 42 | memory_subsystem: &Rc>, 43 | arch_reg_file: &Rc>, 44 | frontend_control: &Rc>, 45 | perf_counters: &Rc>, 46 | ) -> Backend { 47 | let phys_reg_file = Rc::new(RefCell::new(PhysRegFile::new(cpu_config.phys_reg_count))); 48 | let broadcast_buffer = Rc::new(RefCell::new(Vec::with_capacity(cpu_config.eu_count as usize))); 49 | 50 | Backend { 51 | trace: cpu_config.trace.clone(), 52 | instr_queue: Rc::clone(instr_queue), 53 | memory_subsystem: Rc::clone(&memory_subsystem), 54 | arch_reg_file: Rc::clone(arch_reg_file), 55 | rs_table: RSTable::new(cpu_config.rs_count), 56 | phys_reg_file: Rc::clone(&phys_reg_file), 57 | rat: RAT::new(cpu_config.phys_reg_count), 58 | rob: Rc::new(RefCell::new(ROB::new(cpu_config.rob_capacity))), 59 | eu_table: EUTable::new(cpu_config, &memory_subsystem, &phys_reg_file, &perf_counters, &broadcast_buffer), 60 | retire_n_wide: cpu_config.retire_n_wide, 61 | dispatch_n_wide: cpu_config.dispatch_n_wide, 62 | issue_n_wide: cpu_config.issue_n_wide, 63 | cdb_broadcast_buffer: Rc::clone(&broadcast_buffer), 64 | frontend_control: Rc::clone(frontend_control), 65 | exit: false, 66 | perf_counters: Rc::clone(perf_counters), 67 | } 68 | } 69 | 70 | pub(crate) fn do_cycle(&mut self) { 71 | self.cycle_retire(); 72 | self.cycle_eu_table(); 73 | self.cdb_broadcast(); 74 | debug_assert!(self.cdb_broadcast_buffer.borrow().is_empty()); 75 | self.cycle_dispatch(); 76 | self.cycle_rs_allocation(); 77 | self.cycle_issue(); 78 | } 79 | 80 | // issues as many instructions from the instruction queue into the rob as possible. 81 | fn cycle_issue(&mut self) { 82 | let mut perf_counters = self.perf_counters.borrow_mut(); 83 | let mut instr_queue = self.instr_queue.borrow_mut(); 84 | let mut rob = self.rob.borrow_mut(); 85 | 86 | // try to put as many instructions into the rob 87 | for _ in 0..self.issue_n_wide { 88 | // println!("cycle_issue: instr_queue.isempty: {}, self.rob.has_space: {}", instr_queue.is_empty(), self.rob.has_space()); 89 | 90 | if instr_queue.is_empty() || !rob.has_space() { 91 | break; 92 | } 93 | 94 | // todo: register renaming should be done here. 95 | 96 | let instr_queue_head_index = instr_queue.head_index(); 97 | let instr_queue_slot = instr_queue.get_mut(instr_queue_head_index); 98 | 99 | let branch_target_predicted = instr_queue_slot.branch_target_predicted; 100 | let instr = Rc::clone(&instr_queue_slot.instr); 101 | 102 | // // If needed, synchronize of the sb being empty 103 | // if instr.sb_sync() && self.memory_subsystem.borrow().sb.size() > 0 { 104 | // return; 105 | // } 106 | // 107 | // // If needed, synchronize on the rob being empty 108 | // if instr.rob_sync() && self.rob.size() > 0 { 109 | // return; 110 | // } 111 | 112 | let rob_slot_index = rob.allocate(); 113 | let rob_slot = rob.get_mut(rob_slot_index); 114 | 115 | if self.trace.issue { 116 | println!("Issued [{}]", instr); 117 | } 118 | 119 | rob_slot.pc = instr_queue_slot.pc; 120 | rob_slot.state = ROBSlotState::ISSUED; 121 | rob_slot.instr = Some(instr); 122 | rob_slot.branch_target_predicted = branch_target_predicted; 123 | rob.seq_issued += 1; 124 | perf_counters.issue_cnt += 1; 125 | 126 | instr_queue.head_bump(); 127 | } 128 | } 129 | 130 | // For any rob entry that doesn't have a reservation station, try to look up a rs. 131 | fn cycle_rs_allocation(&mut self) { 132 | let arch_reg_file = self.arch_reg_file.borrow(); 133 | let mut phys_reg_file = self.phys_reg_file.borrow_mut(); 134 | let mut memory_subsystem = self.memory_subsystem.borrow_mut(); 135 | let mut rob = self.rob.borrow_mut(); 136 | 137 | for _ in 0..self.issue_n_wide { 138 | if rob.seq_rs_allocated == rob.seq_issued || !self.rs_table.has_idle() { 139 | break; 140 | } 141 | 142 | let rob_slot_index = rob.to_index(rob.seq_rs_allocated); 143 | let rob_slot = rob.get_mut(rob_slot_index); 144 | 145 | debug_assert!(rob_slot.state == ROBSlotState::ISSUED); 146 | debug_assert!(rob_slot.eu_index.is_none()); 147 | debug_assert!(rob_slot.rs_index.is_none()); 148 | 149 | let instr = rob_slot.instr.as_ref().unwrap(); 150 | 151 | if let Instr::LoadStore(load_store) = instr.as_ref() { 152 | if load_store.opcode == Opcode::STR { 153 | if !memory_subsystem.sb.has_space() { 154 | // we can't allocate a slot in the store buffer, we are done 155 | break; 156 | } 157 | 158 | rob_slot.sb_pos = Some(memory_subsystem.sb.allocate()); 159 | } 160 | } 161 | 162 | if self.trace.allocate_rs { 163 | println!("Allocate RS [{}]", instr); 164 | } 165 | 166 | let rs_index = self.rs_table.allocate(); 167 | let rs = self.rs_table.get_mut(rs_index); 168 | debug_assert!(rs.state == RSState::BUSY); 169 | 170 | rob_slot.rs_index = Some(rs_index); 171 | 172 | rs.rob_slot_index = Some(rob_slot_index); 173 | 174 | 175 | match instr.as_ref() { 176 | Instr::DataProcessing(data_processing) => { 177 | rs.instr = RSInstr::DataProcessing { 178 | data_processing: RSDataProcessing { 179 | opcode: data_processing.opcode, 180 | condition: data_processing.condition, 181 | rn: if let Some(rn) = data_processing.rn { 182 | Some(register_rename_src(rn, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file)) 183 | } else { 184 | None 185 | }, 186 | rd_src: if data_processing.rd_read { 187 | Some(register_rename_src(data_processing.rd, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file)) 188 | } else { 189 | None 190 | }, 191 | cpsr: if data_processing.condition == ConditionCode::AL { 192 | None 193 | } else { 194 | Some(register_rename_src(CPSR, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file)) 195 | }, 196 | rd: register_rename_sink(data_processing.rd, &mut phys_reg_file, &mut self.rat), 197 | operand2: match data_processing.operand2 { 198 | Operand2::Unused() => RSOperand2::Unused(), 199 | Operand2::Register { reg_id: register } => { 200 | RSOperand2::Register { 201 | register: register_rename_src(register, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file) 202 | } 203 | } 204 | Operand2::Immediate { value } => RSOperand2::Immediate { value }, 205 | }, 206 | } 207 | }; 208 | } 209 | Instr::Branch(branch) => { 210 | rs.instr = RSInstr::Branch { 211 | branch: RSBranch { 212 | opcode: branch.opcode, 213 | condition: ConditionCode::AL, 214 | target: match branch.target { 215 | BranchTarget::Immediate { offset } => { 216 | RSBranchTarget::Immediate { offset } 217 | } 218 | BranchTarget::Register { register } => { 219 | RSBranchTarget::Register { 220 | register: register_rename_src(register, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file) 221 | } 222 | } 223 | }, 224 | rt: if let Some(rt) = branch.rt { 225 | Some(register_rename_src(rt, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file)) 226 | } else { 227 | None 228 | }, 229 | lr: if branch.link_bit { 230 | Some(register_rename_sink(LR as RegisterType, &mut phys_reg_file, &mut self.rat)) 231 | } else { 232 | None 233 | }, 234 | }, 235 | } 236 | } 237 | Instr::LoadStore(load_store) => { 238 | match load_store.opcode { 239 | Opcode::LDR => rs.instr = RSInstr::LoadStore { 240 | load_store: RSLoadStore { 241 | opcode: load_store.opcode, 242 | condition: load_store.condition, 243 | rn: register_rename_src(load_store.rn, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file), 244 | rd: register_rename_sink(load_store.rd, &mut phys_reg_file, &mut self.rat), 245 | offset: load_store.offset, 246 | } 247 | }, 248 | Opcode::STR => rs.instr = RSInstr::LoadStore { 249 | load_store: RSLoadStore { 250 | opcode: load_store.opcode, 251 | condition: load_store.condition, 252 | rn: register_rename_src(load_store.rn, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file), 253 | rd: register_rename_src(load_store.rd, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file), 254 | offset: load_store.offset, 255 | } 256 | }, 257 | _ => unreachable!(), 258 | } 259 | } 260 | Instr::Printr(printr) => { 261 | rs.instr = RSInstr::Printr { 262 | printr: RSPrintr { 263 | rn: register_rename_src(printr.rn, rs, &mut self.rat, &arch_reg_file, &mut phys_reg_file) 264 | }, 265 | }; 266 | } 267 | Instr::Synchronization { .. } => {} 268 | } 269 | 270 | if rs.pending_cnt == 0 { 271 | //println!("rs_table enqueue ready"); 272 | rob_slot.state = ROBSlotState::STAGED; 273 | self.rs_table.enqueue_ready(rs_index); 274 | } 275 | 276 | rob.seq_rs_allocated += 1; 277 | } 278 | } 279 | 280 | fn cycle_dispatch(&mut self) { 281 | let mut perf_counters = self.perf_counters.borrow_mut(); 282 | let mut rob = self.rob.borrow_mut(); 283 | 284 | for _ in 0..self.dispatch_n_wide { 285 | if !self.rs_table.has_ready() || !self.eu_table.has_idle() { 286 | break; 287 | } 288 | 289 | let rs_index = self.rs_table.deque_ready(); 290 | 291 | let rs = self.rs_table.get_mut(rs_index); 292 | debug_assert!(rs.state == RSState::BUSY); 293 | 294 | let rob_slot_index = rs.rob_slot_index.unwrap(); 295 | let rob_slot = rob.get_mut(rob_slot_index); 296 | //println!("{:?}",rob_slot.state); 297 | debug_assert!(rob_slot.state == ROBSlotState::STAGED); 298 | 299 | let eu_index = self.eu_table.allocate(); 300 | let eu = self.eu_table.get_mut(eu_index); 301 | debug_assert!(eu.state == EUState::EXECUTING); 302 | 303 | let instr = rob_slot.instr.as_ref().unwrap(); 304 | 305 | eu.rs_index = Some(rs_index); 306 | 307 | // todo: correctly configure the cycles 308 | eu.cycles_remaining = 1; 309 | 310 | rob_slot.state = ROBSlotState::DISPATCHED; 311 | rob_slot.eu_index = Some(eu_index); 312 | 313 | if self.trace.dispatch { 314 | println!("Dispatched [{}]", instr); 315 | } 316 | perf_counters.dispatch_cnt += 1; 317 | } 318 | } 319 | 320 | fn cycle_eu_table(&mut self) { 321 | let mut rob = self.rob.borrow_mut(); 322 | // todo: we should only iterate over the used execution units. 323 | for eu_index in 0..self.eu_table.capacity { 324 | let eu = self.eu_table.get_mut(eu_index); 325 | 326 | if eu.state == EUState::IDLE { 327 | continue; 328 | } 329 | 330 | debug_assert!(eu.state == EUState::EXECUTING); 331 | 332 | let rs_index = eu.rs_index.unwrap(); 333 | 334 | let rs = self.rs_table.get_mut(rs_index); 335 | debug_assert!(rs.state == RSState::BUSY); 336 | 337 | let rob_index = rs.rob_slot_index.unwrap(); 338 | let rob_slot = rob.get_mut(rob_index); 339 | debug_assert!(rob_slot.state == ROBSlotState::DISPATCHED, 340 | "rob_slot is not in dispatched state, but in {:?}, rs_index={}", rob_slot.state, rs_index); 341 | debug_assert!(rob_slot.rs_index.is_some()); 342 | debug_assert!(rob_slot.eu_index.is_some()); 343 | 344 | eu.cycle(rs, rob_slot); 345 | 346 | if eu.state == EUState::EXECUTING { 347 | continue; 348 | } 349 | 350 | debug_assert!(eu.state == EUState::COMPLETED); 351 | 352 | let eu_index = eu.index; 353 | self.eu_table.deallocate(eu_index); 354 | rob_slot.eu_index = None; 355 | 356 | self.rs_table.deallocate(rs_index); 357 | rob_slot.rs_index = None; 358 | 359 | rob_slot.state = ROBSlotState::EXECUTED; 360 | } 361 | } 362 | 363 | fn cdb_broadcast(&mut self) { 364 | let rs_table_capacity = self.rs_table.capacity; 365 | let mut rob = self.rob.borrow_mut(); 366 | 367 | 368 | //println!("Broadcast"); 369 | for broadcast in &mut *self.cdb_broadcast_buffer.borrow_mut() { 370 | //println!("\t\tBroadcasting value for phys_reg {}", broadcast.phys_reg); 371 | // Iterate over all RS and replace every matching physical register, by the value 372 | for rs_index in 0..rs_table_capacity { 373 | let rs = self.rs_table.get_mut(rs_index); 374 | if rs.state == RSState::IDLE { 375 | continue; 376 | } 377 | 378 | let rob_slot_index = rs.rob_slot_index.unwrap(); 379 | let rob_slot = rob.get_mut(rob_slot_index); 380 | if rob_slot.state != ROBSlotState::ISSUED { 381 | continue; 382 | } 383 | 384 | let mut rs = self.rs_table.get_mut(rob_slot.rs_index.unwrap()); 385 | //println!("\t\trs.index={} rs.state={:?}", rs.index, rs.state); 386 | 387 | let mut at_least_one_resolved = false; 388 | 389 | // todo: lot of copy paste code 390 | match &mut rs.instr { 391 | RSInstr::DataProcessing { data_processing } => { 392 | if let Some(rn) = &mut data_processing.rn { 393 | if let Some(r) = rn.phys_reg { 394 | if r == broadcast.phys_reg && rn.value.is_none(){ 395 | rn.value = Some(broadcast.value); 396 | at_least_one_resolved = true; 397 | rs.pending_cnt -= 1; 398 | } 399 | }; 400 | }; 401 | 402 | if let RSOperand2::Register { ref mut register } = &mut data_processing.operand2 { 403 | if let Some(r) = register.phys_reg { 404 | if r == broadcast.phys_reg && register.value.is_none(){ 405 | register.value = Some(broadcast.value); 406 | at_least_one_resolved = true; 407 | rs.pending_cnt -= 1; 408 | } 409 | }; 410 | } 411 | 412 | if let Some(rd_src) = &mut data_processing.rd_src { 413 | if let Some(r) = rd_src.phys_reg { 414 | if r == broadcast.phys_reg && rd_src.value.is_none() { 415 | rd_src.value = Some(broadcast.value); 416 | at_least_one_resolved = true; 417 | rs.pending_cnt -= 1; 418 | } 419 | }; 420 | }; 421 | 422 | 423 | if let Some(cpsr) = &mut data_processing.cpsr { 424 | if let Some(r) = cpsr.phys_reg { 425 | if r == broadcast.phys_reg && cpsr.value.is_none(){ 426 | cpsr.value = Some(broadcast.value); 427 | at_least_one_resolved = true; 428 | rs.pending_cnt -= 1; 429 | } 430 | }; 431 | }; 432 | } 433 | RSInstr::Branch { branch } => { 434 | if let RSBranchTarget::Register { register } = &mut branch.target { 435 | if let Some(r) = register.phys_reg { 436 | if r == broadcast.phys_reg && register.value.is_none(){ 437 | register.value = Some(broadcast.value); 438 | at_least_one_resolved = true; 439 | rs.pending_cnt -= 1; 440 | } 441 | }; 442 | } 443 | 444 | if let Some(register) = &mut branch.rt { 445 | if let Some(r) = register.phys_reg { 446 | if r == broadcast.phys_reg && register.value.is_none(){ 447 | register.value = Some(broadcast.value); 448 | at_least_one_resolved = true; 449 | rs.pending_cnt -= 1; 450 | } 451 | }; 452 | } 453 | } 454 | RSInstr::LoadStore { load_store } => { 455 | if let Some(r) = load_store.rn.phys_reg { 456 | if r == broadcast.phys_reg && load_store.rn.value.is_none(){ 457 | load_store.rn.value = Some(broadcast.value); 458 | at_least_one_resolved = true; 459 | rs.pending_cnt -= 1; 460 | } 461 | }; 462 | 463 | if load_store.opcode == Opcode::STR { 464 | if let Some(r) = load_store.rd.phys_reg { 465 | if r == broadcast.phys_reg && load_store.rd.value.is_none(){ 466 | load_store.rd.value = Some(broadcast.value); 467 | at_least_one_resolved = true; 468 | rs.pending_cnt -= 1; 469 | } 470 | }; 471 | } 472 | } 473 | RSInstr::Printr { printr } => { 474 | if let Some(r) = printr.rn.phys_reg { 475 | if r == broadcast.phys_reg && printr.rn.value.is_none(){ 476 | printr.rn.value = Some(broadcast.value); 477 | at_least_one_resolved = true; 478 | rs.pending_cnt -= 1; 479 | } 480 | } 481 | } 482 | RSInstr::Synchronization { .. } => {} 483 | } 484 | 485 | if at_least_one_resolved && rs.pending_cnt == 0 { 486 | rob_slot.state = ROBSlotState::STAGED; 487 | self.rs_table.enqueue_ready(rob_slot.rs_index.unwrap()); 488 | } 489 | } 490 | } 491 | 492 | self.cdb_broadcast_buffer.borrow_mut().clear(); 493 | } 494 | 495 | fn cycle_retire(&mut self) { 496 | let mut bad_speculation = false; 497 | 498 | { 499 | let mut arch_reg_file = self.arch_reg_file.borrow_mut(); 500 | let mut perf_counters = self.perf_counters.borrow_mut(); 501 | let mut phys_reg_file = &mut self.phys_reg_file.borrow_mut(); 502 | //let frontend_control = self.frontend_control.borrow_mut(); 503 | let mut memory_subsytem = self.memory_subsystem.borrow_mut(); 504 | let mut rob = self.rob.borrow_mut(); 505 | 506 | for _ in 0..self.retire_n_wide { 507 | let rob_slot_index = rob.to_index(rob.seq_retired); 508 | let rob_slot = rob.get_mut(rob_slot_index); 509 | 510 | if rob_slot.state != ROBSlotState::EXECUTED { 511 | break; 512 | } 513 | 514 | let instr = rob_slot.instr.as_ref().unwrap(); 515 | 516 | perf_counters.retired_cnt += 1; 517 | 518 | if let Instr::Synchronization(synchronization) = instr.as_ref() { 519 | if synchronization.opcode == Opcode::EXIT { 520 | self.exit = true; 521 | } 522 | } 523 | 524 | if self.trace.retire { 525 | println!("Retiring [{}]", instr); 526 | } 527 | 528 | // Update the architectural registers 529 | for renamed_register in &rob_slot.renamed_registers { 530 | let rat_entry = self.rat.get_mut(renamed_register.arch_reg); 531 | debug_assert!(rat_entry.valid); 532 | 533 | let rat_phys_reg = rat_entry.phys_reg; 534 | let rob_phys_reg = renamed_register.phys_reg.unwrap(); 535 | 536 | // only when the physical register on the rat is the same as the physical register used for that 537 | // instruction, the rat entry should be invalidated 538 | if rat_phys_reg == rob_phys_reg { 539 | rat_entry.valid = false; 540 | } 541 | 542 | // update the architectural register 543 | let value = phys_reg_file.get_value(rob_phys_reg); 544 | arch_reg_file.set_value(renamed_register.arch_reg, value); 545 | 546 | phys_reg_file.deallocate(rob_phys_reg); 547 | } 548 | 549 | // commit the store. 550 | if rob_slot.sb_pos.is_some() { 551 | memory_subsytem.sb.commit(rob_slot.sb_pos.unwrap()) 552 | } 553 | 554 | // deal with any branch misprediction 555 | if let Instr::Branch(_) = &instr.as_ref() { 556 | if rob_slot.branch_target_actual != rob_slot.branch_target_predicted { 557 | // the branch was not correctly predicted 558 | perf_counters.branch_miss_prediction_cnt += 1; 559 | bad_speculation = true; 560 | 561 | // re-steer the frontend 562 | arch_reg_file.set_value(PC, rob_slot.branch_target_actual as DWordType); 563 | } else { 564 | // the branch was correctly predicted 565 | perf_counters.branch_good_predictions_cnt += 1; 566 | } 567 | } 568 | 569 | rob.seq_retired += 1; 570 | rob.deallocate(); 571 | 572 | if bad_speculation { 573 | break; 574 | } 575 | } 576 | } 577 | 578 | if bad_speculation { 579 | self.flush(); 580 | } 581 | } 582 | 583 | fn flush(&mut self) { 584 | let mut perf_counters = self.perf_counters.borrow_mut(); 585 | let mut rob = self.rob.borrow_mut(); 586 | 587 | if self.trace.pipeline_flush { 588 | println!("Pipeline flush"); 589 | } 590 | 591 | perf_counters.pipeline_flushes += 1; 592 | perf_counters.bad_speculation_cnt += rob.size() as u64; 593 | 594 | self.instr_queue.borrow_mut().flush(); 595 | self.phys_reg_file.borrow_mut().flush(); 596 | self.eu_table.flush(); 597 | rob.flush(); 598 | self.rat.flush(); 599 | self.rs_table.flush(); 600 | self.memory_subsystem.borrow_mut().sb.flush(); 601 | } 602 | } 603 | 604 | fn register_rename_src(arch_reg: RegisterType, 605 | rs: &mut RS, 606 | rat: &mut RAT, 607 | arch_reg_file: &ArgRegFile, 608 | phys_reg_file: &mut PhysRegFile, 609 | ) -> RenamedRegister { 610 | let mut phys_reg = None; 611 | let mut value = None; 612 | let rat_entry = rat.get(arch_reg); 613 | if rat_entry.valid { 614 | let phys_reg_entry = phys_reg_file.get(rat_entry.phys_reg); 615 | //println!(" register_rename_src valid=true arch_reg={} phys_reg={}", arch_reg, rat_entry.phys_reg); 616 | 617 | if phys_reg_entry.has_value { 618 | //println!(" has value {}", phys_reg_entry.value); 619 | //we got lucky, there is a value in the physical register. 620 | value = Some(phys_reg_entry.value); 621 | } else { 622 | //println!(" has no value"); 623 | rs.pending_cnt += 1; 624 | //println!(" rs.pending_count {}", rs.pending_cnt); 625 | 626 | // cdb broadcast will update 627 | phys_reg = Some(rat_entry.phys_reg); 628 | } 629 | } else { 630 | //println!(" register_rename_src valid=false arch_reg={}", arch_reg); 631 | value = Some(arch_reg_file.get_value(arch_reg)); 632 | //println!("\t\t\t value={}", value.unwrap()); 633 | } 634 | 635 | RenamedRegister { arch_reg, phys_reg, value } 636 | } 637 | 638 | fn register_rename_sink(arch_reg: RegisterType, 639 | phys_reg_file: &mut PhysRegFile, 640 | rat: &mut RAT, 641 | ) -> RenamedRegister { 642 | //println!(" register_rename_sink arch_reg={}", arch_reg); 643 | let phys_reg = phys_reg_file.allocate(); 644 | rat.update(arch_reg, phys_reg); 645 | 646 | RenamedRegister { arch_reg, phys_reg: Some(phys_reg), value: None } 647 | } -------------------------------------------------------------------------------- /src/backend/execution_unit.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | 4 | use crate::backend::backend::CDBBroadcast; 5 | use crate::backend::physical_register::PhysRegFile; 6 | use crate::backend::reorder_buffer::ROBSlot; 7 | use crate::backend::reservation_station::{RS, RSBranch, RSDataProcessing, RSInstr, RSLoadStore, RSPrintr}; 8 | use crate::cpu::{CARRY_FLAG, CPUConfig, NEGATIVE_FLAG, OVERFLOW_FLAG, PerfCounters, ZERO_FLAG}; 9 | use crate::instructions::instructions::{ConditionCode, DWordType, Opcode, RegisterTypeDisplay}; 10 | use crate::instructions::instructions::Opcode::LDR; 11 | use crate::memory_subsystem::memory_subsystem::MemorySubsystem; 12 | 13 | /// A single execution unit. 14 | pub(crate) struct EU { 15 | pub(crate) index: u8, 16 | pub(crate) rs_index: Option, 17 | pub(crate) cycles_remaining: u8, 18 | pub(crate) state: EUState, 19 | pub(crate) broadcast_buffer: Rc>>, 20 | memory_subsystem: Rc>, 21 | perf_counters: Rc>, 22 | phys_reg_file: Rc>, 23 | trace: bool, 24 | } 25 | 26 | #[derive(Clone, Copy, PartialEq, Debug)] 27 | pub(crate) enum EUState { 28 | IDLE, 29 | EXECUTING, 30 | COMPLETED, 31 | } 32 | 33 | #[allow(non_snake_case)] 34 | impl EU { 35 | fn reset(&mut self) { 36 | self.rs_index = None; 37 | self.cycles_remaining = 0; 38 | self.state = EUState::IDLE; 39 | } 40 | 41 | pub fn cycle(&mut self, 42 | rs: &mut RS, 43 | rob_slot: &mut ROBSlot) { 44 | debug_assert!(self.state == EUState::EXECUTING); 45 | debug_assert!(self.cycles_remaining > 0); 46 | 47 | self.cycles_remaining -= 1; 48 | if self.cycles_remaining > 0 { 49 | // the execution unit isn't finished with its work 50 | return; 51 | } 52 | self.state = EUState::COMPLETED; 53 | self.perf_counters.borrow_mut().execute_cnt += 1; 54 | 55 | if self.trace { 56 | let instr = rob_slot.instr.as_ref().unwrap(); 57 | println!("Executing [{}]", instr); 58 | } 59 | 60 | match &mut rs.instr { 61 | RSInstr::DataProcessing { data_processing } => self.execute_data_processing(data_processing, rob_slot), 62 | RSInstr::Branch { branch } => self.execute_branch(branch, rob_slot), 63 | RSInstr::LoadStore { load_store } => self.execute_load_store(load_store, rob_slot), 64 | RSInstr::Printr { printr } => self.execute_printr(printr), 65 | RSInstr::Synchronization { .. } => {} 66 | } 67 | } 68 | 69 | fn execute_printr(&mut self, printr: &mut RSPrintr) { 70 | println!("PRINTR {}={}", RegisterTypeDisplay { register: printr.rn.arch_reg }, printr.rn.value.unwrap()); 71 | } 72 | 73 | fn execute_data_processing(&mut self, data_processing: &mut RSDataProcessing, rob_slot: &mut ROBSlot) { 74 | let should_execute = if data_processing.condition != ConditionCode::AL { 75 | let cpsr = data_processing.cpsr.as_ref().unwrap().value.unwrap(); 76 | match data_processing.condition { 77 | ConditionCode::EQ => 78 | (cpsr >> ZERO_FLAG) & 0x1 == 1, 79 | ConditionCode::NE => 80 | (cpsr >> ZERO_FLAG) & 0x1 == 0, 81 | ConditionCode::CS => 82 | (cpsr >> CARRY_FLAG) & 0x1 == 1, 83 | ConditionCode::CC => 84 | (cpsr >> CARRY_FLAG) & 0x1 == 0, 85 | ConditionCode::MI => 86 | (cpsr >> NEGATIVE_FLAG) & 0x1 == 1, 87 | ConditionCode::PL => 88 | (cpsr >> NEGATIVE_FLAG) & 0x1 == 0, 89 | ConditionCode::VS => 90 | (cpsr >> OVERFLOW_FLAG) & 0x1 == 1, 91 | ConditionCode::VC => 92 | (cpsr >> OVERFLOW_FLAG) & 0x1 == 0, 93 | ConditionCode::HI => 94 | (cpsr >> CARRY_FLAG) & 0x1 == 1 && (cpsr >> ZERO_FLAG) & 0x1 == 0, 95 | ConditionCode::LS => 96 | (cpsr >> CARRY_FLAG) & 0x1 == 0 || (cpsr >> ZERO_FLAG) & 0x1 == 1, 97 | ConditionCode::GE => 98 | (cpsr >> NEGATIVE_FLAG) & 0x1 == (cpsr >> OVERFLOW_FLAG) & 0x1, 99 | ConditionCode::LT => 100 | (cpsr >> NEGATIVE_FLAG) & 0x1 != (cpsr >> OVERFLOW_FLAG) & 0x1, 101 | ConditionCode::GT => 102 | (cpsr >> ZERO_FLAG) & 0x1 == 0 && ((cpsr >> NEGATIVE_FLAG) & 0x1 == (cpsr >> OVERFLOW_FLAG) & 0x1), 103 | ConditionCode::LE => 104 | (cpsr >> ZERO_FLAG) & 0x1 == 1 || ((cpsr >> NEGATIVE_FLAG) & 0x1 != (cpsr >> OVERFLOW_FLAG) & 0x1), 105 | _ => false, 106 | } 107 | } else { 108 | true 109 | }; 110 | 111 | let result = if should_execute { 112 | match &data_processing.opcode { 113 | Opcode::ADD => self.execute_ADD(data_processing), 114 | Opcode::SUB => self.execute_SUB(data_processing), 115 | Opcode::RSB => self.execute_RSB(data_processing), 116 | Opcode::MUL => self.execute_MUL(data_processing), 117 | Opcode::MOV => self.execute_MOV(data_processing), 118 | Opcode::CMP => self.execute_CMP(data_processing), 119 | Opcode::SDIV => self.execute_SDIV(data_processing), 120 | Opcode::AND => self.execute_AND(data_processing), 121 | Opcode::ORR => self.execute_ORR(data_processing), 122 | Opcode::EOR => self.execute_EOR(data_processing), 123 | Opcode::NEG => self.execute_NEG(data_processing), 124 | Opcode::MVN => self.execute_MVN(data_processing), 125 | Opcode::TST => self.execute_TST(data_processing), 126 | Opcode::TEQ => self.execute_TEQ(data_processing), 127 | _ => unreachable!() 128 | } 129 | } else { 130 | // if the instruction should not be executed, the original value of the rd register will 131 | // be written (this is needed because otherwise register renaming doesn't work) 132 | data_processing.rd_src.as_ref().unwrap().value.unwrap() 133 | }; 134 | 135 | data_processing.rd.value = Some(result); 136 | self.phys_reg_file.borrow_mut().set_value(data_processing.rd.phys_reg.unwrap(), result); 137 | 138 | rob_slot.renamed_registers.push(data_processing.rd.clone()); 139 | 140 | let mut phys_reg_file = self.phys_reg_file.borrow_mut(); 141 | let rd = data_processing.rd.phys_reg.unwrap(); 142 | let phys_reg_entry = phys_reg_file.get_mut(rd); 143 | 144 | self.broadcast_buffer.borrow_mut().push(CDBBroadcast { phys_reg: rd, value: phys_reg_entry.value }); 145 | } 146 | 147 | fn execute_CMP(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 148 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 149 | let operand2_value = data_processing.operand2.value(); 150 | 151 | let rd_value = data_processing.rd_src.as_ref().unwrap().value.unwrap(); 152 | 153 | // Perform the comparison: rn - operand2 154 | let result = rn_value.wrapping_sub(operand2_value); 155 | 156 | // Update the CPSR flags based on the result 157 | let zero_flag = result == 0; 158 | let negative_flag = (result & (1 << 63)) != 0; 159 | let carry_flag = (rn_value as u128).wrapping_sub(operand2_value as u128) > (rn_value as u128); // Checking for borrow 160 | let overflow_flag = (((rn_value ^ operand2_value) & (rn_value ^ result)) >> 63) != 0; 161 | 162 | let mut rd_update = rd_value; 163 | if zero_flag { 164 | rd_update |= 1 << ZERO_FLAG; 165 | } else { 166 | rd_update &= !(1 << ZERO_FLAG); 167 | } 168 | 169 | if negative_flag { 170 | rd_update |= 1 << NEGATIVE_FLAG; 171 | } else { 172 | rd_update &= !(1 << NEGATIVE_FLAG); 173 | } 174 | 175 | if carry_flag { 176 | rd_update |= 1 << CARRY_FLAG; 177 | } else { 178 | rd_update &= !(1 << CARRY_FLAG); 179 | } 180 | 181 | if overflow_flag { 182 | rd_update |= 1 << OVERFLOW_FLAG; 183 | } else { 184 | rd_update &= !(1 << OVERFLOW_FLAG); 185 | } 186 | 187 | rd_update 188 | } 189 | 190 | fn execute_TST(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 191 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 192 | let operand2_value = data_processing.operand2.value(); 193 | 194 | let result = rn_value & operand2_value; 195 | 196 | let zero_flag = result == 0; 197 | let negative_flag = (result & (1 << 63)) != 0; 198 | 199 | let mut rd_update = data_processing.rd_src.as_ref().unwrap().value.unwrap(); 200 | 201 | if zero_flag { 202 | rd_update |= 1 << ZERO_FLAG; 203 | } else { 204 | rd_update &= !(1 << ZERO_FLAG); 205 | } 206 | 207 | if negative_flag { 208 | rd_update |= 1 << NEGATIVE_FLAG; 209 | } else { 210 | rd_update &= !(1 << NEGATIVE_FLAG); 211 | } 212 | 213 | rd_update 214 | } 215 | 216 | fn execute_TEQ(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 217 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 218 | let operand2_value = data_processing.operand2.value(); 219 | 220 | let result = rn_value ^ operand2_value; 221 | 222 | let zero_flag = result == 0; 223 | let negative_flag = (result & (1 << 63)) != 0; 224 | 225 | let mut rd_update = data_processing.rd_src.as_ref().unwrap().value.unwrap(); 226 | 227 | if zero_flag { 228 | rd_update |= 1 << ZERO_FLAG; 229 | } else { 230 | rd_update &= !(1 << ZERO_FLAG); 231 | } 232 | 233 | if negative_flag { 234 | rd_update |= 1 << NEGATIVE_FLAG; 235 | } else { 236 | rd_update &= !(1 << NEGATIVE_FLAG); 237 | } 238 | 239 | rd_update 240 | } 241 | 242 | fn execute_SDIV(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 243 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 244 | let operand2_value = data_processing.operand2.value(); 245 | 246 | rn_value / operand2_value 247 | } 248 | 249 | fn execute_MOV(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 250 | data_processing.operand2.value() 251 | } 252 | 253 | fn execute_MUL(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 254 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 255 | let operand2_value = data_processing.operand2.value(); 256 | rn_value.wrapping_mul(operand2_value) 257 | } 258 | 259 | fn execute_RSB(&mut self, data_processing: &mut RSDataProcessing) -> u64 { // let rn = rs.source[0].value.unwrap(); 260 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 261 | let operand2_value = data_processing.operand2.value(); 262 | operand2_value.wrapping_sub(rn_value) 263 | } 264 | 265 | fn execute_SUB(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 266 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 267 | let operand2_value = data_processing.operand2.value(); 268 | rn_value.wrapping_sub(operand2_value) 269 | } 270 | 271 | fn execute_ADD(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 272 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 273 | let operand2_value = data_processing.operand2.value(); 274 | rn_value.wrapping_add(operand2_value) 275 | } 276 | 277 | fn execute_AND(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 278 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 279 | let operand2_value = data_processing.operand2.value(); 280 | rn_value & operand2_value 281 | } 282 | 283 | fn execute_ORR(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 284 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 285 | let operand2_value = data_processing.operand2.value(); 286 | rn_value | operand2_value 287 | } 288 | 289 | fn execute_MVN(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 290 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 291 | !rn_value 292 | } 293 | 294 | fn execute_EOR(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 295 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 296 | let operand2_value = data_processing.operand2.value(); 297 | rn_value ^ operand2_value 298 | } 299 | 300 | fn execute_NEG(&mut self, data_processing: &mut RSDataProcessing) -> DWordType { 301 | let rn_value = data_processing.rn.as_ref().unwrap().value.unwrap(); 302 | rn_value.wrapping_neg() 303 | } 304 | 305 | fn execute_load_store(&mut self, load_store: &mut RSLoadStore, rob_slot: &mut ROBSlot) { 306 | match &load_store.opcode { 307 | Opcode::LDR => self.execute_LDR(load_store, rob_slot), 308 | Opcode::STR => self.execute_STR(load_store, rob_slot), 309 | _ => unreachable!() 310 | }; 311 | 312 | // todo: This is ugly because it couples to the LDR. Leads to problems when more loads are added 313 | if load_store.opcode == LDR { 314 | let mut phys_reg_file = self.phys_reg_file.borrow_mut(); 315 | let rd = load_store.rd.phys_reg.unwrap(); 316 | let phys_reg_entry = phys_reg_file.get_mut(rd); 317 | self.broadcast_buffer.borrow_mut().push(CDBBroadcast { phys_reg: rd, value: phys_reg_entry.value }); 318 | } 319 | } 320 | 321 | fn execute_STR(&mut self, load_store: &mut RSLoadStore, rob_slot: &mut ROBSlot) { 322 | let value = load_store.rd.value.unwrap(); 323 | let address = load_store.rn.value.unwrap(); 324 | 325 | let mut memory_subsystem = self.memory_subsystem.borrow_mut(); 326 | memory_subsystem.sb.store(rob_slot.sb_pos.unwrap(), address, value); 327 | } 328 | 329 | fn execute_LDR(&mut self, load_store: &mut RSLoadStore, rob_slot: &mut ROBSlot) { 330 | let memory_subsystem = self.memory_subsystem.borrow_mut(); 331 | let address = load_store.rn.value.unwrap() as usize; 332 | let value = memory_subsystem.memory[address]; 333 | 334 | let rd = load_store.rd.phys_reg.unwrap(); 335 | load_store.rd.value = Some(value); 336 | self.phys_reg_file.borrow_mut().set_value(rd, value); 337 | 338 | rob_slot.renamed_registers.push(load_store.rd.clone()) 339 | } 340 | 341 | fn execute_branch(&mut self, branch: &mut RSBranch, rob_slot: &mut ROBSlot) { 342 | let branch_target = match &branch.opcode { 343 | Opcode::B => self.execute_B(branch, rob_slot), 344 | Opcode::BL => self.execute_BL(branch, rob_slot), 345 | Opcode::BX => self.execute_BX(branch, rob_slot), 346 | Opcode::BEQ => self.execute_BEQ(branch, rob_slot), 347 | Opcode::BNE => self.execute_BNE(branch, rob_slot), 348 | Opcode::BGT => self.execute_BGT(branch, rob_slot), 349 | Opcode::BGE => self.execute_BGE(branch, rob_slot), 350 | Opcode::BLT => self.execute_BLT(branch, rob_slot), 351 | Opcode::BLE => self.execute_BLE(branch, rob_slot), 352 | Opcode::CBZ => self.execute_CBZ(branch, rob_slot), 353 | Opcode::CBNZ => self.execute_CBNZ(branch, rob_slot), 354 | Opcode::RET => self.execute_RET(branch, rob_slot), 355 | _ => unreachable!() 356 | }; 357 | 358 | rob_slot.branch_target_actual = branch_target; 359 | 360 | if let Some(lr) = &branch.lr { 361 | let mut phys_reg_file = self.phys_reg_file.borrow_mut(); 362 | let phys_reg = lr.phys_reg.unwrap(); 363 | let phys_reg_entry = phys_reg_file.get_mut(phys_reg); 364 | self.broadcast_buffer.borrow_mut().push(CDBBroadcast { phys_reg, value: phys_reg_entry.value }); 365 | } 366 | } 367 | 368 | fn execute_B(&mut self, branch: &RSBranch, _rob_slot: &mut ROBSlot) -> usize { 369 | branch.target.value() as usize 370 | } 371 | 372 | fn execute_BX(&mut self, branch: &RSBranch, _rob_slot: &mut ROBSlot) -> usize { 373 | branch.target.value() as usize 374 | } 375 | 376 | fn execute_BEQ(&mut self, branch: &RSBranch, rob_slot: &mut ROBSlot) -> usize { 377 | let target = branch.target.value() as u64; 378 | let cpsr = branch.rt.as_ref().unwrap().value.unwrap(); 379 | let pc = rob_slot.pc as DWordType; 380 | 381 | let zero_flag = (cpsr >> ZERO_FLAG) & 0x1; 382 | if zero_flag == 1 { 383 | target as usize 384 | } else { 385 | (pc + 1) as usize 386 | } 387 | } 388 | 389 | fn execute_BNE(&mut self, branch: &RSBranch, rob_slot: &mut ROBSlot) -> usize { 390 | let target = branch.target.value() as u64; 391 | let cpsr = branch.rt.as_ref().unwrap().value.unwrap(); 392 | let pc = rob_slot.pc as DWordType; 393 | 394 | let zero_flag = (cpsr >> ZERO_FLAG) & 0x1; 395 | if zero_flag == 0 { 396 | target as usize 397 | } else { 398 | (pc + 1) as usize 399 | } 400 | } 401 | 402 | fn execute_BLT(&mut self, branch: &RSBranch, rob_slot: &mut ROBSlot) -> usize { 403 | let target = branch.target.value() as u64; 404 | let cpsr = branch.rt.as_ref().unwrap().value.unwrap(); 405 | let pc = rob_slot.pc as DWordType; 406 | 407 | let negative_flag = (cpsr >> NEGATIVE_FLAG) & 0x1; 408 | let overflow_flag = (cpsr >> OVERFLOW_FLAG) & 0x1; 409 | 410 | if negative_flag != overflow_flag { 411 | target as usize 412 | } else { 413 | (pc + 1) as usize 414 | } 415 | } 416 | 417 | fn execute_BLE(&mut self, branch: &RSBranch, rob_slot: &mut ROBSlot) -> usize { 418 | let target = branch.target.value() as u64; 419 | let cpsr = branch.rt.as_ref().unwrap().value.unwrap(); 420 | let pc = rob_slot.pc as DWordType; 421 | 422 | let zero_flag = (cpsr >> ZERO_FLAG) & 0x1; 423 | let negative_flag = (cpsr >> NEGATIVE_FLAG) & 0x1; 424 | let overflow_flag = (cpsr >> OVERFLOW_FLAG) & 0x1; 425 | 426 | if (zero_flag == 1) || (negative_flag != overflow_flag) { 427 | target as usize 428 | } else { 429 | (pc + 1) as usize 430 | } 431 | } 432 | 433 | fn execute_BGT(&mut self, branch: &RSBranch, rob_slot: &mut ROBSlot) -> usize { 434 | let target = branch.target.value() as u64; 435 | let cpsr = branch.rt.as_ref().unwrap().value.unwrap(); 436 | let pc = rob_slot.pc as DWordType; 437 | 438 | let zero_flag = (cpsr >> ZERO_FLAG) & 0x1; 439 | let negative_flag = (cpsr >> NEGATIVE_FLAG) & 0x1; 440 | let overflow_flag = (cpsr >> OVERFLOW_FLAG) & 0x1; 441 | 442 | if (zero_flag == 0) && (negative_flag == overflow_flag) { 443 | target as usize 444 | } else { 445 | (pc + 1) as usize 446 | } 447 | } 448 | 449 | fn execute_BGE(&mut self, branch: &RSBranch, rob_slot: &mut ROBSlot) -> usize { 450 | let target = branch.target.value() as u64; 451 | let cpsr = branch.rt.as_ref().unwrap().value.unwrap(); 452 | let pc = rob_slot.pc as DWordType; 453 | 454 | let negative_flag = (cpsr >> NEGATIVE_FLAG) & 0x1; 455 | let overflow_flag = (cpsr >> OVERFLOW_FLAG) & 0x1; 456 | 457 | if negative_flag == overflow_flag { 458 | target as usize 459 | } else { 460 | (pc + 1) as usize 461 | } 462 | } 463 | 464 | fn execute_CBZ(&mut self, branch: &RSBranch, rob_slot: &mut ROBSlot) -> usize { 465 | let reg_value = branch.rt.as_ref().unwrap().value.unwrap(); 466 | let target = branch.target.value() as u64; 467 | let pc = rob_slot.pc as DWordType; 468 | 469 | if reg_value == 0 { 470 | target as usize 471 | } else { 472 | (pc + 1) as usize 473 | } 474 | } 475 | 476 | fn execute_CBNZ(&mut self, branch: &RSBranch, rob_slot: &mut ROBSlot) -> usize { 477 | let reg_value = branch.rt.as_ref().unwrap().value.unwrap(); 478 | let target = branch.target.value() as u64; 479 | let pc = rob_slot.pc as DWordType; 480 | 481 | if reg_value != 0 { 482 | target as usize 483 | } else { 484 | (pc + 1) as usize 485 | } 486 | } 487 | 488 | fn execute_BL(&mut self, branch: &mut RSBranch, rob_slot: &mut ROBSlot) -> usize { 489 | let branch_target = branch.target.value(); 490 | rob_slot.branch_target_actual = branch_target as usize; 491 | 492 | let pc_update = branch_target; 493 | 494 | // update LR 495 | let value = (rob_slot.pc + 1) as DWordType; 496 | let lr = branch.lr.as_mut().unwrap(); 497 | lr.value = Some(value); 498 | self.phys_reg_file.borrow_mut().set_value(lr.phys_reg.unwrap(), value); 499 | pc_update as usize 500 | } 501 | 502 | fn execute_RET(&mut self, branch: &mut RSBranch, _rob_slot: &mut ROBSlot) -> usize { 503 | // update the PC 504 | let branch_target = branch.target.value(); 505 | branch_target as usize 506 | } 507 | 508 | fn execute_ADR(&mut self, _rs: &mut RS, _rob_slot: &mut ROBSlot) { 509 | panic!("ADR is not implemented"); 510 | } 511 | } 512 | 513 | /// The table containing all execution units of a CPU core. 514 | pub(crate) struct EUTable { 515 | pub(crate) capacity: u8, 516 | idle_stack: Vec, 517 | array: Vec, 518 | } 519 | 520 | impl EUTable { 521 | pub(crate) fn new( 522 | cpu_config: &CPUConfig, 523 | memory_subsystem: &Rc>, 524 | phys_reg_file: &Rc>, 525 | perf_counters: &Rc>, 526 | broadcast_buffer: &Rc>>, 527 | ) -> EUTable { 528 | let capacity = cpu_config.eu_count; 529 | let mut free_stack = Vec::with_capacity(capacity as usize); 530 | let mut array = Vec::with_capacity(capacity as usize); 531 | for i in 0..capacity { 532 | array.push(EU { 533 | index: i, 534 | cycles_remaining: 0, 535 | rs_index: None, 536 | state: EUState::IDLE, 537 | trace: cpu_config.trace.execute, 538 | memory_subsystem: Rc::clone(memory_subsystem), 539 | perf_counters: Rc::clone(perf_counters), 540 | phys_reg_file: Rc::clone(phys_reg_file), 541 | broadcast_buffer: Rc::clone(broadcast_buffer), 542 | }); 543 | free_stack.push(i); 544 | } 545 | 546 | EUTable { 547 | capacity, 548 | array, 549 | idle_stack: free_stack, 550 | } 551 | } 552 | 553 | pub(crate) fn flush(&mut self) { 554 | self.idle_stack.clear(); 555 | for k in 0..self.capacity { 556 | self.idle_stack.push(k); 557 | self.array.get_mut(k as usize).unwrap().reset(); 558 | } 559 | } 560 | 561 | pub(crate) fn has_idle(&self) -> bool { 562 | return !self.idle_stack.is_empty(); 563 | } 564 | 565 | pub(crate) fn get_mut(&mut self, eu_index: u8) -> &mut EU { 566 | self.array.get_mut(eu_index as usize).unwrap() 567 | } 568 | 569 | pub(crate) fn allocate(&mut self) -> u8 { 570 | if let Some(last_element) = self.idle_stack.pop() { 571 | let eu = self.array.get_mut(last_element as usize).unwrap(); 572 | debug_assert!(eu.state == EUState::IDLE); 573 | debug_assert!(eu.rs_index.is_none()); 574 | debug_assert!(eu.cycles_remaining == 0); 575 | 576 | eu.state = EUState::EXECUTING; 577 | return last_element; 578 | } else { 579 | panic!("No free PhysReg") 580 | } 581 | } 582 | 583 | pub(crate) fn deallocate(&mut self, eu_index: u8) { 584 | let eu = self.array.get_mut(eu_index as usize).unwrap(); 585 | debug_assert!(eu.state == EUState::EXECUTING || eu.state == EUState::COMPLETED); 586 | debug_assert!(eu.rs_index.is_some()); 587 | debug_assert!(!self.idle_stack.contains(&eu_index)); 588 | 589 | eu.reset(); 590 | self.idle_stack.push(eu_index); 591 | } 592 | } 593 | -------------------------------------------------------------------------------- /src/backend/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod backend; 2 | mod reservation_station; 3 | mod reorder_buffer; 4 | mod physical_register; 5 | mod register_alias_table; 6 | mod execution_unit; -------------------------------------------------------------------------------- /src/backend/physical_register.rs: -------------------------------------------------------------------------------- 1 | use crate::instructions::instructions::{DWordType, RegisterType}; 2 | 3 | #[derive(Clone, Copy, PartialEq, Debug)] 4 | enum PhysRegEntryState { 5 | IDLE, 6 | BUSY, 7 | } 8 | 9 | pub(crate) struct PhysRegEntry { 10 | pub(crate) value: DWordType, 11 | pub(crate) has_value: bool, 12 | state: PhysRegEntryState, 13 | } 14 | 15 | impl PhysRegEntry { 16 | fn reset(&mut self) { 17 | self.value = 0; 18 | self.has_value = false; 19 | self.state = PhysRegEntryState::IDLE; 20 | } 21 | } 22 | 23 | pub(crate) struct PhysRegFile { 24 | free_stack: Vec, 25 | count: u16, 26 | entries: Vec, 27 | } 28 | 29 | impl PhysRegFile { 30 | pub(crate) fn new(count: u16) -> PhysRegFile { 31 | let mut free_stack = Vec::with_capacity(count as usize); 32 | let mut entries = Vec::with_capacity(count as usize); 33 | for i in 0..count { 34 | entries.push(PhysRegEntry { 35 | value: 0, 36 | has_value: false, 37 | state: PhysRegEntryState::IDLE, 38 | }); 39 | free_stack.push(count - 1 - i); 40 | } 41 | 42 | PhysRegFile { count, entries, free_stack } 43 | } 44 | 45 | pub(crate) fn get(&self, reg: RegisterType) -> &PhysRegEntry { 46 | let entry = self.entries.get(reg as usize).unwrap(); 47 | debug_assert!(entry.state == PhysRegEntryState::BUSY, "phys register {} is not in busy state", reg); 48 | return entry; 49 | } 50 | 51 | pub(crate) fn get_mut(&mut self, reg: RegisterType) -> &mut PhysRegEntry { 52 | let entry = self.entries.get_mut(reg as usize).unwrap(); 53 | debug_assert!(entry.state == PhysRegEntryState::BUSY, "phys register {} is not in busy state", reg); 54 | return entry; 55 | } 56 | 57 | pub(crate) fn set_value(&mut self, reg: RegisterType, value: DWordType) { 58 | let entry = self.get_mut(reg); 59 | debug_assert!(!entry.has_value); 60 | entry.has_value = true; 61 | entry.value = value; 62 | } 63 | 64 | pub(crate) fn get_value(&self, reg: RegisterType) -> DWordType { 65 | let entry = self.get(reg); 66 | debug_assert!(entry.has_value); 67 | entry.value 68 | } 69 | 70 | pub(crate) fn allocate(&mut self) -> RegisterType { 71 | if let Some(reg) = self.free_stack.pop() { 72 | let entry = self.entries.get_mut(reg as usize).unwrap(); 73 | debug_assert!(entry.state == PhysRegEntryState::IDLE); 74 | debug_assert!(!entry.has_value, " The allocated physical register {} should not have a value", reg); 75 | entry.state = PhysRegEntryState::BUSY; 76 | return reg; 77 | } else { 78 | panic!("No free PhysReg") 79 | } 80 | } 81 | 82 | pub(crate) fn flush(&mut self) { 83 | self.free_stack.clear(); 84 | 85 | for i in 0..self.count { 86 | let entry = &mut self.entries[i as usize]; 87 | 88 | self.free_stack.push(i); 89 | entry.reset(); 90 | } 91 | } 92 | 93 | pub(crate) fn deallocate(&mut self, reg: RegisterType) { 94 | debug_assert!(!self.free_stack.contains(®), "Phys register {} can't be deallocated while it is also on the free stack", reg); 95 | 96 | let entry = self.get_mut(reg); 97 | 98 | debug_assert!(entry.state == PhysRegEntryState::BUSY); 99 | 100 | entry.reset(); 101 | 102 | self.free_stack.push(reg); 103 | } 104 | } 105 | 106 | #[cfg(test)] 107 | mod tests { 108 | use super::*; 109 | 110 | #[test] 111 | fn test_allocate() { 112 | let mut reg_file = PhysRegFile::new(256); 113 | let reg = reg_file.allocate(); 114 | assert_eq!(reg, 0); 115 | 116 | let entry = reg_file.get(reg); 117 | assert_eq!(entry.state, PhysRegEntryState::BUSY); 118 | assert_eq!(entry.has_value, false); 119 | assert_eq!(entry.value, 0); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/backend/register_alias_table.rs: -------------------------------------------------------------------------------- 1 | use crate::instructions::instructions::RegisterType; 2 | 3 | pub(crate) struct RATEntry { 4 | // the physical register in the arch register to phys register mapping 5 | pub(crate) phys_reg: RegisterType, 6 | // if the entry currently contains a valid architectural to physical register mapping 7 | pub(crate) valid: bool, 8 | } 9 | 10 | /// The Register Alias Table. This structure is used for the register 11 | /// renaming process. The RAT entry for a given architectural register 12 | /// points to the physical register to use. As long as such a entry 13 | /// exists, it should be used. 14 | pub(crate) struct RAT { 15 | pub(crate) table: Vec, 16 | } 17 | 18 | impl RAT { 19 | pub(crate) fn new(phys_reg_count: u16) -> Self { 20 | let mut table = Vec::with_capacity(phys_reg_count as usize); 21 | for _ in 0..phys_reg_count { 22 | table.push(RATEntry { phys_reg: 0, valid: false }); 23 | } 24 | Self { table } 25 | } 26 | 27 | pub(crate) fn flush(&mut self) { 28 | for k in 0..self.table.len() { 29 | let option = self.table.get_mut(k).unwrap(); 30 | option.valid = false; 31 | } 32 | } 33 | 34 | pub(crate) fn update(&mut self, reg_a: RegisterType, reg_p: RegisterType) { 35 | // update the RAT entry to point to the newest phys_reg 36 | let rat_entry = self.get_mut(reg_a); 37 | rat_entry.phys_reg = reg_p; 38 | rat_entry.valid = true; 39 | } 40 | 41 | pub(crate) fn get(&self, reg_a: RegisterType) -> &RATEntry { 42 | return self.table.get(reg_a as usize).unwrap(); 43 | } 44 | 45 | pub(crate) fn get_mut(&mut self, reg_a: RegisterType) -> &mut RATEntry { 46 | return self.table.get_mut(reg_a as usize).unwrap(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/backend/reorder_buffer.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::backend::reservation_station::RenamedRegister; 4 | use crate::instructions::instructions::Instr; 5 | 6 | #[derive(Clone, Copy, PartialEq, Debug)] 7 | pub(crate) enum ROBSlotState { 8 | // the initial state 9 | IDLE, 10 | // the instruction is issued into the rob where it is waiting for its operands. 11 | ISSUED, 12 | // todo: better name, the rob slot has all operands ready but isn't dispatched yet 13 | STAGED, 14 | // the instruction is dispatched to an EU where it will be processed 15 | DISPATCHED, 16 | // the instruction has executed 17 | EXECUTED, 18 | } 19 | 20 | pub(crate) struct ROBSlot { 21 | // the pc of the current instr. 22 | pub(crate) pc: usize, 23 | pub(crate) instr: Option>, 24 | pub(crate) state: ROBSlotState, 25 | pub(crate) index: u16, 26 | pub(crate) rs_index: Option, 27 | pub(crate) renamed_registers: Vec, 28 | pub(crate) branch_target_predicted: usize, 29 | pub(crate) branch_target_actual: usize, 30 | pub(crate) sb_pos: Option, 31 | pub(crate) eu_index: Option, 32 | } 33 | 34 | impl ROBSlot { 35 | fn reset(&mut self) { 36 | self.branch_target_predicted = 0; 37 | self.branch_target_actual = 0; 38 | self.state = ROBSlotState::IDLE; 39 | self.rs_index = None; 40 | self.instr = None; 41 | self.sb_pos = None; 42 | self.eu_index = None; 43 | self.pc = 0; 44 | self.renamed_registers.clear(); 45 | } 46 | } 47 | 48 | pub(crate) struct ROB { 49 | pub(crate) capacity: u16, 50 | pub(crate) seq_issued: u64, 51 | pub(crate) seq_dispatched: u64, 52 | pub(crate) seq_rs_allocated: u64, 53 | pub(crate) seq_retired: u64, 54 | pub(crate) head: u64, 55 | pub(crate) tail: u64, 56 | pub(crate) slots: Vec, 57 | } 58 | 59 | impl ROB { 60 | pub(crate) fn new(capacity: u16) -> Self { 61 | let mut slots = Vec::with_capacity(capacity as usize); 62 | for k in 0..capacity { 63 | slots.push(ROBSlot { 64 | index: k, 65 | instr: None, 66 | state: ROBSlotState::IDLE, 67 | rs_index: None, 68 | renamed_registers: Vec::new(), 69 | branch_target_predicted: 0, 70 | branch_target_actual: 0, 71 | sb_pos: None, 72 | eu_index: None, 73 | pc: 0, 74 | }); 75 | } 76 | 77 | Self { 78 | capacity, 79 | seq_issued: 0, 80 | seq_dispatched: 0, 81 | seq_rs_allocated: 0, 82 | seq_retired: 0, 83 | tail: 0, 84 | head: 0, 85 | slots, 86 | } 87 | } 88 | 89 | pub(crate) fn get_mut(&mut self, slot_index: u16) -> &mut ROBSlot { 90 | // todo: should be between head and tail 91 | &mut self.slots[slot_index as usize] 92 | } 93 | 94 | pub(crate) fn allocate(&mut self) -> u16 { 95 | debug_assert!(self.has_space(), "ROB: Can't allocate if the ROB has no space."); 96 | 97 | let index = self.to_index(self.tail); 98 | 99 | self.tail += 1; 100 | return index; 101 | } 102 | 103 | pub(crate) fn to_index(&self, seq: u64) -> u16 { 104 | (seq % self.capacity as u64) as u16 105 | } 106 | 107 | pub(crate) fn deallocate(&mut self) { 108 | debug_assert!(!self.is_empty(), "ROB: Can't deallocate if ROB is empty"); 109 | 110 | let index = self.to_index(self.head) as usize; 111 | self.slots[index].reset(); 112 | self.head += 1; 113 | } 114 | 115 | pub(crate) fn size(&self) -> u16 { 116 | return (self.tail - self.head) as u16; 117 | } 118 | 119 | pub(crate) fn is_empty(&self) -> bool { 120 | return self.head == self.tail; 121 | } 122 | 123 | pub(crate) fn has_space(&self) -> bool { 124 | return self.capacity > self.size(); 125 | } 126 | 127 | pub(crate) fn flush(&mut self) { 128 | // todo: we don't need to go over the whole rob; just over the busy slots 129 | for i in 0..self.capacity { 130 | let slot = &mut self.slots[i as usize]; 131 | slot.reset(); 132 | } 133 | self.head = 0; 134 | self.tail = 0; 135 | self.seq_retired = 0; 136 | self.seq_issued = 0; 137 | self.seq_rs_allocated = 0; 138 | self.seq_dispatched = 0; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/backend/reservation_station.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashSet, VecDeque}; 2 | 3 | use crate::instructions::instructions::{ConditionCode, DWordType, Opcode, RegisterType}; 4 | use crate::instructions::instructions::Opcode::NOP; 5 | 6 | #[derive(Clone)] 7 | pub(crate) struct RenamedRegister { 8 | pub(crate) phys_reg: Option, 9 | pub(crate) arch_reg: RegisterType, 10 | pub(crate) value: Option, 11 | } 12 | 13 | #[derive(Clone, Copy, PartialEq, Debug)] 14 | pub(crate) enum RSState { 15 | IDLE, 16 | BUSY, 17 | } 18 | 19 | pub enum RSOperand2 { 20 | Immediate { 21 | value: DWordType, 22 | }, 23 | Register { 24 | register: RenamedRegister, 25 | }, 26 | Unused(), 27 | } 28 | 29 | impl RSOperand2 { 30 | pub fn value(&self) -> DWordType { 31 | match self { 32 | RSOperand2::Immediate { value } => *value, 33 | RSOperand2::Register { register } => register.value.unwrap(), 34 | RSOperand2::Unused() => panic!(), 35 | } 36 | } 37 | } 38 | 39 | pub struct RSDataProcessing { 40 | pub opcode: Opcode, 41 | pub condition: ConditionCode, 42 | pub rn: Option, 43 | pub rd: RenamedRegister, 44 | // the original value of the rd register (needed for condition codes) 45 | pub rd_src: Option, 46 | // the cpsr for condition codes 47 | pub cpsr: Option, 48 | pub operand2: RSOperand2, 49 | } 50 | 51 | pub enum RSBranchTarget { 52 | Immediate { 53 | offset: u32, 54 | }, 55 | Register { 56 | register: RenamedRegister, 57 | }, 58 | } 59 | 60 | impl RSBranchTarget { 61 | pub fn value(&self) -> u32 { 62 | match self { 63 | RSBranchTarget::Immediate { offset } => *offset, 64 | RSBranchTarget::Register { register } => register.value.unwrap() as u32, 65 | } 66 | } 67 | } 68 | 69 | pub struct RSBranch { 70 | pub opcode: Opcode, 71 | pub condition: ConditionCode, 72 | pub lr: Option, 73 | pub target: RSBranchTarget, 74 | pub rt: Option, 75 | } 76 | 77 | pub struct RSLoadStore { 78 | pub opcode: Opcode, 79 | pub condition: ConditionCode, 80 | pub rn: RenamedRegister, 81 | pub rd: RenamedRegister, 82 | pub offset: u16, 83 | } 84 | 85 | pub struct RSPrintr { 86 | pub rn: RenamedRegister, 87 | } 88 | 89 | pub struct RSSynchronization { 90 | pub opcode: Opcode, 91 | } 92 | 93 | pub(crate) enum RSInstr { 94 | DataProcessing { 95 | data_processing: RSDataProcessing, 96 | }, 97 | 98 | Branch { 99 | branch: RSBranch, 100 | }, 101 | 102 | LoadStore { 103 | load_store: RSLoadStore, 104 | }, 105 | 106 | Printr { 107 | printr: RSPrintr, 108 | }, 109 | 110 | Synchronization { 111 | synchronization: RSSynchronization, 112 | }, 113 | } 114 | 115 | // A single reservation station 116 | pub(crate) struct RS { 117 | pub(crate) rob_slot_index: Option, 118 | pub(crate) opcode: Opcode, 119 | pub(crate) state: RSState, 120 | pub(crate) pending_cnt: u8, 121 | pub(crate) instr: RSInstr, 122 | pub(crate) index: u16, 123 | } 124 | 125 | impl RS { 126 | fn new(index: u16) -> Self { 127 | Self { 128 | opcode: NOP, 129 | state: RSState::IDLE, 130 | pending_cnt: 0, 131 | rob_slot_index: None, 132 | index, 133 | instr: RSInstr::Synchronization { 134 | synchronization: RSSynchronization { opcode: NOP }, 135 | }, 136 | } 137 | } 138 | 139 | fn reset(&mut self) { 140 | self.rob_slot_index = None; 141 | self.opcode = NOP; 142 | self.state = RSState::IDLE; 143 | self.pending_cnt = 0; 144 | self.instr = RSInstr::Synchronization { 145 | synchronization: RSSynchronization { opcode: NOP } 146 | }; 147 | } 148 | } 149 | 150 | pub(crate) struct RSTable { 151 | idle_stack: Vec, 152 | ready_queue: VecDeque, 153 | pub(crate) capacity: u16, 154 | array: Vec, 155 | // delete 156 | pub(crate) allocated: HashSet, 157 | } 158 | 159 | impl RSTable { 160 | pub(crate) fn new(capacity: u16) -> Self { 161 | let mut free_stack = Vec::with_capacity(capacity as usize); 162 | let mut array = Vec::with_capacity(capacity as usize); 163 | for i in 0..capacity { 164 | array.push(RS::new(i)); 165 | free_stack.push(i); 166 | } 167 | 168 | 169 | RSTable { 170 | capacity, 171 | array, 172 | idle_stack: free_stack, 173 | ready_queue: VecDeque::new(), 174 | allocated: HashSet::new(), 175 | } 176 | } 177 | 178 | pub(crate) fn get_mut(&mut self, rs_index: u16) -> &mut RS { 179 | return &mut self.array[rs_index as usize]; 180 | } 181 | 182 | pub(crate) fn enqueue_ready(&mut self, rs_index: u16) { 183 | debug_assert!(!self.ready_queue.contains(&rs_index), "Can't enqueue ready rs_index={}, it is already on the ready queue", rs_index); 184 | debug_assert!(self.allocated.contains(&rs_index), "Can't enqueue ready rs_index={}, it isn't in the allocated set", rs_index); 185 | 186 | self.ready_queue.push_front(rs_index); 187 | } 188 | 189 | // todo: has_ready/dequeue_ready can be simplified by using an Option 190 | pub(crate) fn has_ready(&self) -> bool { 191 | !self.ready_queue.is_empty() 192 | 193 | //return self.ready_queue_head != self.ready_queue_tail; 194 | } 195 | 196 | pub(crate) fn flush(&mut self) { 197 | self.ready_queue.clear(); 198 | self.idle_stack.clear(); 199 | self.allocated.clear(); 200 | 201 | for k in 0..self.capacity { 202 | self.array.get_mut(k as usize).unwrap().reset(); 203 | self.idle_stack.push(k); 204 | } 205 | } 206 | 207 | pub(crate) fn deque_ready(&mut self) -> u16 { 208 | debug_assert!(self.has_ready(), "RSTable: can't dequeue ready when there are no ready items"); 209 | //let index = self.to_index(self.ready_queue_head); 210 | let rs_ready_index = self.ready_queue.pop_front().unwrap(); 211 | 212 | debug_assert!(self.allocated.contains(&rs_ready_index), 213 | " deque_ready for rs_ready_index {} failed, it is not in the allocated set", rs_ready_index); 214 | 215 | #[cfg(debug_assertions)] 216 | { 217 | let rs = &self.array[rs_ready_index as usize]; 218 | //println!("RS dequeue ready {:?}", rs.opcode); 219 | 220 | debug_assert!(rs.state == RSState::BUSY, "RS should be busy state, rs_index {}", rs_ready_index); 221 | debug_assert!(rs.rob_slot_index.is_some()); 222 | } 223 | 224 | return rs_ready_index; 225 | } 226 | 227 | pub(crate) fn has_idle(&self) -> bool { 228 | return !self.idle_stack.is_empty(); 229 | } 230 | 231 | pub(crate) fn allocate(&mut self) -> u16 { 232 | if let Some(rs_index) = self.idle_stack.pop() { 233 | if self.allocated.contains(&rs_index) { 234 | panic!("Duplicate allocation {}", rs_index); 235 | } 236 | 237 | self.allocated.insert(rs_index); 238 | 239 | let rs = &mut self.array[rs_index as usize]; 240 | 241 | debug_assert!(rs.state == RSState::IDLE); 242 | rs.state = RSState::BUSY; 243 | return rs_index; 244 | } else { 245 | panic!("No free RS") 246 | } 247 | } 248 | 249 | pub(crate) fn deallocate(&mut self, rs_index: u16) { 250 | let rs = &mut self.array[rs_index as usize]; 251 | 252 | debug_assert!(!self.ready_queue.contains(&rs_index), 253 | "rs_index {} can't be deallocated if it is still on the ready queue", rs_index); 254 | 255 | if !self.allocated.contains(&rs_index) { 256 | panic!("Deallocate while not allocated {}", rs_index); 257 | } 258 | 259 | self.allocated.remove(&rs_index); 260 | 261 | debug_assert!(rs_index == rs.index); 262 | 263 | 264 | debug_assert!(rs.state == RSState::BUSY); 265 | debug_assert!(!self.idle_stack.contains(&rs_index)); 266 | rs.reset(); 267 | 268 | self.idle_stack.push(rs_index); 269 | } 270 | } 271 | 272 | -------------------------------------------------------------------------------- /src/cpu.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::error::Error; 3 | use std::fs::File; 4 | use std::ops::Add; 5 | use std::rc::Rc; 6 | use std::thread; 7 | use std::time::{Duration, Instant}; 8 | 9 | use serde::Deserialize; 10 | 11 | use crate::backend::backend::Backend; 12 | use crate::frontend::frontend::{Frontend, FrontendControl}; 13 | use crate::instructions::instructions::{DWordType, InstrQueue, Program, RegisterType}; 14 | use crate::memory_subsystem::memory_subsystem::MemorySubsystem; 15 | 16 | pub struct PerfCounters { 17 | pub branch_miss_prediction_cnt: u64, 18 | pub branch_good_predictions_cnt: u64, 19 | pub decode_cnt: u64, 20 | pub issue_cnt: u64, 21 | pub dispatch_cnt: u64, 22 | pub execute_cnt: u64, 23 | pub retired_cnt: u64, 24 | pub bad_speculation_cnt: u64, 25 | pub pipeline_flushes: u64, 26 | pub cycle_cnt: u64, 27 | } 28 | 29 | impl PerfCounters { 30 | pub fn new() -> Self { 31 | Self { 32 | decode_cnt: 0, 33 | issue_cnt: 0, 34 | dispatch_cnt: 0, 35 | execute_cnt: 0, 36 | retired_cnt: 0, 37 | cycle_cnt: 0, 38 | bad_speculation_cnt: 0, 39 | branch_miss_prediction_cnt: 0, 40 | branch_good_predictions_cnt: 0, 41 | pipeline_flushes: 0, 42 | } 43 | } 44 | } 45 | 46 | #[derive(Clone, Deserialize, Debug)] 47 | pub struct Trace { 48 | pub decode: bool, 49 | pub issue: bool, 50 | pub allocate_rs: bool, 51 | pub dispatch: bool, 52 | pub execute: bool, 53 | pub retire: bool, 54 | pub pipeline_flush: bool, 55 | } 56 | 57 | impl Default for Trace { 58 | fn default() -> Self { 59 | Trace { 60 | decode: false, 61 | issue: false, 62 | allocate_rs: false, 63 | dispatch: false, 64 | execute: false, 65 | retire: false, 66 | pipeline_flush: false, 67 | } 68 | } 69 | } 70 | 71 | #[derive(Clone, Deserialize, Debug)] 72 | pub struct CPUConfig { 73 | // the number of physical registers 74 | pub phys_reg_count: u16, 75 | // the number of instructions the frontend can fetch/decode per clock cycle. 76 | pub frontend_n_wide: u8, 77 | // the size of the instruction queue between frontend and backend 78 | pub instr_queue_capacity: u16, 79 | // the frequency of the CPU in Hz. 80 | pub frequency_hz: u64, 81 | // the number of reservation stations 82 | pub rs_count: u16, 83 | // the size of the memory in machine words 84 | pub memory_size: u32, 85 | // the capacity of the store buffer 86 | pub sb_capacity: u16, 87 | // the number of line fill buffers; currently there are no line fill buffer 88 | // it is just a limit of the number of stores that can commit to memory 89 | // per clock cycle (there is also no cache) 90 | pub lfb_count: u8, 91 | // the capacity of the reorder buffer 92 | pub rob_capacity: u16, 93 | // the number of execution units 94 | pub eu_count: u8, 95 | // if processing of a single instruction should be traced (printed) 96 | pub trace: Trace, 97 | // the number of instructions that can retire per clock cycle 98 | pub retire_n_wide: u8, 99 | // the number of instructions that can be dispatched (send to execution units) every clock cycle. 100 | pub dispatch_n_wide: u8, 101 | // the number of instructions that can be issued to the rob or finding reservation stations, every clock cycle. 102 | pub issue_n_wide: u8, 103 | // The delay between writing the CPU stats. A value of 0 means that stat writing is disabled. 104 | pub stats_seconds: u32, 105 | } 106 | 107 | impl Default for CPUConfig { 108 | fn default() -> Self { 109 | CPUConfig { 110 | phys_reg_count: 64, 111 | frontend_n_wide: 4, 112 | instr_queue_capacity: 64, 113 | frequency_hz: 4, 114 | rs_count: 64, 115 | memory_size: 128, 116 | sb_capacity: 16, 117 | lfb_count: 4, 118 | rob_capacity: 32, 119 | eu_count: 10, 120 | trace: Trace::default(), 121 | retire_n_wide: 4, 122 | dispatch_n_wide: 4, 123 | issue_n_wide: 4, 124 | stats_seconds: 0, 125 | } 126 | } 127 | } 128 | 129 | pub fn load_cpu_config(file_path: &str) -> Result> { 130 | let file = File::open(file_path)?; 131 | let config = serde_yaml::from_reader(file)?; 132 | Ok(config) 133 | } 134 | 135 | pub struct CPU { 136 | pub(crate) backend: Backend, 137 | pub(crate) frontend: Frontend, 138 | pub(crate) memory_subsystem: Rc>, 139 | pub(crate) arch_reg_file: Rc>, 140 | pub(crate) cycle_period: Duration, 141 | pub(crate) trace: Trace, 142 | pub(crate) perf_counters: Rc>, 143 | pub(crate) stats_seconds: u32, 144 | } 145 | 146 | impl CPU { 147 | pub fn new(cpu_config: &CPUConfig) -> CPU { 148 | let instr_queue = Rc::new(RefCell::new(InstrQueue::new(cpu_config.instr_queue_capacity))); 149 | 150 | let perf_counters = Rc::new(RefCell::new(PerfCounters::new())); 151 | 152 | let memory_subsystem = Rc::new(RefCell::new( 153 | MemorySubsystem::new(cpu_config))); 154 | 155 | let arch_reg_file = Rc::new(RefCell::new( 156 | ArgRegFile::new(GENERAL_ARG_REG_CNT + SPECIAL_ARG_REG_CNT))); 157 | 158 | // on ARM the stack grows down (from larger address to smaller address) 159 | arch_reg_file.borrow_mut().set_value(SP, cpu_config.memory_size as DWordType); 160 | 161 | let frontend_control = Rc::new(RefCell::new( 162 | FrontendControl { halted: false })); 163 | 164 | let backend = Backend::new( 165 | cpu_config, 166 | &instr_queue, 167 | &memory_subsystem, 168 | &arch_reg_file, 169 | &frontend_control, 170 | &perf_counters, 171 | ); 172 | 173 | let frontend = Frontend::new( 174 | cpu_config, 175 | &instr_queue, 176 | &frontend_control, 177 | &perf_counters, 178 | &arch_reg_file, 179 | ); 180 | 181 | CPU { 182 | backend, 183 | frontend, 184 | memory_subsystem, 185 | arch_reg_file, 186 | stats_seconds: cpu_config.stats_seconds, 187 | cycle_period: Duration::from_micros(1_000_000 / cpu_config.frequency_hz), 188 | trace: cpu_config.trace.clone(), 189 | perf_counters: perf_counters, 190 | } 191 | } 192 | 193 | pub fn run(&mut self, program: &Rc) { 194 | self.frontend.init(program); 195 | 196 | self.memory_subsystem.borrow_mut().init(program); 197 | 198 | let log_stats_interval = Duration::new(self.stats_seconds as u64, 0); // n seconds 199 | println!("log_stats_interval: {:?}", log_stats_interval); 200 | let mut last_log_stats_time = Instant::now().add(log_stats_interval); 201 | 202 | while !self.backend.exit { 203 | self.perf_counters.borrow_mut().cycle_cnt += 1; 204 | self.memory_subsystem.borrow_mut().do_cycle(); 205 | self.backend.do_cycle(); 206 | self.frontend.do_cycle(); 207 | thread::sleep(self.cycle_period); 208 | 209 | if self.stats_seconds > 0 && last_log_stats_time.elapsed() >= log_stats_interval { 210 | self.log_stats(); 211 | last_log_stats_time = Instant::now(); 212 | } 213 | } 214 | 215 | loop { 216 | if self.memory_subsystem.borrow_mut().sb.is_empty() { 217 | break; 218 | } 219 | 220 | self.memory_subsystem.borrow_mut().do_cycle(); 221 | } 222 | 223 | println!("Program complete!"); 224 | } 225 | 226 | fn log_stats(&mut self) { 227 | let perf_counters = self.perf_counters.borrow_mut(); 228 | let branch_total = perf_counters.branch_miss_prediction_cnt + perf_counters.branch_good_predictions_cnt; 229 | 230 | let ipc = perf_counters.retired_cnt as f32 / perf_counters.cycle_cnt as f32; 231 | 232 | let branch_prediction = if branch_total != 0 { 233 | 100.0 * perf_counters.branch_good_predictions_cnt as f32 / branch_total as f32 234 | } else { 235 | 0.0 236 | }; 237 | 238 | let mut message = String::new(); 239 | 240 | message.push_str(&format!("[Cycles:{}]", perf_counters.cycle_cnt)); 241 | message.push_str(&format!("[IPC={:.2}]", ipc)); 242 | message.push_str(&format!("[Decoded={}]", perf_counters.decode_cnt)); 243 | message.push_str(&format!("[Issued={}]", perf_counters.issue_cnt)); 244 | message.push_str(&format!("[Dispatched={}]", perf_counters.dispatch_cnt)); 245 | message.push_str(&format!("[Executed={}]", perf_counters.execute_cnt)); 246 | message.push_str(&format!("[Retired={}]", perf_counters.retired_cnt)); 247 | message.push_str(&format!("[Branch Tot={}, Pred={:.2}%]", branch_total, branch_prediction)); 248 | message.push_str(&format!("[Pipeline Flush={}]", perf_counters.pipeline_flushes)); 249 | 250 | println!("{}", message); 251 | } 252 | } 253 | 254 | pub const GENERAL_ARG_REG_CNT: u16 = 31; 255 | pub const SPECIAL_ARG_REG_CNT: u16 = 1; 256 | pub const FP: RegisterType = 11; 257 | pub const SP: RegisterType = 13; 258 | pub const LR: RegisterType = 14; 259 | pub const PC: RegisterType = 15; 260 | pub const CPSR: u16 = GENERAL_ARG_REG_CNT; 261 | 262 | pub const ZERO_FLAG: u8 = 30; 263 | pub const NEGATIVE_FLAG: u8 = 31; 264 | pub const CARRY_FLAG: u8 = 29; 265 | pub const OVERFLOW_FLAG: u8 = 28; 266 | 267 | struct ArgRegEntry { 268 | value: DWordType, 269 | } 270 | 271 | pub struct ArgRegFile { 272 | entries: Vec, 273 | } 274 | 275 | impl ArgRegFile { 276 | fn new(rs_count: u16) -> ArgRegFile { 277 | let mut array = Vec::with_capacity(rs_count as usize); 278 | for _ in 0..rs_count { 279 | array.push(ArgRegEntry { value: 0 }); 280 | } 281 | 282 | ArgRegFile { entries: array } 283 | } 284 | 285 | pub fn get_value(&self, reg: RegisterType) -> DWordType { 286 | let entry = self.entries.get(reg as usize).unwrap(); 287 | return entry.value; 288 | } 289 | 290 | pub fn set_value(&mut self, reg: RegisterType, value: DWordType) { 291 | let entry = self.entries.get_mut(reg as usize).unwrap(); 292 | entry.value = value; 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /src/cpu_tests.rs: -------------------------------------------------------------------------------- 1 | use crate::instructions::instructions::{Program, RegisterType}; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use std::rc::Rc; 6 | use crate::cpu::{CPU, CPUConfig}; 7 | use crate::instructions::instructions::DWordType; 8 | use crate::loader::loader::{load_from_string, LoadError}; 9 | 10 | use super::*; 11 | 12 | #[test] 13 | fn test_same_src_dst_reg() { 14 | let src = r#" 15 | .text 16 | MOV r0, #5; 17 | ADD r0, r0, #10; 18 | "#; 19 | let mut harness = TestHarness::default(); 20 | harness.run(src); 21 | harness.assert_reg_value(0, 15); 22 | } 23 | 24 | #[test] 25 | fn test_ADD() { 26 | let src = r#" 27 | .text 28 | MOV r0, #100; 29 | MOV r1, #10; 30 | ADD r2, r0, r1; 31 | "#; 32 | let mut harness = TestHarness::default(); 33 | harness.run(src); 34 | harness.assert_reg_value(0, 100); 35 | harness.assert_reg_value(1, 10); 36 | harness.assert_reg_value(2, 110); 37 | } 38 | 39 | #[test] 40 | fn test_NEG() { 41 | let src = r#" 42 | .text 43 | MOV r0, #100; 44 | NEG r1, r0; 45 | MOV r0, #0; 46 | NEG r2, r0; 47 | "#; 48 | let mut harness = TestHarness::default(); 49 | harness.run(src); 50 | // todo: ugly constant (2^64-100) 51 | harness.assert_reg_value(1, 18446744073709551516); 52 | harness.assert_reg_value(2, 0); 53 | } 54 | 55 | #[test] 56 | fn test_AND() { 57 | let src = r#" 58 | .text 59 | MOV r0, #0; 60 | MOV r1, #0; 61 | AND r2, r0, r1; 62 | MOV r0, #1; 63 | MOV r1, #0; 64 | AND r3, r0, r1; 65 | MOV r0, #0; 66 | MOV r1, #1; 67 | AND r4, r0, r1; 68 | MOV r0, #1; 69 | MOV r1, #1; 70 | AND r5, r0, r1; 71 | "#; 72 | let mut harness = TestHarness::default(); 73 | harness.run(src); 74 | harness.assert_reg_value(2, 0); 75 | harness.assert_reg_value(3, 0); 76 | harness.assert_reg_value(4, 0); 77 | harness.assert_reg_value(5, 1); 78 | } 79 | 80 | #[test] 81 | fn test_ORR() { 82 | let src = r#" 83 | .text 84 | MOV r0, #0; 85 | MOV r1, #0; 86 | ORR r2, r0, r1; 87 | MOV r0, #1; 88 | MOV r1, #0; 89 | ORR r3, r0, r1; 90 | MOV r0, #0; 91 | MOV r1, #1; 92 | ORR r4, r0, r1; 93 | MOV r0, #1; 94 | MOV r1, #1; 95 | ORR r5, r0, r1; 96 | "#; 97 | let mut harness = TestHarness::default(); 98 | harness.run(src); 99 | harness.assert_reg_value(2, 0); 100 | harness.assert_reg_value(3, 1); 101 | harness.assert_reg_value(4, 1); 102 | harness.assert_reg_value(5, 1); 103 | } 104 | 105 | #[test] 106 | fn test_EOR() { 107 | let src = r#" 108 | .text 109 | MOV r0, #0; 110 | MOV r1, #0; 111 | EOR r2, r0, r1; 112 | MOV r0, #1; 113 | MOV r1, #0; 114 | EOR r3, r0, r1; 115 | MOV r0, #0; 116 | MOV r1, #1; 117 | EOR r4, r0, r1; 118 | MOV r0, #1; 119 | MOV r1, #1; 120 | EOR r5, r0, r1; 121 | "#; 122 | let mut harness = TestHarness::default(); 123 | harness.run(src); 124 | harness.assert_reg_value(2, 0); 125 | harness.assert_reg_value(3, 1); 126 | harness.assert_reg_value(4, 1); 127 | harness.assert_reg_value(5, 0); 128 | } 129 | 130 | #[test] 131 | fn test_MVN() { 132 | let src = r#" 133 | .text 134 | MOV r0, #100; 135 | MVN r1, r0; 136 | MOV r0, #0; 137 | MVN r2, r0; 138 | "#; 139 | let mut harness = TestHarness::default(); 140 | harness.run(src); 141 | harness.assert_reg_value(1, 18446744073709551515); // NOT 100 = 2^64 - 101 142 | harness.assert_reg_value(2, 18446744073709551615); // NOT 0 = 2^64 - 1 143 | } 144 | 145 | #[test] 146 | fn test_SUB() { 147 | let src = r#" 148 | .text 149 | MOV r0, #100; 150 | MOV r1, #10; 151 | SUB r2, r0, r1; 152 | "#; 153 | let mut harness = TestHarness::default(); 154 | harness.load_program(src); 155 | harness.run(src); 156 | harness.assert_reg_value(0, 100); 157 | harness.assert_reg_value(1, 10); 158 | harness.assert_reg_value(2, 90); 159 | } 160 | 161 | #[test] 162 | fn test_RSB() { 163 | let src = r#" 164 | .text 165 | MOV r0, #10; 166 | MOV r1, #100; 167 | RSB r2, r0, r1; 168 | "#; 169 | let mut harness = TestHarness::default(); 170 | harness.load_program(src); 171 | harness.run(src); 172 | harness.assert_reg_value(2, 90); 173 | } 174 | 175 | #[test] 176 | fn test_MUL() { 177 | let src = r#" 178 | .text 179 | MOV r0, #100; 180 | MOV r1, #10; 181 | MUL r2, r0, r1; 182 | "#; 183 | let mut harness = TestHarness::default(); 184 | harness.run(src); 185 | harness.assert_reg_value(0, 100); 186 | harness.assert_reg_value(1, 10); 187 | harness.assert_reg_value(2, 1000); 188 | } 189 | 190 | #[test] 191 | fn test_loop_CMP_BNE() { 192 | let src = r#" 193 | .text 194 | MOV r0, #10; 195 | MOV r1, #0; 196 | loop: 197 | SUB r0, r0, #1; 198 | PRINTR r0; 199 | ADD r1, r1, #1; 200 | CMP r0, #0; 201 | BNE loop; 202 | "#; 203 | let mut harness = TestHarness::default(); 204 | harness.run(src); 205 | 206 | harness.assert_reg_value(0, 0); 207 | harness.assert_reg_value(1, 10); 208 | } 209 | 210 | #[test] 211 | fn test_loop_CMP_BGT() { 212 | let src = r#" 213 | .text 214 | MOV r0, #10; 215 | MOV r1, #0; 216 | loop: 217 | SUB r0, r0, #1; 218 | ADD r1, r1, #1; 219 | CMP r0, #1; 220 | BGT loop; 221 | "#; 222 | let mut harness = TestHarness::default(); 223 | harness.run(src); 224 | 225 | harness.assert_reg_value(0, 1); 226 | harness.assert_reg_value(1, 9); 227 | } 228 | 229 | #[test] 230 | fn test_loop_CMP_BGE() { 231 | let src = r#" 232 | .text 233 | MOV r0, #10; 234 | MOV r1, #0; 235 | loop: 236 | SUB r0, r0, #1; 237 | ADD r1, r1, #1; 238 | CMP r0, #1; 239 | BGE loop; 240 | "#; 241 | let mut harness = TestHarness::default(); 242 | harness.run(src); 243 | 244 | harness.assert_reg_value(0, 0); 245 | harness.assert_reg_value(1, 10); 246 | } 247 | 248 | #[test] 249 | fn test_loop_CMP_BLE() { 250 | let src = r#" 251 | .text 252 | loop: 253 | ADD r0, r0, #1; 254 | CMP r0, #10; 255 | BLE loop; 256 | "#; 257 | let mut harness = TestHarness::default(); 258 | harness.run(src); 259 | 260 | harness.assert_reg_value(0, 11); 261 | } 262 | 263 | #[test] 264 | fn test_loop_CMP_BLT() { 265 | let src = r#" 266 | .text 267 | loop: 268 | ADD r0, r0, #1; 269 | CMP r0, #10; 270 | BLT loop; 271 | "#; 272 | let mut harness = TestHarness::default(); 273 | harness.run(src); 274 | 275 | harness.assert_reg_value(0, 10); 276 | } 277 | 278 | #[test] 279 | fn test_loop_CMP_BEQ() { 280 | let src = r#" 281 | .text 282 | MOV r0, #10; 283 | MOV r1, #0; 284 | loop: 285 | SUB r0, r0, #1; 286 | ADD r1, r1, #1; 287 | CMP r0, #0; 288 | BEQ end; 289 | B loop; 290 | end: 291 | "#; 292 | let mut harness = TestHarness::default(); 293 | harness.run(src); 294 | 295 | harness.assert_reg_value(0, 0); 296 | harness.assert_reg_value(1, 10); 297 | } 298 | 299 | #[test] 300 | fn test_TEQ() { 301 | let src = r#" 302 | .text 303 | MOV r0, #5; 304 | MOV r1, #5; 305 | TEQ r0, r1; 306 | BNE fail; 307 | TEQ r0, #0; 308 | BEQ fail; 309 | B end; 310 | fail: 311 | MOV r2, #1; 312 | end: 313 | "#; 314 | let mut harness = TestHarness::default(); 315 | harness.run(src); 316 | harness.assert_reg_value(2, 0); // Ensure fail indicator is not set 317 | } 318 | 319 | #[test] 320 | fn test_TST() { 321 | let src = r#" 322 | .text 323 | MOV r0, #10; 324 | MOV r1, #12; 325 | TST r0, r1; 326 | BEQ fail; 327 | TST r0, #5; 328 | BNE fail; 329 | B end; 330 | fail: 331 | MOV r2, #1; 332 | end: 333 | "#; 334 | let mut harness = TestHarness::default(); 335 | harness.run(src); 336 | harness.assert_reg_value(2, 0); // Ensure fail indicator is not set 337 | } 338 | 339 | #[test] 340 | fn test_LDR_STR() { 341 | let src = r#" 342 | .data 343 | var_a: .dword 5 344 | var_b: .dword 10 345 | var_c: .dword 0 346 | .text 347 | MOV r0, =var_a; 348 | LDR r0, [r0]; 349 | MOV r1, =var_b; 350 | LDR r1, [r1]; 351 | ADD r2, r0, r1; 352 | MOV r0, =var_c; 353 | STR r2, [r0]; 354 | "#; 355 | let mut harness = TestHarness::default(); 356 | harness.run(src); 357 | harness.assert_variable_value("var_c", 15); 358 | } 359 | 360 | #[test] 361 | fn test_LDR() { 362 | let src = r#" 363 | .data 364 | var_a: .dword 5 365 | .text 366 | MOV r0, =var_a; 367 | LDR r0, [r0]; 368 | "#; 369 | let mut harness = TestHarness::default(); 370 | harness.run(src); 371 | 372 | harness.assert_reg_value(0, 5); 373 | } 374 | 375 | #[test] 376 | fn test_STR() { 377 | let src = r#" 378 | .data 379 | var_a: .dword 0 380 | .text 381 | MOV r0, =var_a; 382 | MOV r1, #10; 383 | STR r1, [r0]; 384 | "#; 385 | let mut harness = TestHarness::default(); 386 | harness.run(src); 387 | 388 | harness.assert_variable_value("var_a", 10); 389 | } 390 | 391 | // Ensures that stores update to memory out of order even they can be performed in order. 392 | #[test] 393 | fn test_STR_WAW() { 394 | let src = r#" 395 | .data 396 | var_a: .dword 0 397 | .text 398 | mov r0, =var_a; 399 | mov r1, #1; 400 | str r1, [r0]; 401 | mov r2, #2; 402 | str r2, [r0]; 403 | mov r3, #3; 404 | str r3, [r0]; 405 | mov r4, #4; 406 | str r4, [r0]; 407 | mov r5, #5; 408 | str r5, [r0]; 409 | mov r6, #6; 410 | str r6, [r0]; 411 | mov r7, #7; 412 | str r7, [r0]; 413 | mov r8, #8; 414 | str r8, [r0]; 415 | "#; 416 | let mut harness = TestHarness::default(); 417 | harness.run(src); 418 | 419 | harness.assert_variable_value("var_a", 8); 420 | } 421 | 422 | #[test] 423 | fn test_STR_loop() { 424 | let src = r#" 425 | .data 426 | var_a: .dword 0 427 | .text 428 | MOV r0, #100; 429 | MOV r1, =var_a; 430 | MOV r2, #0; 431 | loop: 432 | ADD r2, r2, #1; 433 | SUB r0, r0, #1; 434 | STR r2, [r1]; 435 | CBNZ r0, loop; 436 | "#; 437 | let mut harness = TestHarness::default(); 438 | harness.run(src); 439 | 440 | harness.assert_variable_value("var_a", 100); 441 | } 442 | 443 | #[test] 444 | fn test_waw() { 445 | let src = r#" 446 | .text 447 | MOV r0, #1; 448 | MOV r0, #2; 449 | MOV r0, #3; 450 | MOV r0, #4; 451 | MOV r0, #5; 452 | MOV r0, #6; 453 | MOV r0, #7; 454 | MOV r0, #8; 455 | "#; 456 | 457 | let mut harness = TestHarness::default(); 458 | harness.run(src); 459 | 460 | harness.assert_reg_value(0, 8); 461 | } 462 | 463 | #[test] 464 | fn test_dependency_chain() { 465 | let src = r#" 466 | .text 467 | MOV r0, #1; 468 | MOV r1, r0; 469 | MOV r2, r1; 470 | MOV r3, r2; 471 | MOV r4, r3; 472 | MOV r5, r4; 473 | MOV r6, r5; 474 | MOV r7, r6; 475 | MOV r8, r7; 476 | "#; 477 | 478 | let mut harness = TestHarness::default(); 479 | harness.run(src); 480 | 481 | harness.assert_reg_value(8, 1); 482 | } 483 | 484 | #[test] 485 | fn test_BL_RET() { 486 | let src = r#" 487 | .global _start 488 | .text 489 | _add_numbers: 490 | ADD r2, r0, r1; 491 | RET; 492 | _start: 493 | MOV r0, #5; 494 | MOV r1, #10; 495 | BL _add_numbers; 496 | ADD r2, r2, #1; 497 | "#; 498 | let mut harness = TestHarness::default(); 499 | harness.run(src); 500 | 501 | harness.assert_reg_value(2, 16); 502 | } 503 | 504 | #[test] 505 | fn test_loop_CBZ() { 506 | let src = r#" 507 | .text 508 | MOV r0, #10; 509 | _loop: 510 | SUB r0, r0, #1; 511 | ADD r1, r1, #1; 512 | CBZ r0, _end; 513 | B _loop; 514 | _end: 515 | "#; 516 | let mut harness = TestHarness::default(); 517 | harness.run(src); 518 | 519 | harness.assert_reg_value(1, 10); 520 | } 521 | 522 | #[test] 523 | fn test_loop_CBNZ() { 524 | let src = r#" 525 | .text 526 | MOV r0, #10; 527 | MOV r1, #20; 528 | loop: 529 | SUB r0, r0, #1; 530 | ADD r1, r1, #1; 531 | CBNZ r0, loop; 532 | "#; 533 | let mut harness = TestHarness::default(); 534 | harness.run(src); 535 | 536 | harness.assert_reg_value(0, 0); 537 | harness.assert_reg_value(1, 30); 538 | } 539 | 540 | #[test] 541 | fn test_nested_loop_CBNZ() { 542 | let src = r#" 543 | .text 544 | MOV r0, #10; 545 | _loop_outer: 546 | MOV r1, #10; 547 | _loop_inner: 548 | SUB r1, r1, #1; 549 | ADD r2, r2, #1; 550 | CBNZ r1, _loop_inner; 551 | SUB r0, r0, #1; 552 | CBNZ r0, _loop_outer; 553 | "#; 554 | let mut harness = TestHarness::default(); 555 | harness.run(src); 556 | 557 | harness.assert_reg_value(2, 100); 558 | } 559 | 560 | #[test] 561 | fn test_BL_BX() { 562 | let src = r#" 563 | .global _start 564 | .text 565 | _add_numbers: 566 | ADD r2, r0, r1; 567 | BX lr; 568 | _start: 569 | MOV r0, #5; 570 | MOV r1, #10; 571 | BL _add_numbers; 572 | ADD r2, r2, #1; 573 | "#; 574 | let mut harness = TestHarness::default(); 575 | harness.run(src); 576 | 577 | harness.assert_reg_value(2, 16); 578 | } 579 | 580 | #[test] 581 | fn test_binary_value() { 582 | let src = r#" 583 | .text 584 | MOV r0, #0b1010; 585 | MOV r1, #0b1100; 586 | ADD r2, r0, r1; 587 | "#; 588 | let mut harness = TestHarness::default(); 589 | harness.run(src); 590 | harness.assert_reg_value(0, 10); 591 | harness.assert_reg_value(1, 12); 592 | harness.assert_reg_value(2, 22); 593 | } 594 | 595 | #[test] 596 | fn test_hexadecimal_value() { 597 | let src = r#" 598 | .text 599 | MOV r0, #0xA; 600 | MOV r1, #0xC; 601 | ADD r2, r0, r1; 602 | "#; 603 | let mut harness = TestHarness::default(); 604 | harness.run(src); 605 | harness.assert_reg_value(0, 10); 606 | harness.assert_reg_value(1, 12); 607 | harness.assert_reg_value(2, 22); 608 | } 609 | 610 | #[test] 611 | fn test_octal_values() { 612 | let src = r#" 613 | .text 614 | MOV r0, #0o10; 615 | MOV r1, #0o20; 616 | ADD r2, r0, r1; 617 | "#; 618 | let mut harness = TestHarness::default(); 619 | harness.run(src); 620 | harness.assert_reg_value(0, 8); // Check r0 for 8 621 | harness.assert_reg_value(1, 16); // Check r1 for 16 622 | harness.assert_reg_value(2, 24); // Check r2 for 24 (8 + 16) 623 | } 624 | 625 | struct TestHarness { 626 | program: Option>, 627 | cpu: Option, 628 | cpu_config: CPUConfig, 629 | } 630 | 631 | impl TestHarness { 632 | fn default() -> TestHarness { 633 | let cpu_config = Self::new_test_cpu_config(); 634 | TestHarness { 635 | program: None, 636 | cpu: Some(CPU::new(&cpu_config.clone())), 637 | cpu_config: cpu_config, 638 | } 639 | } 640 | 641 | fn new_test_cpu_config() -> CPUConfig { 642 | let mut cpu_config = CPUConfig::default(); 643 | cpu_config.frequency_hz = 1000; 644 | cpu_config 645 | } 646 | 647 | fn run(&mut self, src: &str) { 648 | self.program = Some(self.load_program(src)); 649 | let program = Rc::clone(self.program.as_ref().unwrap()); 650 | self.cpu.as_mut().unwrap().run(&program); 651 | } 652 | 653 | fn load_program(&mut self, src: &str) -> Rc { 654 | let load_result = load_from_string(self.cpu_config.clone(), src.to_string()); 655 | let program = match load_result { 656 | Ok(p) => Rc::new(p), 657 | Err(err) => { 658 | match err { 659 | LoadError::ParseError(msg) => { 660 | println!("{}", msg); 661 | assert!(false); 662 | unreachable!(); 663 | } 664 | 665 | LoadError::AnalysisError(msg_vec) => { 666 | for msg in msg_vec { 667 | println!("{}", msg); 668 | } 669 | assert!(false); 670 | unreachable!(); 671 | } 672 | LoadError::NotFoundError(msg) => { 673 | println!("{}", msg); 674 | unreachable!(); 675 | } 676 | LoadError::IOError(msg) => { 677 | println!("{}", msg); 678 | unreachable!(); 679 | } 680 | } 681 | } 682 | }; 683 | program 684 | } 685 | 686 | fn assert_reg_value(&self, reg: RegisterType, value: DWordType) { 687 | if let Some(ref cpu) = self.cpu { 688 | let reg_file = cpu.arch_reg_file.borrow(); 689 | assert_eq!(reg_file.get_value(reg), value); 690 | } else { 691 | panic!("CPU is not initialized"); 692 | } 693 | } 694 | 695 | fn assert_variable_value(&self, name: &str, value: DWordType) { 696 | if let Some(ref cpu) = self.cpu { 697 | let program = self.program.as_ref().expect("Program not initialized"); 698 | let data_item = program.data_items.get(name).expect("Data item not found"); 699 | let offset = data_item.offset; 700 | let memory_subsystem = cpu.memory_subsystem.borrow(); 701 | match memory_subsystem.memory.get(offset as usize) { 702 | Some(&actual_value) => { 703 | assert_eq!(actual_value, value, "Variable '{}' does not have the expected value", name); 704 | } 705 | None => { 706 | panic!("Memory offset {} is invalid", offset); 707 | } 708 | } 709 | } else { 710 | panic!("CPU is not initialized"); 711 | } 712 | } 713 | } 714 | } 715 | -------------------------------------------------------------------------------- /src/frontend/frontend.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | 4 | use crate::cpu::{ArgRegFile, CPUConfig, PC, PerfCounters, Trace}; 5 | use crate::instructions::instructions::{Branch, BranchTarget, DWordType, EXIT, Instr, InstrQueue, Opcode, Program}; 6 | 7 | pub(crate) struct FrontendControl { 8 | pub(crate) halted: bool, 9 | } 10 | 11 | pub(crate) struct Frontend { 12 | instr_queue: Rc>, 13 | n_wide: u8, 14 | frontend_control: Rc>, 15 | program_option: Option>, 16 | trace: Trace, 17 | exit: bool, 18 | perf_counters: Rc>, 19 | arch_reg_file: Rc>, 20 | } 21 | 22 | impl Frontend { 23 | pub(crate) fn new( 24 | cpu_config: &CPUConfig, 25 | instr_queue: &Rc>, 26 | frontend_control: &Rc>, 27 | perf_counters: &Rc>, 28 | arch_reg_file: &Rc>, 29 | ) -> Frontend { 30 | Frontend { 31 | instr_queue: Rc::clone(instr_queue), 32 | n_wide: cpu_config.frontend_n_wide, 33 | program_option: None, 34 | trace: cpu_config.trace.clone(), 35 | frontend_control: Rc::clone(frontend_control), 36 | exit: false, 37 | perf_counters: Rc::clone(perf_counters), 38 | arch_reg_file: Rc::clone(arch_reg_file), 39 | } 40 | } 41 | 42 | pub(crate) fn init(&mut self, program: &Rc) { 43 | self.program_option = Some(Rc::clone(program)); 44 | self.arch_reg_file.borrow_mut().set_value(PC, program.entry_point as DWordType); 45 | } 46 | 47 | pub(crate) fn do_cycle(&mut self) { 48 | match &self.program_option { 49 | None => return, 50 | Some(program) => { 51 | let mut instr_queue = self.instr_queue.borrow_mut(); 52 | let frontend_control = self.frontend_control.borrow_mut(); 53 | let mut perf_counters = self.perf_counters.borrow_mut(); 54 | let mut arch_reg_file = self.arch_reg_file.borrow_mut(); 55 | 56 | if frontend_control.halted { 57 | return; 58 | } 59 | 60 | for _ in 0..self.n_wide { 61 | if self.exit { 62 | return; 63 | } 64 | 65 | if instr_queue.is_full() { 66 | break; 67 | } 68 | 69 | // todo: we still need mechanism to 'stall' the pipeline. E.g 70 | 71 | // MOV IP, 10 72 | // B foobar 73 | 74 | let pc = arch_reg_file.get_value(PC) as usize; 75 | let instr = if program.code.len() == pc { 76 | // at the end of the program 77 | Rc::new(EXIT) 78 | } else { 79 | program.get_instr(pc) 80 | }; 81 | 82 | if self.trace.decode { 83 | println!("Frontend: pc: {} '{}'", pc, instr); 84 | } 85 | 86 | if let Instr::Synchronization (synchronization ) = instr.as_ref() { 87 | if synchronization.opcode == Opcode::EXIT { 88 | self.exit = true; 89 | } 90 | } 91 | 92 | let tail_index = instr_queue.tail_index(); 93 | let slot = instr_queue.get_mut(tail_index); 94 | 95 | let pc_value_next = match instr.as_ref() { 96 | Instr::Branch (branch ) => { 97 | slot.branch_target_predicted = Self::predict(pc, branch); 98 | //println!("Frontend branch predicted={}", slot.branch_target_predicted); 99 | slot.branch_target_predicted 100 | } 101 | _ => pc + 1, 102 | }; 103 | arch_reg_file.set_value(PC, pc_value_next as DWordType); 104 | 105 | slot.instr = instr; 106 | slot.pc = pc; 107 | instr_queue.tail_bump(); 108 | perf_counters.decode_cnt += 1; 109 | } 110 | } 111 | } 112 | } 113 | 114 | // A static branch predictor that will speculate that backwards branches are taken. 115 | // In the future better branch predictors can be added. 116 | fn predict(ip: usize, branch: &Branch) -> usize { 117 | let branch_target = match branch.opcode { 118 | Opcode::B | 119 | Opcode::BL => if let BranchTarget::Immediate { offset } = branch.target { 120 | // unconditional branches can be predicted with 100% certainty 121 | return offset as usize; 122 | } else { 123 | panic!(); 124 | } 125 | Opcode::RET => 0, 126 | Opcode::BX => 0, 127 | Opcode::CBNZ | 128 | Opcode::CBZ | 129 | Opcode::BNE | 130 | Opcode::BLE | 131 | Opcode::BLT | 132 | Opcode::BGE | 133 | Opcode::BGT | 134 | Opcode::BEQ => if let BranchTarget::Immediate { offset } = branch.target { 135 | offset as usize 136 | } else { 137 | panic!(); 138 | }, 139 | _ => unreachable!(), 140 | }; 141 | 142 | if (branch_target as usize) < ip { 143 | // backwards branches are always taken 144 | branch_target as usize 145 | } else { 146 | ip + 1 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/frontend/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod frontend; -------------------------------------------------------------------------------- /src/instructions/instructions.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fmt; 3 | use std::fmt::{Debug, Display, Formatter}; 4 | use std::rc::Rc; 5 | 6 | use crate::cpu::{CPSR, SP}; 7 | use crate::cpu::FP; 8 | use crate::cpu::LR; 9 | use crate::cpu::PC; 10 | 11 | pub type RegisterType = u16; 12 | pub type DWordType = u64; 13 | 14 | pub struct RegisterTypeDisplay { 15 | pub register: RegisterType, 16 | } 17 | 18 | impl Display for RegisterTypeDisplay { 19 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 20 | match self.register as u16 { 21 | FP => write!(f, "FP"), 22 | LR => write!(f, "LR"), 23 | SP => write!(f, "SP"), 24 | PC => write!(f, "PC"), 25 | CPSR => write!(f, "CPSR"), 26 | _ => write!(f, "R{}", self.register), 27 | } 28 | } 29 | } 30 | 31 | #[derive(Debug, Clone, Copy, PartialEq)] 32 | pub struct SourceLocation { 33 | pub line: usize, 34 | pub column: usize, 35 | } 36 | 37 | impl Display for SourceLocation { 38 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 39 | write!(f, "{}:{}", self.line, self.column) 40 | } 41 | } 42 | 43 | #[derive(Clone, Copy, PartialEq, Debug)] 44 | pub enum Opcode { 45 | ADD, 46 | SUB, 47 | RSB, 48 | MUL, 49 | SDIV, 50 | ADR, 51 | LDR, 52 | STR, 53 | NOP, 54 | PRINTR, 55 | MOV, 56 | B, 57 | BX, 58 | BL, 59 | RET, 60 | CBZ, 61 | CBNZ, 62 | // Acts like a poison pill. It isn't a public instruction. 63 | EXIT, 64 | NEG, 65 | AND, 66 | ORR, 67 | EOR, 68 | MVN, 69 | CMP, 70 | TST, 71 | TEQ, 72 | BEQ, 73 | BNE, 74 | BLE, 75 | BLT, 76 | BGE, 77 | BGT, 78 | DSB, 79 | } 80 | 81 | pub(crate) fn mnemonic(opcode: Opcode) -> &'static str { 82 | match opcode { 83 | Opcode::ADD => "ADD", 84 | Opcode::SUB => "SUB", 85 | Opcode::RSB => "RSB", 86 | Opcode::MUL => "MUL", 87 | Opcode::SDIV => "SDIV", 88 | Opcode::NEG => "NEG", 89 | Opcode::ADR => "ADR", 90 | Opcode::LDR => "LDR", 91 | Opcode::STR => "STR", 92 | Opcode::NOP => "NOP", 93 | Opcode::PRINTR => "PRINTR", 94 | Opcode::MOV => "MOV", 95 | Opcode::B => "B", 96 | Opcode::RET => "RET", 97 | Opcode::BX => "BX", 98 | Opcode::BL => "BL", 99 | Opcode::CBZ => "CBZ", 100 | Opcode::CBNZ => "CBNZ", 101 | Opcode::AND => "AND", 102 | Opcode::ORR => "ORR", 103 | Opcode::EOR => "EOR", 104 | Opcode::MVN => "MVN", 105 | Opcode::EXIT => "EXIT", 106 | Opcode::CMP => "CMP", 107 | Opcode::BEQ => "BEQ", 108 | Opcode::BNE => "BNE", 109 | Opcode::BLE => "BLE", 110 | Opcode::BLT => "BLT", 111 | Opcode::BGE => "BGE", 112 | Opcode::BGT => "BGT", 113 | Opcode::DSB => "DSB", 114 | Opcode::TST => "TST", 115 | Opcode::TEQ => "TEQ", 116 | } 117 | } 118 | 119 | pub(crate) fn get_opcode(mnemonic: &str) -> Option { 120 | let string = mnemonic.to_uppercase(); 121 | let mnemonic_uppercased = string.as_str(); 122 | 123 | match mnemonic_uppercased { 124 | "ADD" => Some(Opcode::ADD), 125 | "SUB" => Some(Opcode::SUB), 126 | "RSB" => Some(Opcode::RSB), 127 | "MUL" => Some(Opcode::MUL), 128 | "SDIV" => Some(Opcode::SDIV), 129 | "NEG" => Some(Opcode::NEG), 130 | "ADR" => Some(Opcode::ADR), 131 | "LDR" => Some(Opcode::LDR), 132 | "STR" => Some(Opcode::STR), 133 | "NOP" => Some(Opcode::NOP), 134 | "PRINTR" => Some(Opcode::PRINTR), 135 | "MOV" => Some(Opcode::MOV), 136 | "B" => Some(Opcode::B), 137 | "RET" => Some(Opcode::RET), 138 | "BX" => Some(Opcode::BX), 139 | "CBZ" => Some(Opcode::CBZ), 140 | "CBNZ" => Some(Opcode::CBNZ), 141 | "AND" => Some(Opcode::AND), 142 | "ORR" => Some(Opcode::ORR), 143 | "EOR" => Some(Opcode::EOR), 144 | "MVN" => Some(Opcode::MVN), 145 | "BL" => Some(Opcode::BL), 146 | "EXIT" => Some(Opcode::EXIT), 147 | "CMP" => Some(Opcode::CMP), 148 | "BEQ" => Some(Opcode::BEQ), 149 | "BNE" => Some(Opcode::BNE), 150 | "BLE" => Some(Opcode::BLE), 151 | "BLT" => Some(Opcode::BLT), 152 | "BGE" => Some(Opcode::BGE), 153 | "BGT" => Some(Opcode::BGT), 154 | "DSB" => Some(Opcode::DSB), 155 | "TST" => Some(Opcode::TST), 156 | "TEQ" => Some(Opcode::TEQ), 157 | _ => None, 158 | } 159 | } 160 | 161 | pub(crate) const NOP: Instr = Instr::Synchronization( 162 | Synchronization { 163 | opcode: Opcode::NOP, 164 | loc: None, 165 | } 166 | ); 167 | 168 | pub(crate) const EXIT: Instr = Instr::Synchronization( 169 | Synchronization { 170 | opcode: Opcode::EXIT, 171 | loc: None, 172 | } 173 | ); 174 | 175 | #[derive(Clone, Copy, Debug)] 176 | pub enum Operand2 { 177 | Immediate { 178 | value: DWordType, 179 | }, 180 | Register { 181 | reg_id: RegisterType, 182 | }, 183 | Unused(), 184 | } 185 | 186 | impl Display for Operand2 { 187 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 188 | match self { 189 | Operand2::Immediate { value } => write!(f, "{}", *value), 190 | Operand2::Register { reg_id } => write!(f, "{}", RegisterTypeDisplay { register: *reg_id }), 191 | Operand2::Unused() => write!(f, "Unused"), 192 | } 193 | } 194 | } 195 | 196 | #[derive(Debug, PartialEq, Clone, Copy)] 197 | pub enum ConditionCode { 198 | EQ, // Equal 199 | NE, // Not Equal 200 | CS, // Carry Set 201 | CC, // Carry Clear 202 | MI, // Minus/Negative 203 | PL, // Plus/Positive or Zero 204 | VS, // Overflow 205 | VC, // No Overflow 206 | HI, // Unsigned Higher 207 | LS, // Unsigned Lower or Same 208 | GE, // Signed Greater Than or Equal 209 | LT, // Signed Less Than 210 | GT, // Signed Greater Than 211 | LE, // Signed Less Than or Equal 212 | AL, // Always (unconditional) 213 | } 214 | 215 | #[derive(Clone, Copy, Debug)] 216 | pub struct DataProcessing { 217 | pub opcode: Opcode, 218 | pub condition: ConditionCode, 219 | pub loc: SourceLocation, 220 | // First operand register. 221 | pub rn: Option, 222 | // Destination register 223 | pub rd: RegisterType, 224 | // Second operand, which can be an immediate value or a shifted register. 225 | pub operand2: Operand2, 226 | // If the destination register should be read before it is written to 227 | pub rd_read: bool, 228 | } 229 | 230 | impl Display for DataProcessing { 231 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 232 | match self.opcode { 233 | Opcode::SUB | 234 | Opcode::MUL | 235 | Opcode::SDIV | 236 | Opcode::AND | 237 | Opcode::ORR | 238 | Opcode::EOR | 239 | Opcode::RSB | 240 | Opcode::ADD => write!(f, "{:?} {}, {}, {}", 241 | self.opcode, 242 | RegisterTypeDisplay { register: self.rd }, 243 | RegisterTypeDisplay { register: self.rn.unwrap() }, 244 | self.operand2), 245 | Opcode::NEG| 246 | Opcode::MOV => write!(f, "{:?} {}, {}", 247 | self.opcode, RegisterTypeDisplay { register: self.rd }, self.operand2), 248 | Opcode::MVN => write!(f, "{:?} {}, {}", 249 | self.opcode, RegisterTypeDisplay { register: self.rd }, RegisterTypeDisplay{register:self.rn.unwrap()}), 250 | Opcode::TEQ | 251 | Opcode::TST | 252 | Opcode::CMP => write!(f, "{:?} {}, {}", 253 | self.opcode, 254 | RegisterTypeDisplay { register: self.rn.unwrap() }, 255 | self.operand2), 256 | _ => unreachable!("Unknown opcode {:?}", self.opcode), 257 | } 258 | } 259 | } 260 | 261 | #[derive(Clone, Copy, Debug)] 262 | pub enum BranchTarget { 263 | Immediate { 264 | offset: u32, 265 | }, 266 | Register { 267 | register: RegisterType, 268 | }, 269 | } 270 | 271 | impl Display for BranchTarget { 272 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 273 | match self { 274 | BranchTarget::Immediate { offset } => write!(f, "{}", offset), 275 | BranchTarget::Register { register } => write!(f, "{}", RegisterTypeDisplay { register: *register }), 276 | } 277 | } 278 | } 279 | 280 | #[derive(Clone, Copy, Debug)] 281 | pub struct Branch { 282 | pub opcode: Opcode, 283 | pub condition: ConditionCode, 284 | pub loc: SourceLocation, 285 | pub link_bit: bool, 286 | pub target: BranchTarget, 287 | // the register to test against. 288 | pub rt: Option, 289 | } 290 | 291 | impl Display for Branch { 292 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 293 | match self.opcode { 294 | Opcode::RET | 295 | Opcode::B | 296 | Opcode::BX | 297 | Opcode::BL => write!(f, "{:?} {}", self.opcode, self.target), 298 | Opcode::CBZ | 299 | Opcode::CBNZ => write!(f, "{:?} {}, {}", self.opcode, self.rt.unwrap(), self.target), 300 | Opcode::BEQ | 301 | Opcode::BNE | 302 | Opcode::BLT | 303 | Opcode::BLE | 304 | Opcode::BGT | 305 | Opcode::BGE => write!(f, "{:?} {}", self.opcode, self.target), 306 | _ => unreachable!("Unknown opcode {:?}", self.opcode), 307 | } 308 | } 309 | } 310 | 311 | #[derive(Clone, Copy, Debug)] 312 | pub struct LoadStore { 313 | pub opcode: Opcode, 314 | pub condition: ConditionCode, 315 | pub loc: SourceLocation, 316 | pub rn: RegisterType, 317 | pub rd: RegisterType, 318 | pub offset: u16, 319 | } 320 | 321 | impl Display for LoadStore { 322 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 323 | match self.opcode { 324 | Opcode::LDR => write!(f, "LDR {}, [{}]", RegisterTypeDisplay { register: self.rd }, RegisterTypeDisplay { register: self.rn }), 325 | Opcode::STR => write!(f, "STR {}, [{}]", RegisterTypeDisplay { register: self.rd }, RegisterTypeDisplay { register: self.rn }), 326 | _ => unreachable!("Unknown opcode {:?}", self.opcode), 327 | } 328 | } 329 | } 330 | 331 | #[derive(Clone, Copy, Debug)] 332 | pub struct Synchronization { 333 | pub opcode: Opcode, 334 | pub loc: Option, 335 | } 336 | 337 | impl Display for Synchronization { 338 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 339 | write!(f, "{:?}", self.opcode) 340 | } 341 | } 342 | 343 | #[derive(Clone, Copy, Debug)] 344 | pub struct Printr { 345 | pub loc: Option, 346 | pub rn: RegisterType, 347 | } 348 | 349 | impl Display for Printr { 350 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 351 | write!(f, "PRINTR {}", RegisterTypeDisplay { register: self.rn }) 352 | } 353 | } 354 | 355 | #[derive(Clone, Copy, Debug)] 356 | pub enum Instr { 357 | DataProcessing(DataProcessing), 358 | Branch(Branch), 359 | LoadStore(LoadStore), 360 | Synchronization(Synchronization), 361 | Printr(Printr), 362 | } 363 | 364 | impl Display for Instr { 365 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 366 | match self { 367 | Instr::DataProcessing(dp) => Display::fmt(dp, f), 368 | Instr::Branch(branch) => Display::fmt(branch, f), 369 | Instr::LoadStore(load_store) => Display::fmt(load_store, f), 370 | Instr::Synchronization(synchronization) => Display::fmt(synchronization, f), 371 | Instr::Printr(printr) => Display::fmt(printr, f), 372 | } 373 | } 374 | } 375 | 376 | pub(crate) struct InstrQueueSlot { 377 | pub(crate) instr: Rc, 378 | // The pc of the current instr. 379 | pub(crate) pc: usize, 380 | pub(crate) branch_target_predicted: usize, 381 | } 382 | 383 | // The InstrQueue sits between frontend and backend 384 | pub(crate) struct InstrQueue { 385 | pub(crate) capacity: u16, 386 | pub(crate) head: u64, 387 | pub(crate) tail: u64, 388 | pub(crate) slots: Vec, 389 | } 390 | 391 | impl InstrQueue { 392 | pub fn new(capacity: u16) -> Self { 393 | let mut slots = Vec::with_capacity(capacity as usize); 394 | 395 | for _ in 0..capacity { 396 | slots.push(InstrQueueSlot { pc: 0, branch_target_predicted: 0, instr: Rc::new(NOP) }); 397 | } 398 | 399 | InstrQueue { 400 | capacity, 401 | head: 0, 402 | tail: 0, 403 | slots, 404 | } 405 | } 406 | 407 | pub fn head_index(&self) -> usize { 408 | return (self.head % self.capacity as u64) as usize; 409 | } 410 | 411 | pub fn tail_index(&self) -> usize { 412 | return (self.tail % self.capacity as u64) as usize; 413 | } 414 | 415 | pub fn get_mut(&mut self, index: usize) -> &mut InstrQueueSlot { 416 | return self.slots.get_mut(index).unwrap(); 417 | } 418 | 419 | pub fn tail_bump(&mut self) { 420 | self.tail += 1; 421 | } 422 | 423 | pub fn head_bump(&mut self) { 424 | self.head += 1; 425 | } 426 | 427 | pub fn size(&self) -> u16 { 428 | (self.tail - self.head) as u16 429 | } 430 | 431 | pub fn is_empty(&self) -> bool { 432 | self.head == self.tail 433 | } 434 | 435 | pub fn is_full(&self) -> bool { 436 | self.size() == self.capacity 437 | } 438 | 439 | pub fn flush(&mut self) { 440 | self.head = 0; 441 | self.tail = 0; 442 | } 443 | } 444 | 445 | // True if the instruction is a control instruction; so a partly serializing instruction (no other instructions) 446 | // A control instruction gets issued into the rob, but it will prevent the next instruction to be issued, so 447 | // That the branch condition can be determined. 448 | pub(crate) const INSTR_FLAG_IS_BRANCH: u8 = 0; 449 | pub(crate) const INSTR_FLAG_SB_SYNC: u8 = 1; 450 | pub(crate) const INSTR_FLAG_ROB_SYNC: u8 = 2; 451 | 452 | pub struct Data { 453 | pub value: DWordType, 454 | pub offset: u64, 455 | } 456 | 457 | pub struct Program { 458 | pub data_items: HashMap::>, 459 | pub code: Vec>, 460 | pub entry_point: usize, 461 | } 462 | 463 | impl Program { 464 | pub fn get_instr(&self, pos: usize) -> Rc { 465 | Rc::clone(&self.code[pos]) 466 | } 467 | } 468 | 469 | -------------------------------------------------------------------------------- /src/instructions/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod instructions; -------------------------------------------------------------------------------- /src/loader/assembly.lalrpop: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | use crate::instructions::instructions::RegisterType; 3 | use crate::loader::ast::{ 4 | ASTOperand, ASTRegisterOperand, ASTImmediateOperand, ASTLabelOperand, ASTAddressOfOperand, ASTInstr, 5 | ASTData, ASTTextSection, ASTDataSection, ASTAssemblyFile, ASTDirective, ASTTextLine, ASTDataLine, 6 | ASTLabel, ASTPreamble, ASTMemRegisterIndirectOperand}; 7 | use crate::cpu::{SP,FP,LR,PC}; 8 | // https://gist.github.com/brendanzab/4c5e5e1836ecc3a46afd05ed046c695c 9 | use lalrpop_util::ParseError; 10 | 11 | grammar; 12 | 13 | Integer: u64 = { 14 | Decimal, 15 | Hexadecimal, 16 | Binary, 17 | Octal, 18 | }; 19 | 20 | Decimal: u64 = { 21 | r"[0-9]+" =>? u64::from_str(<>) 22 | .map_err(|_| ParseError::User {error: "decimal literal is too large"}) 23 | }; 24 | 25 | Hexadecimal: u64 = { 26 | r"0x[0-9A-Fa-f]+" =>? u64::from_str_radix(&<>[2..], 16) 27 | .map_err(|_| ParseError::User {error: "hexidecimal literal is too large"}), 28 | r"0X[0-9A-Fa-f]+" =>? u64::from_str_radix(&<>[2..], 16) 29 | .map_err(|_| ParseError::User {error: "hexidecimal literal is too large"}), 30 | }; 31 | 32 | Binary: u64 = { 33 | r"0b[01]+" =>? u64::from_str_radix(&<>[2..], 2) 34 | .map_err(|_| ParseError::User {error: "binary literal is too large"}), 35 | r"0B[01]+" =>? u64::from_str_radix(&<>[2..], 2) 36 | .map_err(|_| ParseError::User {error: "binary literal is too large"}), 37 | }; 38 | 39 | Octal: u64 = { 40 | r"0o[0-7]+" =>? u64::from_str_radix(&<>[2..], 8) 41 | .map_err(|_| ParseError::User {error: "octal literal is too large"}), 42 | r"0O[0-7]+" =>? u64::from_str_radix(&<>[2..], 8) 43 | .map_err(|_| ParseError::User {error: "octal literal is too large"}), 44 | }; 45 | 46 | Mnemonic: String = { 47 | r"[a-zA-Z_][a-zA-Z0-9_]*" => String::from(<>), 48 | }; 49 | 50 | VariableName: String = { 51 | r"[a-zA-Z_][a-zA-Z0-9_]*" => String::from(<>), 52 | }; 53 | 54 | Operand_Sep: () = { 55 | "," => (), 56 | } 57 | 58 | LabelName: String = { 59 | r"[a-zA-Z_][a-zA-Z0-9_]*" => String::from(<>), 60 | } 61 | 62 | Operand: ASTOperand = { 63 | => ASTOperand::Register(o), 64 | => ASTOperand::Immediate(o), 65 | => ASTOperand::Label(o), 66 | => ASTOperand::AddressOf(o), 67 | => ASTOperand::MemRegisterIndirect(o), 68 | // MemoryAccess, 69 | } 70 | 71 | MemoryAccessOperand: ASTMemRegisterIndirectOperand = { 72 | "[" "]" => ASTMemRegisterIndirectOperand{register:b.register, pos:start}, 73 | // "[" "," "]" => { 74 | // let ASTOperand::Register(register, _) = b else { panic!() }; 75 | // let ASTOperand::Immediate(offset, _) = b else { panic!() }; 76 | // ASTOperand::MemRegIndirectWithOffset(register, offset, start) 77 | // }, 78 | // "[" "," "]" => { 79 | // let ASTOperand::Register(register, _) = b else { panic!() }; 80 | // let ASTOperand::Register(offset, _) = b else { panic!() }; 81 | // ASTOperand::MemRegIndirectWithRegOffset(register, offset, start) 82 | // }, 83 | } 84 | 85 | // with a regular expression, the following would lead to conflicts. 86 | // For the time being the capitalized versions are explicitly added due to difficulties with case insensitivity. 87 | RegisterOperand: ASTRegisterOperand = { 88 | "r0" => ASTRegisterOperand{register:0 as RegisterType, pos:start}, 89 | "R0" => ASTRegisterOperand{register:0 as RegisterType, pos:start}, 90 | "r1" => ASTRegisterOperand{register:1 as RegisterType, pos:start}, 91 | "R1" => ASTRegisterOperand{register:1 as RegisterType, pos:start}, 92 | "r2" => ASTRegisterOperand{register:2 as RegisterType, pos:start}, 93 | "R2" => ASTRegisterOperand{register:2 as RegisterType, pos:start}, 94 | "r3" => ASTRegisterOperand{register:3 as RegisterType, pos:start}, 95 | "R3" => ASTRegisterOperand{register:3 as RegisterType, pos:start}, 96 | "r4" => ASTRegisterOperand{register:4 as RegisterType, pos:start}, 97 | "R4" => ASTRegisterOperand{register:4 as RegisterType, pos:start}, 98 | "r5" => ASTRegisterOperand{register:5 as RegisterType, pos:start}, 99 | "R5" => ASTRegisterOperand{register:5 as RegisterType, pos:start}, 100 | "r6" => ASTRegisterOperand{register:6 as RegisterType, pos:start}, 101 | "R6" => ASTRegisterOperand{register:6 as RegisterType, pos:start}, 102 | "r7" => ASTRegisterOperand{register:7 as RegisterType, pos:start}, 103 | "R7" => ASTRegisterOperand{register:7 as RegisterType, pos:start}, 104 | "r8" => ASTRegisterOperand{register:8 as RegisterType, pos:start}, 105 | "R8" => ASTRegisterOperand{register:8 as RegisterType, pos:start}, 106 | "r9" => ASTRegisterOperand{register:9 as RegisterType, pos:start}, 107 | "R9" => ASTRegisterOperand{register:9 as RegisterType, pos:start}, 108 | "r10" => ASTRegisterOperand{register:10 as RegisterType, pos:start}, 109 | "R10" => ASTRegisterOperand{register:10 as RegisterType, pos:start}, 110 | "r11" => ASTRegisterOperand{register:11 as RegisterType, pos:start}, 111 | "R11" => ASTRegisterOperand{register:11 as RegisterType, pos:start}, 112 | "r12" => ASTRegisterOperand{register:12 as RegisterType, pos:start}, 113 | "R12" => ASTRegisterOperand{register:12 as RegisterType, pos:start}, 114 | "r13" => ASTRegisterOperand{register:13 as RegisterType, pos:start}, 115 | "R13" => ASTRegisterOperand{register:13 as RegisterType, pos:start}, 116 | "r14" => ASTRegisterOperand{register:14 as RegisterType, pos:start}, 117 | "R14" => ASTRegisterOperand{register:14 as RegisterType, pos:start}, 118 | "r15" => ASTRegisterOperand{register:15 as RegisterType, pos:start}, 119 | "R15" => ASTRegisterOperand{register:15 as RegisterType, pos:start}, 120 | "r16" => ASTRegisterOperand{register:16 as RegisterType, pos:start}, 121 | "R16" => ASTRegisterOperand{register:16 as RegisterType, pos:start}, 122 | "r17" => ASTRegisterOperand{register:17 as RegisterType, pos:start}, 123 | "R17" => ASTRegisterOperand{register:17 as RegisterType, pos:start}, 124 | "r18" => ASTRegisterOperand{register:18 as RegisterType, pos:start}, 125 | "R18" => ASTRegisterOperand{register:18 as RegisterType, pos:start}, 126 | "r19" => ASTRegisterOperand{register:19 as RegisterType, pos:start}, 127 | "R19" => ASTRegisterOperand{register:19 as RegisterType, pos:start}, 128 | "r20" => ASTRegisterOperand{register:20 as RegisterType, pos:start}, 129 | "R20" => ASTRegisterOperand{register:20 as RegisterType, pos:start}, 130 | "r21" => ASTRegisterOperand{register:21 as RegisterType, pos:start}, 131 | "R21" => ASTRegisterOperand{register:21 as RegisterType, pos:start}, 132 | "r22" => ASTRegisterOperand{register:22 as RegisterType, pos:start}, 133 | "R22" => ASTRegisterOperand{register:22 as RegisterType, pos:start}, 134 | "r23" => ASTRegisterOperand{register:23 as RegisterType, pos:start}, 135 | "R23" => ASTRegisterOperand{register:23 as RegisterType, pos:start}, 136 | "r24" => ASTRegisterOperand{register:24 as RegisterType, pos:start}, 137 | "R24" => ASTRegisterOperand{register:24 as RegisterType, pos:start}, 138 | "r25" => ASTRegisterOperand{register:25 as RegisterType, pos:start}, 139 | "R25" => ASTRegisterOperand{register:25 as RegisterType, pos:start}, 140 | "r26" => ASTRegisterOperand{register:26 as RegisterType, pos:start}, 141 | "R26" => ASTRegisterOperand{register:26 as RegisterType, pos:start}, 142 | "r27" => ASTRegisterOperand{register:27 as RegisterType, pos:start}, 143 | "R27" => ASTRegisterOperand{register:27 as RegisterType, pos:start}, 144 | "r28" => ASTRegisterOperand{register:28 as RegisterType, pos:start}, 145 | "R28" => ASTRegisterOperand{register:28 as RegisterType, pos:start}, 146 | "r29" => ASTRegisterOperand{register:29 as RegisterType, pos:start}, 147 | "R29" => ASTRegisterOperand{register:29 as RegisterType, pos:start}, 148 | "r30" => ASTRegisterOperand{register:30 as RegisterType, pos:start}, 149 | "R30" => ASTRegisterOperand{register:30 as RegisterType, pos:start}, 150 | "fp" => ASTRegisterOperand{register:FP, pos:start}, 151 | "FP" => ASTRegisterOperand{register:FP, pos:start}, 152 | "sp" => ASTRegisterOperand{register:SP, pos:start}, 153 | "SP" => ASTRegisterOperand{register:SP, pos:start}, 154 | "lr" => ASTRegisterOperand{register:LR, pos:start}, 155 | "LR" => ASTRegisterOperand{register:LR, pos:start}, 156 | "pc" => ASTRegisterOperand{register:PC, pos:start}, 157 | "PC" => ASTRegisterOperand{register:PC, pos:start} 158 | }; 159 | 160 | ImmediateOperand: ASTImmediateOperand = { 161 | "#" => ASTImmediateOperand{value:v, pos:start}, 162 | }; 163 | 164 | AddressOfOperand: ASTAddressOfOperand = { 165 | "=" => ASTAddressOfOperand{label:l, offset:0, pos:start}, 166 | }; 167 | 168 | LabelOperand: ASTLabelOperand = { 169 | => ASTLabelOperand{label:l, offset:0, pos:start}, 170 | }; 171 | 172 | Directive: ASTDirective = { 173 | ".global" => ASTDirective::Global(l, start), 174 | } 175 | 176 | DataLine: ASTDataLine = { 177 | Data => ASTDataLine::Data(<>), 178 | Directive => ASTDataLine::Directive(<>) 179 | } 180 | 181 | Data: ASTData = { 182 | ":" ".dword" => ASTData{name:n, value:v, pos:start} 183 | } 184 | 185 | DataSection:ASTDataSection = { 186 | ".data" => ASTDataSection{lines:l}, 187 | ".section" ".data" => ASTDataSection{lines:l}, 188 | } 189 | 190 | Label: ASTLabel = { 191 | ":" => ASTLabel{name:n, pos:start}, 192 | } 193 | 194 | Instr: ASTInstr = { 195 | ";" 196 | => ASTInstr{mnemonic:m, op1:ASTOperand::Unused(), op2:ASTOperand::Unused(), op3:ASTOperand::Unused(), pos:start}, 197 | ";" 198 | => ASTInstr{mnemonic:m, op1:o1, op2:ASTOperand::Unused(), op3:ASTOperand::Unused(), pos:start}, 199 | Operand_Sep ";" 200 | => ASTInstr{mnemonic:m, op1:o1, op2:o2, op3:ASTOperand::Unused(), pos:start}, 201 | Operand_Sep Operand_Sep ";" 202 | => ASTInstr{mnemonic:m, op1:o1, op2:o2, op3:o3, pos:start}, 203 | } 204 | 205 | TextSection: ASTTextSection = { 206 | ".text" => ASTTextSection{lines:l}, 207 | ".section" ".text" => ASTTextSection{lines:l}, 208 | } 209 | 210 | TextLine: ASTTextLine = { 211 | Instr => ASTTextLine::Text(<>), 212 | Directive => ASTTextLine::Directive(<>), 213 | Label => ASTTextLine::Label(<>), 214 | } 215 | 216 | Preamble: ASTPreamble ={ 217 | Directive* => ASTPreamble{directives:<>}, 218 | } 219 | 220 | pub AssemblyFile: ASTAssemblyFile = { 221 | 222 | => ASTAssemblyFile{preamble: p, ds_before, ts, ds_after}, 223 | } -------------------------------------------------------------------------------- /src/loader/ast.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::instructions::instructions::{DWordType, RegisterType}; 4 | 5 | /// The AST for an AssemblyFile 6 | /// 7 | /// The reason why I'm using an AST that isn't tied to a particular parser generator is that 8 | /// it decouples the analysis of the assembly file from the parsing. I have used Pest and 9 | /// I'm now using Lalrpop. The latter gives much better parse errors, but I'm still not too 10 | /// excited because trivial things like case insensitivity, linefeeds, comments are not 11 | /// that easy to fix and the documentation isn't particularly helpful. 12 | /// 13 | /// Because it is decoupled, it will make it easier to switch to a different parser generator 14 | /// at some point. 15 | #[derive(Debug, Clone)] 16 | pub struct ASTRegisterOperand { 17 | pub register: RegisterType, 18 | pub pos: usize, 19 | } 20 | 21 | #[derive(Debug, Clone)] 22 | pub struct ASTImmediateOperand { 23 | pub value: u64, 24 | pub pos: usize, 25 | } 26 | 27 | #[derive(Debug, Clone)] 28 | pub struct ASTLabelOperand { 29 | pub label: String, 30 | pub offset: DWordType, 31 | pub pos: usize, 32 | } 33 | 34 | #[derive(Debug, Clone)] 35 | pub struct ASTAddressOfOperand { 36 | pub label: String, 37 | pub pos: usize, 38 | pub offset: DWordType, 39 | } 40 | 41 | #[derive(Debug, Clone)] 42 | pub struct ASTMemRegisterIndirectOperand { 43 | pub register: RegisterType, 44 | pub pos: usize, 45 | } 46 | 47 | #[derive(Debug, Clone)] 48 | pub enum ASTOperand { 49 | Register(ASTRegisterOperand), 50 | Immediate(ASTImmediateOperand), 51 | Label(ASTLabelOperand), 52 | AddressOf(ASTAddressOfOperand), 53 | MemRegisterIndirect(ASTMemRegisterIndirectOperand), 54 | // Uncomment and add these if needed 55 | // MemRegIndirectWithOffset(MemRegIndirectWithOffset), 56 | // MemRegIndirectWithRegOffset(MemRegIndirectWithRegOffset), 57 | Unused(), 58 | } 59 | 60 | impl ASTOperand { 61 | pub fn get_type(&self)->ASTOperandType{ 62 | match self { 63 | ASTOperand::Register(_) => ASTOperandType::Register, 64 | ASTOperand::Immediate(_) => ASTOperandType::Immediate, 65 | ASTOperand::Label(_) => ASTOperandType::Label, 66 | ASTOperand::AddressOf(_) => ASTOperandType::AddressOf, 67 | ASTOperand::MemRegisterIndirect(_) => ASTOperandType::MemRegisterIndirect, 68 | ASTOperand::Unused() => ASTOperandType::Unused, 69 | } 70 | } 71 | } 72 | 73 | pub enum ASTOperandType{ 74 | Register, 75 | Immediate, 76 | Label, 77 | AddressOf, 78 | MemRegisterIndirect, 79 | Unused, 80 | } 81 | 82 | impl ASTOperandType { 83 | pub fn base_name(&self)->&str{ 84 | match self { 85 | ASTOperandType::Register => "Register", 86 | ASTOperandType::Immediate => "Immediate", 87 | ASTOperandType::Label => "Label", 88 | ASTOperandType::AddressOf => "AddressOf", 89 | ASTOperandType::MemRegisterIndirect => "MemRegisterIndirect", 90 | ASTOperandType::Unused => "Unused", 91 | } 92 | } 93 | } 94 | 95 | // The visitor is a DFS visitor which is good enough for now. If more flexibility is needed 96 | // then the traversal of the visitor could be externalized 97 | impl ASTOperand { 98 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 99 | visitor.visit_operand(self) 100 | } 101 | 102 | pub fn get_register(&self) -> RegisterType { 103 | match self { 104 | ASTOperand::Register(reg) => reg.register, 105 | _ => panic!("Operation is not a Register but of type {:?}", self), 106 | } 107 | } 108 | 109 | pub fn get_immediate(&self) -> DWordType { 110 | match self { 111 | ASTOperand::Immediate(immediate) => immediate.value, 112 | _ => panic!("Operand is not a Constant but of type {:?}", self), 113 | } 114 | } 115 | 116 | pub fn get_code_address(&self) -> DWordType { 117 | match self { 118 | ASTOperand::Label(label) => label.offset, 119 | ASTOperand::AddressOf(address_of) => address_of.offset, 120 | _ => panic!("Operand is not a Constant but of type {:?}", self), 121 | } 122 | } 123 | // 124 | // pub fn get_memory_addr(&self) -> DWordType { 125 | // match self { 126 | // ASTOperand::(addr) => *addr, 127 | // _ => panic!("Operand is not a Memory but of type {:?}", self), 128 | // } 129 | // } 130 | } 131 | 132 | #[derive(Debug)] 133 | pub struct ASTData { 134 | pub name: String, 135 | pub value: u64, 136 | pub pos: usize, 137 | } 138 | 139 | impl ASTData { 140 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 141 | visitor.visit_data(self) 142 | } 143 | } 144 | 145 | #[derive(Debug)] 146 | pub struct ASTInstr { 147 | pub mnemonic: String, 148 | pub op1: ASTOperand, 149 | pub op2: ASTOperand, 150 | pub op3: ASTOperand, 151 | pub pos: usize, 152 | } 153 | 154 | impl ASTInstr { 155 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 156 | if !self.op1.accept(visitor) { return false; } 157 | if !self.op2.accept(visitor) { return false; } 158 | if !self.op3.accept(visitor) { return false; } 159 | visitor.visit_instr(self) 160 | } 161 | } 162 | 163 | // Define the Directive enum 164 | #[derive(Debug)] 165 | pub enum ASTDirective { 166 | Global(String, usize), 167 | } 168 | 169 | impl ASTDirective { 170 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 171 | visitor.visit_directive(self) 172 | } 173 | } 174 | 175 | #[derive(Debug)] 176 | pub enum ASTGlobalDirective { 177 | Label(String), 178 | Immediate(), 179 | } 180 | 181 | #[derive(Debug)] 182 | pub enum ASTDataLine { 183 | Data(ASTData), 184 | Directive(ASTDirective), 185 | } 186 | 187 | impl ASTDataLine { 188 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 189 | let result = match self { 190 | ASTDataLine::Data(data) => data.accept(visitor), 191 | ASTDataLine::Directive(directive) => directive.accept(visitor), 192 | }; 193 | if !result { return false; } 194 | visitor.visit_data_line(self) 195 | } 196 | } 197 | 198 | #[derive(Debug)] 199 | pub struct ASTLabel { 200 | pub name: String, 201 | pub pos: usize, 202 | } 203 | 204 | impl ASTLabel { 205 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 206 | visitor.visit_label(self) 207 | } 208 | } 209 | 210 | #[derive(Debug)] 211 | pub enum ASTTextLine { 212 | Text(ASTInstr), 213 | Directive(ASTDirective), 214 | Label(ASTLabel), 215 | } 216 | 217 | impl ASTTextLine { 218 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 219 | let result = match self { 220 | ASTTextLine::Text(instr) => instr.accept(visitor), 221 | ASTTextLine::Directive(directive) => directive.accept(visitor), 222 | ASTTextLine::Label(label) => label.accept(visitor), 223 | }; 224 | if !result { return false; } 225 | visitor.visit_text_line(self) 226 | } 227 | } 228 | 229 | #[derive(Debug)] 230 | pub struct ASTTextSection { 231 | pub lines: Vec, 232 | } 233 | 234 | impl ASTTextSection { 235 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 236 | for line in &mut self.lines { 237 | if !line.accept(visitor) { return false; } 238 | } 239 | visitor.visit_text_section(self) 240 | } 241 | } 242 | 243 | #[derive(Debug)] 244 | pub struct ASTDataSection { 245 | pub lines: Vec, 246 | } 247 | 248 | impl ASTDataSection { 249 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 250 | for line in &mut self.lines { 251 | if !line.accept(visitor) { return false; } 252 | } 253 | visitor.visit_data_section(self) 254 | } 255 | } 256 | 257 | #[derive(Debug)] 258 | pub struct ASTPreamble { 259 | pub directives: Vec, 260 | } 261 | 262 | impl ASTPreamble { 263 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 264 | for directive in &mut self.directives { 265 | if !directive.accept(visitor) { return false; } 266 | } 267 | visitor.visit_preamble(self) 268 | } 269 | } 270 | 271 | #[derive(Debug)] 272 | pub struct ASTAssemblyFile { 273 | pub preamble: ASTPreamble, 274 | pub ds_before: Vec, 275 | pub ts: ASTTextSection, 276 | pub ds_after: Vec, 277 | } 278 | 279 | impl ASTAssemblyFile { 280 | pub fn accept(&mut self, visitor: &mut dyn ASTVisitor) -> bool { 281 | if !self.preamble.accept(visitor) { return false; } 282 | 283 | for section in &mut self.ds_before { 284 | if !section.accept(visitor) { return false; } 285 | } 286 | 287 | if !self.ts.accept(visitor) { return false; } 288 | 289 | for section in &mut self.ds_after { 290 | if !section.accept(visitor) { return false; } 291 | } 292 | visitor.visit_assembly_file(self) 293 | } 294 | } 295 | 296 | pub trait ASTVisitor { 297 | fn visit_operand(&mut self, _ast_operand: &mut ASTOperand) -> bool { true } 298 | fn visit_data(&mut self, _ast_data: &mut ASTData) -> bool { true } 299 | fn visit_instr(&mut self, _ast_instr: &mut ASTInstr) -> bool { true } 300 | fn visit_directive(&mut self, _ast_directive: &mut ASTDirective) -> bool { true } 301 | fn visit_label(&mut self, _ast_label: &mut ASTLabel) -> bool { true } 302 | fn visit_text_section(&mut self, _ast_label: &mut ASTTextSection) -> bool { true } 303 | fn visit_text_line(&mut self, _ast_text_line: &mut ASTTextLine) -> bool { true } 304 | fn visit_data_section(&mut self, _ast_label: &mut ASTDataSection) -> bool { true } 305 | fn visit_data_line(&mut self, _ast_data_line: &mut ASTDataLine) -> bool { true } 306 | fn visit_preamble(&mut self, _ast_preamble: &mut ASTPreamble) -> bool { true } 307 | fn visit_assembly_file(&mut self, _ast_assembly: &mut ASTAssemblyFile) -> bool { true } 308 | } -------------------------------------------------------------------------------- /src/loader/loader.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fs; 3 | use std::path::Path; 4 | use std::rc::Rc; 5 | 6 | use lalrpop_util::ParseError; 7 | use regex::Regex; 8 | 9 | use crate::assembly; 10 | use crate::cpu::{CPSR, CPUConfig, GENERAL_ARG_REG_CNT, LR}; 11 | use crate::instructions::instructions::{Branch, BranchTarget, ConditionCode, Data, DataProcessing, DWordType, 12 | get_opcode, Instr, LoadStore, Opcode, Operand2, Printr, Program, RegisterType, 13 | SourceLocation, Synchronization}; 14 | use crate::loader::ast::{ASTAssemblyFile, ASTData, ASTDirective, ASTInstr, ASTLabel, ASTOperand, ASTOperandType, ASTVisitor}; 15 | use crate::loader::loader::LoadError::AnalysisError; 16 | 17 | struct Loader { 18 | cpu_config: CPUConfig, 19 | src: String, 20 | heap_limit: u32, 21 | code: Vec, 22 | data_section: HashMap::>, 23 | labels: HashMap, 24 | instr_cnt: usize, 25 | entry_point: usize, 26 | errors: Vec, 27 | } 28 | 29 | pub enum LoadError { 30 | NotFoundError(String), 31 | IOError(String), 32 | ParseError(String), 33 | AnalysisError(Vec), 34 | } 35 | 36 | impl Loader { 37 | fn load(&mut self) -> Result { 38 | if !self.src.ends_with('\n') { 39 | self.src.push('\n'); 40 | } 41 | 42 | let mut assembly = match self.parse() { 43 | Ok(value) => value, 44 | Err(error) => return error, 45 | }; 46 | 47 | let mut symbolic_scan = SymbolScan { loader: self }; 48 | assembly.accept(&mut symbolic_scan); 49 | 50 | let mut program_generation = ProgramGeneration { loader: self, operand_stack: Vec::new() }; 51 | assembly.accept(&mut program_generation); 52 | 53 | let mut code = Vec::with_capacity(self.code.len()); 54 | for k in 0..self.code.len() { 55 | code.push(Rc::new(*self.code.get_mut(k).unwrap())); 56 | } 57 | 58 | return if self.errors.is_empty() { 59 | Ok(Program { code, data_items: self.data_section.clone(), entry_point: self.entry_point }) 60 | } else { 61 | Err(AnalysisError(self.errors.clone())) 62 | }; 63 | } 64 | 65 | fn parse(&mut self) -> Result> { 66 | // todo: ugly variable name 67 | let x = &self.src; 68 | let parse_result = assembly::AssemblyFileParser::new() 69 | .parse(x.as_str()); 70 | 71 | let assembly_file: ASTAssemblyFile = match parse_result { 72 | Ok(a) => a, 73 | Err(err) => { 74 | let cause = match err { 75 | ParseError::InvalidToken { location } => { 76 | let loc = self.to_source_location(location); 77 | format!("Invalid token at {}:{}", loc.line, loc.column) 78 | } 79 | ParseError::UnrecognizedToken { token, expected } => { 80 | let loc = self.to_source_location(token.0); 81 | format!("Unrecognized token '{}' at {}:{}. Expected: {}", token.1, loc.line, loc.column, expected.join(" or ")) 82 | } 83 | ParseError::ExtraToken { token } => { 84 | let loc = self.to_source_location(token.0); 85 | format!("Extra token '{}' at {}:{}", token.1, loc.line, loc.column) 86 | } 87 | _ => format!("{:?}", err), 88 | }; 89 | 90 | return Err(Err(LoadError::ParseError(cause))); 91 | } 92 | }; 93 | Ok(assembly_file) 94 | } 95 | 96 | fn to_source_location(&self, offset: usize) -> SourceLocation { 97 | let mut line = 1; 98 | let mut col = 1; 99 | let src = &self.src; 100 | let src_slice = src.as_str(); 101 | for (i, c) in src_slice.char_indices() { 102 | if i == offset { 103 | break; 104 | } 105 | if c == '\n' { 106 | line += 1; 107 | col = 1; 108 | } else { 109 | col += 1; 110 | } 111 | } 112 | SourceLocation { line: line, column: col } 113 | } 114 | } 115 | 116 | pub(crate) fn create_instr(opcode: Opcode, operands: &Vec, loc: SourceLocation) -> Result { 117 | let instr = match opcode { 118 | Opcode::SUB | 119 | Opcode::MUL | 120 | Opcode::SDIV | 121 | Opcode::AND | 122 | Opcode::ORR | 123 | Opcode::EOR | 124 | Opcode::RSB | 125 | Opcode::ADD => { 126 | validate_operand_count(3, operands, opcode, loc)?; 127 | 128 | let rd = match &operands[0] { 129 | ASTOperand::Register(o) => o.register, 130 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 131 | vec![ASTOperandType::Register])) 132 | }; 133 | 134 | let rn = match &operands[1] { 135 | ASTOperand::Register(o) => o.register, 136 | _ => return Err(type_mismatch(opcode, 1, &operands[1], 137 | vec![ASTOperandType::Register])) 138 | }; 139 | 140 | let operand2 = match &operands[2] { 141 | ASTOperand::Register(register) => Operand2::Register { reg_id: register.register }, 142 | ASTOperand::Immediate(immediate) => Operand2::Immediate { value: immediate.value }, 143 | _ => return Err(type_mismatch(opcode, 2, &operands[2], 144 | vec![ASTOperandType::Register, ASTOperandType::Immediate])) 145 | }; 146 | 147 | Instr::DataProcessing( 148 | DataProcessing { 149 | opcode, 150 | condition: ConditionCode::AL, 151 | loc, 152 | rn: Some(rn), 153 | rd, 154 | rd_read: false, 155 | operand2, 156 | } 157 | ) 158 | } 159 | Opcode::MVN | 160 | Opcode::NEG => { 161 | validate_operand_count(2, operands, opcode, loc)?; 162 | 163 | let rd = match &operands[0] { 164 | ASTOperand::Register(o) => o.register, 165 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 166 | vec![ASTOperandType::Register])) 167 | }; 168 | 169 | let rn = match &operands[1] { 170 | ASTOperand::Register(o) => o.register, 171 | _ => return Err(type_mismatch(opcode, 1, &operands[1], 172 | vec![ASTOperandType::Register])) 173 | }; 174 | 175 | Instr::DataProcessing( 176 | DataProcessing { 177 | opcode, 178 | condition: ConditionCode::AL, 179 | loc, 180 | rn: Some(rn), 181 | rd, 182 | rd_read: false, 183 | operand2: Operand2::Unused(), 184 | } 185 | ) 186 | } 187 | Opcode::TEQ | 188 | Opcode::TST | 189 | Opcode::CMP => { 190 | validate_operand_count(2, operands, opcode, loc)?; 191 | 192 | let rd = CPSR as RegisterType; 193 | 194 | let rn = match &operands[0] { 195 | ASTOperand::Register(o) => o.register, 196 | _ => return Err(type_mismatch(opcode, 1, &operands[1], 197 | vec![ASTOperandType::Register])) 198 | }; 199 | 200 | let operand2 = match &operands[1] { 201 | ASTOperand::Register(register) => Operand2::Register { reg_id: register.register }, 202 | ASTOperand::Immediate(immediate) => Operand2::Immediate { value: immediate.value }, 203 | _ => return Err(type_mismatch(opcode, 2, &operands[2], 204 | vec![ASTOperandType::Register, ASTOperandType::Immediate])) 205 | }; 206 | 207 | Instr::DataProcessing( 208 | DataProcessing { 209 | opcode, 210 | condition: ConditionCode::AL, 211 | loc, 212 | rn: Some(rn), 213 | rd, 214 | rd_read: true, 215 | operand2, 216 | } 217 | ) 218 | } 219 | Opcode::MOV => { 220 | validate_operand_count(2, operands, opcode, loc)?; 221 | 222 | let rd = match &operands[0] { 223 | ASTOperand::Register(o) => o.register, 224 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 225 | vec![ASTOperandType::Register])) 226 | }; 227 | 228 | let operand2 = match &operands[1] { 229 | ASTOperand::Register(register) => Operand2::Register { reg_id: register.register }, 230 | ASTOperand::Immediate(immediate) => Operand2::Immediate { value: immediate.value }, 231 | ASTOperand::AddressOf(address_of) => Operand2::Immediate { value: address_of.offset }, 232 | _ => return Err(type_mismatch(opcode, 2, &operands[2], 233 | vec![ASTOperandType::Register, ASTOperandType::Immediate, ASTOperandType::AddressOf])) 234 | }; 235 | 236 | Instr::DataProcessing( 237 | DataProcessing { 238 | opcode, 239 | condition: ConditionCode::AL, 240 | loc, 241 | rn: None, 242 | rd, 243 | rd_read: false, 244 | operand2, 245 | } 246 | ) 247 | } 248 | Opcode::ADR => { panic!() } 249 | Opcode::STR | 250 | Opcode::LDR => { 251 | validate_operand_count(2, operands, opcode, loc)?; 252 | 253 | let rd = match &operands[0] { 254 | ASTOperand::Register(o) => o.register, 255 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 256 | vec![ASTOperandType::Register])) 257 | }; 258 | 259 | let rn = match &operands[1] { 260 | ASTOperand::MemRegisterIndirect(mem_register_indirect) => mem_register_indirect.register, 261 | _ => return Err(type_mismatch(opcode, 1, &operands[1], 262 | vec![ASTOperandType::MemRegisterIndirect])) 263 | }; 264 | 265 | Instr::LoadStore( 266 | LoadStore { 267 | opcode, 268 | condition: ConditionCode::AL, 269 | loc, 270 | rd, 271 | rn, 272 | offset: 0, 273 | } 274 | ) 275 | } 276 | Opcode::PRINTR => { 277 | validate_operand_count(1, operands, opcode, loc)?; 278 | 279 | let rn = match &operands[0] { 280 | ASTOperand::Register(o) => o.register, 281 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 282 | vec![ASTOperandType::Register])) 283 | }; 284 | 285 | Instr::Printr( 286 | Printr { 287 | loc: Some(loc), 288 | rn, 289 | } 290 | ) 291 | } 292 | 293 | Opcode::RET => { 294 | if operands.len() > 1 { 295 | return Err(format!("Operand count mismatch. {:?} expects 0 or 1 argument, but {} are provided at {}:{}", 296 | opcode, operands.len(), loc.line, loc.column)); 297 | } 298 | 299 | let target = if operands.len() == 0 { 300 | LR as RegisterType 301 | } else { 302 | match &operands[0] { 303 | ASTOperand::Register(o) => o.register, 304 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 305 | vec![ASTOperandType::Register])) 306 | } 307 | }; 308 | 309 | Instr::Branch( 310 | Branch { 311 | opcode, 312 | condition: ConditionCode::AL, 313 | loc, 314 | link_bit: false, 315 | target: BranchTarget::Register { register: target }, 316 | rt: None, 317 | } 318 | ) 319 | } 320 | Opcode::B => { 321 | validate_operand_count(1, operands, opcode, loc)?; 322 | 323 | let offset = match &operands[0] { 324 | ASTOperand::Label(o) => o.offset , 325 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 326 | vec![ASTOperandType::Label])) 327 | }; 328 | 329 | Instr::Branch( 330 | Branch { 331 | opcode, 332 | condition: ConditionCode::AL, 333 | loc, 334 | link_bit: false, 335 | target: BranchTarget::Immediate { offset: offset as u32 }, 336 | rt: None, 337 | } 338 | ) 339 | } 340 | Opcode::BX => { 341 | validate_operand_count(1, operands, opcode, loc)?; 342 | 343 | let target = match &operands[0] { 344 | ASTOperand::Register(o) => o.register, 345 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 346 | vec![ASTOperandType::Register])) 347 | }; 348 | 349 | Instr::Branch( 350 | Branch { 351 | opcode, 352 | condition: ConditionCode::AL, 353 | loc, 354 | link_bit: false, 355 | target: BranchTarget::Register { register: target }, 356 | rt: None, 357 | } 358 | ) 359 | } 360 | Opcode::BL => { 361 | validate_operand_count(1, operands, opcode, loc)?; 362 | 363 | let target = match &operands[0] { 364 | ASTOperand::Label(o) => o.offset , 365 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 366 | vec![ASTOperandType::Label])) 367 | }; 368 | Instr::Branch( 369 | Branch { 370 | opcode, 371 | condition: ConditionCode::AL, 372 | loc, 373 | link_bit: true, 374 | target: BranchTarget::Immediate { offset: target as u32 }, 375 | rt: None, 376 | } 377 | ) 378 | } 379 | Opcode::CBZ | 380 | Opcode::CBNZ => { 381 | validate_operand_count(2, operands, opcode, loc)?; 382 | 383 | let rt = match &operands[0] { 384 | ASTOperand::Register(o) => o.register, 385 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 386 | vec![ASTOperandType::Register])) 387 | }; 388 | 389 | let target = match &operands[1] { 390 | ASTOperand::Label(o) => o.offset , 391 | _ => return Err(type_mismatch(opcode, 1, &operands[1], 392 | vec![ASTOperandType::Label])) 393 | }; 394 | 395 | Instr::Branch( 396 | Branch { 397 | opcode, 398 | condition: ConditionCode::AL, 399 | loc, 400 | link_bit: false, 401 | target: BranchTarget::Immediate { offset: target as u32 }, 402 | rt: Some(rt), 403 | } 404 | ) 405 | } 406 | Opcode::BEQ | 407 | Opcode::BNE | 408 | Opcode::BLT | 409 | Opcode::BLE | 410 | Opcode::BGT | 411 | Opcode::BGE => { 412 | validate_operand_count(1, operands, opcode, loc)?; 413 | 414 | let offset = match &operands[0] { 415 | ASTOperand::Label(o) => o.offset , 416 | _ => return Err(type_mismatch(opcode, 0, &operands[0], 417 | vec![ASTOperandType::Label])) 418 | }; 419 | 420 | Instr::Branch( 421 | Branch { 422 | opcode, 423 | condition: ConditionCode::AL, 424 | loc, 425 | link_bit: false, 426 | target: BranchTarget::Immediate { offset: offset as u32 }, 427 | rt: Some(CPSR), 428 | } 429 | ) 430 | } 431 | 432 | Opcode::NOP | 433 | Opcode::EXIT | 434 | Opcode::DSB => { 435 | validate_operand_count(0, operands, opcode, loc)?; 436 | 437 | Instr::Synchronization( 438 | Synchronization { 439 | opcode, 440 | loc: Some(loc), 441 | } 442 | ) 443 | } 444 | }; 445 | 446 | // todo: handling of instructions with control like modifying the IP need to be detected. 447 | // 448 | // if !instr.is_branch() && has_control_operands(&instr) { 449 | // instr.set_branch(); 450 | // } 451 | 452 | return Ok(instr); 453 | } 454 | 455 | fn type_mismatch(opcode: Opcode, op_index: i32, found: &ASTOperand, acceptable_types: Vec) -> String { 456 | let acceptable_names: Vec<&str> = acceptable_types.iter().map(|t| t.base_name()).collect(); 457 | let acceptable_names_str = acceptable_names.join(", "); 458 | 459 | format!("Operand type mismatch. {:?} expects {} as argument nr {}, but {} was provided", 460 | opcode, acceptable_names_str, op_index + 1, found.get_type().base_name()) 461 | } 462 | 463 | fn validate_operand_count(expected: usize, 464 | operands: &Vec, 465 | opcode: Opcode, 466 | loc: SourceLocation) -> Result<(), String> { 467 | if operands.len() != expected { 468 | return Err(format!("Operand count mismatch. {:?} expects {} arguments, but {} are provided at {}:{}", 469 | opcode, expected, operands.len(), loc.line, loc.column)); 470 | } 471 | Ok(()) 472 | } 473 | 474 | pub struct SymbolScan<'a> { 475 | loader: &'a mut Loader, 476 | } 477 | 478 | impl ASTVisitor for SymbolScan<'_> { 479 | fn visit_data(&mut self, ast_data: &mut ASTData) -> bool { 480 | if self.loader.heap_limit == self.loader.cpu_config.memory_size { 481 | let loc = self.loader.to_source_location(ast_data.pos); 482 | self.loader.errors.push(format!("Insufficient heap to declare variable '{}' at {}:{}", ast_data.name, loc.line, loc.column)); 483 | return false; 484 | } 485 | 486 | if !is_valid_variable_name(&ast_data.name) { 487 | let loc = self.loader.to_source_location(ast_data.pos); 488 | self.loader.errors.push(format!("Illegal variable name '{}' at {}:{}", ast_data.name, loc.line, loc.column)); 489 | } 490 | 491 | if self.loader.labels.contains_key(&ast_data.name) { 492 | let loc = self.loader.to_source_location(ast_data.pos); 493 | self.loader.errors.push(format!("There already exists a label with name '{}' at {}:{}", ast_data.name, loc.line, loc.column)); 494 | } 495 | 496 | if self.loader.data_section.contains_key(&ast_data.name) { 497 | let loc = self.loader.to_source_location(ast_data.pos); 498 | self.loader.errors.push(format!("Duplicate variable '{}' at {}:{}", ast_data.name, loc.line, loc.column)); 499 | } 500 | 501 | self.loader.data_section.insert(ast_data.name.clone(), 502 | Rc::new(Data { value: ast_data.value as DWordType, offset: self.loader.heap_limit as u64 })); 503 | self.loader.heap_limit += 1; 504 | true 505 | } 506 | 507 | fn visit_instr(&mut self, _: &mut ASTInstr) -> bool { 508 | self.loader.instr_cnt += 1; 509 | true 510 | } 511 | 512 | fn visit_label(&mut self, ast_label: &mut ASTLabel) -> bool { 513 | if self.loader.data_section.contains_key(&ast_label.name) { 514 | let loc = self.loader.to_source_location(ast_label.pos); 515 | self.loader.errors.push(format!("There already exists a variable with name '{}' at {}:{}", ast_label.name, loc.line, loc.column)); 516 | } 517 | 518 | if self.loader.labels.contains_key(&ast_label.name) { 519 | let loc = self.loader.to_source_location(ast_label.pos); 520 | self.loader.errors.push(format!("Duplicate label '{}' at {}:{}", ast_label.name, loc.line, loc.column)); 521 | } else { 522 | self.loader.labels.insert(ast_label.name.clone(), self.loader.instr_cnt); 523 | } 524 | true 525 | } 526 | } 527 | 528 | pub struct ProgramGeneration<'a> { 529 | loader: &'a mut Loader, 530 | operand_stack: Vec, 531 | } 532 | 533 | impl ASTVisitor for ProgramGeneration<'_> { 534 | fn visit_operand(&mut self, ast_operand: &mut ASTOperand) -> bool { 535 | match ast_operand { 536 | ASTOperand::Register(register) => { 537 | if register.register >= GENERAL_ARG_REG_CNT as RegisterType { 538 | let loc = self.loader.to_source_location(register.pos); 539 | self.loader.errors.push(format!("Unknown register r'{}' at {}:{}", register.register, loc.line, loc.column)); 540 | return false; 541 | } 542 | 543 | self.operand_stack.push(ast_operand.clone()); 544 | } 545 | ASTOperand::Immediate(_) => { 546 | self.operand_stack.push(ast_operand.clone()); 547 | } 548 | ASTOperand::Label(label) => { 549 | match self.loader.labels.get(&mut label.label) { 550 | Some(code_address) => { 551 | label.offset = *code_address as DWordType; 552 | self.operand_stack.push(ast_operand.clone()); 553 | } 554 | None => { 555 | let loc = self.loader.to_source_location(label.pos); 556 | self.loader.errors.push(format!("Unknown label '{}' at {}:{}", label.label, loc.line, loc.column)); 557 | return false; 558 | } 559 | } 560 | } 561 | ASTOperand::AddressOf(address_of) => { 562 | match self.loader.data_section.get(&address_of.label) { 563 | Some(data) => { 564 | address_of.offset = data.offset as DWordType; 565 | self.operand_stack.push(ast_operand.clone()); 566 | } 567 | None => { 568 | let loc = self.loader.to_source_location(address_of.pos); 569 | self.loader.errors.push(format!("Unknown variable '{}' at {}:{}", address_of.label, loc.line, loc.column)); 570 | return false; 571 | } 572 | } 573 | } 574 | 575 | ASTOperand::Unused() => {} 576 | ASTOperand::MemRegisterIndirect(_) => { 577 | self.operand_stack.push(ast_operand.clone()); 578 | } 579 | //ASTOperand::MemoryAccessWithImmediate(_, _, _) => {} 580 | }; 581 | 582 | true 583 | } 584 | 585 | fn visit_instr(&mut self, ast_instr: &mut ASTInstr) -> bool { 586 | // todo: this is very inefficient because for every instruction the whole file content is scanned. 587 | let loc = self.loader.to_source_location(ast_instr.pos); 588 | let opcode_option = get_opcode(&ast_instr.mnemonic); 589 | 590 | if opcode_option.is_none() || opcode_option.unwrap() == Opcode::EXIT { 591 | self.loader.errors.push(format!("Unknown mnemonic '{}' at {}:{}", ast_instr.mnemonic, loc.line, loc.column)); 592 | return false; 593 | } 594 | 595 | let opcode = opcode_option.unwrap(); 596 | match create_instr(opcode, &self.operand_stack, loc) { 597 | Ok(instr) => { 598 | self.loader.code.push(instr); 599 | } 600 | Err(msg) => { 601 | self.loader.errors.push(format!("{} at {}:{}", msg, loc.line, loc.column)); 602 | } 603 | }; 604 | self.operand_stack.clear(); 605 | true 606 | } 607 | 608 | fn visit_directive(&mut self, ast_directive: &mut ASTDirective) -> bool { 609 | match ast_directive { 610 | ASTDirective::Global(start_label, pos) => { 611 | match self.loader.labels.get(start_label) { 612 | Some(code_address) => { 613 | self.loader.entry_point = *code_address as usize; 614 | return true; 615 | } 616 | None => { 617 | let loc = self.loader.to_source_location(*pos); 618 | self.loader.errors.push(format!("Unknown label '{}' at {}:{}", start_label, loc.line, loc.column)); 619 | return false; 620 | } 621 | } 622 | } 623 | } 624 | } 625 | } 626 | 627 | fn is_valid_variable_name(name: &String) -> bool { 628 | if name.is_empty() { 629 | return false; 630 | } 631 | 632 | // todo: the other registers are ignored. 633 | let re = Regex::new(r"^(?i)R\d+$").unwrap(); 634 | if re.is_match(name) { 635 | return false; 636 | } 637 | 638 | if get_opcode(name).is_some() { 639 | // It can't be an existing mnemonic 640 | return false; 641 | } 642 | 643 | true 644 | } 645 | 646 | pub fn load_from_file(cpu_config: CPUConfig, path_str: &str) -> Result { 647 | let path = Path::new(path_str); 648 | 649 | if !path.exists() { 650 | return Err(LoadError::NotFoundError(format!("File '{}' does not exist.", path_str))); 651 | } 652 | 653 | let src = match fs::read_to_string(&path) { 654 | Ok(content) => content, 655 | Err(err) => { 656 | return Err(LoadError::IOError(err.to_string())); 657 | } 658 | }; 659 | 660 | return load_from_string(cpu_config, src); 661 | } 662 | 663 | pub fn load_from_string(cpu_config: CPUConfig, src: String) -> Result { 664 | let mut loader = Loader { 665 | heap_limit: 0, 666 | cpu_config, 667 | src, 668 | code: Vec::new(), 669 | data_section: HashMap::>::new(), 670 | labels: HashMap::::new(), 671 | instr_cnt: 0, 672 | entry_point: 0, 673 | errors: Vec::new(), 674 | }; 675 | 676 | return loader.load(); 677 | } -------------------------------------------------------------------------------- /src/loader/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod loader; 2 | pub mod ast; 3 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | use std::process::exit; 3 | use std::rc::Rc; 4 | 5 | use lalrpop_util::lalrpop_mod; 6 | use structopt::StructOpt; 7 | 8 | use crate::cpu::{CPU, load_cpu_config}; 9 | use crate::loader::loader::{load_from_file, LoadError}; 10 | 11 | mod cpu; 12 | mod loader; 13 | mod frontend; 14 | mod backend; 15 | mod instructions; 16 | mod memory_subsystem; 17 | mod cpu_tests; 18 | 19 | 20 | lalrpop_mod!(pub assembly, "/loader/assembly.rs"); 21 | 22 | #[derive(StructOpt, Debug)] 23 | #[structopt(name = "ARM CPU Emulator")] 24 | struct Opt { 25 | /// Path of the file to load 26 | #[structopt(short, long, parse(from_os_str))] 27 | file: PathBuf, 28 | 29 | /// Sets a custom config file 30 | #[structopt(short, long, parse(from_os_str), default_value = "cpu.yaml")] 31 | config: PathBuf, 32 | 33 | #[structopt(short, long)] 34 | stats: bool, 35 | } 36 | 37 | fn main() { 38 | let opt = Opt::from_args(); 39 | 40 | let cpu_config_path = opt.config.to_str().unwrap(); 41 | let cpu_config = match load_cpu_config(cpu_config_path) { 42 | Ok(config) => config, 43 | Err(error) => { 44 | println!("Failed to load {}. Cause: {}", cpu_config_path, error); 45 | exit(0); 46 | } 47 | }; 48 | 49 | let path = opt.file.to_str().unwrap(); 50 | println!("Loading {}", path); 51 | let load_result = load_from_file(cpu_config.clone(), path); 52 | let program = match load_result { 53 | Ok(p) => Rc::new(p), 54 | Err(err) => { 55 | println!("Loading program '{}' failed.", path); 56 | match err { 57 | LoadError::ParseError(msg) => { 58 | println!("{}", msg); 59 | exit(1); 60 | } 61 | 62 | LoadError::AnalysisError(msg_vec) => { 63 | for msg in msg_vec { 64 | println!("{}", msg); 65 | } 66 | exit(1); 67 | } 68 | LoadError::NotFoundError(msg) => { 69 | println!("{}", msg); 70 | exit(1); 71 | } 72 | LoadError::IOError(msg) => { 73 | println!("{}", msg); 74 | exit(1); 75 | } 76 | } 77 | } 78 | }; 79 | 80 | let mut cpu = CPU::new(&cpu_config); 81 | cpu.run(&program); 82 | 83 | if opt.stats { 84 | show_stats(&mut cpu); 85 | } 86 | } 87 | 88 | fn show_stats(cpu: &CPU) { 89 | let perf_counters = cpu.perf_counters.borrow(); 90 | 91 | let branch_total = perf_counters.branch_miss_prediction_cnt + perf_counters.branch_good_predictions_cnt; 92 | 93 | let ipc = perf_counters.retired_cnt as f32 / perf_counters.cycle_cnt as f32; 94 | 95 | let branch_prediction = if branch_total != 0 { 96 | 100.0 * perf_counters.branch_good_predictions_cnt as f32 / branch_total as f32 97 | } else { 98 | 0.0 99 | }; 100 | 101 | println!("-------------------- [ stats ] -------------------------"); 102 | println!("ipc {:.2}", ipc); 103 | println!("branch pred {:.2}%", branch_prediction); 104 | println!("branch miss prediction cnt: {}", perf_counters.branch_miss_prediction_cnt); 105 | println!("branch good predictions cnt: {}", perf_counters.branch_good_predictions_cnt); 106 | println!("decode cnt: {}", perf_counters.decode_cnt); 107 | println!("issue cnt: {}", perf_counters.issue_cnt); 108 | println!("dispatch cnt: {}", perf_counters.dispatch_cnt); 109 | println!("execute cnt: {}", perf_counters.execute_cnt); 110 | println!("retired cnt: {}", perf_counters.retired_cnt); 111 | println!("cycle cnt: {}", perf_counters.cycle_cnt); 112 | println!("bad speculation cnt: {}", perf_counters.bad_speculation_cnt); 113 | println!("pipeline flushes: {}", perf_counters.pipeline_flushes); 114 | } 115 | -------------------------------------------------------------------------------- /src/memory_subsystem/memory_subsystem.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::cpu::CPUConfig; 4 | use crate::instructions::instructions::{DWordType, Program}; 5 | use crate::memory_subsystem::store_buffer::SB; 6 | 7 | pub(crate) struct MemorySubsystem { 8 | pub(crate) memory: Vec, 9 | pub(crate) sb: SB, 10 | } 11 | 12 | impl MemorySubsystem { 13 | pub fn new(cpu_config: &CPUConfig) -> MemorySubsystem { 14 | let mut memory = Vec::with_capacity(cpu_config.memory_size as usize); 15 | 16 | for _ in 0..cpu_config.memory_size { 17 | memory.push(0); 18 | } 19 | 20 | let sb = SB::new(cpu_config); 21 | 22 | MemorySubsystem { 23 | memory, 24 | sb, 25 | } 26 | } 27 | 28 | pub(crate) fn init(&mut self, program: &Rc) { 29 | for k in 0..self.memory.len() { 30 | self.memory[k] = 0; 31 | } 32 | 33 | for data in program.data_items.values() { 34 | self.memory[data.offset as usize] = data.value; 35 | } 36 | } 37 | 38 | pub(crate) fn do_cycle(&mut self) { 39 | self.sb.do_cycle(&mut self.memory); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/memory_subsystem/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod memory_subsystem; 2 | mod store_buffer; -------------------------------------------------------------------------------- /src/memory_subsystem/store_buffer.rs: -------------------------------------------------------------------------------- 1 | use SBEntryState::{ALLOCATED, COMMITTED, IDLE, READY}; 2 | 3 | use crate::cpu::CPUConfig; 4 | use crate::instructions::instructions::DWordType; 5 | 6 | enum SBEntryState { 7 | // not used. 8 | IDLE, 9 | // it is allocated for a store 10 | ALLOCATED, 11 | // the value is stored, but it is still in speculative state. So there 12 | // is no guarantee that the store is going to be written to main memory 13 | READY, 14 | // the value is stored, and is not any longer in speculative state 15 | // and is guaranteed to be written to main memory 16 | COMMITTED, 17 | } 18 | 19 | struct SBEntry { 20 | value: DWordType, 21 | addr: DWordType, 22 | state: SBEntryState, 23 | } 24 | 25 | impl SBEntry { 26 | fn reset(&mut self) { 27 | self.state = IDLE; 28 | self.addr = 0; 29 | self.value = 0; 30 | } 31 | } 32 | 33 | pub(crate) struct SB { 34 | head: u64, 35 | tail: u64, 36 | entries: Vec, 37 | capacity: u16, 38 | lfb_count: u8, 39 | } 40 | 41 | impl SB { 42 | pub(crate) fn new(cpu_config: &CPUConfig) -> SB { 43 | let mut entries = Vec::with_capacity(cpu_config.sb_capacity as usize); 44 | for _ in 0..cpu_config.sb_capacity { 45 | entries.push(SBEntry { 46 | value: 0, 47 | addr: 0, 48 | state: IDLE, 49 | }) 50 | } 51 | 52 | SB { 53 | capacity: cpu_config.sb_capacity, 54 | head: 0, 55 | tail: 0, 56 | entries, 57 | lfb_count: cpu_config.lfb_count, 58 | } 59 | } 60 | 61 | pub(crate) fn size(&self) -> u16 { 62 | return (self.tail - self.head) as u16; 63 | } 64 | 65 | pub(crate) fn is_empty(&self) -> bool { 66 | self.size() == 0 67 | } 68 | 69 | pub(crate) fn has_space(&self) -> bool { 70 | return self.size() < self.capacity; 71 | } 72 | 73 | pub(crate) fn allocate(&mut self) -> u16 { 74 | assert!(self.has_space(), "StoreBuffer: can't allocate because there is no space"); 75 | 76 | let index = self.to_index(self.tail); 77 | self.entries[index].state = ALLOCATED; 78 | self.tail += 1; 79 | return index as u16; 80 | } 81 | 82 | fn to_index(&self, seq: u64) -> usize { 83 | (seq % self.capacity as u64) as usize 84 | } 85 | 86 | pub(crate) fn store(&mut self, index: u16, addr: DWordType, value: DWordType) { 87 | let sb_entry = &mut self.entries[index as usize]; 88 | 89 | match sb_entry.state { 90 | ALLOCATED => { 91 | sb_entry.addr = addr; 92 | sb_entry.value = value; 93 | sb_entry.state = READY; 94 | } 95 | _ => unreachable!(), 96 | } 97 | } 98 | 99 | pub(crate) fn commit(&mut self, index: u16) { 100 | let sb_entry = &mut self.entries[index as usize]; 101 | 102 | match sb_entry.state { 103 | READY => sb_entry.state = COMMITTED, 104 | _ => unreachable!(), 105 | } 106 | } 107 | 108 | pub(crate) fn flush(&mut self) { 109 | // to flush, we go backwards from the tail and 'deallocate' every store 110 | // until a committed store is found. Behind that committed store can only 111 | // be other committed stores because commits are done in order (retire). 112 | 113 | for k in (self.head..self.tail).rev() { 114 | let index = self.to_index(k); 115 | let sb_entry = &mut self.entries[index]; 116 | match sb_entry.state { 117 | ALLOCATED | 118 | READY => { 119 | sb_entry.reset(); 120 | self.tail -= 1; 121 | } 122 | COMMITTED => { 123 | break; 124 | } 125 | _ => unreachable!(), 126 | } 127 | } 128 | } 129 | 130 | pub(crate) fn do_cycle(&mut self, memory: &mut Vec) { 131 | for _ in 0..self.lfb_count { 132 | if self.is_empty() { 133 | break; 134 | } 135 | 136 | let index = self.to_index(self.head); 137 | let sb_entry = &mut self.entries[index]; 138 | match sb_entry.state { 139 | ALLOCATED | 140 | READY => {} 141 | COMMITTED => { 142 | // write the store to memory 143 | memory[sb_entry.addr as usize] = sb_entry.value; 144 | sb_entry.reset(); 145 | self.head += 1; 146 | } 147 | _ => unreachable!(), 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | BUGS 2 | 3 | - restore the 'control' instr 4 | 5 | TODO 6 | 7 | - the AST operands should get their own visit methods 8 | 9 | - program/loader should not construct 'Instr'; they are CPU internal detail for decoded instructions 10 | 11 | - add ConditionCode operand 12 | 13 | - add CSINC 14 | 15 | - add CSEL 16 | 17 | - add CSET 18 | 19 | - integration test: stack push 20 | 21 | - integration test: stack pop 22 | 23 | - Backend.cycle_eu_table should move to EUTable. 24 | 25 | - option to disable speculative execution 26 | 27 | - dedicated EU for ALU, LOAD/STORE etc 28 | 29 | - optimize the flush of the ROB (idle entries can be skipped) 30 | 31 | - syntax: case insensitive keywords 32 | 33 | - syntax: new lines 34 | --------------------------------------------------------------------------------