├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── assets └── PixelOperatorMono.hex ├── pkg ├── Assets │ └── camera │ │ └── common │ │ └── .gitkeep ├── Cores │ └── agg23.Camera │ │ ├── audio.json │ │ ├── core.json │ │ ├── data.json │ │ ├── icon.bin │ │ ├── info.txt │ │ ├── input.json │ │ ├── interact.json │ │ ├── variants.json │ │ └── video.json ├── Platforms │ └── camera.json └── agg23.Camera-Internal0.zip ├── platform └── pocket │ ├── apf.qip │ ├── apf_constraints.sdc │ ├── apf_top.v │ ├── build_cdf.tcl │ ├── build_id_gen.tcl │ ├── common.v │ ├── io_bridge_peripheral.v │ ├── io_pad_controller.v │ ├── mf_datatable.qip │ ├── mf_datatable.v │ ├── mf_ddio_bidir_12.qip │ ├── mf_ddio_bidir_12.v │ └── pocket.tcl ├── projects ├── camera_pocket.qip ├── camera_pocket.qpf ├── camera_pocket.qsf ├── camera_pocket.sdc └── stp1.stp ├── rtl ├── camera.qip ├── cart_verifier.sv ├── file │ ├── double_bcd.sv │ ├── file.qip │ ├── file_controller.sv │ ├── open_file_struct.sv │ ├── save_dumper.sv │ └── write_path.sv ├── gb │ ├── T80 │ │ ├── GBse.vhd │ │ ├── T80.qip │ │ ├── T80.vhd │ │ ├── T80_ALU.vhd │ │ ├── T80_MCode.vhd │ │ ├── T80_Pack.vhd │ │ └── T80_Reg.vhd │ ├── audio │ │ ├── audio.qip │ │ ├── audio_mixer.sv │ │ ├── filters │ │ │ ├── audio_filters.sv │ │ │ ├── audio_mix.sv │ │ │ ├── dc_blocker.sv │ │ │ ├── filter_loader.sv │ │ │ ├── filters_rom.sv │ │ │ ├── iir_filter.sv │ │ │ └── iir_filter_tap.sv │ │ ├── mf_audio_pll.ppf │ │ ├── mf_audio_pll.qip │ │ ├── mf_audio_pll.v │ │ └── mf_audio_pll │ │ │ ├── mf_audio_pll_0002.qip │ │ │ └── mf_audio_pll_0002.v │ ├── bus_savestates.vhd │ ├── cart.v │ ├── cheatcodes.sv │ ├── ddram.sv │ ├── dpram.vhd │ ├── gb.qip │ ├── gb.v │ ├── gb_savestates.vhd │ ├── gb_statemanager.vhd │ ├── gbc_snd.vhd │ ├── hdma.v │ ├── lcd.v │ ├── link.v │ ├── mappers │ │ ├── gb_camera.v │ │ └── mappers.v │ ├── megaswizzle.sv │ ├── reg_savestates.vhd │ ├── rtc_loader.sv │ ├── rtc_ram │ │ ├── rtc_ram.qip │ │ ├── rtc_ram.v │ │ ├── rtc_ram_bb.v │ │ └── rtc_ram_inst.v │ ├── rumbler.sv │ ├── savestate_ui.sv │ ├── sdram.sv │ ├── sgb.v │ ├── speedcontrol.vhd │ ├── spram.vhd │ ├── sprites.v │ ├── timer.v │ └── video.v └── ui │ ├── char_rom.sv │ ├── ui.qip │ └── ui.sv └── target └── pocket ├── cart_pll.ppf ├── cart_pll.qip ├── cart_pll.v ├── cart_pll ├── cart_pll_0002.qip └── cart_pll_0002.v ├── core.qip ├── core_bridge_cmd.v ├── core_constraints.sdc ├── core_top.sv ├── data_loader.sv ├── data_unloader.sv ├── mf_pllbase.ppf ├── mf_pllbase.qip ├── mf_pllbase.v ├── mf_pllbase ├── mf_pllbase_0002.qip └── mf_pllbase_0002.v ├── pin_ddio_clk.ppf ├── pin_ddio_clk.qip ├── pin_ddio_clk.v ├── psram.sv ├── sound_i2s.sv ├── stp1.stp └── sync_fifo.sv /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: agg23 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | sim/ 2 | sim2/ 3 | assets/*/*.hex 4 | **/vunit_out 5 | **/__pycache__ 6 | 7 | *.zip 8 | *.rbf_r 9 | *.rev 10 | *.wlf 11 | *.vcd 12 | db 13 | greybox_tmp 14 | hps_isw_handoff 15 | incremental_db 16 | output_files 17 | PLLJ_PLLSPE_INFO.txt 18 | simulation 19 | vip 20 | .qsys_edit 21 | *_netlist 22 | *_sim 23 | *.bak 24 | *.bsf 25 | *.cdf 26 | *.cmp 27 | *.csv 28 | *.done 29 | *.f 30 | *.pin 31 | *.pof 32 | *.ptf.* 33 | *.qar 34 | *.qarlog 35 | *.qdf 36 | *.qws 37 | *.rbf 38 | *.rpt 39 | *.sip 40 | *.sld 41 | *.smsg 42 | *.sof 43 | *.sopc_builder 44 | *.sopcinfo 45 | *.spd 46 | *.summary 47 | *.xml 48 | *~ 49 | **/.DS_Store 50 | build_id.mif 51 | build_id.v 52 | c5_pin_model_dump.txt 53 | cr_ie_info.json 54 | # Gateman directories and files 55 | !.gateman/* 56 | !gateware.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gameboy/Game Boy Color for Analogue Pocket 2 | Ported from the original core developed at https://github.com/MiSTer-devel/Gameboy_MiSTer 3 | 4 | Please report any issues encountered to this repo. Issues will be upstreamed as necessary. 5 | 6 | ## Installation 7 | To install the core, copy the `Assets`, `Cores`, and `Platform` folders over to the root of your SD card. Please note that Finder on macOS automatically _replaces_ folders, rather than merging them like Windows does, so you have to manually merge the folders. 8 | 9 | Place the GBC bios in `/Assets/gbc/common` named "gbc_bios.bin", and the GB bios in `/Assets/gb/common` named "gb_bios.bin". 10 | 11 | 12 | ## Usage 13 | ROMs should be placed in `/Assets/gbc/common`, and `/Assets/gb/common` 14 | 15 | ## Features 16 | 17 | ### Supported 18 | * Real-Time Clock 19 | * Fastforward 20 | * Original Gameboy display modes 21 | * Super Gameboy Emulation 22 | * Custom Borders (SGB) 23 | * Custom Palettes (SGB) 24 | * Enhance GBA features 25 | 26 | ### In Progress 27 | * Save States 28 | -------------------------------------------------------------------------------- /assets/PixelOperatorMono.hex: -------------------------------------------------------------------------------- 1 | 00 2 | 00 3 | 00 4 | 00 5 | 00 6 | 00 7 | 00 8 | 00 9 | 10 10 | 10 11 | 10 12 | 10 13 | 10 14 | 00 15 | 10 16 | 00 17 | 28 18 | 28 19 | 28 20 | 00 21 | 00 22 | 00 23 | 00 24 | 00 25 | 00 26 | 24 27 | 7e 28 | 24 29 | 24 30 | 7e 31 | 24 32 | 00 33 | 10 34 | 38 35 | 50 36 | 38 37 | 14 38 | 78 39 | 10 40 | 00 41 | 40 42 | a4 43 | a8 44 | 54 45 | 2a 46 | 4a 47 | 04 48 | 00 49 | 38 50 | 44 51 | 40 52 | 38 53 | 44 54 | 44 55 | 3c 56 | 00 57 | 10 58 | 10 59 | 10 60 | 00 61 | 00 62 | 00 63 | 00 64 | 00 65 | 04 66 | 08 67 | 10 68 | 10 69 | 10 70 | 08 71 | 04 72 | 00 73 | 40 74 | 20 75 | 10 76 | 10 77 | 10 78 | 20 79 | 40 80 | 00 81 | 10 82 | 54 83 | 38 84 | 54 85 | 10 86 | 00 87 | 00 88 | 00 89 | 00 90 | 10 91 | 10 92 | 7c 93 | 10 94 | 10 95 | 00 96 | 00 97 | 00 98 | 00 99 | 00 100 | 00 101 | 00 102 | 10 103 | 10 104 | 20 105 | 00 106 | 00 107 | 00 108 | 3c 109 | 00 110 | 00 111 | 00 112 | 00 113 | 00 114 | 00 115 | 00 116 | 00 117 | 00 118 | 00 119 | 10 120 | 00 121 | 08 122 | 08 123 | 10 124 | 10 125 | 10 126 | 20 127 | 20 128 | 00 129 | 38 130 | 44 131 | 4c 132 | 54 133 | 64 134 | 44 135 | 38 136 | 00 137 | 10 138 | 30 139 | 50 140 | 10 141 | 10 142 | 10 143 | 7c 144 | 00 145 | 38 146 | 44 147 | 04 148 | 08 149 | 10 150 | 20 151 | 7c 152 | 00 153 | 38 154 | 44 155 | 04 156 | 18 157 | 04 158 | 44 159 | 38 160 | 00 161 | 0c 162 | 14 163 | 24 164 | 44 165 | 7c 166 | 04 167 | 04 168 | 00 169 | 7c 170 | 40 171 | 78 172 | 04 173 | 04 174 | 44 175 | 38 176 | 00 177 | 38 178 | 44 179 | 40 180 | 78 181 | 44 182 | 44 183 | 38 184 | 00 185 | 7c 186 | 04 187 | 08 188 | 10 189 | 20 190 | 40 191 | 40 192 | 00 193 | 38 194 | 44 195 | 44 196 | 38 197 | 44 198 | 44 199 | 38 200 | 00 201 | 38 202 | 44 203 | 44 204 | 3c 205 | 04 206 | 44 207 | 38 208 | 00 209 | 00 210 | 00 211 | 10 212 | 00 213 | 00 214 | 00 215 | 10 216 | 00 217 | 00 218 | 00 219 | 10 220 | 00 221 | 00 222 | 10 223 | 10 224 | 20 225 | 00 226 | 08 227 | 10 228 | 20 229 | 10 230 | 08 231 | 00 232 | 00 233 | 00 234 | 00 235 | 3c 236 | 00 237 | 3c 238 | 00 239 | 00 240 | 00 241 | 00 242 | 20 243 | 10 244 | 08 245 | 10 246 | 20 247 | 00 248 | 00 249 | 38 250 | 44 251 | 04 252 | 08 253 | 10 254 | 00 255 | 10 256 | 00 257 | 7c 258 | 82 259 | 9a 260 | aa 261 | aa 262 | 9c 263 | 80 264 | 7c 265 | 38 266 | 44 267 | 44 268 | 44 269 | 7c 270 | 44 271 | 44 272 | 00 273 | 78 274 | 44 275 | 44 276 | 78 277 | 44 278 | 44 279 | 78 280 | 00 281 | 38 282 | 44 283 | 40 284 | 40 285 | 40 286 | 44 287 | 38 288 | 00 289 | 78 290 | 44 291 | 44 292 | 44 293 | 44 294 | 44 295 | 78 296 | 00 297 | 7c 298 | 40 299 | 40 300 | 70 301 | 40 302 | 40 303 | 7c 304 | 00 305 | 7c 306 | 40 307 | 40 308 | 70 309 | 40 310 | 40 311 | 40 312 | 00 313 | 38 314 | 44 315 | 40 316 | 4c 317 | 44 318 | 44 319 | 3c 320 | 00 321 | 44 322 | 44 323 | 44 324 | 7c 325 | 44 326 | 44 327 | 44 328 | 00 329 | 7c 330 | 10 331 | 10 332 | 10 333 | 10 334 | 10 335 | 7c 336 | 00 337 | 1e 338 | 04 339 | 04 340 | 04 341 | 04 342 | 44 343 | 38 344 | 00 345 | 44 346 | 48 347 | 50 348 | 60 349 | 50 350 | 48 351 | 44 352 | 00 353 | 40 354 | 40 355 | 40 356 | 40 357 | 40 358 | 40 359 | 7c 360 | 00 361 | 82 362 | 82 363 | c6 364 | aa 365 | 92 366 | 82 367 | 82 368 | 00 369 | 44 370 | 44 371 | 64 372 | 54 373 | 4c 374 | 44 375 | 44 376 | 00 377 | 38 378 | 44 379 | 44 380 | 44 381 | 44 382 | 44 383 | 38 384 | 00 385 | 78 386 | 44 387 | 44 388 | 44 389 | 78 390 | 40 391 | 40 392 | 00 393 | 38 394 | 44 395 | 44 396 | 44 397 | 54 398 | 48 399 | 34 400 | 00 401 | 78 402 | 44 403 | 44 404 | 44 405 | 78 406 | 48 407 | 44 408 | 00 409 | 38 410 | 44 411 | 40 412 | 38 413 | 04 414 | 44 415 | 38 416 | 00 417 | 7c 418 | 10 419 | 10 420 | 10 421 | 10 422 | 10 423 | 10 424 | 00 425 | 44 426 | 44 427 | 44 428 | 44 429 | 44 430 | 44 431 | 38 432 | 00 433 | 44 434 | 44 435 | 44 436 | 44 437 | 44 438 | 28 439 | 10 440 | 00 441 | 82 442 | 82 443 | 92 444 | 92 445 | 92 446 | 92 447 | 6c 448 | 00 449 | 44 450 | 44 451 | 28 452 | 10 453 | 28 454 | 44 455 | 44 456 | 00 457 | 44 458 | 44 459 | 28 460 | 10 461 | 10 462 | 10 463 | 10 464 | 00 465 | 7c 466 | 04 467 | 08 468 | 10 469 | 20 470 | 40 471 | 7c 472 | 00 473 | 1c 474 | 10 475 | 10 476 | 10 477 | 10 478 | 10 479 | 1c 480 | 00 481 | 20 482 | 20 483 | 10 484 | 10 485 | 10 486 | 08 487 | 08 488 | 00 489 | 70 490 | 10 491 | 10 492 | 10 493 | 10 494 | 10 495 | 70 496 | 00 497 | 10 498 | 28 499 | 44 500 | 00 501 | 00 502 | 00 503 | 00 504 | 00 505 | 00 506 | 00 507 | 00 508 | 00 509 | 00 510 | 00 511 | 00 512 | ff 513 | 20 514 | 10 515 | 00 516 | 00 517 | 00 518 | 00 519 | 00 520 | 00 521 | 00 522 | 00 523 | 38 524 | 04 525 | 3c 526 | 44 527 | 3c 528 | 00 529 | 40 530 | 40 531 | 78 532 | 44 533 | 44 534 | 44 535 | 78 536 | 00 537 | 00 538 | 00 539 | 38 540 | 44 541 | 40 542 | 44 543 | 38 544 | 00 545 | 04 546 | 04 547 | 3c 548 | 44 549 | 44 550 | 44 551 | 3c 552 | 00 553 | 00 554 | 00 555 | 38 556 | 44 557 | 7c 558 | 40 559 | 38 560 | 00 561 | 0c 562 | 10 563 | 3c 564 | 10 565 | 10 566 | 10 567 | 10 568 | 00 569 | 00 570 | 00 571 | 3c 572 | 44 573 | 44 574 | 3c 575 | 04 576 | 38 577 | 40 578 | 40 579 | 78 580 | 44 581 | 44 582 | 44 583 | 44 584 | 00 585 | 10 586 | 00 587 | 70 588 | 10 589 | 10 590 | 10 591 | 7c 592 | 00 593 | 04 594 | 00 595 | 1c 596 | 04 597 | 04 598 | 04 599 | 44 600 | 38 601 | 40 602 | 40 603 | 48 604 | 50 605 | 70 606 | 48 607 | 44 608 | 00 609 | 70 610 | 10 611 | 10 612 | 10 613 | 10 614 | 10 615 | 7c 616 | 00 617 | 00 618 | 00 619 | ec 620 | 92 621 | 92 622 | 82 623 | 82 624 | 00 625 | 00 626 | 00 627 | 78 628 | 44 629 | 44 630 | 44 631 | 44 632 | 00 633 | 00 634 | 00 635 | 38 636 | 44 637 | 44 638 | 44 639 | 38 640 | 00 641 | 00 642 | 00 643 | 78 644 | 44 645 | 44 646 | 78 647 | 40 648 | 40 649 | 00 650 | 00 651 | 3c 652 | 44 653 | 44 654 | 3c 655 | 04 656 | 04 657 | 00 658 | 00 659 | 4c 660 | 50 661 | 60 662 | 40 663 | 40 664 | 00 665 | 00 666 | 00 667 | 38 668 | 40 669 | 38 670 | 04 671 | 78 672 | 00 673 | 00 674 | 10 675 | 3c 676 | 10 677 | 10 678 | 10 679 | 0c 680 | 00 681 | 00 682 | 00 683 | 44 684 | 44 685 | 44 686 | 44 687 | 38 688 | 00 689 | 00 690 | 00 691 | 44 692 | 44 693 | 44 694 | 28 695 | 10 696 | 00 697 | 00 698 | 00 699 | 82 700 | 82 701 | 92 702 | 92 703 | 6c 704 | 00 705 | 00 706 | 00 707 | 44 708 | 28 709 | 10 710 | 28 711 | 44 712 | 00 713 | 00 714 | 00 715 | 44 716 | 44 717 | 44 718 | 3c 719 | 04 720 | 38 721 | 00 722 | 00 723 | 7c 724 | 08 725 | 10 726 | 20 727 | 7c 728 | 00 729 | 0c 730 | 10 731 | 10 732 | 20 733 | 10 734 | 10 735 | 0c 736 | 00 737 | 10 738 | 10 739 | 10 740 | 10 741 | 10 742 | 10 743 | 10 744 | 00 745 | 60 746 | 10 747 | 10 748 | 08 749 | 10 750 | 10 751 | 60 752 | 00 753 | 32 754 | 4c 755 | 00 756 | 00 757 | 00 758 | 00 759 | 00 760 | 00 761 | -------------------------------------------------------------------------------- /pkg/Assets/camera/common/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agg23/openfpga-Camera/937a50999e97a010e2b97e9a79b0d4c9d35a2cc6/pkg/Assets/camera/common/.gitkeep -------------------------------------------------------------------------------- /pkg/Cores/agg23.Camera/audio.json: -------------------------------------------------------------------------------- 1 | { 2 | "audio": { 3 | "magic": "APF_VER_1" 4 | } 5 | } -------------------------------------------------------------------------------- /pkg/Cores/agg23.Camera/core.json: -------------------------------------------------------------------------------- 1 | { 2 | "core": { 3 | "magic": "APF_VER_1", 4 | "metadata": { 5 | "platform_ids": [ 6 | "camera" 7 | ], 8 | "shortname": "Camera", 9 | "description": "Game Boy Camera with local ROMs", 10 | "author": "agg23", 11 | "url": "https://github.com/agg23/openfpga-Camera", 12 | "version": "1.0.0-beta", 13 | "date_release": "2024-08-01" 14 | }, 15 | "framework": { 16 | "target_product": "Analogue Pocket", 17 | "version_required": "1.1", 18 | "sleep_supported": false, 19 | "dock": { 20 | "supported": true, 21 | "analog_output": false 22 | }, 23 | "hardware": { 24 | "link_port": true, 25 | "cartridge_adapter": 0 26 | } 27 | }, 28 | "cores": [ 29 | { 30 | "name": "default", 31 | "id": 0, 32 | "filename": "camera.rev" 33 | } 34 | ] 35 | } 36 | } -------------------------------------------------------------------------------- /pkg/Cores/agg23.Camera/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "magic": "APF_VER_1", 4 | "data_slots": [ 5 | { 6 | "name": "Cartridge", 7 | "id": 1, 8 | "required": true, 9 | "filename": "photo.gbc", 10 | "parameters": 0, 11 | "extensions": [ 12 | "gbc", 13 | "gb" 14 | ], 15 | "address": "0x10000000" 16 | }, 17 | { 18 | "name": "GBC BIOS", 19 | "filename": "gbc_bios.bin", 20 | "id": 4, 21 | "required": true, 22 | "parameters": 136, 23 | "nonvolatile": false, 24 | "extensions": [ 25 | "bin" 26 | ], 27 | "size_exact": 2304, 28 | "size_maximum": 2304, 29 | "address": "0x10000000" 30 | }, 31 | { 32 | "name": "SRAM Backup", 33 | "id": 5, 34 | "parameters": 0, 35 | "extensions": [ 36 | "sav" 37 | ], 38 | "address": "0x20000000" 39 | } 40 | ] 41 | } 42 | } -------------------------------------------------------------------------------- /pkg/Cores/agg23.Camera/icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agg23/openfpga-Camera/937a50999e97a010e2b97e9a79b0d4c9d35a2cc6/pkg/Cores/agg23.Camera/icon.bin -------------------------------------------------------------------------------- /pkg/Cores/agg23.Camera/info.txt: -------------------------------------------------------------------------------- 1 | Game Boy Camera Access Tool 2 | 3 | Use your physical Game Boy Camera with custom ROMs stored on your Analogue Pocket's SD card. Press left and right trigger simultaneously to dump SRAM to your SD card. 4 | 5 | Created by agg23. Port to openFPGA by budude2, with original core created by MiSTer community. -------------------------------------------------------------------------------- /pkg/Cores/agg23.Camera/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "magic": "APF_VER_1", 4 | "controllers": [ 5 | { 6 | "type": "default", 7 | "mappings": [ 8 | { 9 | "id": 0, 10 | "name": "A Button", 11 | "key": "pad_btn_a" 12 | }, 13 | { 14 | "id": 1, 15 | "name": "B Button", 16 | "key": "pad_btn_b" 17 | }, 18 | { 19 | "id": 2, 20 | "name": "Start", 21 | "key": "pad_btn_start" 22 | }, 23 | { 24 | "id": 3, 25 | "name": "Select", 26 | "key": "pad_btn_select" 27 | }, 28 | { 29 | "id": 10, 30 | "name": "Dump Button 1", 31 | "key": "pad_trig_l" 32 | }, 33 | { 34 | "id": 11, 35 | "name": "Dump Button 2", 36 | "key": "pad_trig_r" 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | } -------------------------------------------------------------------------------- /pkg/Cores/agg23.Camera/interact.json: -------------------------------------------------------------------------------- 1 | { 2 | "interact": { 3 | "magic": "APF_VER_1", 4 | "variables": [ 5 | { 6 | "name": "Dump SRAM", 7 | "id": 10, 8 | "type": "action", 9 | "enabled": true, 10 | "address": "0xF0000004", 11 | "value": 1 12 | } 13 | ], 14 | "messages": [] 15 | } 16 | } -------------------------------------------------------------------------------- /pkg/Cores/agg23.Camera/variants.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "magic": "APF_VER_1", 4 | "variant_list": [] 5 | } 6 | } -------------------------------------------------------------------------------- /pkg/Cores/agg23.Camera/video.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": { 3 | "magic": "APF_VER_1", 4 | "scaler_modes": [ 5 | { 6 | "width": 160, 7 | "height": 144, 8 | "aspect_w": 10, 9 | "aspect_h": 9, 10 | "rotation": 0, 11 | "mirror": 0 12 | } 13 | ], 14 | "display_modes": [ 15 | { 16 | "id": "0x10" 17 | }, 18 | { 19 | "id": "0x20" 20 | }, 21 | { 22 | "id": "0x21" 23 | }, 24 | { 25 | "id": "0x22" 26 | }, 27 | { 28 | "id": "0x23" 29 | }, 30 | { 31 | "id": "0x30" 32 | }, 33 | { 34 | "id": "0x31" 35 | }, 36 | { 37 | "id": "0x32" 38 | }, 39 | { 40 | "id": "0x40" 41 | }, 42 | { 43 | "id": "0xE0" 44 | } 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /pkg/Platforms/camera.json: -------------------------------------------------------------------------------- 1 | { 2 | "platform": { 3 | "category": "Utility", 4 | "name": "Camera", 5 | "manufacturer": "agg23", 6 | "year": 2024 7 | } 8 | } -------------------------------------------------------------------------------- /pkg/agg23.Camera-Internal0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agg23/openfpga-Camera/937a50999e97a010e2b97e9a79b0d4c9d35a2cc6/pkg/agg23.Camera-Internal0.zip -------------------------------------------------------------------------------- /platform/pocket/apf.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "apf_top.v"] 2 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "common.v"] 3 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "io_bridge_peripheral.v"] 4 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "io_pad_controller.v"] 5 | set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) "apf_constraints.sdc"] 6 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "mf_ddio_bidir_12.qip"] 7 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "mf_datatable.qip"] 8 | -------------------------------------------------------------------------------- /platform/pocket/apf_constraints.sdc: -------------------------------------------------------------------------------- 1 | # 2 | # APF constraints 3 | # Do not edit this file. 4 | # 5 | # Add your own constraints in the \core_constraints.sdc in the core directory, which will also be loaded. 6 | 7 | create_clock -name clk_74a -period 13.468 [get_ports clk_74a] 8 | create_clock -name clk_74b -period 13.468 [get_ports clk_74b] 9 | create_clock -name bridge_spiclk -period 13.468 [get_ports bridge_spiclk] 10 | 11 | # autogenerate PLL clock names for use down below 12 | derive_pll_clocks 13 | -------------------------------------------------------------------------------- /platform/pocket/build_cdf.tcl: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # SPDX-License-Identifier: CC0-1.0 3 | # SPDX-FileType: SOURCE 4 | # SPDX-FileCopyrightText: (c) 2022, OpenGateware authors and contributors 5 | # ============================================================================== 6 | # @file: build_cd.h 7 | # @brief: Generate a JTAG Chain Description File. 8 | # Create a .cdf file to be used with Quartus Prime Programmer 9 | # ============================================================================== 10 | proc createChainDescriptionFile {revision device outpath project_name} { 11 | set outputFileName "$project_name.cdf" 12 | set outputFile [open $outputFileName "w"] 13 | 14 | puts $outputFile "JedecChain;" 15 | puts $outputFile " FileRevision(JESD32A);" 16 | puts $outputFile " DefaultMfr(6E);" 17 | puts $outputFile "" 18 | puts $outputFile " P ActionCode(Cfg)" 19 | puts $outputFile " Device PartName($device) Path(\"$outpath/\") File(\"$revision.sof\") MfrSpec(OpMask(1));" 20 | puts $outputFile "ChainEnd;" 21 | puts $outputFile "" 22 | puts $outputFile "AlteraBegin;" 23 | puts $outputFile " ChainType(JTAG);" 24 | puts $outputFile "AlteraEnd;" 25 | } 26 | 27 | set project_name [lindex $quartus(args) 1] 28 | set revision [lindex $quartus(args) 2] 29 | 30 | if {[project_exists $project_name]} { 31 | if {[string equal "" $revision]} { 32 | project_open $project_name -revision [get_current_revision $project_name] 33 | } else { 34 | project_open $project_name -revision $revision 35 | } 36 | } else { 37 | post_message -type error "Project $project_name does not exist" 38 | exit 39 | } 40 | 41 | set device [get_global_assignment -name DEVICE] 42 | set outpath [get_global_assignment -name PROJECT_OUTPUT_DIRECTORY] 43 | 44 | if [is_project_open] { 45 | project_close 46 | } 47 | 48 | createChainDescriptionFile $revision $device $outpath $project_name 49 | -------------------------------------------------------------------------------- /platform/pocket/common.v: -------------------------------------------------------------------------------- 1 | // Software License Agreement 2 | 3 | // The software supplied herewith by Analogue Enterprises Limited (the "Company”), 4 | // the Analogue Pocket Framework (“APF”), is provided and licensed to you, the 5 | // Company's customer, solely for use in designing, testing and creating 6 | // applications for use with Company's Products or Services. The software is 7 | // owned by the Company and/or its licensors, and is protected under applicable 8 | // laws, including, but not limited to, U.S. copyright law. All rights are 9 | // reserved. By using the APF code you are agreeing to the terms of the End User 10 | // License Agreement (“EULA”) located at [https://www.analogue.link/pocket-eula] 11 | // and incorporated herein by reference. To the extent any use of the APF requires 12 | // application of the MIT License or the GNU General Public License and terms of 13 | // this APF Software License Agreement and EULA are inconsistent with such license, 14 | // the applicable terms of the MIT License or the GNU General Public License, as 15 | // applicable, will prevail. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS-IS" AND WE EXPRESSLY DISCLAIM ANY IMPLIED 18 | // WARRANTIES TO THE FULLEST EXTENT PROVIDED BY LAW, INCLUDING BUT NOT LIMITED TO, 19 | // ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR 20 | // NON-INFRINGEMENT. TO THE EXTENT APPLICABLE LAWS PROHIBIT TERMS OF USE FROM 21 | // DISCLAIMING ANY IMPLIED WARRANTY, SUCH IMPLIED WARRANTY SHALL BE LIMITED TO THE 22 | // MINIMUM WARRANTY PERIOD REQUIRED BY LAW, AND IF NO SUCH PERIOD IS REQUIRED, 23 | // THEN THIRTY (30) DAYS FROM FIRST USE OF THE SOFTWARE. WE CANNOT GUARANTEE AND 24 | // DO NOT PROMISE ANY SPECIFIC RESULTS FROM USE OF THE SOFTWARE. WITHOUT LIMITING 25 | // THE FOREGOING, WE DO NOT WARRANT THAT THE SOFTWARE WILL BE UNINTERRUPTED OR 26 | // ERROR-FREE. IN NO EVENT WILL WE BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY 27 | // INDIRECT, CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, SPECIAL OR PUNITIVE DAMAGES, 28 | // INCLUDING BUT NOT LIMITED TO, LOST PROFITS ARISING OUT OF YOUR USE, OR 29 | // INABILITY TO USE, THE SOFTWARE, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY 30 | // OF SUCH DAMAGES. UNDER NO CIRCUMSTANCES SHALL OUR LIABILITY TO YOU FOR ANY 31 | // CLAIM OR CAUSE OF ACTION WHATSOEVER, AND REGARDLESS OF THE FORM OF THE ACTION, 32 | // WHETHER ARISING IN CONTRACT, TORT OR OTHERWISE, EXCEED THE AMOUNT PAID BY YOU 33 | // TO US, IF ANY, DURING THE 90 DAY PERIOD IMMEDIATELY PRECEDING THE DATE ON WHICH 34 | // YOU FIRST ASSERT ANY SUCH CLAIM. THE FOREGOING LIMITATIONS SHALL APPLY TO THE 35 | // FULLEST EXTENT PERMITTED BY APPLICABLE LAW. 36 | // 37 | // 2-stage synchronizer 38 | // 39 | module synch_2 #(parameter WIDTH = 1) ( 40 | input wire [WIDTH-1:0] i, // input signal 41 | output reg [WIDTH-1:0] o, // synchronized output 42 | input wire clk, // clock to synchronize on 43 | output wire rise, // one-cycle rising edge pulse 44 | output wire fall // one-cycle falling edge pulse 45 | ); 46 | 47 | reg [WIDTH-1:0] stage_1; 48 | reg [WIDTH-1:0] stage_2; 49 | reg [WIDTH-1:0] stage_3; 50 | 51 | assign rise = (WIDTH == 1) ? (o & ~stage_2) : 1'b0; 52 | assign fall = (WIDTH == 1) ? (~o & stage_2) : 1'b0; 53 | always @(posedge clk) 54 | {stage_2, o, stage_1} <= {o, stage_1, i}; 55 | 56 | endmodule 57 | 58 | 59 | // 60 | // 3-stage synchronizer 61 | // 62 | module synch_3 #(parameter WIDTH = 1) ( 63 | input wire [WIDTH-1:0] i, // input signal 64 | output reg [WIDTH-1:0] o, // synchronized output 65 | input wire clk, // clock to synchronize on 66 | output wire rise, // one-cycle rising edge pulse 67 | output wire fall // one-cycle falling edge pulse 68 | ); 69 | 70 | reg [WIDTH-1:0] stage_1; 71 | reg [WIDTH-1:0] stage_2; 72 | reg [WIDTH-1:0] stage_3; 73 | 74 | assign rise = (WIDTH == 1) ? (o & ~stage_3) : 1'b0; 75 | assign fall = (WIDTH == 1) ? (~o & stage_3) : 1'b0; 76 | always @(posedge clk) 77 | {stage_3, o, stage_2, stage_1} <= {o, stage_2, stage_1, i}; 78 | 79 | endmodule 80 | 81 | 82 | module bram_block_dp #( 83 | parameter DATA = 32, 84 | parameter ADDR = 7 85 | ) ( 86 | input wire a_clk, 87 | input wire a_wr, 88 | input wire [ADDR-1:0] a_addr, 89 | input wire [DATA-1:0] a_din, 90 | output reg [DATA-1:0] a_dout, 91 | 92 | input wire b_clk, 93 | input wire b_wr, 94 | input wire [ADDR-1:0] b_addr, 95 | input wire [DATA-1:0] b_din, 96 | output reg [DATA-1:0] b_dout 97 | ); 98 | 99 | reg [DATA-1:0] mem [(2**ADDR)-1:0]; 100 | 101 | always @(posedge a_clk) begin 102 | if(a_wr) begin 103 | a_dout <= a_din; 104 | mem[a_addr] <= a_din; 105 | end else 106 | a_dout <= mem[a_addr]; 107 | end 108 | 109 | always @(posedge b_clk) begin 110 | if(b_wr) begin 111 | b_dout <= b_din; 112 | mem[b_addr] <= b_din; 113 | end else 114 | b_dout <= mem[b_addr]; 115 | end 116 | 117 | endmodule 118 | 119 | 120 | module bram_block_dp_nonstd #( 121 | parameter DATA = 32, 122 | parameter ADDR = 7, 123 | parameter DEPTH = 128 124 | ) ( 125 | input wire a_clk, 126 | input wire a_wr, 127 | input wire [ADDR-1:0] a_addr, 128 | input wire [DATA-1:0] a_din, 129 | output reg [DATA-1:0] a_dout, 130 | 131 | input wire b_clk, 132 | input wire b_wr, 133 | input wire [ADDR-1:0] b_addr, 134 | input wire [DATA-1:0] b_din, 135 | output reg [DATA-1:0] b_dout 136 | ); 137 | 138 | reg [DATA-1:0] mem [DEPTH-1:0]; 139 | 140 | always @(posedge a_clk) begin 141 | if(a_wr) begin 142 | a_dout <= a_din; 143 | mem[a_addr] <= a_din; 144 | end else 145 | a_dout <= mem[a_addr]; 146 | end 147 | 148 | always @(posedge b_clk) begin 149 | if(b_wr) begin 150 | b_dout <= b_din; 151 | mem[b_addr] <= b_din; 152 | end else 153 | b_dout <= mem[b_addr]; 154 | end 155 | 156 | endmodule 157 | -------------------------------------------------------------------------------- /platform/pocket/mf_datatable.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name IP_TOOL_NAME "RAM: 2-PORT" 2 | set_global_assignment -name IP_TOOL_VERSION "18.1" 3 | set_global_assignment -name IP_GENERATED_DEVICE_FAMILY "{Cyclone V}" 4 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "mf_datatable.v"] 5 | -------------------------------------------------------------------------------- /platform/pocket/mf_ddio_bidir_12.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name IP_TOOL_NAME "ALTDDIO_BIDIR" 2 | set_global_assignment -name IP_TOOL_VERSION "18.1" 3 | set_global_assignment -name IP_GENERATED_DEVICE_FAMILY "{Cyclone V}" 4 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "mf_ddio_bidir_12.v"] 5 | set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "mf_ddio_bidir_12.ppf"] 6 | -------------------------------------------------------------------------------- /platform/pocket/mf_ddio_bidir_12.v: -------------------------------------------------------------------------------- 1 | // megafunction wizard: %ALTDDIO_BIDIR% 2 | // GENERATION: STANDARD 3 | // VERSION: WM1.0 4 | // MODULE: ALTDDIO_BIDIR 5 | 6 | // ============================================================ 7 | // File Name: mf_ddio_bidir_12.v 8 | // Megafunction Name(s): 9 | // ALTDDIO_BIDIR 10 | // 11 | // Simulation Library Files(s): 12 | // altera_mf 13 | // ============================================================ 14 | // ************************************************************ 15 | // THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! 16 | // 17 | // 18.1.1 Build 646 04/11/2019 SJ Lite Edition 18 | // ************************************************************ 19 | 20 | 21 | //Copyright (C) 2019 Intel Corporation. All rights reserved. 22 | //Your use of Intel Corporation's design tools, logic functions 23 | //and other software and tools, and any partner logic 24 | //functions, and any output files from any of the foregoing 25 | //(including device programming or simulation files), and any 26 | //associated documentation or information are expressly subject 27 | //to the terms and conditions of the Intel Program License 28 | //Subscription Agreement, the Intel Quartus Prime License Agreement, 29 | //the Intel FPGA IP License Agreement, or other applicable license 30 | //agreement, including, without limitation, that your use is for 31 | //the sole purpose of programming logic devices manufactured by 32 | //Intel and sold by Intel or its authorized distributors. Please 33 | //refer to the applicable agreement for further details, at 34 | //https://fpgasoftware.intel.com/eula. 35 | 36 | 37 | // synopsys translate_off 38 | `timescale 1 ps / 1 ps 39 | // synopsys translate_on 40 | module mf_ddio_bidir_12 ( 41 | datain_h, 42 | datain_l, 43 | inclock, 44 | oe, 45 | outclock, 46 | dataout_h, 47 | dataout_l, 48 | padio); 49 | 50 | input [11:0] datain_h; 51 | input [11:0] datain_l; 52 | input inclock; 53 | input oe; 54 | input outclock; 55 | output [11:0] dataout_h; 56 | output [11:0] dataout_l; 57 | inout [11:0] padio; 58 | 59 | wire [11:0] sub_wire0; 60 | wire [11:0] sub_wire1; 61 | wire [11:0] dataout_h = sub_wire0[11:0]; 62 | wire [11:0] dataout_l = sub_wire1[11:0]; 63 | 64 | altddio_bidir ALTDDIO_BIDIR_component ( 65 | .datain_h (datain_h), 66 | .datain_l (datain_l), 67 | .inclock (inclock), 68 | .oe (oe), 69 | .outclock (outclock), 70 | .padio (padio), 71 | .dataout_h (sub_wire0), 72 | .dataout_l (sub_wire1), 73 | .aclr (1'b0), 74 | .aset (1'b0), 75 | .combout (), 76 | .dqsundelayedout (), 77 | .inclocken (1'b1), 78 | .oe_out (), 79 | .outclocken (1'b1), 80 | .sclr (1'b0), 81 | .sset (1'b0)); 82 | defparam 83 | ALTDDIO_BIDIR_component.extend_oe_disable = "OFF", 84 | ALTDDIO_BIDIR_component.implement_input_in_lcell = "OFF", 85 | ALTDDIO_BIDIR_component.intended_device_family = "Cyclone V", 86 | ALTDDIO_BIDIR_component.invert_output = "OFF", 87 | ALTDDIO_BIDIR_component.lpm_hint = "UNUSED", 88 | ALTDDIO_BIDIR_component.lpm_type = "altddio_bidir", 89 | ALTDDIO_BIDIR_component.oe_reg = "UNREGISTERED", 90 | ALTDDIO_BIDIR_component.power_up_high = "OFF", 91 | ALTDDIO_BIDIR_component.width = 12; 92 | 93 | 94 | endmodule 95 | 96 | // ============================================================ 97 | // CNX file retrieval info 98 | // ============================================================ 99 | // Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all 100 | // Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone V" 101 | // Retrieval info: CONSTANT: EXTEND_OE_DISABLE STRING "OFF" 102 | // Retrieval info: CONSTANT: IMPLEMENT_INPUT_IN_LCELL STRING "OFF" 103 | // Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone V" 104 | // Retrieval info: CONSTANT: INVERT_OUTPUT STRING "OFF" 105 | // Retrieval info: CONSTANT: LPM_HINT STRING "UNUSED" 106 | // Retrieval info: CONSTANT: LPM_TYPE STRING "altddio_bidir" 107 | // Retrieval info: CONSTANT: OE_REG STRING "UNREGISTERED" 108 | // Retrieval info: CONSTANT: POWER_UP_HIGH STRING "OFF" 109 | // Retrieval info: CONSTANT: WIDTH NUMERIC "12" 110 | // Retrieval info: USED_PORT: datain_h 0 0 12 0 INPUT NODEFVAL "datain_h[11..0]" 111 | // Retrieval info: CONNECT: @datain_h 0 0 12 0 datain_h 0 0 12 0 112 | // Retrieval info: USED_PORT: datain_l 0 0 12 0 INPUT NODEFVAL "datain_l[11..0]" 113 | // Retrieval info: CONNECT: @datain_l 0 0 12 0 datain_l 0 0 12 0 114 | // Retrieval info: USED_PORT: dataout_h 0 0 12 0 OUTPUT NODEFVAL "dataout_h[11..0]" 115 | // Retrieval info: CONNECT: dataout_h 0 0 12 0 @dataout_h 0 0 12 0 116 | // Retrieval info: USED_PORT: dataout_l 0 0 12 0 OUTPUT NODEFVAL "dataout_l[11..0]" 117 | // Retrieval info: CONNECT: dataout_l 0 0 12 0 @dataout_l 0 0 12 0 118 | // Retrieval info: USED_PORT: inclock 0 0 0 0 INPUT_CLK_EXT NODEFVAL "inclock" 119 | // Retrieval info: CONNECT: @inclock 0 0 0 0 inclock 0 0 0 0 120 | // Retrieval info: USED_PORT: oe 0 0 0 0 INPUT NODEFVAL "oe" 121 | // Retrieval info: CONNECT: @oe 0 0 0 0 oe 0 0 0 0 122 | // Retrieval info: USED_PORT: outclock 0 0 0 0 INPUT_CLK_EXT NODEFVAL "outclock" 123 | // Retrieval info: CONNECT: @outclock 0 0 0 0 outclock 0 0 0 0 124 | // Retrieval info: USED_PORT: padio 0 0 12 0 BIDIR NODEFVAL "padio[11..0]" 125 | // Retrieval info: CONNECT: padio 0 0 12 0 @padio 0 0 12 0 126 | // Retrieval info: GEN_FILE: TYPE_NORMAL mf_ddio_bidir_12.v TRUE FALSE 127 | // Retrieval info: GEN_FILE: TYPE_NORMAL mf_ddio_bidir_12.qip TRUE FALSE 128 | // Retrieval info: GEN_FILE: TYPE_NORMAL mf_ddio_bidir_12.bsf FALSE TRUE 129 | // Retrieval info: GEN_FILE: TYPE_NORMAL mf_ddio_bidir_12_inst.v FALSE TRUE 130 | // Retrieval info: GEN_FILE: TYPE_NORMAL mf_ddio_bidir_12_bb.v FALSE TRUE 131 | // Retrieval info: GEN_FILE: TYPE_NORMAL mf_ddio_bidir_12.inc FALSE TRUE 132 | // Retrieval info: GEN_FILE: TYPE_NORMAL mf_ddio_bidir_12.cmp FALSE TRUE 133 | // Retrieval info: GEN_FILE: TYPE_NORMAL mf_ddio_bidir_12.ppf TRUE FALSE 134 | // Retrieval info: LIB_FILE: altera_mf 135 | -------------------------------------------------------------------------------- /projects/camera_pocket.qip: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # Quartus Prime Platform Specific Modules File 3 | # Generated by OpenGateware - Gateman CLI v0.1.0 4 | # ============================================================================== 5 | # A single file that contains paths to platform specific third-party modules. 6 | # Quartus will use this file but won't edit it. 7 | # You need to edit it manually to add/remove files here. 8 | # ============================================================================== 9 | 10 | -------------------------------------------------------------------------------- /projects/camera_pocket.qpf: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # Quartus Prime Project File 3 | # Generated by OpenGateware - Gateman CLI v0.1.0 4 | # ============================================================================== 5 | 6 | QUARTUS_VERSION = "18.1" 7 | DATE = "11:27:04 February 28, 2023" 8 | 9 | # Revisions 10 | 11 | PROJECT_REVISION = "camera_pocket" 12 | -------------------------------------------------------------------------------- /projects/camera_pocket.qsf: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # Quartus Prime Settings File 3 | # Generated by OpenGateware - Gateman CLI v0.1.0 4 | # ============================================================================== 5 | # WARNING: DO NOT ADD FILES TO THE PROJECT VIA THE QUARTUS IDE! 6 | # Add them manually to camera_pocket.qip or Quartus will overwrite this file. 7 | # ============================================================================== 8 | 9 | # ============================================================================== 10 | # Project-Wide Assignments 11 | # ============================================================================== 12 | set_global_assignment -name ORIGINAL_QUARTUS_VERSION 18.1.1 13 | set_global_assignment -name LAST_QUARTUS_VERSION "21.1.1 Lite Edition" 14 | set_global_assignment -name TOP_LEVEL_ENTITY apf_top 15 | set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top 16 | set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top 17 | set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top 18 | set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files 19 | 20 | # ============================================================================== 21 | # Compiler Assignments 22 | # ============================================================================== 23 | set_global_assignment -name NUM_PARALLEL_PROCESSORS 6 24 | set_global_assignment -name OPTIMIZATION_MODE "HIGH PERFORMANCE EFFORT" 25 | set_global_assignment -name SAVE_DISK_SPACE OFF 26 | set_global_assignment -name SEED 1 27 | set_global_assignment -name SMART_RECOMPILE ON 28 | 29 | # ============================================================================== 30 | # Analysis & Synthesis Assignments 31 | # ============================================================================== 32 | set_global_assignment -name ADV_NETLIST_OPT_SYNTH_WYSIWYG_REMAP ON 33 | set_global_assignment -name MUX_RESTRUCTURE OFF 34 | set_global_assignment -name OPTIMIZATION_TECHNIQUE SPEED 35 | set_global_assignment -name PRE_MAPPING_RESYNTHESIS ON 36 | set_global_assignment -name SAFE_STATE_MACHINE ON 37 | set_global_assignment -name SYNTH_PROTECT_SDC_CONSTRAINT ON 38 | 39 | # ============================================================================== 40 | # Fitter Assignments 41 | # ============================================================================== 42 | set_global_assignment -name ACTIVE_SERIAL_CLOCK FREQ_100MHZ 43 | set_global_assignment -name CRC_ERROR_OPEN_DRAIN ON 44 | set_global_assignment -name ECO_OPTIMIZE_TIMING ON 45 | set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 256 46 | set_global_assignment -name FITTER_EFFORT "AUTO FIT" 47 | set_global_assignment -name PERIPHERY_TO_CORE_PLACEMENT_AND_ROUTING_OPTIMIZATION ON 48 | set_global_assignment -name PHYSICAL_SYNTHESIS_ASYNCHRONOUS_SIGNAL_PIPELINING ON 49 | set_global_assignment -name PHYSICAL_SYNTHESIS_COMBO_LOGIC ON 50 | set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_DUPLICATION ON 51 | set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_RETIMING ON 52 | set_global_assignment -name ROUTER_CLOCKING_TOPOLOGY_ANALYSIS ON 53 | set_global_assignment -name ROUTER_LCELL_INSERTION_AND_LOGIC_DUPLICATION ON 54 | set_global_assignment -name STRATIXV_CONFIGURATION_SCHEME "PASSIVE SERIAL" 55 | 56 | # ============================================================================== 57 | # Platform/Core Assignments 58 | # ============================================================================== 59 | source ../platform/pocket/pocket.tcl 60 | set_global_assignment -name QIP_FILE camera_pocket.qip 61 | set_global_assignment -name SDC_FILE camera_pocket.sdc 62 | set_global_assignment -name QIP_FILE ../rtl/camera.qip 63 | 64 | # ============================================================================== 65 | 66 | 67 | set_global_assignment -name TIMING_ANALYZER_MULTICORNER_ANALYSIS ON 68 | set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 69 | set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 70 | set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" 71 | set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" 72 | set_global_assignment -name ENABLE_SIGNALTAP ON 73 | set_global_assignment -name USE_SIGNALTAP_FILE stp1.stp 74 | set_global_assignment -name SIGNALTAP_FILE stp1.stp 75 | set_global_assignment -name SLD_FILE db/stp1_auto_stripped.stp 76 | set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top -------------------------------------------------------------------------------- /projects/camera_pocket.sdc: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # Quartus Prime Synopsys Design Constraint File 3 | # Generated by OpenGateware - Gateman CLI v0.1.0 4 | # ============================================================================== 5 | # pocket SDC settings 6 | # Users are recommended to modify this file to match users logic. 7 | # Put your clock groups in here as well as any net assignments. 8 | # ============================================================================== 9 | 10 | # ============================================================================== 11 | # Time Information 12 | # ============================================================================== 13 | 14 | # ============================================================================== 15 | # Create Clock 16 | # ============================================================================== 17 | 18 | # ============================================================================== 19 | # Create Generated Clock 20 | # ============================================================================== 21 | 22 | # ============================================================================== 23 | # Set Clock Latency 24 | # ============================================================================== 25 | 26 | # ============================================================================== 27 | # Set Clock Uncertainty 28 | # ============================================================================== 29 | 30 | # ============================================================================== 31 | # Set Input Delay 32 | # ============================================================================== 33 | 34 | # ============================================================================== 35 | # Set Output Delay 36 | # ============================================================================== 37 | 38 | # ============================================================================== 39 | # Set Clock Groups 40 | # ============================================================================== 41 | 42 | # ============================================================================== 43 | # Set False Path 44 | # ============================================================================== 45 | 46 | # ============================================================================== 47 | # Set Multicycle Path 48 | # ============================================================================== 49 | 50 | # ============================================================================== 51 | # Set Maximum Delay 52 | # ============================================================================== 53 | 54 | # ============================================================================== 55 | # Set Minimum Delay 56 | # ============================================================================== 57 | 58 | # ============================================================================== 59 | # Set Input Transition 60 | # ============================================================================== 61 | 62 | -------------------------------------------------------------------------------- /rtl/camera.qip: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # Quartus Prime QIP Index File 3 | # ============================================================================== 4 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "file/file.qip"] 5 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "gb/gb.qip"] 6 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "ui/ui.qip"] 7 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "cart_verifier.sv"] -------------------------------------------------------------------------------- /rtl/cart_verifier.sv: -------------------------------------------------------------------------------- 1 | module cart_verifier ( 2 | input wire clk_6_7, 3 | 4 | input wire verification_req, 5 | 6 | input wire [7:0] cart_data, 7 | output reg cart_rd, 8 | output reg [15:0] cart_addr, 9 | 10 | output wire verifying, 11 | output reg verification_complete, 12 | output reg verification_passed 13 | ); 14 | 15 | localparam CAMERA_TEXT_LENGTH = 13; 16 | reg [7:0] camera_text[CAMERA_TEXT_LENGTH] = '{ 17 | "G", 18 | "A", 19 | "M", 20 | "E", 21 | "B", 22 | "O", 23 | "Y", 24 | "C", 25 | "A", 26 | "M", 27 | "E", 28 | "R", 29 | "A" 30 | }; 31 | 32 | reg [2:0] read_delay; 33 | reg [3:0] address_offset; 34 | 35 | localparam STATE_NONE = 0; 36 | localparam STATE_READ = 1; 37 | localparam STATE_WAIT_READ = 2; 38 | localparam STATE_SUCCESS = 3; 39 | localparam STATE_ERROR = 4; 40 | 41 | reg [7:0] state = STATE_NONE; 42 | 43 | assign verifying = state == STATE_READ || state == STATE_WAIT_READ; 44 | 45 | always @(posedge clk_6_7) begin 46 | case (state) 47 | STATE_NONE: begin 48 | verification_complete <= 0; 49 | verification_passed <= 0; 50 | cart_rd <= 0; 51 | cart_addr <= 0; 52 | 53 | if (verification_req) begin 54 | state <= STATE_READ; 55 | end 56 | end 57 | STATE_READ: begin 58 | state <= STATE_WAIT_READ; 59 | 60 | cart_addr <= 16'h134 + {12'h0, address_offset}; 61 | cart_rd <= 1; 62 | 63 | read_delay <= 3'h7; 64 | end 65 | STATE_WAIT_READ: begin 66 | read_delay <= read_delay - 3'h1; 67 | 68 | if (read_delay == 0) begin 69 | address_offset <= address_offset + 4'h1; 70 | 71 | if (cart_data == camera_text[address_offset]) begin 72 | // Continue processing 73 | if (address_offset == CAMERA_TEXT_LENGTH - 1) begin 74 | state <= STATE_SUCCESS; 75 | end else begin 76 | state <= STATE_READ; 77 | end 78 | end else begin 79 | // Failed to match 80 | state <= STATE_ERROR; 81 | end 82 | end 83 | end 84 | STATE_SUCCESS: begin 85 | verification_complete <= 1; 86 | verification_passed <= 1; 87 | 88 | cart_rd <= 0; 89 | end 90 | STATE_ERROR: begin 91 | verification_complete <= 1; 92 | 93 | cart_rd <= 0; 94 | end 95 | endcase 96 | end 97 | 98 | endmodule 99 | -------------------------------------------------------------------------------- /rtl/file/double_bcd.sv: -------------------------------------------------------------------------------- 1 | module double_bcd( 2 | input wire [6:0] binary, // Binary input (0 to 99) - 7 bits to represent 99 3 | output reg [7:0] bcd // BCD output - 2 digits, each 4 bits 4 | ); 5 | reg [3:0] tens; 6 | reg [3:0] units; 7 | integer i; 8 | 9 | always_comb begin 10 | // Initialize the BCD digits 11 | tens = 4'b0000; 12 | units = 4'b0000; 13 | 14 | // Double Dabble algorithm 15 | for (i = 6; i >= 0; i = i - 1) begin 16 | // Add 3 to columns >= 5 17 | if (tens >= 5) begin 18 | tens = tens + 3; 19 | end 20 | if (units >= 5) begin 21 | units = units + 3; 22 | end 23 | 24 | // Shift left one bit 25 | tens = tens << 1; 26 | tens[0] = units[3]; 27 | units = units << 1; 28 | units[0] = binary[i]; 29 | end 30 | 31 | // Combine the two BCD digits into the output 32 | bcd = {tens, units}; 33 | end 34 | endmodule -------------------------------------------------------------------------------- /rtl/file/file.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "double_bcd.sv"] 2 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "file_controller.sv"] 3 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "open_file_struct.sv"] 4 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "save_dumper.sv"] 5 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "write_path.sv"] 6 | -------------------------------------------------------------------------------- /rtl/file/file_controller.sv: -------------------------------------------------------------------------------- 1 | module file_controller ( 2 | input wire clk, 3 | input wire reset, 4 | 5 | input wire dump_sram, 6 | output wire dumping, 7 | 8 | output wire [7:0] save_index_bcd, 9 | output reg no_slots_error, 10 | 11 | // input wire bridge_rd, 12 | input wire [31:0] bridge_8bit_addr, 13 | output wire [ 7:0] bridge_8bit_rd_data, 14 | 15 | output reg target_dataslot_write, 16 | output reg target_dataslot_openfile, 17 | output wire [15:0] target_dataslot_id, 18 | output wire [31:0] target_dataslot_slotoffset, 19 | output wire [31:0] target_dataslot_bridgeaddr, 20 | output wire [31:0] target_dataslot_length, 21 | 22 | input wire target_dataslot_ack, 23 | input wire target_dataslot_done, 24 | input wire [2:0] target_dataslot_err 25 | ); 26 | assign target_dataslot_id = 5; 27 | assign target_dataslot_slotoffset = 0; 28 | assign target_dataslot_bridgeaddr = use_path ? 32'h3000_0000 : 32'h2000_0000; 29 | // TODO: Grab actual size from cart 30 | assign target_dataslot_length = 32'h2_0000; 31 | 32 | assign dumping = state != STATE_NONE; 33 | 34 | // If set, use write_path 35 | reg use_path = 0; 36 | 37 | reg [7:0] current_save_index = 0; 38 | 39 | localparam STATE_NONE = 0; 40 | localparam STATE_START_OPENING_FILE = 1; 41 | localparam STATE_OPENING_FILE = 2; 42 | localparam STATE_EVALUATE_FILE = 3; 43 | localparam STATE_START_DUMP = 4; 44 | localparam STATE_DUMPING = 5; 45 | 46 | reg [2:0] state = STATE_NONE; 47 | 48 | wire [7:0] path_character; 49 | 50 | write_path write_path ( 51 | .clk(clk), 52 | 53 | .save_index(current_save_index), 54 | .save_index_bcd(save_index_bcd), 55 | 56 | .address(bridge_8bit_addr), 57 | .q(path_character) 58 | ); 59 | 60 | open_file_struct open_file_struct ( 61 | .address(bridge_8bit_addr), 62 | .path_character(path_character), 63 | 64 | .file_size(target_dataslot_length), 65 | 66 | .q(bridge_8bit_rd_data) 67 | ); 68 | 69 | always @(posedge clk) begin 70 | if (reset) begin 71 | no_slots_error <= 0; 72 | end else begin 73 | use_path <= 0; 74 | 75 | target_dataslot_write <= 0; 76 | target_dataslot_openfile <= 0; 77 | 78 | case (state) 79 | STATE_NONE: begin 80 | if (dump_sram) begin 81 | state <= STATE_START_OPENING_FILE; 82 | end 83 | end 84 | STATE_START_OPENING_FILE: begin 85 | target_dataslot_openfile <= 1; 86 | use_path <= 1; 87 | 88 | if (target_dataslot_ack) begin 89 | state <= STATE_OPENING_FILE; 90 | end 91 | end 92 | STATE_OPENING_FILE: begin 93 | use_path <= 1; 94 | 95 | if (target_dataslot_done) begin 96 | state <= STATE_EVALUATE_FILE; 97 | end 98 | end 99 | STATE_EVALUATE_FILE: begin 100 | // Look for specifically created and opened. Normal opened is not OK, nor is any error case 101 | if (target_dataslot_err == 1) begin 102 | state <= STATE_START_DUMP; 103 | end else begin 104 | // This file slot is taken. Increment count 105 | state <= STATE_START_OPENING_FILE; 106 | 107 | current_save_index <= current_save_index + 8'h1; 108 | 109 | if (current_save_index == 8'd99) begin 110 | // If we've exhausted all the way up to 99, there are no more slots left. Fail 111 | no_slots_error <= 1; 112 | 113 | state <= STATE_NONE; 114 | end 115 | end 116 | end 117 | STATE_START_DUMP: begin 118 | target_dataslot_write <= 1; 119 | 120 | if (target_dataslot_ack) begin 121 | state <= STATE_DUMPING; 122 | end 123 | end 124 | STATE_DUMPING: begin 125 | if (target_dataslot_done) begin 126 | state <= STATE_NONE; 127 | end 128 | end 129 | endcase 130 | end 131 | end 132 | 133 | endmodule 134 | -------------------------------------------------------------------------------- /rtl/file/open_file_struct.sv: -------------------------------------------------------------------------------- 1 | module open_file_struct ( 2 | input wire [8:0] address, 3 | input wire [7:0] path_character, 4 | 5 | input wire [31:0] file_size, 6 | 7 | output reg [7:0] q 8 | ); 9 | 10 | always_comb begin 11 | q = 0; 12 | 13 | if (address <= 9'hFF) begin 14 | q = path_character; 15 | end else if (address == 9'h103) begin 16 | // Big endian operation flags 17 | // Create file if it does not exist 18 | // Resize file to expected size 19 | q = 8'h3; 20 | end else if (address == 9'h104) begin 21 | q = file_size[31:24]; 22 | end else if (address == 9'h105) begin 23 | q = file_size[23:16]; 24 | end else if (address == 9'h106) begin 25 | q = file_size[15:8]; 26 | end else if (address == 9'h107) begin 27 | q = file_size[7:0]; 28 | end 29 | end 30 | 31 | endmodule 32 | -------------------------------------------------------------------------------- /rtl/file/save_dumper.sv: -------------------------------------------------------------------------------- 1 | module save_dumper ( 2 | input wire clk_74a, 3 | 4 | input wire bridge_rd, 5 | input wire [31:0] bridge_8bit_addr, 6 | output reg [7:0] bridge_8bit_rd_data, 7 | 8 | output wire [15:0] cart_address, 9 | 10 | output wire [7:4] cart_tran_bank0_out, 11 | 12 | input wire [7:0] cart_tran_bank1_in, 13 | output wire [7:0] cart_tran_bank1_out, 14 | output reg cart_tran_bank1_dir 15 | ); 16 | assign cart_tran_bank1_out = current_bank; 17 | 18 | reg read_req = 0; 19 | reg read_ack = 0; 20 | reg set_bank_req = 0; 21 | reg [15:0] read_addr = 0; 22 | 23 | reg cart_read = 0; 24 | reg cart_write = 0; 25 | 26 | // CS is high when writing 27 | assign cart_tran_bank0_out = {1'b0, ~cart_write, ~cart_read, cart_write}; 28 | 29 | localparam STATE_NONE = 0; 30 | localparam STATE_READ_REQ = 1; 31 | localparam STATE_READ_DATA = 2; 32 | 33 | reg [1:0] state = STATE_NONE; 34 | 35 | localparam STATE_CART_NONE = 0; 36 | localparam STATE_CART_READ = 1; 37 | localparam STATE_CART_DELAY = 2; 38 | localparam STATE_CART_READ_COMPLETE = 3; 39 | localparam STATE_CART_SET_BANK = 4; 40 | localparam STATE_CART_SET_BANK_COMPLETE = 5; 41 | 42 | reg [2:0] cart_state = STATE_CART_NONE; 43 | 44 | reg [7:0] current_bank = 0; 45 | 46 | reg [7:0] cart_delay = 0; 47 | reg [7:0] cart_delay_state = 0; 48 | 49 | reg prev_bridge_rd = 0; 50 | 51 | always @(posedge clk_74a) begin 52 | prev_bridge_rd <= bridge_rd; 53 | 54 | read_req <= 0; 55 | read_ack <= 0; 56 | set_bank_req <= 0; 57 | 58 | // Bridge state machine 59 | case (state) 60 | STATE_NONE: begin 61 | if (bridge_rd && ~prev_bridge_rd) begin 62 | state <= STATE_READ_REQ; 63 | end 64 | end 65 | STATE_READ_REQ: begin 66 | state <= STATE_READ_DATA; 67 | 68 | read_req <= 1; 69 | end 70 | STATE_READ_DATA: begin 71 | if (read_ack) begin 72 | state <= STATE_NONE; 73 | 74 | if (bridge_8bit_addr[12:0] == 13'h1FFF) begin 75 | // End of bank, increment bank 76 | current_bank <= current_bank + 8'h1; 77 | 78 | set_bank_req <= 1; 79 | end 80 | end 81 | end 82 | endcase 83 | 84 | // Cart state machine 85 | case (cart_state) 86 | STATE_CART_NONE: begin 87 | if (read_req) begin 88 | cart_state <= STATE_CART_READ; 89 | end else if (set_bank_req) begin 90 | cart_state <= STATE_CART_SET_BANK; 91 | end 92 | end 93 | STATE_CART_READ: begin 94 | cart_state <= STATE_CART_DELAY; 95 | cart_delay_state <= STATE_CART_READ_COMPLETE; 96 | cart_delay <= 8'hF; 97 | 98 | cart_read <= 1; 99 | cart_address <= bridge_8bit_addr[12:0] + 16'hA000; 100 | end 101 | STATE_CART_DELAY: begin 102 | cart_delay <= cart_delay - 8'h1; 103 | 104 | if (cart_delay == 8'h0) begin 105 | cart_state <= cart_delay_state; 106 | end 107 | end 108 | STATE_CART_READ_COMPLETE: begin 109 | cart_state <= STATE_CART_NONE; 110 | 111 | cart_read <= 0; 112 | read_ack <= 1; 113 | 114 | bridge_8bit_rd_data <= cart_tran_bank1_in; 115 | end 116 | 117 | STATE_CART_SET_BANK: begin 118 | cart_state <= STATE_CART_DELAY; 119 | cart_delay_state <= STATE_CART_SET_BANK_COMPLETE; 120 | cart_delay <= 8'h3F; 121 | 122 | cart_write <= 1; 123 | cart_address <= 16'h4000; 124 | 125 | // Write bank data 126 | cart_tran_bank1_dir <= 1; 127 | end 128 | STATE_CART_SET_BANK_COMPLETE: begin 129 | cart_state <= STATE_CART_NONE; 130 | 131 | cart_write <= 0; 132 | cart_tran_bank1_dir <= 0; 133 | end 134 | endcase 135 | end 136 | 137 | endmodule 138 | -------------------------------------------------------------------------------- /rtl/file/write_path.sv: -------------------------------------------------------------------------------- 1 | module write_path ( 2 | input wire clk, 3 | 4 | input wire [6:0] save_index, 5 | output wire [7:0] save_index_bcd, 6 | 7 | input wire [7:0] address, 8 | output reg [7:0] q 9 | ); 10 | 11 | localparam PATH_PREFIX_LENGTH = 26; 12 | localparam PATH_SUFFIX_LENGTH = 4; 13 | 14 | reg [7:0] path_prefix[PATH_PREFIX_LENGTH] = '{ 15 | "/", 16 | "S", 17 | "a", 18 | "v", 19 | "e", 20 | "s", 21 | "/", 22 | "c", 23 | "a", 24 | "m", 25 | "e", 26 | "r", 27 | "a", 28 | "/", 29 | "c", 30 | "o", 31 | "m", 32 | "m", 33 | "o", 34 | "n", 35 | "/", 36 | "S", 37 | "R", 38 | "A", 39 | "M", 40 | "_" 41 | }; 42 | 43 | reg [7:0] path_suffix[PATH_SUFFIX_LENGTH] = '{".", "s", "a", "v"}; 44 | 45 | double_bcd double_bcd ( 46 | .binary(save_index), 47 | .bcd(save_index_bcd) 48 | ); 49 | 50 | always @(posedge clk) begin 51 | q <= 0; 52 | 53 | if (address < PATH_PREFIX_LENGTH) begin 54 | q <= path_prefix[address]; 55 | end else if (address < PATH_PREFIX_LENGTH + 2) begin 56 | // Save index 57 | case (address - PATH_PREFIX_LENGTH) 58 | 0: begin 59 | q <= {4'h0, save_index_bcd[7:4]} + 8'h30; 60 | end 61 | 1: begin 62 | q <= {4'h0, save_index_bcd[3:0]} + 8'h30; 63 | end 64 | endcase 65 | end else if (address < PATH_PREFIX_LENGTH + 2 + PATH_SUFFIX_LENGTH) begin 66 | q <= path_suffix[address-PATH_PREFIX_LENGTH+2]; 67 | end 68 | end 69 | 70 | endmodule 71 | -------------------------------------------------------------------------------- /rtl/gb/T80/T80.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) GBse.vhd ] 2 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80_Reg.vhd ] 3 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80_MCode.vhd ] 4 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80_ALU.vhd ] 5 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80.vhd ] 6 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80_Pack.vhd ] 7 | -------------------------------------------------------------------------------- /rtl/gb/T80/T80_Reg.vhd: -------------------------------------------------------------------------------- 1 | -- **** 2 | -- T80(b) core. In an effort to merge and maintain bug fixes .... 3 | -- 4 | -- 5 | -- Ver 300 started tidyup 6 | -- MikeJ March 2005 7 | -- Latest version from www.fpgaarcade.com (original www.opencores.org) 8 | -- 9 | -- **** 10 | -- 11 | -- T80 Registers, technology independent 12 | -- 13 | -- Version : 0244 14 | -- 15 | -- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org) 16 | -- 17 | -- All rights reserved 18 | -- 19 | -- Redistribution and use in source and synthezised forms, with or without 20 | -- modification, are permitted provided that the following conditions are met: 21 | -- 22 | -- Redistributions of source code must retain the above copyright notice, 23 | -- this list of conditions and the following disclaimer. 24 | -- 25 | -- Redistributions in synthesized form must reproduce the above copyright 26 | -- notice, this list of conditions and the following disclaimer in the 27 | -- documentation and/or other materials provided with the distribution. 28 | -- 29 | -- Neither the name of the author nor the names of other contributors may 30 | -- be used to endorse or promote products derived from this software without 31 | -- specific prior written permission. 32 | -- 33 | -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 34 | -- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 35 | -- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 36 | -- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE 37 | -- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 38 | -- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 39 | -- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 40 | -- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 41 | -- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 42 | -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 43 | -- POSSIBILITY OF SUCH DAMAGE. 44 | -- 45 | -- Please report bugs to the author, but before you do so, please 46 | -- make sure that this is not a derivative work and that 47 | -- you have the latest version of this file. 48 | -- 49 | -- The latest version of this file can be found at: 50 | -- http://www.opencores.org/cvsweb.shtml/t51/ 51 | -- 52 | -- Limitations : 53 | -- 54 | -- File history : 55 | -- 56 | -- 0242 : Initial release 57 | -- 58 | -- 0244 : Changed to single register file 59 | -- 60 | 61 | library IEEE; 62 | use IEEE.std_logic_1164.all; 63 | use IEEE.numeric_std.all; 64 | 65 | use work.pBus_savestates.all; 66 | use work.pReg_savestates.all; 67 | 68 | entity T80_Reg is 69 | port( 70 | RESET_n : in std_logic; 71 | Clk : in std_logic; 72 | CEN : in std_logic; 73 | WEH : in std_logic; 74 | WEL : in std_logic; 75 | AddrA : in std_logic_vector(2 downto 0); 76 | AddrB : in std_logic_vector(2 downto 0); 77 | AddrC : in std_logic_vector(2 downto 0); 78 | DIH : in std_logic_vector(7 downto 0); 79 | DIL : in std_logic_vector(7 downto 0); 80 | DOAH : out std_logic_vector(7 downto 0); 81 | DOAL : out std_logic_vector(7 downto 0); 82 | DOBH : out std_logic_vector(7 downto 0); 83 | DOBL : out std_logic_vector(7 downto 0); 84 | DOCH : out std_logic_vector(7 downto 0); 85 | DOCL : out std_logic_vector(7 downto 0); 86 | -- savestates 87 | SaveStateBus_Din : in std_logic_vector(BUS_buswidth-1 downto 0); 88 | SaveStateBus_Adr : in std_logic_vector(BUS_busadr-1 downto 0); 89 | SaveStateBus_wren : in std_logic; 90 | SaveStateBus_rst : in std_logic; 91 | SaveStateBus_Dout : out std_logic_vector(BUS_buswidth-1 downto 0) 92 | ); 93 | end T80_Reg; 94 | 95 | architecture rtl of T80_Reg is 96 | 97 | -- GB doesn't have alternate registers, only lower 4 can be addressed! 98 | type Register_Image is array (natural range <>) of std_logic_vector(7 downto 0); 99 | signal RegsH : Register_Image(0 to 7); 100 | signal RegsL : Register_Image(0 to 7); 101 | 102 | -- savestates 103 | signal SS_REGS : std_logic_vector(REG_SAVESTATE_CPUREGS.upper downto REG_SAVESTATE_CPUREGS.lower); 104 | signal SS_REGS_BACK : std_logic_vector(REG_SAVESTATE_CPUREGS.upper downto REG_SAVESTATE_CPUREGS.lower); 105 | 106 | begin 107 | 108 | iREG_SAVESTATE_CPUREGS : entity work.eReg_Savestate generic map ( REG_SAVESTATE_CPUREGS ) port map (Clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_Dout, SS_REGS_BACK, SS_REGS); 109 | 110 | SS_REGS_BACK(63 downto 56) <= RegsH(3); 111 | SS_REGS_BACK(55 downto 48) <= RegsH(2); 112 | SS_REGS_BACK(47 downto 40) <= RegsH(1); 113 | SS_REGS_BACK(39 downto 32) <= RegsH(0); 114 | SS_REGS_BACK(31 downto 24) <= RegsL(3); 115 | SS_REGS_BACK(23 downto 16) <= RegsL(2); 116 | SS_REGS_BACK(15 downto 8) <= RegsL(1); 117 | SS_REGS_BACK( 7 downto 0) <= RegsL(0); 118 | 119 | process (Clk) 120 | begin 121 | if Clk'event and Clk = '1' then 122 | if RESET_n = '0' then 123 | RegsH(3) <= SS_REGS(63 downto 56); 124 | RegsH(2) <= SS_REGS(55 downto 48); 125 | RegsH(1) <= SS_REGS(47 downto 40); 126 | RegsH(0) <= SS_REGS(39 downto 32); 127 | RegsL(3) <= SS_REGS(31 downto 24); 128 | RegsL(2) <= SS_REGS(23 downto 16); 129 | RegsL(1) <= SS_REGS(15 downto 8); 130 | RegsL(0) <= SS_REGS( 7 downto 0); 131 | elsif CEN = '1' then 132 | if WEH = '1' then 133 | RegsH(to_integer(unsigned(AddrA))) <= DIH; 134 | end if; 135 | if WEL = '1' then 136 | RegsL(to_integer(unsigned(AddrA))) <= DIL; 137 | end if; 138 | end if; 139 | end if; 140 | end process; 141 | 142 | DOAH <= RegsH(to_integer(unsigned(AddrA))); 143 | DOAL <= RegsL(to_integer(unsigned(AddrA))); 144 | DOBH <= RegsH(to_integer(unsigned(AddrB))); 145 | DOBL <= RegsL(to_integer(unsigned(AddrB))); 146 | DOCH <= RegsH(to_integer(unsigned(AddrC))); 147 | DOCL <= RegsL(to_integer(unsigned(AddrC))); 148 | 149 | end; 150 | -------------------------------------------------------------------------------- /rtl/gb/audio/audio.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio_mixer.sv"] 2 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "filters/audio_filters.sv"] 3 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "filters/audio_mix.sv"] 4 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "filters/dc_blocker.sv"] 5 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "filters/filter_loader.sv"] 6 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "filters/filters_rom.sv"] 7 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "filters/iir_filter_tap.sv"] 8 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "filters/iir_filter.sv"] -------------------------------------------------------------------------------- /rtl/gb/audio/audio_mixer.sv: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SPDX-License-Identifier: MIT 3 | // SPDX-FileType: SOURCE 4 | // SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors 5 | //------------------------------------------------------------------------------ 6 | // 7 | // Analogue Pocket Audio Mixer 8 | // 9 | // Copyright (c) 2023, Marcus Andrade 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | //------------------------------------------------------------------------------ 30 | 31 | `default_nettype none 32 | 33 | module audio_mixer #( 34 | parameter DW = 16, //! Audio Data Width 35 | // parameter MUTE_PAUSE = 1, //! Mute on Pause 36 | parameter STEREO = 1 //! Stereo Audio 37 | ) ( 38 | // Clocks and Reset 39 | input logic clk_74b, //! Clock 74.25Mhz 40 | input logic clk_audio, 41 | input logic reset, //! Reset 42 | // Controls 43 | // input logic [ 3:0] afilter_sw, //! Predefined Audio Filter Switch 44 | input logic [ 3:0] vol_att, //! Volume ([0] Max | [7] Min) 45 | input logic [ 1:0] mix, //! [0] No Mix | [1] 25% | [2] 50% | [3] 100% (mono) 46 | // input logic pause_core, //! Mute Audio 47 | // Audio From Core 48 | input logic is_signed, //! Signed Audio 49 | input logic [DW-1:0] core_l, //! Left Channel Audio from Core 50 | input logic [DW-1:0] core_r, //! Right Channel Audio from Core 51 | // Pocket I2S 52 | output logic audio_mclk, //! Serial Master Clock 53 | output logic audio_lrck, //! Left/Right clock 54 | output logic audio_dac //! Serialized data 55 | ); 56 | 57 | //! ------------------------------------------------------------------------ 58 | //! Audio Clocks 59 | //! MCLK: 12.288MHz (256*Fs, where Fs = 48000) 60 | //! SCLK: 3.072mhz (MCLK/4) 61 | //! ------------------------------------------------------------------------ 62 | wire audio_sclk; 63 | 64 | mf_audio_pll audio_pll ( 65 | .refclk (clk_74b), 66 | .rst (0), 67 | .outclk_0(audio_mclk), 68 | .outclk_1(audio_sclk) 69 | ); 70 | 71 | //! ------------------------------------------------------------------------ 72 | //! Pad core_l/core_r with zeros to maintain a consistent size of 16 bits 73 | //! ------------------------------------------------------------------------ 74 | logic [15:0] core_al, core_ar; 75 | 76 | always_comb begin 77 | core_al = DW == 16 ? core_l : {core_l, {16 - DW{1'b0}}}; 78 | core_ar = STEREO ? DW == 16 ? core_r : {core_r, {16 - DW{1'b0}}} : core_al; 79 | end 80 | 81 | //! ------------------------------------------------------------------------ 82 | //! Low Pass Filter 83 | //! ------------------------------------------------------------------------ 84 | reg [31:0] aflt_rate = 32'd7056000; // Sampling Frequency 85 | reg [39:0] acx = 40'd4258969; // Base gain 86 | reg [ 7:0] acx0 = 8'd3; // gain scale for X0 87 | reg [ 7:0] acx1 = 8'd3; // gain scale for X1 88 | reg [ 7:0] acx2 = 8'd1; // gain scale for X2 89 | reg [23:0] acy0 = -24'd6216759; // gain scale for Y0 90 | reg [23:0] acy1 = 24'd6143386; // gain scale for Y1 91 | reg [23:0] acy2 = -24'd2023767; // gain scale for Y2 92 | 93 | // logic [31:0] aflt_rate; 94 | // logic [39:0] acx; 95 | // logic [7:0] acx0, acx1, acx2; 96 | // logic [23:0] acy0, acy1, acy2; 97 | 98 | // arcade_filters arcade_filters 99 | // ( 100 | // .clk ( audio_mclk ), 101 | // .afilter_sw ( afilter_sw ), 102 | // .aflt_rate ( aflt_rate ), 103 | // .acx ( acx ), 104 | // .acx0 ( acx0 ), 105 | // .acx1 ( acx1 ), 106 | // .acx2 ( acx2 ), 107 | // .acy0 ( acy0 ), 108 | // .acy1 ( acy1 ), 109 | // .acy2 ( acy2 ) 110 | // ); 111 | 112 | //! ------------------------------------------------------------------------ 113 | //! Synchronization 114 | //! ------------------------------------------------------------------------ 115 | 116 | logic [15:0] core_al_s, core_ar_s; 117 | 118 | sync_fifo #( 119 | .WIDTH(32) 120 | ) sync_fifo ( 121 | .clk_write(clk_audio), 122 | .clk_read (audio_mclk), 123 | 124 | .write_en(write_en), 125 | .data({core_al, core_ar}), 126 | .data_s({core_al_s, core_ar_s}) 127 | ); 128 | 129 | reg write_en = 0; 130 | reg [15:0] prev_left; 131 | reg [15:0] prev_right; 132 | 133 | // Mark write when necessary 134 | always @(posedge clk_audio) begin 135 | prev_left <= core_al; 136 | prev_right <= core_ar; 137 | 138 | write_en <= 0; 139 | 140 | if (core_al != prev_left || core_ar != prev_right) begin 141 | write_en <= 1; 142 | end 143 | end 144 | 145 | //! ------------------------------------------------------------------------ 146 | //! Audio Filters 147 | //! ------------------------------------------------------------------------ 148 | logic [15:0] audio_l, audio_r; 149 | // logic mute_audio = MUTE_PAUSE ? pause_core : 1'b0; 150 | 151 | audio_filters audio_filters ( 152 | .clk (audio_mclk), 153 | .reset (reset), 154 | // Controls 155 | .att ({1'b0, vol_att}), 156 | .mix (mix), 157 | // Audio Filter 158 | .flt_rate (aflt_rate), 159 | .cx (acx), 160 | .cx0 (acx0), 161 | .cx1 (acx1), 162 | .cx2 (acx2), 163 | .cy0 (acy0), 164 | .cy1 (acy1), 165 | .cy2 (acy2), 166 | // Audio from Core 167 | .is_signed(is_signed), 168 | .core_l (core_al_s), 169 | .core_r (core_ar_s), 170 | // Filtered Audio Output 171 | .audio_l (audio_l), 172 | .audio_r (audio_r) 173 | ); 174 | 175 | //! ------------------------------------------------------------------------ 176 | //! Pocket I2S Output 177 | //! ------------------------------------------------------------------------ 178 | 179 | sound_i2s sound_i2s ( 180 | .audio_sclk(audio_sclk), 181 | 182 | .audio_l(audio_l), 183 | .audio_r(audio_r), 184 | 185 | .audio_lrck(audio_lrck), 186 | .audio_dac (audio_dac) 187 | ); 188 | 189 | endmodule 190 | -------------------------------------------------------------------------------- /rtl/gb/audio/filters/audio_filters.sv: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SPDX-License-Identifier: GPL-3.0-or-later 3 | // SPDX-FileType: SOURCE 4 | // SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors 5 | //------------------------------------------------------------------------------ 6 | // 7 | // Audio Filters 8 | // 9 | // Copyright (c) 2023, Marcus Andrade 10 | // Copyright (c) 2020, Alexey Melnikov 11 | // 12 | // This source file is free software: you can redistribute it and/or modify 13 | // it under the terms of the GNU General Public License as published 14 | // by the Free Software Foundation, either version 3 of the License, or 15 | // (at your option) any later version. 16 | // 17 | // This program is distributed in the hope that it will be useful, but 18 | // WITHOUT ANY WARRANTY; without even the implied warranty of 19 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | // General Public License for more details. 21 | // 22 | // You should have received a copy of the GNU General Public License 23 | // along with this program. If not, see . 24 | // 25 | //------------------------------------------------------------------------------ 26 | 27 | `default_nettype none 28 | 29 | module audio_filters 30 | #( 31 | parameter CLK_RATE = 12288000 32 | ) ( 33 | input wire clk, 34 | input wire reset, 35 | 36 | input wire [31:0] flt_rate, 37 | input wire [39:0] cx, 38 | input wire [7:0] cx0, 39 | input wire [7:0] cx1, 40 | input wire [7:0] cx2, 41 | input wire [23:0] cy0, 42 | input wire [23:0] cy1, 43 | input wire [23:0] cy2, 44 | input wire [4:0] att, 45 | 46 | input wire is_signed, 47 | input wire [1:0] mix, 48 | 49 | input wire [15:0] core_l, 50 | input wire [15:0] core_r, 51 | 52 | // Audio Output 53 | output wire [15:0] audio_l, 54 | output wire [15:0] audio_r 55 | ); 56 | 57 | reg sample_rate = 0; //0 - 48KHz, 1 - 96KHz 58 | 59 | reg sample_ce; 60 | always @(posedge clk) begin 61 | reg [8:0] div = 0; 62 | reg [1:0] add = 0; 63 | 64 | div <= div + add; 65 | if(!div) begin 66 | div <= 2'd1 << sample_rate; 67 | add <= 2'd1 << sample_rate; 68 | end 69 | 70 | sample_ce <= !div; 71 | end 72 | 73 | reg flt_ce; 74 | always @(posedge clk) begin 75 | reg [31:0] cnt = 0; 76 | 77 | flt_ce = 0; 78 | cnt = cnt + {flt_rate[30:0],1'b0}; 79 | if(cnt >= CLK_RATE) begin 80 | cnt = cnt - CLK_RATE; 81 | flt_ce = 1; 82 | end 83 | end 84 | 85 | reg [15:0] cl,cr; 86 | always @(posedge clk) begin 87 | reg [15:0] cl1, cl2; 88 | reg [15:0] cr1, cr2; 89 | 90 | cl1 <= core_l; 91 | cl2 <= cl1; 92 | if(cl2 == cl1) 93 | cl <= cl2; 94 | 95 | cr1 <= core_r; 96 | cr2 <= cr1; 97 | if(cr2 == cr1) 98 | cr <= cr2; 99 | end 100 | 101 | reg a_en1 = 0, a_en2 = 0; 102 | always @(posedge clk, posedge reset) begin 103 | reg [1:0] dly1; 104 | reg [14:0] dly2; 105 | 106 | if(reset) begin 107 | dly1 <= 0; 108 | dly2 <= 0; 109 | a_en1 <= 0; 110 | a_en2 <= 0; 111 | end 112 | else begin 113 | if(flt_ce) begin 114 | if(~&dly1) 115 | dly1 <= dly1 + 1'd1; 116 | else 117 | a_en1 <= 1; 118 | end 119 | 120 | if(sample_ce) begin 121 | if(!dly2[13+sample_rate]) 122 | dly2 <= dly2 + 1'd1; 123 | else 124 | a_en2 <= 1; 125 | end 126 | end 127 | end 128 | 129 | wire [15:0] acl, acr; 130 | iir_filter #(.use_params(0)) iir_filter 131 | ( 132 | .clk ( clk ), 133 | .reset ( reset ), 134 | 135 | .ce ( flt_ce & a_en1 ), 136 | .sample_ce ( sample_ce ), 137 | 138 | .cx ( cx ), 139 | .cx0 ( cx0 ), 140 | .cx1 ( cx1 ), 141 | .cx2 ( cx2 ), 142 | .cy0 ( cy0 ), 143 | .cy1 ( cy1 ), 144 | .cy2 ( cy2 ), 145 | 146 | .input_l ( {~is_signed ^ cl[15], cl[14:0]} ), 147 | .input_r ( {~is_signed ^ cr[15], cr[14:0]} ), 148 | .output_l ( acl ), 149 | .output_r ( acr ) 150 | ); 151 | 152 | wire [15:0] adl; 153 | dc_blocker dcb_l 154 | ( 155 | .clk ( clk ), 156 | .ce ( sample_ce ), 157 | .sample_rate ( sample_rate ), 158 | .mute ( ~a_en2 ), 159 | .din ( acl ), 160 | .dout ( adl ) 161 | ); 162 | 163 | wire [15:0] adr; 164 | dc_blocker dcb_r 165 | ( 166 | .clk ( clk ), 167 | .ce ( sample_ce ), 168 | .sample_rate ( sample_rate ), 169 | .mute ( ~a_en2 ), 170 | .din ( acr ), 171 | .dout ( adr ) 172 | ); 173 | 174 | wire [15:0] audio_l_pre; 175 | audio_mix audmix_l 176 | ( 177 | .clk ( clk ), 178 | .ce ( sample_ce ), 179 | .att ( att ), 180 | .mix ( mix ), 181 | 182 | .core_audio ( adl ), 183 | .pre_in ( audio_r_pre ), 184 | 185 | .pre_out ( audio_l_pre ), 186 | .out ( audio_l ) 187 | ); 188 | 189 | wire [15:0] audio_r_pre; 190 | audio_mix audmix_r 191 | ( 192 | .clk ( clk ), 193 | .ce ( sample_ce ), 194 | .att ( att ), 195 | .mix ( mix ), 196 | 197 | .core_audio ( adr ), 198 | .pre_in ( audio_l_pre ), 199 | 200 | .pre_out ( audio_r_pre ), 201 | .out ( audio_r ) 202 | ); 203 | 204 | endmodule 205 | -------------------------------------------------------------------------------- /rtl/gb/audio/filters/audio_mix.sv: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SPDX-License-Identifier: GPL-3.0-or-later 3 | // SPDX-FileType: SOURCE 4 | // SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors 5 | //------------------------------------------------------------------------------ 6 | // 7 | // Audio Mix 8 | // 9 | // Copyright (c) 2023, Marcus Andrade 10 | // Copyright (c) 2020, Alexey Melnikov 11 | // 12 | // This source file is free software: you can redistribute it and/or modify 13 | // it under the terms of the GNU General Public License as published 14 | // by the Free Software Foundation, either version 3 of the License, or 15 | // (at your option) any later version. 16 | // 17 | // This program is distributed in the hope that it will be useful, but 18 | // WITHOUT ANY WARRANTY; without even the implied warranty of 19 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | // General Public License for more details. 21 | // 22 | // You should have received a copy of the GNU General Public License 23 | // along with this program. If not, see . 24 | // 25 | //------------------------------------------------------------------------------ 26 | 27 | `default_nettype none 28 | 29 | module audio_mix 30 | ( 31 | input wire clk, // system clock 32 | input wire ce, // clock enable signal 33 | 34 | input wire [4:0] att, // attenuation value 35 | input wire [1:0] mix, // mixing value 36 | 37 | input wire [15:0] core_audio, // audio input from core 38 | input wire [15:0] pre_in, // audio input from preamp 39 | 40 | output reg [15:0] pre_out = 0, // preamp output 41 | output reg [15:0] out = 0 // final mixed audio output 42 | ); 43 | 44 | reg signed [16:0] a1, a2, a3; // signed registers for audio processing 45 | 46 | always @(posedge clk) begin 47 | if (ce) begin 48 | a1 <= {core_audio[15],core_audio}; // extend core_audio to 17 bits and store in a1 49 | pre_out <= a1[16:1]; // store the upper 16 bits of a1 in pre_out 50 | 51 | // select mixing options 52 | case(mix) 53 | 0: begin a2 <= a1; end // mix core_audio 54 | 1: begin a2 <= $signed(a1) - $signed(a1[16:3]) + $signed(pre_in[15:2]); end // mix core_audio, and pre_in with attenuation 55 | 2: begin a2 <= $signed(a1) - $signed(a1[16:2]) + $signed(pre_in[15:1]); end // mix core_audio, and pre_in with greater attenuation 56 | 3: begin a2 <= {a1[16],a1[16:1]} + {pre_in[15],pre_in}; end // mix core_audio, and pre_in with clipping 57 | endcase 58 | 59 | // if the highest bit of att is set, set a3 to 0 (Mute) else shift a2 right by att and store in a3 60 | a3 <= (att[4]) ? 0 : a2 >>> att[3:0]; 61 | 62 | // Clamping 63 | out <= ^a3[16:15] ? {a3[16],{15{a3[15]}}} : a3[15:0]; // clamp the upper 16 bits of a3 and store in out 64 | end 65 | end 66 | 67 | endmodule 68 | -------------------------------------------------------------------------------- /rtl/gb/audio/filters/dc_blocker.sv: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SPDX-License-Identifier: GPL-3.0-or-later 3 | // SPDX-FileType: SOURCE 4 | // SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors 5 | //------------------------------------------------------------------------------ 6 | // 7 | // DC blocker filter using a simplified IIR structure. 8 | // 9 | // Copyright (c) 2023, Marcus Andrade 10 | // Copyright (c) 2020, Alexey Melnikov 11 | // 12 | // This source file is free software: you can redistribute it and/or modify 13 | // it under the terms of the GNU General Public License as published 14 | // by the Free Software Foundation, either version 3 of the License, or 15 | // (at your option) any later version. 16 | // 17 | // This program is distributed in the hope that it will be useful, but 18 | // WITHOUT ANY WARRANTY; without even the implied warranty of 19 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | // General Public License for more details. 21 | // 22 | // You should have received a copy of the GNU General Public License 23 | // along with this program. If not, see . 24 | // 25 | //------------------------------------------------------------------------------ 26 | // 27 | // It utilizes feedback to achieve filtering, operating on a delayed version of 28 | // the input signal (x0), a delayed version of the output signal (y1), and the 29 | // current input and output signals (x1 and y0, respectively). 30 | // The filter coefficients are determined by the sample rate of the input signal. 31 | // 32 | //------------------------------------------------------------------------------ 33 | 34 | `default_nettype none 35 | 36 | module dc_blocker 37 | ( 38 | input logic clk, // Input clock signal 39 | input logic ce, // Control enable signal 40 | input logic mute, // Mute output signal 41 | 42 | input logic sample_rate, // Sample rate input signal 43 | input logic [15:0] din, // Input data signal 44 | output logic [15:0] dout // Output data signal 45 | ); 46 | 47 | // Pad the input signal with zeros 48 | wire [39:0] x = {din[15], din, 23'd0}; 49 | // Subtract previous input sample from current input sample 50 | wire [39:0] x0 = x - (sample_rate ? {{11{x[39]}}, x[39:11]} : {{10{x[39]}}, x[39:10]}); 51 | // Subtract previous output sample from current output sample 52 | wire [39:0] y1 = y - (sample_rate ? {{10{y[39]}}, y[39:10]} : {{09{y[39]}}, y[39:09]}); 53 | // Subtract difference between previous input and output sample from current input sample 54 | wire [39:0] y0 = x0 - x1 + y1; 55 | 56 | // Registers to store previous input and output samples 57 | reg [39:0] x1, y; 58 | 59 | always @(posedge clk) begin 60 | if(ce) begin 61 | // Update the previous input sample 62 | x1 <= x0; 63 | // Update the previous output sample 64 | y <= ^y0[39:38] ? {{2{y0[39]}},{38{y0[38]}}} : y0; 65 | end 66 | end 67 | 68 | // Output the filtered sample, or zero if the mute signal is high 69 | assign dout = mute ? 16'd0 : y[38:23]; 70 | 71 | endmodule 72 | -------------------------------------------------------------------------------- /rtl/gb/audio/filters/filter_loader.sv: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SPDX-License-Identifier: MIT 3 | // SPDX-FileType: SOURCE 4 | // SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors 5 | //------------------------------------------------------------------------------ 6 | // 7 | // Analogue Pocket Audio Filter Loader 8 | // 9 | // Copyright (c) 2023, Marcus Andrade 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | //------------------------------------------------------------------------------ 30 | 31 | `default_nettype none 32 | 33 | module filter_loader 34 | ( 35 | // Clocks and Reset 36 | input logic clk_sys, 37 | input logic afilter_wr, 38 | input logic [7:0] afilter_addr, 39 | input logic [7:0] afilter_din, 40 | // Audio From Core 41 | output logic [31:0] flt_rate, // Sampling Frequency 42 | output logic [39:0] cx, // Base gain 43 | output logic [7:0] cx0, cx1, cx2, // gain scale for X0, X1, X2 44 | output logic [23:0] cy0, cy1, cy2 // gain scale for Y0, Y1, Y2 45 | ); 46 | 47 | always_comb begin 48 | flt_rate = r_flt_rate; 49 | cx = r_cx; 50 | cx0 = r_cx0; 51 | cx1 = r_cx1; 52 | cx2 = r_cx2; 53 | cy0 = r_cy0; 54 | cy1 = r_cy1; 55 | cy2 = r_cy2; 56 | end 57 | 58 | reg [31:0] r_flt_rate = 7056000; // Sampling Frequency 59 | reg [39:0] r_cx = 4258969; // Base gain 60 | reg [7:0] r_cx0 = 3; // gain scale for X0 61 | reg [7:0] r_cx1 = 3; // gain scale for X1 62 | reg [7:0] r_cx2 = 1; // gain scale for X2 63 | reg [23:0] r_cy0 = -24'd6216759; // gain scale for Y0 64 | reg [23:0] r_cy1 = 24'd6143386; // gain scale for Y1 65 | reg [23:0] r_cy2 = -24'd2023767; // gain scale for Y2 66 | 67 | always_ff @(posedge clk_sys) begin 68 | if (afilter_wr) begin 69 | case(afilter_addr) 70 | //! Sampling Frequency 71 | 8'h00: r_flt_rate[7:0] <= afilter_din; 72 | 8'h01: r_flt_rate[15:8] <= afilter_din; 73 | 8'h02: r_flt_rate[23:16] <= afilter_din; 74 | 8'h03: r_flt_rate[31:24] <= afilter_din; 75 | //! Base gain 76 | 8'h04: r_cx[7:0] <= afilter_din; 77 | 8'h05: r_cx[15:8] <= afilter_din; 78 | 8'h06: r_cx[23:16] <= afilter_din; 79 | 8'h07: r_cx[31:24] <= afilter_din; 80 | 8'h08: r_cx[39:32] <= afilter_din; 81 | // 8'h09: _SKIP_ 82 | // 8'h0a: _SKIP_ 83 | // 8'h0b: _SKIP_ 84 | //! gain scale for X0 85 | 8'h0c: r_cx0 <= afilter_din; 86 | //! gain scale for X1 87 | 8'h0d: r_cx1 <= afilter_din; 88 | //! gain scale for X2 89 | 8'h0e: r_cx2 <= afilter_din; 90 | //! gain scale for Y0 91 | 8'h0f: r_cy0[7:0] <= afilter_din; 92 | 8'h10: r_cy0[15:8] <= afilter_din; 93 | 8'h11: r_cy0[23:16] <= afilter_din; 94 | // 8'h12: _SKIP_ 95 | //! gain scale for Y1 96 | 8'h13: r_cy1[7:0] <= afilter_din; 97 | 8'h14: r_cy1[15:8] <= afilter_din; 98 | 8'h15: r_cy1[23:16] <= afilter_din; 99 | // 8'h16: _SKIP_ 100 | //! gain scale for Y2 101 | 8'h17: r_cy2[7:0] <= afilter_din; 102 | 8'h18: r_cy2[15:8] <= afilter_din; 103 | 8'h19: r_cy2[23:16] <= afilter_din; 104 | // 8'h1a: _SKIP_ 105 | // 8'h1b: _SKIP_ 106 | default:; 107 | endcase 108 | end 109 | end 110 | 111 | endmodule 112 | -------------------------------------------------------------------------------- /rtl/gb/audio/filters/filters_rom.sv: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SPDX-License-Identifier: MIT 3 | // SPDX-FileType: SOURCE 4 | // SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors 5 | //------------------------------------------------------------------------------ 6 | // 7 | // Analogue Pocket Audio Filters ROM 8 | // 9 | // Copyright (c) 2023, Marcus Andrade 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | //------------------------------------------------------------------------------ 30 | 31 | `default_nettype none 32 | 33 | module filters_rom 34 | ( 35 | // Clock 36 | input logic clk, 37 | // Filter Switch 38 | input logic [3:0] afilter_sw, 39 | // Filter Config 40 | output logic [31:0] aflt_rate, // Sampling Frequency 41 | output logic [39:0] acx, // Base gain 42 | output logic [7:0] acx0, acx1, acx2, // gain scale for X0, X1, X2 43 | output logic [23:0] acy0, acy1, acy2 // gain scale for Y0, Y1, Y2 44 | ); 45 | 46 | (* ramstyle = "no_rw_check" *) reg [31:0] flt_rate[9]; 47 | (* ramstyle = "no_rw_check" *) reg [39:0] cx[9]; 48 | (* ramstyle = "no_rw_check" *) reg [7:0] cx0[9]; 49 | (* ramstyle = "no_rw_check" *) reg [7:0] cx1[9]; 50 | (* ramstyle = "no_rw_check" *) reg [7:0] cx2[9]; 51 | (* ramstyle = "no_rw_check" *) reg [23:0] cy0[9]; 52 | (* ramstyle = "no_rw_check" *) reg [23:0] cy1[9]; 53 | (* ramstyle = "no_rw_check" *) reg [23:0] cy2[9]; 54 | 55 | always @(posedge clk) begin 56 | aflt_rate <= flt_rate[afilter_sw]; 57 | acx <= cx[afilter_sw]; 58 | acx0 <= cx0[afilter_sw]; 59 | acx1 <= cx1[afilter_sw]; 60 | acx2 <= cx2[afilter_sw]; 61 | acy0 <= cy0[afilter_sw]; 62 | acy1 <= cy1[afilter_sw]; 63 | acy2 <= cy2[afilter_sw]; 64 | end 65 | //! Assign Outputs --------------------------------------------------------- 66 | 67 | //! Arcade Filters --------------------------------------------------------- 68 | initial begin 69 | // Default 70 | flt_rate[0] = 7056000; 71 | cx[0] = 4258969; 72 | cx0[0] = 3; 73 | cx1[0] = 3; 74 | cx2[0] = 1; 75 | cy0[0] = -24'd6216759; 76 | cy1[0] = 24'd6143386; 77 | cy2[0] = -24'd2023767; 78 | 79 | // Arcade LPF 2khz 1st 80 | flt_rate[1] = 32'd7056000; 81 | cx[1] = 40'd425898; 82 | cx0[1] = 8'd3; 83 | cx1[1] = 8'd3; 84 | cx2[1] = 8'd1; 85 | cy0[1] = -24'd6234907; 86 | cy1[1] = 24'd6179109; 87 | cy2[1] = -24'd2041353; 88 | 89 | // Arcade LPF 2khz 2nd 90 | flt_rate[2] = 32'd7056000; 91 | cx[2] = 40'd2420697; 92 | cx0[2] = 8'd2; 93 | cx1[2] = 8'd1; 94 | cx2[2] = 8'd0; 95 | cy0[2] = -24'd4189022; 96 | cy1[2] = 24'd2091876; 97 | cy2[2] = 24'd0; 98 | 99 | // Arcade LPF 4khz 1st 100 | flt_rate[3] = 32'd7056000; 101 | cx[3] = 40'd851040; 102 | cx0[3] = 8'd3; 103 | cx1[3] = 8'd3; 104 | cx2[3] = 8'd1; 105 | cy0[3] = -24'd6231182; 106 | cy1[3] = 24'd6171753; 107 | cy2[3] = -24'd2037720; 108 | 109 | // Arcade LPF 4khz 2nd 110 | flt_rate[4] = 32'd7056000; 111 | cx[4] = 40'd9670619; 112 | cx0[4] = 8'd2; 113 | cx1[4] = 8'd1; 114 | cx2[4] = 8'd0; 115 | cy0[4] = -24'd4183740; 116 | cy1[4] = 24'd2086614; 117 | cy2[4] = 24'd0; 118 | 119 | // Arcade LPF 6khz 1st 120 | flt_rate[5] = 32'd7056000; 121 | cx[5] = 40'd1275428; 122 | cx0[5] = 8'd3; 123 | cx1[5] = 8'd3; 124 | cx2[5] = 8'd1; 125 | cy0[5] = -24'd6227464; 126 | cy1[5] = 24'd6164410; 127 | cy2[5] = -24'd2034094; 128 | 129 | // Arcade LPF 6khz 2nd 130 | flt_rate[6] = 32'd7056000; 131 | cx[6] = 40'd21731566; 132 | cx0[6] = 8'd2; 133 | cx1[6] = 8'd1; 134 | cx2[6] = 8'd0; 135 | cy0[6] = -24'd4178458; 136 | cy1[6] = 24'd2081365; 137 | cy2[6] = 24'd0; 138 | 139 | // Arcade LPF 8khz 1st 140 | flt_rate[7] = 32'd7056000; 141 | cx[7] = 40'd1699064; 142 | cx0[7] = 8'd3; 143 | cx1[7] = 8'd3; 144 | cx2[7] = 8'd1; 145 | cy0[7] = -24'd6223752; 146 | cy1[7] = 24'd6157080; 147 | cy2[7] = -24'd2030475; 148 | 149 | // Arcade LPF 8khz 2nd 150 | flt_rate[8] = 32'd7056000; 151 | cx[8] = 40'd38585417; 152 | cx0[8] = 8'd2; 153 | cx1[8] = 8'd1; 154 | cx2[8] = 8'd0; 155 | cy0[8] = -24'd4173176; 156 | cy1[8] = 24'd2076130; 157 | cy2[8] = 24'd0; 158 | 159 | end 160 | 161 | // [ 9 ] LPF 10khz 1st + Aa 162 | // [ 10 ] LPF 12khz 1st + Aa 163 | // [ 11 ] LPF 14khz 1st + Aa 164 | // [ 12 ] LPF 16khz 1st + Aa 165 | // [ 13 ] LPF 16khz 3rd Ch 1db 166 | // [ 14 ] LPF 18khz 3rd Ch 1db 167 | // [ 15 ] LPF 20khz 2nd Bw 168 | // [ 16 ] LPF 20khz 3rd Bw 169 | // [ 17 ] LPF 20khz 3rd Ch 1db 170 | 171 | // [ 18 ] SNES Gpm-02 LPF 172 | 173 | endmodule 174 | -------------------------------------------------------------------------------- /rtl/gb/audio/filters/iir_filter.sv: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SPDX-License-Identifier: GPL-3.0-or-later 3 | // SPDX-FileType: SOURCE 4 | // SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors 5 | //------------------------------------------------------------------------------ 6 | // 7 | // 3-tap IIR filter for 2 channels. 8 | // 9 | // Copyright (c) 2023, Marcus Andrade 10 | // Copyright (c) 2020, Alexey Melnikov 11 | // 12 | // This source file is free software: you can redistribute it and/or modify 13 | // it under the terms of the GNU General Public License as published 14 | // by the Free Software Foundation, either version 3 of the License, or 15 | // (at your option) any later version. 16 | // 17 | // This program is distributed in the hope that it will be useful, but 18 | // WITHOUT ANY WARRANTY; without even the implied warranty of 19 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | // General Public License for more details. 21 | // 22 | // You should have received a copy of the GNU General Public License 23 | // along with this program. If not, see . 24 | // 25 | //------------------------------------------------------------------------------ 26 | // 27 | // Can be converted to: 28 | // - 2-tap (coeff_x2 = 0, coeff_y2 = 0) 29 | // - 1-tap (coeff_x1,2 = 0, coeff_y1,2 = 0) 30 | // 31 | //------------------------------------------------------------------------------ 32 | 33 | `default_nettype none 34 | 35 | module iir_filter 36 | #( 37 | parameter use_params = 1, // set to 1 to use following parameters, 0 for input port variables. 38 | parameter stereo = 1, // 0 for mono (input_l) 39 | 40 | parameter coeff_x = 0.00000774701983513660, // Base gain value for X. Float. Range: 0.0 ... 0.999(9) 41 | parameter coeff_x0 = 3, // Gain scale factor for X0. Integer. Range -7 ... +7 42 | parameter coeff_x1 = 3, // Gain scale factor for X1. Integer. Range -7 ... +7 43 | parameter coeff_x2 = 1, // Gain scale factor for X2. Integer. Range -7 ... +7 44 | parameter coeff_y0 = -2.96438150626551080000, // Coefficient for Y0. Float. Range -3.999(9) ... 3.999(9) 45 | parameter coeff_y1 = 2.92939452735121100000, // Coefficient for Y1. Float. Range -3.999(9) ... 3.999(9) 46 | parameter coeff_y2 = -0.96500747158831091000 // Coefficient for Y2. Float. Range -3.999(9) ... 3.999(9) 47 | ) ( 48 | input clk, 49 | input reset, 50 | 51 | input ce, // must be double of calculated rate for stereo! 52 | input sample_ce, // desired output sample rate 53 | 54 | input [39:0] cx, 55 | input [7:0] cx0, 56 | input [7:0] cx1, 57 | input [7:0] cx2, 58 | input [23:0] cy0, 59 | input [23:0] cy1, 60 | input [23:0] cy2, 61 | 62 | input [15:0] input_l, input_r, // signed samples 63 | output [15:0] output_l, output_r // signed samples 64 | ); 65 | 66 | localparam [39:0] pcoeff_x = coeff_x * 40'h8000000000; 67 | localparam [31:0] pcoeff_y0 = coeff_y0 * 24'h200000; 68 | localparam [31:0] pcoeff_y1 = coeff_y1 * 24'h200000; 69 | localparam [31:0] pcoeff_y2 = coeff_y2 * 24'h200000; 70 | 71 | wire [39:0] vcoeff = use_params ? pcoeff_x : cx; 72 | wire [23:0] vcoeff_y0 = use_params ? pcoeff_y0[23:0] : cy0; 73 | wire [23:0] vcoeff_y1 = use_params ? pcoeff_y1[23:0] : cy1; 74 | wire [23:0] vcoeff_y2 = use_params ? pcoeff_y2[23:0] : cy2; 75 | 76 | wire [59:0] inp_mul = $signed(inp) * $signed(vcoeff); 77 | 78 | wire [39:0] x = inp_mul[59:20]; 79 | wire [39:0] y = x + tap0; 80 | 81 | wire [39:0] tap0; 82 | iir_filter_tap iir_tap_0 83 | ( 84 | .clk ( clk ), 85 | .reset ( reset ), 86 | .ce ( ce ), 87 | .ch ( ch ), 88 | .cx ( use_params ? coeff_x0[7:0] : cx0 ), 89 | .cy ( vcoeff_y0 ), 90 | .x ( x ), 91 | .y ( y ), 92 | .z ( tap1 ), 93 | .tap ( tap0 ) 94 | ); 95 | 96 | wire [39:0] tap1; 97 | iir_filter_tap iir_tap_1 98 | ( 99 | .clk ( clk ), 100 | .reset ( reset ), 101 | .ce ( ce ), 102 | .ch ( ch ), 103 | .cx ( use_params ? coeff_x1[7:0] : cx1 ), 104 | .cy ( vcoeff_y1 ), 105 | .x ( x ), 106 | .y ( y ), 107 | .z ( tap2 ), 108 | .tap ( tap1 ) 109 | ); 110 | 111 | wire [39:0] tap2; 112 | iir_filter_tap iir_tap_2 113 | ( 114 | .clk ( clk ), 115 | .reset ( reset ), 116 | .ce ( ce ), 117 | .ch ( ch ), 118 | .cx ( use_params ? coeff_x2[7:0] : cx2 ), 119 | .cy ( vcoeff_y2 ), 120 | .x ( x ), 121 | .y ( y ), 122 | .z ( 0 ), 123 | .tap ( tap2 ) 124 | ); 125 | 126 | wire [15:0] y_clamp = (~y[39] & |y[38:35]) ? 16'h7FFF : (y[39] & ~&y[38:35]) ? 16'h8000 : y[35:20]; 127 | 128 | reg ch = 0; 129 | reg [15:0] out_l, out_r, out_m; 130 | reg [15:0] inp, inp_m; 131 | 132 | always @(posedge clk) begin 133 | if (ce) begin 134 | if(!stereo) begin 135 | ch <= 0; 136 | inp <= input_l; 137 | out_l <= y_clamp; 138 | out_r <= y_clamp; 139 | end 140 | else begin 141 | ch <= ~ch; 142 | if(ch) begin 143 | out_m <= y_clamp; 144 | inp <= inp_m; 145 | end 146 | else begin 147 | out_l <= out_m; 148 | out_r <= y_clamp; 149 | inp <= input_l; 150 | inp_m <= input_r; 151 | end 152 | end 153 | end 154 | end 155 | 156 | reg [31:0] out; 157 | always @(posedge clk) begin 158 | if (sample_ce) begin 159 | out <= {out_l, out_r}; 160 | end 161 | end 162 | 163 | assign {output_l, output_r} = out; 164 | 165 | endmodule 166 | -------------------------------------------------------------------------------- /rtl/gb/audio/filters/iir_filter_tap.sv: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SPDX-License-Identifier: GPL-3.0-or-later 3 | // SPDX-FileType: SOURCE 4 | // SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors 5 | //------------------------------------------------------------------------------ 6 | // 7 | // Infinite Impulse Response (IIR) filter tap. 8 | // 9 | // Copyright (c) 2023, Marcus Andrade 10 | // Copyright (c) 2020, Alexey Melnikov 11 | // 12 | // This source file is free software: you can redistribute it and/or modify 13 | // it under the terms of the GNU General Public License as published 14 | // by the Free Software Foundation, either version 3 of the License, or 15 | // (at your option) any later version. 16 | // 17 | // This program is distributed in the hope that it will be useful, but 18 | // WITHOUT ANY WARRANTY; without even the implied warranty of 19 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | // General Public License for more details. 21 | // 22 | // You should have received a copy of the GNU General Public License 23 | // along with this program. If not, see . 24 | // 25 | //------------------------------------------------------------------------------ 26 | // 27 | // The iir_filter_tap module implements an infinite impulse response (IIR) filter 28 | // with one tap. It takes in input data x, previous output data y, and an input z 29 | // and calculates the output tap using a set of coefficients cx and cy. The 30 | // coefficients are used to multiply the input data and previous output data, 31 | // and the resulting values are then added together to produce the output tap. 32 | // The module stores intermediate values in a logic RAM, and updates the values 33 | // on each clock cycle based on the control signals ce and ch. The module uses 34 | // a Verilog function to perform the multiplication of the input data with the 35 | // coefficients. 36 | // 37 | //------------------------------------------------------------------------------ 38 | 39 | `default_nettype none 40 | 41 | module iir_filter_tap 42 | ( 43 | input wire clk, // clock signal 44 | input wire reset, // reset signal 45 | 46 | input wire ce, // control signal 47 | input wire ch, // control signal 48 | 49 | input wire [7:0] cx, // coefficient value 50 | input wire [23:0] cy, // coefficient value 51 | 52 | input wire [39:0] x, // input data 53 | input wire [39:0] y, // previous output data 54 | input wire [39:0] z, // input data 55 | output wire [39:0] tap // output data 56 | ); 57 | 58 | // multiply previous output y with coefficient cy 59 | wire signed [60:0] y_mul = $signed(y[36:0]) * $signed(cy); 60 | 61 | // multiply input x with coefficient cx 62 | function [39:0] x_mul; 63 | input [39:0] x; 64 | begin 65 | x_mul = 0; 66 | if(cx[0]) x_mul = x_mul + {{4{x[39]}}, x[39:4]}; // multiply x[39:36] with cx[0] 67 | if(cx[1]) x_mul = x_mul + {{3{x[39]}}, x[39:3]}; // multiply x[39:35] with cx[1] 68 | if(cx[2]) x_mul = x_mul + {{2{x[39]}}, x[39:2]}; // multiply x[39:34] with cx[2] 69 | if(cx[7]) x_mul = ~x_mul; // negate result if cx[7] is set 70 | end 71 | endfunction 72 | 73 | // use logic RAM to store intermediate values 74 | (* ramstyle = "logic" *) reg [39:0] intreg[2]; 75 | always @(posedge clk, posedge reset) begin 76 | if(reset) begin 77 | {intreg[0],intreg[1]} <= 80'd0; 78 | end 79 | else if(ce) begin 80 | // update intreg[ch] with new value 81 | intreg[ch] <= x_mul(x) - y_mul[60:21] + z; 82 | // multiply input x with cx, subtract a portion of previous output y (specified by cy), 83 | // add input z, and store the result in intreg[ch] 84 | end 85 | end 86 | 87 | // assign output tap to value in intreg corresponding to ch signal 88 | assign tap = intreg[ch]; 89 | 90 | endmodule 91 | -------------------------------------------------------------------------------- /rtl/gb/audio/mf_audio_pll.ppf: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /rtl/gb/audio/mf_audio_pll/mf_audio_pll_0002.qip: -------------------------------------------------------------------------------- 1 | set_instance_assignment -name PLL_COMPENSATION_MODE DIRECT -to "*mf_audio_pll_0002*|altera_pll:altera_pll_i*|*" 2 | set_instance_assignment -name PLL_CHANNEL_SPACING "0.0 KHz" -to "*mf_audio_pll_0002*|altera_pll:altera_pll_i*|*" 3 | set_instance_assignment -name PLL_AUTO_RESET OFF -to "*mf_audio_pll_0002*|altera_pll:altera_pll_i*|*" 4 | set_instance_assignment -name PLL_BANDWIDTH_PRESET AUTO -to "*mf_audio_pll_0002*|altera_pll:altera_pll_i*|*" 5 | -------------------------------------------------------------------------------- /rtl/gb/audio/mf_audio_pll/mf_audio_pll_0002.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/10ps 2 | module mf_audio_pll_0002( 3 | 4 | // interface 'refclk' 5 | input wire refclk, 6 | 7 | // interface 'reset' 8 | input wire rst, 9 | 10 | // interface 'outclk0' 11 | output wire outclk_0, 12 | 13 | // interface 'outclk1' 14 | output wire outclk_1, 15 | 16 | // interface 'locked' 17 | output wire locked 18 | ); 19 | 20 | altera_pll #( 21 | .fractional_vco_multiplier("true"), 22 | .reference_clock_frequency("74.25 MHz"), 23 | .operation_mode("direct"), 24 | .number_of_clocks(2), 25 | .output_clock_frequency0("12.288000 MHz"), 26 | .phase_shift0("0 ps"), 27 | .duty_cycle0(50), 28 | .output_clock_frequency1("3.072000 MHz"), 29 | .phase_shift1("0 ps"), 30 | .duty_cycle1(50), 31 | .output_clock_frequency2("0 MHz"), 32 | .phase_shift2("0 ps"), 33 | .duty_cycle2(50), 34 | .output_clock_frequency3("0 MHz"), 35 | .phase_shift3("0 ps"), 36 | .duty_cycle3(50), 37 | .output_clock_frequency4("0 MHz"), 38 | .phase_shift4("0 ps"), 39 | .duty_cycle4(50), 40 | .output_clock_frequency5("0 MHz"), 41 | .phase_shift5("0 ps"), 42 | .duty_cycle5(50), 43 | .output_clock_frequency6("0 MHz"), 44 | .phase_shift6("0 ps"), 45 | .duty_cycle6(50), 46 | .output_clock_frequency7("0 MHz"), 47 | .phase_shift7("0 ps"), 48 | .duty_cycle7(50), 49 | .output_clock_frequency8("0 MHz"), 50 | .phase_shift8("0 ps"), 51 | .duty_cycle8(50), 52 | .output_clock_frequency9("0 MHz"), 53 | .phase_shift9("0 ps"), 54 | .duty_cycle9(50), 55 | .output_clock_frequency10("0 MHz"), 56 | .phase_shift10("0 ps"), 57 | .duty_cycle10(50), 58 | .output_clock_frequency11("0 MHz"), 59 | .phase_shift11("0 ps"), 60 | .duty_cycle11(50), 61 | .output_clock_frequency12("0 MHz"), 62 | .phase_shift12("0 ps"), 63 | .duty_cycle12(50), 64 | .output_clock_frequency13("0 MHz"), 65 | .phase_shift13("0 ps"), 66 | .duty_cycle13(50), 67 | .output_clock_frequency14("0 MHz"), 68 | .phase_shift14("0 ps"), 69 | .duty_cycle14(50), 70 | .output_clock_frequency15("0 MHz"), 71 | .phase_shift15("0 ps"), 72 | .duty_cycle15(50), 73 | .output_clock_frequency16("0 MHz"), 74 | .phase_shift16("0 ps"), 75 | .duty_cycle16(50), 76 | .output_clock_frequency17("0 MHz"), 77 | .phase_shift17("0 ps"), 78 | .duty_cycle17(50), 79 | .pll_type("General"), 80 | .pll_subtype("General") 81 | ) altera_pll_i ( 82 | .rst (rst), 83 | .outclk ({outclk_1, outclk_0}), 84 | .locked (locked), 85 | .fboutclk ( ), 86 | .fbclk (1'b0), 87 | .refclk (refclk) 88 | ); 89 | endmodule 90 | 91 | -------------------------------------------------------------------------------- /rtl/gb/bus_savestates.vhd: -------------------------------------------------------------------------------- 1 | ----------------------------------------------------------------- 2 | --------------- Bus Package -------------------------------- 3 | ----------------------------------------------------------------- 4 | 5 | library IEEE; 6 | use IEEE.std_logic_1164.all; 7 | use IEEE.numeric_std.all; 8 | 9 | package pBus_savestates is 10 | 11 | constant BUS_buswidth : integer := 64; 12 | constant BUS_busadr : integer := 10; 13 | 14 | type regmap_type is record 15 | Adr : integer range 0 to (2**BUS_busadr)-1; 16 | upper : integer range 0 to BUS_buswidth-1; 17 | lower : integer range 0 to BUS_buswidth-1; 18 | size : integer range 0 to (2**BUS_busadr)-1; 19 | default : std_logic_vector(BUS_buswidth-1 downto 0); 20 | end record; 21 | 22 | end package; 23 | 24 | ----------------------------------------------------------------- 25 | --------------- Reg Interface, verbose for Verilog -------------- 26 | ----------------------------------------------------------------- 27 | 28 | library IEEE; 29 | use IEEE.std_logic_1164.all; 30 | use IEEE.numeric_std.all; 31 | 32 | library work; 33 | use work.pBus_savestates.all; 34 | 35 | entity eReg_SavestateV is 36 | generic 37 | ( 38 | index : integer := 0; 39 | Adr : integer range 0 to (2**BUS_busadr)-1; 40 | upper : integer range 0 to BUS_buswidth-1; 41 | lower : integer range 0 to BUS_buswidth-1; 42 | def : std_logic_vector(BUS_buswidth-1 downto 0) 43 | ); 44 | port 45 | ( 46 | clk : in std_logic; 47 | BUS_Din : in std_logic_vector(BUS_buswidth-1 downto 0); 48 | BUS_Adr : in std_logic_vector(BUS_busadr-1 downto 0); 49 | BUS_wren : in std_logic; 50 | BUS_rst : in std_logic; 51 | BUS_Dout : out std_logic_vector(BUS_buswidth-1 downto 0) := (others => '0'); 52 | Din : in std_logic_vector(upper downto lower); 53 | Dout : out std_logic_vector(upper downto lower) 54 | ); 55 | end entity; 56 | 57 | architecture arch of eReg_SavestateV is 58 | 59 | signal Dout_buffer : std_logic_vector(upper downto lower) := def(upper downto lower); 60 | 61 | signal AdrI : std_logic_vector(BUS_Adr'left downto 0); 62 | 63 | begin 64 | 65 | AdrI <= std_logic_vector(to_unsigned(Adr + index, BUS_Adr'length)); 66 | 67 | process (clk) 68 | begin 69 | if rising_edge(clk) then 70 | 71 | if (BUS_rst = '1') then 72 | 73 | Dout_buffer <= def(upper downto lower); 74 | 75 | else 76 | 77 | if (BUS_Adr = AdrI and BUS_wren = '1') then 78 | for i in lower to upper loop 79 | Dout_buffer(i) <= BUS_Din(i); 80 | end loop; 81 | end if; 82 | 83 | end if; 84 | 85 | end if; 86 | end process; 87 | 88 | Dout <= Dout_buffer; 89 | 90 | goutputbit: for i in lower to upper generate 91 | BUS_Dout(i) <= Din(i) when BUS_Adr = AdrI else '0'; 92 | end generate; 93 | 94 | glowzero_required: if lower > 0 generate 95 | glowzero: for i in 0 to lower - 1 generate 96 | BUS_Dout(i) <= '0'; 97 | end generate; 98 | end generate; 99 | 100 | ghighzero_required: if upper < BUS_buswidth-1 generate 101 | ghighzero: for i in upper + 1 to BUS_buswidth-1 generate 102 | BUS_Dout(i) <= '0'; 103 | end generate; 104 | end generate; 105 | 106 | end architecture; 107 | 108 | ----------------------------------------------------------------- 109 | --------------- Reg Interface, nonverbose ----------------------- 110 | ----------------------------------------------------------------- 111 | 112 | library IEEE; 113 | use IEEE.std_logic_1164.all; 114 | use IEEE.numeric_std.all; 115 | 116 | library work; 117 | use work.pBus_savestates.all; 118 | 119 | entity eReg_Savestate is 120 | generic 121 | ( 122 | Reg : regmap_type; 123 | index : integer := 0 124 | ); 125 | port 126 | ( 127 | clk : in std_logic; 128 | BUS_Din : in std_logic_vector(BUS_buswidth-1 downto 0); 129 | BUS_Adr : in std_logic_vector(BUS_busadr-1 downto 0); 130 | BUS_wren : in std_logic; 131 | BUS_rst : in std_logic; 132 | BUS_Dout : out std_logic_vector(BUS_buswidth-1 downto 0) := (others => '0'); 133 | Din : in std_logic_vector(Reg.upper downto Reg.lower); 134 | Dout : out std_logic_vector(Reg.upper downto Reg.lower) 135 | ); 136 | end entity; 137 | 138 | architecture arch of eReg_Savestate is 139 | 140 | begin 141 | 142 | iReg_SavestateV : entity work.eReg_SavestateV 143 | generic map 144 | ( 145 | index => index, 146 | Adr => Reg.Adr, 147 | upper => Reg.upper, 148 | lower => Reg.lower, 149 | def => Reg.default 150 | ) 151 | port map 152 | ( 153 | clk => clk, 154 | BUS_Din => BUS_Din, 155 | BUS_Adr => BUS_Adr, 156 | BUS_wren => BUS_wren, 157 | BUS_rst => BUS_rst, 158 | BUS_Dout => BUS_Dout, 159 | Din => Din, 160 | Dout => Dout 161 | ); 162 | 163 | 164 | end architecture; 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /rtl/gb/cheatcodes.sv: -------------------------------------------------------------------------------- 1 | // Cheat Code handling by Kitrinx 2 | // Apr 21, 2019 3 | 4 | // Code layout: 5 | // {clock bit, code flags, 32'b address, 32'b compare, 32'b replace} 6 | // 128 127:96 95:64 63:32 31:0 7 | // Integer values are in BIG endian byte order, so it up to the loader 8 | // or generator of the code to re-arrange them correctly. 9 | 10 | module CODES( 11 | input clk, // Best to not make it too high speed for timing reasons 12 | input reset, // This should only be triggered when a new rom is loaded or before new codes load, not warm reset 13 | input enable, 14 | output available, 15 | input [ADDR_WIDTH - 1:0] addr_in, 16 | input [DATA_WIDTH - 1:0] data_in, 17 | input [128:0] code, 18 | output genie_ovr, 19 | output [DATA_WIDTH - 1:0] genie_data 20 | ); 21 | 22 | parameter ADDR_WIDTH = 16; // Not more than 32 23 | parameter DATA_WIDTH = 8; // Not more than 32 24 | parameter MAX_CODES = 32; 25 | 26 | localparam INDEX_SIZE = $clog2(MAX_CODES-1); // Number of bits for index, must accomodate MAX_CODES 27 | 28 | localparam DATA_S = DATA_WIDTH - 1; 29 | localparam COMP_S = DATA_S + DATA_WIDTH; 30 | localparam ADDR_S = COMP_S + ADDR_WIDTH; 31 | localparam COMP_F_S = ADDR_S + 1; 32 | localparam ENA_F_S = COMP_F_S + 1; 33 | localparam CODE_WIDTH = ENA_F_S + 1; 34 | 35 | reg [ENA_F_S:0] codes[MAX_CODES]; 36 | 37 | wire [ADDR_WIDTH-1: 0] code_addr = code[64+:ADDR_WIDTH]; 38 | wire [DATA_WIDTH-1: 0] code_compare = code[32+:DATA_WIDTH]; 39 | wire [DATA_WIDTH-1: 0] code_data = code[0+:DATA_WIDTH]; 40 | wire code_comp_f = code[96]; 41 | 42 | wire [COMP_F_S:0] code_trimmed = {code_comp_f, code_addr, code_compare, code_data}; 43 | 44 | // If MAX_INDEX is changes, these need to be made larger 45 | wire [INDEX_SIZE-1:0] index, dup_index; 46 | reg [INDEX_SIZE:0] next_index; 47 | wire found_dup; 48 | 49 | assign index = found_dup ? dup_index : next_index[INDEX_SIZE-1:0]; 50 | 51 | // See if the code exists already, so it can be disabled if loaded again 52 | always_comb begin 53 | int x; 54 | dup_index = 0; 55 | found_dup = 0; 56 | 57 | for (x = 0; x < MAX_CODES; x = x + 1) begin 58 | if (codes[x][ADDR_S-:ADDR_WIDTH] == code_addr) begin 59 | dup_index = x[INDEX_SIZE-1:0]; 60 | found_dup = 1; 61 | end 62 | end 63 | end 64 | 65 | assign available = |next_index; 66 | 67 | reg code_change; 68 | always_ff @(posedge clk) begin 69 | int x; 70 | if (reset) begin 71 | next_index <= 0; 72 | code_change <= 0; 73 | for (x = 0; x < MAX_CODES; x = x + 1) codes[x] <= '0; 74 | end else begin 75 | code_change <= code[128]; 76 | if (code[128] && ~code_change && (found_dup || next_index < MAX_CODES)) begin // detect posedge 77 | // replace it enabled if it has the same address, otherwise, add a new code 78 | codes[index] <= {1'b1, code_trimmed}; 79 | if (~found_dup) next_index <= next_index + 1'b1; 80 | end 81 | end 82 | end 83 | 84 | always_comb begin 85 | int x; 86 | genie_ovr = 0; 87 | genie_data = '0; 88 | 89 | if (enable) begin 90 | for (x = 0; x < MAX_CODES; x = x + 1) begin 91 | if (codes[x][ENA_F_S] && codes[x][ADDR_S-:ADDR_WIDTH] == addr_in) begin 92 | if (!codes[x][COMP_F_S] || (codes[x][COMP_S-:DATA_WIDTH] == data_in)) begin 93 | genie_ovr = 1; 94 | genie_data = codes[x][DATA_S-:DATA_WIDTH]; 95 | end 96 | end 97 | end 98 | end 99 | end 100 | 101 | endmodule 102 | -------------------------------------------------------------------------------- /rtl/gb/ddram.sv: -------------------------------------------------------------------------------- 1 | // 2 | // ddram.v 3 | // Copyright (c) 2019 Sorgelig 4 | // 5 | // 6 | // This source file is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published 8 | // by the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // This source file is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | // 19 | // ------------------------------------------ 20 | // 21 | 22 | module ddram 23 | ( 24 | input DDRAM_CLK, 25 | input DDRAM_BUSY, 26 | output [7:0] DDRAM_BURSTCNT, 27 | output [28:0] DDRAM_ADDR, 28 | input [63:0] DDRAM_DOUT, 29 | input DDRAM_DOUT_READY, 30 | output DDRAM_RD, 31 | output [63:0] DDRAM_DIN, 32 | output [7:0] DDRAM_BE, 33 | output DDRAM_WE, 34 | 35 | // save state 36 | input [27:1] ch1_addr, 37 | output [63:0] ch1_dout, 38 | input [63:0] ch1_din, 39 | input ch1_req, 40 | input ch1_rnw, 41 | input [7:0] ch1_be, 42 | output ch1_ready 43 | ); 44 | 45 | reg [7:0] ram_burst; 46 | reg [63:0] ram_q[1:1]; 47 | reg [63:0] ram_data; 48 | reg [27:1] ram_address; 49 | reg ram_read = 0; 50 | reg ram_write = 0; 51 | reg [7:0] ram_be; 52 | 53 | reg [5:1] ready; 54 | 55 | assign DDRAM_BURSTCNT = ram_burst; 56 | assign DDRAM_BE = ram_read ? 8'hFF : ram_be; 57 | assign DDRAM_ADDR = {4'b0011, ram_address[27:3]}; // RAM at 0x30000000 58 | assign DDRAM_RD = ram_read; 59 | assign DDRAM_DIN = ram_data; 60 | assign DDRAM_WE = ram_write; 61 | 62 | assign ch1_dout = ram_q[1]; 63 | assign ch1_ready = ready[1]; 64 | 65 | reg state = 0; 66 | reg [0:0] ch = 0; 67 | reg [1:1] ch_rq; 68 | 69 | always @(posedge DDRAM_CLK) begin 70 | 71 | 72 | ch_rq <= ch_rq | {ch1_req}; 73 | ready <= 0; 74 | 75 | if(!DDRAM_BUSY) begin 76 | ram_write <= 0; 77 | ram_read <= 0; 78 | 79 | case(state) 80 | 0: if(ch_rq[1] || ch1_req) begin 81 | ch_rq[1] <= 0; 82 | ch <= 1; 83 | ram_data <= ch1_din; 84 | ram_be <= ch1_be; 85 | ram_address <= ch1_addr; 86 | ram_burst <= 1; 87 | if(~ch1_rnw) begin 88 | ram_write <= 1; 89 | ready[1] <= 1; 90 | end 91 | else begin 92 | ram_read <= 1; 93 | state <= 1; 94 | end 95 | end 96 | 97 | 1: if(DDRAM_DOUT_READY) begin 98 | ram_q[ch] <= DDRAM_DOUT; 99 | ready[ch] <= 1; 100 | state <= 0; 101 | end 102 | 103 | endcase 104 | end 105 | end 106 | 107 | endmodule 108 | -------------------------------------------------------------------------------- /rtl/gb/dpram.vhd: -------------------------------------------------------------------------------- 1 | LIBRARY ieee; 2 | USE ieee.std_logic_1164.all; 3 | 4 | LIBRARY altera_mf; 5 | USE altera_mf.altera_mf_components.all; 6 | 7 | ENTITY dpram IS 8 | generic ( 9 | addr_width : integer := 8; 10 | data_width : integer := 8 11 | ); 12 | PORT 13 | ( 14 | clock_a : IN STD_LOGIC; 15 | clken_a : IN STD_LOGIC := '1'; 16 | address_a : IN STD_LOGIC_VECTOR (addr_width-1 DOWNTO 0); 17 | data_a : IN STD_LOGIC_VECTOR (data_width-1 DOWNTO 0); 18 | wren_a : IN STD_LOGIC := '0'; 19 | q_a : OUT STD_LOGIC_VECTOR (data_width-1 DOWNTO 0); 20 | 21 | clock_b : IN STD_LOGIC; 22 | clken_b : IN STD_LOGIC := '1'; 23 | address_b : IN STD_LOGIC_VECTOR (addr_width-1 DOWNTO 0); 24 | data_b : IN STD_LOGIC_VECTOR (data_width-1 DOWNTO 0) := (others => '0'); 25 | wren_b : IN STD_LOGIC := '0'; 26 | q_b : OUT STD_LOGIC_VECTOR (data_width-1 DOWNTO 0) 27 | ); 28 | END dpram; 29 | 30 | 31 | ARCHITECTURE SYN OF dpram IS 32 | BEGIN 33 | altsyncram_component : altsyncram 34 | GENERIC MAP ( 35 | address_reg_b => "CLOCK1", 36 | clock_enable_input_a => "NORMAL", 37 | clock_enable_input_b => "NORMAL", 38 | clock_enable_output_a => "BYPASS", 39 | clock_enable_output_b => "BYPASS", 40 | indata_reg_b => "CLOCK1", 41 | intended_device_family => "Cyclone V", 42 | lpm_type => "altsyncram", 43 | numwords_a => 2**addr_width, 44 | numwords_b => 2**addr_width, 45 | operation_mode => "BIDIR_DUAL_PORT", 46 | outdata_aclr_a => "NONE", 47 | outdata_aclr_b => "NONE", 48 | outdata_reg_a => "UNREGISTERED", 49 | outdata_reg_b => "UNREGISTERED", 50 | power_up_uninitialized => "FALSE", 51 | read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ", 52 | read_during_write_mode_port_b => "NEW_DATA_NO_NBE_READ", 53 | widthad_a => addr_width, 54 | widthad_b => addr_width, 55 | width_a => data_width, 56 | width_b => data_width, 57 | width_byteena_a => 1, 58 | width_byteena_b => 1, 59 | wrcontrol_wraddress_reg_b => "CLOCK1" 60 | ) 61 | PORT MAP ( 62 | address_a => address_a, 63 | address_b => address_b, 64 | clock0 => clock_a, 65 | clock1 => clock_b, 66 | clocken0 => clken_a, 67 | clocken1 => clken_b, 68 | data_a => data_a, 69 | data_b => data_b, 70 | wren_a => wren_a, 71 | wren_b => wren_b, 72 | q_a => q_a, 73 | q_b => q_b 74 | ); 75 | 76 | END SYN; 77 | 78 | 79 | -------------------------------------------------------------- 80 | -- Dual port Block RAM different parameters on ports 81 | -------------------------------------------------------------- 82 | LIBRARY ieee; 83 | USE ieee.std_logic_1164.all; 84 | 85 | LIBRARY altera_mf; 86 | USE altera_mf.altera_mf_components.all; 87 | 88 | entity dpram_dif is 89 | generic ( 90 | addr_width_a : integer := 8; 91 | data_width_a : integer := 8; 92 | addr_width_b : integer := 8; 93 | data_width_b : integer := 8; 94 | mem_init_file : string := " " 95 | ); 96 | PORT 97 | ( 98 | clock : in STD_LOGIC; 99 | 100 | address_a : in STD_LOGIC_VECTOR (addr_width_a-1 DOWNTO 0); 101 | data_a : in STD_LOGIC_VECTOR (data_width_a-1 DOWNTO 0) := (others => '0'); 102 | enable_a : in STD_LOGIC := '1'; 103 | wren_a : in STD_LOGIC := '0'; 104 | q_a : out STD_LOGIC_VECTOR (data_width_a-1 DOWNTO 0); 105 | cs_a : in std_logic := '1'; 106 | 107 | address_b : in STD_LOGIC_VECTOR (addr_width_b-1 DOWNTO 0) := (others => '0'); 108 | data_b : in STD_LOGIC_VECTOR (data_width_b-1 DOWNTO 0) := (others => '0'); 109 | enable_b : in STD_LOGIC := '1'; 110 | wren_b : in STD_LOGIC := '0'; 111 | q_b : out STD_LOGIC_VECTOR (data_width_b-1 DOWNTO 0); 112 | cs_b : in std_logic := '1' 113 | ); 114 | end entity; 115 | 116 | 117 | ARCHITECTURE SYN OF dpram_dif IS 118 | 119 | signal q0 : std_logic_vector((data_width_a - 1) downto 0); 120 | signal q1 : std_logic_vector((data_width_b - 1) downto 0); 121 | 122 | signal wren_a_comb : std_logic; 123 | signal wren_b_comb : std_logic; 124 | 125 | BEGIN 126 | q_a<= q0 when cs_a = '1' else (others => '1'); 127 | q_b<= q1 when cs_b = '1' else (others => '1'); 128 | 129 | wren_a_comb <= wren_a and cs_a; 130 | wren_b_comb <= wren_b and cs_b; 131 | 132 | altsyncram_component : altsyncram 133 | GENERIC MAP ( 134 | address_reg_b => "CLOCK1", 135 | clock_enable_input_a => "NORMAL", 136 | clock_enable_input_b => "NORMAL", 137 | clock_enable_output_a => "BYPASS", 138 | clock_enable_output_b => "BYPASS", 139 | indata_reg_b => "CLOCK1", 140 | intended_device_family => "Cyclone V", 141 | lpm_type => "altsyncram", 142 | numwords_a => 2**addr_width_a, 143 | numwords_b => 2**addr_width_b, 144 | operation_mode => "BIDIR_DUAL_PORT", 145 | outdata_aclr_a => "NONE", 146 | outdata_aclr_b => "NONE", 147 | outdata_reg_a => "UNREGISTERED", 148 | outdata_reg_b => "UNREGISTERED", 149 | power_up_uninitialized => "FALSE", 150 | read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ", 151 | read_during_write_mode_port_b => "NEW_DATA_NO_NBE_READ", 152 | init_file => mem_init_file, 153 | widthad_a => addr_width_a, 154 | widthad_b => addr_width_b, 155 | width_a => data_width_a, 156 | width_b => data_width_b, 157 | width_byteena_a => 1, 158 | width_byteena_b => 1, 159 | wrcontrol_wraddress_reg_b => "CLOCK1" 160 | ) 161 | PORT MAP ( 162 | address_a => address_a, 163 | address_b => address_b, 164 | clock0 => clock, 165 | clock1 => clock, 166 | clocken0 => enable_a, 167 | clocken1 => enable_b, 168 | data_a => data_a, 169 | data_b => data_b, 170 | wren_a => wren_a_comb, 171 | wren_b => wren_b_comb, 172 | q_a => q0, 173 | q_b => q1 174 | ); 175 | 176 | END SYN; 177 | -------------------------------------------------------------------------------- /rtl/gb/gb.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "rtc_loader.sv"] 2 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "rumbler.sv"] 3 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "rtc_ram/rtc_ram.qip"] 4 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "audio/mf_audio_pll.qip"] 5 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T80/T80_Reg.vhd"] 6 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T80/T80_Pack.vhd"] 7 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T80/T80_MCode.vhd"] 8 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T80/T80_ALU.vhd"] 9 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T80/T80.vhd"] 10 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "T80/T80.qip"] 11 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "T80/GBse.vhd"] 12 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "video.v"] 13 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "timer.v"] 14 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "sprites.v"] 15 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "spram.vhd"] 16 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "savestate_ui.sv"] 17 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "reg_savestates.vhd"] 18 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "megaswizzle.sv"] 19 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "link.v"] 20 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "hdma.v"] 21 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "gbc_snd.vhd"] 22 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "gb_statemanager.vhd"] 23 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "gb_savestates.vhd"] 24 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "dpram.vhd"] 25 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "ddram.sv"] 26 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "cheatcodes.sv"] 27 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "bus_savestates.vhd"] 28 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "mappers/mappers.v"] 29 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "mappers/gb_camera.v"] 30 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio/filters/iir_filter_tap.sv"] 31 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio/filters/iir_filter.sv"] 32 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio/filters/filters_rom.sv"] 33 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio/filters/filter_loader.sv"] 34 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio/filters/dc_blocker.sv"] 35 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio/filters/audio_mix.sv"] 36 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio/filters/audio_filters.sv"] 37 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio/audio_mixer.sv"] 38 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "audio/audio.qip"] 39 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "speedcontrol.vhd"] 40 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "sgb.v"] 41 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "sdram.sv"] 42 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "lcd.v"] 43 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "gb.v"] 44 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "cart.v"] 45 | -------------------------------------------------------------------------------- /rtl/gb/gb_statemanager.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.std_logic_1164.all; 3 | use IEEE.numeric_std.all; 4 | 5 | entity gb_statemanager is 6 | generic 7 | ( 8 | Softmap_SaveState_ADDR : integer; -- count: 262144 -- 256 Kbyte Data for Savestate 9 | Softmap_Rewind_ADDR : integer -- count: 262144*128 -- 128*256 Kbyte Data for Savestates 10 | ); 11 | port 12 | ( 13 | clk : in std_logic; 14 | reset : in std_logic; 15 | 16 | rewind_on : in std_logic; 17 | rewind_active : in std_logic; 18 | 19 | savestate_number : in integer; 20 | save : in std_logic; 21 | load : in std_logic; 22 | 23 | sleep_rewind : out std_logic := '0'; 24 | vsync : in std_logic; 25 | 26 | request_savestate : out std_logic := '0'; 27 | request_loadstate : out std_logic := '0'; 28 | request_address : out integer; 29 | request_busy : in std_logic 30 | ); 31 | end entity; 32 | 33 | architecture arch of gb_statemanager is 34 | 35 | constant SAVESTATESIZE : integer := 16#10000#; -- 65536 Dwords = 256kbyte 36 | constant REWIND_COUNT : integer := 128; 37 | constant TIME_CAPTURE : integer := 10000000; -- 320 ms sim 1000000; 38 | constant TIME_REWIND : integer := 5000000; -- 160 ms sim 1200000; 39 | 40 | signal save_1 : std_logic := '0'; 41 | signal load_1 : std_logic := '0'; 42 | signal save_buffer : std_logic := '0'; 43 | signal load_buffer : std_logic := '0'; 44 | 45 | signal rewind_enabled : std_logic := '0'; 46 | signal rewind_load_next : std_logic := '0'; 47 | 48 | signal timer_rewind : integer range 0 to TIME_CAPTURE := 0; 49 | signal rewind_slow : integer range 0 to TIME_REWIND := 0; 50 | signal savestatecount : integer range 0 to REWIND_COUNT := 0; 51 | signal savestatepos : integer range 0 to REWIND_COUNT - 1 := 0; 52 | 53 | signal vsync_counter : integer range 0 to 2 := 0; 54 | 55 | signal vsync_1 : std_logic; 56 | 57 | begin 58 | 59 | process (clk) 60 | begin 61 | if rising_edge(clk) then 62 | 63 | request_savestate <= '0'; 64 | request_loadstate <= '0'; 65 | rewind_load_next <= '0'; 66 | 67 | vsync_1 <= vsync; 68 | 69 | save_1 <= save; 70 | if (save = '1' and save_1 = '0') then 71 | save_buffer <= '1'; 72 | end if; 73 | 74 | load_1 <= load; 75 | if (load = '1' and load_1 = '0') then 76 | load_buffer <= '1'; 77 | end if; 78 | 79 | if (rewind_on = '0' or reset = '1') then 80 | rewind_enabled <= '0'; 81 | end if; 82 | 83 | if (rewind_active = '0') then 84 | rewind_slow <= 0; 85 | elsif (rewind_slow < TIME_REWIND) then 86 | rewind_slow <= rewind_slow + 1; 87 | end if; 88 | 89 | if (rewind_active = '1') then 90 | timer_rewind <= 0; 91 | elsif (timer_rewind < TIME_CAPTURE) then 92 | timer_rewind <= timer_rewind + 1; 93 | end if; 94 | 95 | if (vsync_counter < 2 and vsync = '1' and vsync_1 = '0') then 96 | vsync_counter <= vsync_counter + 1; 97 | end if; 98 | 99 | sleep_rewind <= '0'; 100 | if (vsync_counter = 2 and rewind_active = '1') then 101 | sleep_rewind <= '1'; 102 | end if; 103 | 104 | if (reset = '0' and request_busy = '0') then 105 | 106 | if (save_buffer = '1') then 107 | request_address <= Softmap_SaveState_ADDR + (savestate_number * SAVESTATESIZE); 108 | request_savestate <= '1'; 109 | save_buffer <= '0'; 110 | elsif (load_buffer = '1') then 111 | request_address <= Softmap_SaveState_ADDR + (savestate_number * SAVESTATESIZE); 112 | request_loadstate <= '1'; 113 | load_buffer <= '0'; 114 | elsif (rewind_enabled = '0' and rewind_on = '1') then 115 | request_address <= Softmap_Rewind_ADDR; 116 | request_savestate <= '1'; 117 | rewind_enabled <= '1'; 118 | timer_rewind <= 0; 119 | savestatecount <= 1; 120 | savestatepos <= 1; 121 | elsif (rewind_enabled = '1' and timer_rewind = TIME_CAPTURE) then 122 | request_address <= Softmap_Rewind_ADDR + (savestatepos * SAVESTATESIZE); 123 | request_savestate <= '1'; 124 | timer_rewind <= 0; 125 | if (savestatecount < REWIND_COUNT) then 126 | savestatecount <= savestatecount + 1; 127 | end if; 128 | if (savestatepos < (REWIND_COUNT - 1)) then 129 | savestatepos <= savestatepos + 1; 130 | else 131 | savestatepos <= 0; 132 | end if; 133 | elsif (rewind_enabled = '1' and rewind_slow = TIME_REWIND) then 134 | if (savestatecount > 1) then 135 | savestatecount <= savestatecount - 1; 136 | if (savestatepos > 0) then 137 | savestatepos <= savestatepos - 1; 138 | else 139 | savestatepos <= REWIND_COUNT - 1; 140 | end if; 141 | rewind_load_next <= '1'; 142 | end if; 143 | rewind_slow <= 0; 144 | elsif (rewind_load_next = '1') then 145 | request_address <= Softmap_Rewind_ADDR + (savestatepos * SAVESTATESIZE); 146 | request_loadstate <= '1'; 147 | vsync_counter <= 0; 148 | end if; 149 | 150 | end if; 151 | 152 | end if; 153 | end process; 154 | 155 | 156 | end architecture; 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /rtl/gb/link.v: -------------------------------------------------------------------------------- 1 | module link #( 2 | parameter CLK_DIV = 511 3 | )( 4 | // system signals 5 | input clk_sys, 6 | input ce, 7 | input rst, 8 | 9 | input sel_sc, 10 | input sel_sb, 11 | input cpu_wr_n, 12 | input sc_start_in, 13 | input sc_int_clock_in, 14 | 15 | input [7:0] sb_in, 16 | 17 | input serial_clk_in, 18 | input serial_data_in, 19 | 20 | output serial_clk_out, 21 | output serial_data_out, 22 | output [7:0] sb, 23 | output serial_irq, 24 | output reg sc_start, 25 | output reg sc_int_clock, 26 | 27 | // savestates 28 | input [63:0] SaveStateBus_Din, 29 | input [9:0] SaveStateBus_Adr, 30 | input SaveStateBus_wren, 31 | input SaveStateBus_rst, 32 | output [63:0] SaveStateBus_Dout 33 | ); 34 | 35 | // savestates 36 | wire [16:0] SS_Link; 37 | wire [16:0] SS_Link_BACK; 38 | 39 | eReg_SavestateV #(0, 8, 16, 0, 64'h0000000000000000) iREG_SAVESTATE_HDMA (clk_sys, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_Dout, SS_Link_BACK, SS_Link); 40 | 41 | 42 | reg [7:0] sb_r = 0; 43 | assign sb = sb_r; 44 | 45 | reg [3:0] serial_counter; 46 | 47 | reg serial_out_r = 0; 48 | assign serial_data_out = serial_out_r; 49 | 50 | reg serial_clk_out_r = 1; 51 | assign serial_clk_out = serial_clk_out_r; 52 | 53 | reg serial_irq_r; 54 | assign serial_irq = serial_irq_r; 55 | 56 | reg [8:0] serial_clk_div; //8192Hz 57 | 58 | reg [1:0] serial_clk_in_last; 59 | 60 | assign SS_Link_BACK[ 0] = sc_start ; 61 | assign SS_Link_BACK[ 1] = sc_int_clock ; 62 | assign SS_Link_BACK[ 5: 2] = serial_counter ; 63 | assign SS_Link_BACK[ 6] = serial_out_r ; 64 | assign SS_Link_BACK[ 7] = serial_clk_out_r; 65 | assign SS_Link_BACK[16: 8] = serial_clk_div ; 66 | 67 | // serial master 68 | always @(posedge clk_sys) begin 69 | if(rst) begin 70 | sc_start <= SS_Link[ 0]; //1'b0; 71 | sc_int_clock <= SS_Link[ 1]; //1'b0; 72 | serial_counter <= SS_Link[ 5: 2]; //4'd0; 73 | serial_out_r <= SS_Link[ 6]; //1'b0; 74 | serial_clk_out_r <= SS_Link[ 7]; //1'b0; 75 | serial_clk_div <= SS_Link[16: 8]; //9'd0; 76 | 77 | sb_r <= sb_in; 78 | serial_clk_in_last <= {1'b0,serial_clk_in}; 79 | end else if (ce) begin 80 | serial_irq_r <= 1'b0; 81 | if (sel_sc && !cpu_wr_n) begin //cpu write 82 | sc_start <= sc_start_in; 83 | sc_int_clock <= sc_int_clock_in; 84 | if (sc_start_in) begin //enable transfer 85 | serial_clk_div <= CLK_DIV[8:0]; 86 | serial_counter <= 4'd8; 87 | serial_clk_out_r <= 1'b1; 88 | //serial_clk_in_last <= serial_clk_in; 89 | serial_clk_in_last <= {1'b0,serial_clk_in}; 90 | end 91 | end else if (sel_sb && !cpu_wr_n) begin 92 | sb_r <= sb_in; 93 | end else if (sc_start) begin // serial transfer 94 | if (sc_int_clock) begin // internal clock 95 | serial_clk_div <= serial_clk_div - 9'd1; 96 | 97 | if (serial_counter != 0) begin 98 | if (serial_clk_div == CLK_DIV/2+1) begin 99 | serial_clk_out_r <= ~serial_clk_out_r; 100 | serial_out_r <= sb[7]; 101 | end else if (!serial_clk_div) begin 102 | sb_r <= {sb[6:0], serial_data_in}; 103 | serial_clk_out_r <= ~serial_clk_out; 104 | serial_counter <= serial_counter - 1'd1; 105 | serial_clk_div <= CLK_DIV[8:0]; 106 | end 107 | end else begin 108 | serial_irq_r <= 1'b1; 109 | sc_start <= 1'b0; 110 | serial_clk_div <= CLK_DIV[8:0]; 111 | serial_counter <= 4'd8; 112 | end 113 | end else begin // external clock 114 | serial_clk_in_last[0] <= serial_clk_in; 115 | serial_clk_in_last[1] <= serial_clk_in_last[0] ; 116 | if (serial_clk_in_last[1] != serial_clk_in_last[0]) begin 117 | if (serial_clk_in_last[1] == 0) begin 118 | serial_out_r <= sb[7]; // send out bit to linked gb 119 | serial_counter <= serial_counter - 1'd1; 120 | end else begin // posedge external clock 121 | sb_r <= {sb[6:0], serial_data_in}; // capture bit into sb 122 | if (serial_counter == 0) begin // read in 8 bits? 123 | serial_irq_r <= 1'b1; // set interrupt, reset counter/sc_start for next read 124 | sc_start <= 1'b0; 125 | serial_counter <= 4'd8; 126 | end 127 | end 128 | end 129 | end 130 | end 131 | end 132 | end 133 | 134 | endmodule 135 | // vim:sw=3:ts=3:et: 136 | -------------------------------------------------------------------------------- /rtl/gb/mappers/gb_camera.v: -------------------------------------------------------------------------------- 1 | module gb_camera ( 2 | input enable, 3 | input reset, 4 | 5 | input clk_sys, 6 | input clk_cart, 7 | input ce_cpu, 8 | 9 | input savestate_load, 10 | input [15:0] savestate_data, 11 | inout [15:0] savestate_back_b, 12 | 13 | input [3:0] ram_mask, 14 | input [8:0] rom_mask, 15 | 16 | // Address in from the CPU 17 | input [14:0] cart_addr, 18 | input cart_a15, 19 | 20 | input nCS, 21 | 22 | input [7:0] cart_mbc_type, 23 | 24 | input cart_rd, 25 | input cart_wr, 26 | // Data in from CPU 27 | input [7:0] cart_di, 28 | output cart_oe, 29 | 30 | // Cart RAM data in from the virtual cart 31 | // input [7:0] cram_di, 32 | // Cart RAM data out from the mapper 33 | output [7:0] cram_do, 34 | // Cart RAM address out to hit the virtual cart 35 | // inout [16:0] cram_addr_b, 36 | 37 | output [22:0] mbc_addr, 38 | // inout ram_enabled_b, 39 | inout has_battery_b, 40 | 41 | output reg cam_en, 42 | 43 | output [7:4] cart_tran_bank0_out, 44 | input [7:0] cart_tran_bank1_in, 45 | output [7:0] cart_tran_bank1_out, 46 | output cart_tran_bank1_dir, 47 | output [7:0] cart_tran_bank2_out, 48 | output [7:0] cart_tran_bank3_out 49 | ); 50 | 51 | // wire ram_enabled; 52 | wire has_battery; 53 | wire [15:0] savestate_back; 54 | 55 | // assign ram_enabled_b = enable ? ram_enabled : 1'hZ; 56 | assign has_battery_b = enable ? has_battery : 1'hZ; 57 | assign savestate_back_b = enable ? savestate_back : 16'hZ; 58 | 59 | // --------------------- CPU register interface ------------------ 60 | 61 | // clock low, ~wr, ~rd, ~cs (mreq) = wr? 62 | // Try substituting ~cart_a15 for nCS 63 | assign cart_tran_bank0_out = {clk_cart, ~cart_wr, ~cart_rd, ~cart_a15}; 64 | 65 | assign cart_tran_bank1_out = cart_di; 66 | assign cram_do = cart_tran_bank1_in; 67 | assign cart_tran_bank1_dir = cart_wr; 68 | assign {cart_tran_bank2_out, cart_tran_bank3_out} = {cart_a15, cart_addr}; 69 | 70 | reg [5:0] rom_bank_reg; 71 | reg [3:0] ram_bank_reg; 72 | reg ram_write_en; 73 | // reg cam_en; 74 | 75 | always @(posedge clk_sys) begin 76 | if (~enable) begin 77 | rom_bank_reg <= 6'd1; 78 | ram_bank_reg <= 4'd0; 79 | cam_en <= 1'b0; 80 | ram_write_en <= 1'b0; 81 | end else if (ce_cpu) begin 82 | if (cart_wr & ~cart_a15) begin 83 | case (cart_addr[14:13]) 84 | 2'b01: rom_bank_reg <= cart_di[5:0]; //write to ROM bank register 85 | // TODO: Remove 86 | 2'b10: begin 87 | if (cart_di[4]) begin 88 | cam_en <= 1'b1; //enable CAM registers 89 | end else begin 90 | cam_en <= 1'b0; //enable RAM 91 | end 92 | end 93 | endcase 94 | end 95 | end 96 | end 97 | 98 | wire [3:0] ram_bank = ram_bank_reg & ram_mask[3:0]; 99 | 100 | // 0x0000-0x3FFF = Bank 0 101 | wire [5:0] rom_bank = (~cart_addr[14]) ? 6'd0 : rom_bank_reg; 102 | 103 | // mask address lines to enable proper mirroring 104 | wire [5:0] rom_bank_m = rom_bank & rom_mask[5:0]; //64 105 | 106 | assign mbc_addr = {3'b000, rom_bank_m, cart_addr[13:0]}; // 16k ROM Bank 0-63 107 | 108 | // assign cram_do = cam_en ? 8'h00 : cram_di; // Reading from RAM or CAM is always enabled 109 | // assign cram_addr = { ram_bank, cart_addr[12:0] }; 110 | 111 | wire is_cram_addr = ~nCS & ~cart_addr[14]; 112 | 113 | wire cram_rd = cart_rd & is_cram_addr; 114 | 115 | assign cart_oe = (cart_rd & ~cart_a15) | cram_rd; 116 | 117 | assign has_battery = 1; 118 | // assign ram_enabled = ~cam_en & ram_write_en; // Writing RAM 119 | 120 | endmodule 121 | -------------------------------------------------------------------------------- /rtl/gb/mappers/mappers.v: -------------------------------------------------------------------------------- 1 | module mappers ( 2 | input reset, 3 | 4 | input clk_sys, 5 | input clk_cart, 6 | input ce_cpu, 7 | input ce_cpu2x, 8 | input speed, 9 | 10 | input mbc1, 11 | input mbc1m, 12 | input mbc2, 13 | input mbc3, 14 | input mbc30, 15 | input mbc5, 16 | input mbc6, 17 | input mbc7, 18 | input mmm01, 19 | input huc1, 20 | input huc3, 21 | input gb_camera, 22 | input tama, 23 | input rocket, 24 | input sachen, 25 | input wisdom_tree, 26 | input mani161, 27 | 28 | input megaduck, 29 | 30 | input isGBC_game, 31 | 32 | input [15:0] joystick_analog_0, 33 | 34 | input ce_32k, 35 | input [32:0] RTC_time, 36 | output [31:0] RTC_timestampOut, 37 | output [47:0] RTC_savedtimeOut, 38 | output RTC_inuse, 39 | 40 | input savestate_load, 41 | input [15:0] savestate_data, 42 | output [15:0] savestate_back, 43 | input [63:0] savestate_data2, 44 | output [63:0] savestate_back2, 45 | 46 | input has_ram, 47 | input [3:0] ram_mask, 48 | input [8:0] rom_mask, 49 | 50 | input [14:0] cart_addr, 51 | input cart_a15, 52 | 53 | input [7:0] cart_mbc_type, 54 | 55 | input cart_rd, 56 | input cart_wr, 57 | input [7:0] cart_di, 58 | output cart_oe, 59 | 60 | input nCS, 61 | 62 | // input cram_rd, 63 | // input [ 7:0] cram_di, // input from Cart RAM q 64 | output [7:0] cram_do, // output to CPU 65 | // output [16:0] cram_addr, 66 | 67 | output [22:0] mbc_addr, 68 | // output ram_enabled, 69 | output has_battery, 70 | output rumbling, 71 | 72 | output [7:4] cart_tran_bank0_out, 73 | input [7:0] cart_tran_bank1_in, 74 | output [7:0] cart_tran_bank1_out, 75 | output cart_tran_bank1_dir, 76 | output [7:0] cart_tran_bank2_out, 77 | output [7:0] cart_tran_bank3_out 78 | ); 79 | 80 | tri0 has_battery_b; 81 | tri0 [15:0] savestate_back_b; 82 | tri0 [63:0] savestate_back2_b; 83 | tri0 [31:0] RTC_timestampOut_b; 84 | tri0 [47:0] RTC_savedtimeOut_b; 85 | tri0 RTC_inuse_b; 86 | 87 | 88 | wire ce = speed ? ce_cpu2x : ce_cpu; 89 | wire no_mapper = ~gb_camera; 90 | wire no_mapper_single_bank = no_mapper & ~rom_mask[1]; 91 | wire no_mapper_multi_bank = no_mapper & rom_mask[1]; // size > 32KB 92 | wire rom_override = (rocket); 93 | wire cart_oe_override = (mbc3 | mbc7 | huc1 | huc3 | gb_camera | tama); 94 | 95 | gb_camera map_gb_camera ( 96 | .enable(gb_camera), 97 | 98 | .clk_sys (clk_sys), 99 | .clk_cart(clk_cart), 100 | .ce_cpu (ce), 101 | 102 | .savestate_load (savestate_load), 103 | .savestate_data (savestate_data), 104 | .savestate_back_b(savestate_back_b), 105 | 106 | .ram_mask(ram_mask), 107 | .rom_mask(rom_mask), 108 | 109 | .cart_addr(cart_addr), 110 | .cart_a15 (cart_a15), 111 | 112 | .nCS(nCS), 113 | 114 | .cart_mbc_type(cart_mbc_type), 115 | 116 | .cart_rd(cart_rd), 117 | .cart_wr(cart_wr), 118 | .cart_di(cart_di), 119 | .cart_oe(cart_oe), 120 | 121 | // .cram_rd (cram_rd), 122 | // .cram_di (cram_di), 123 | .cram_do(cram_do), 124 | 125 | .mbc_addr (mbc_addr), 126 | // .ram_enabled_b(ram_enabled_b), 127 | .has_battery_b(has_battery_b), 128 | 129 | .cart_tran_bank0_out(cart_tran_bank0_out), 130 | .cart_tran_bank1_in (cart_tran_bank1_in), 131 | .cart_tran_bank1_out(cart_tran_bank1_out), 132 | .cart_tran_bank1_dir(cart_tran_bank1_dir), 133 | .cart_tran_bank2_out(cart_tran_bank2_out), 134 | .cart_tran_bank3_out(cart_tran_bank3_out) 135 | ); 136 | 137 | // assign {cram_do} = {cram_do_b}; 138 | assign {savestate_back, savestate_back2} = {savestate_back_b, savestate_back2_b}; 139 | assign {RTC_timestampOut, RTC_savedtimeOut, RTC_inuse} = { 140 | RTC_timestampOut_b, RTC_savedtimeOut_b, RTC_inuse_b 141 | }; 142 | // assign {cram_wr_do, cram_wr} = {cram_wr_do_b, cram_wr_b}; 143 | 144 | // assign mbc_addr = no_mapper_single_bank ? {8'd0, cart_addr[14:0]} : mbc_addr_b; 145 | // assign cram_addr = no_mapper_single_bank ? {4'd0, cart_addr[12:0]} : cram_addr_b; 146 | assign has_battery = no_mapper_single_bank ? (cart_mbc_type == 8'h09) : has_battery_b; 147 | // assign ram_enabled = no_mapper_single_bank ? has_ram : ram_enabled_b; 148 | 149 | endmodule 150 | -------------------------------------------------------------------------------- /rtl/gb/megaswizzle.sv: -------------------------------------------------------------------------------- 1 | module megaduck_swizzle 2 | ( 3 | input megaduck, 4 | input [15:0] a_in, 5 | output [15:0] a_out, 6 | 7 | input [7:0] snd_in_di, 8 | output [7:0] snd_in_do, 9 | 10 | input [7:0] snd_out_di, 11 | output [7:0] snd_out_do 12 | ); 13 | 14 | // Swizzle around MegaDuck register to match GB registers. 15 | 16 | always_comb begin 17 | a_out = a_in; 18 | snd_in_do = snd_in_di; 19 | snd_out_do = snd_out_di; 20 | if (megaduck) begin 21 | case (a_in) 22 | 16'hFF10: a_out = 16'hFF40; // LCDC 23 | 16'hFF11: a_out = 16'hFF41; // STAT 24 | 16'hFF12: a_out = 16'hFF42; // SCY 25 | 16'hFF13: a_out = 16'hFF43; // SCX 26 | 16'hFF18: a_out = 16'hFF44; // LY 27 | 16'hFF19: a_out = 16'hFF45; // LYC 28 | 16'hFF1A: a_out = 16'hFF46; // DMA 29 | 16'hFF1B: a_out = 16'hFF47; // BGP 30 | 16'hFF14: a_out = 16'hFF48; // OBP0 31 | 16'hFF15: a_out = 16'hFF49; // OBP1 32 | 16'hFF16: a_out = 16'hFF4A; // WY 33 | 16'hFF17: a_out = 16'hFF4B; // WX 34 | 35 | // Audio registers 36 | 16'hFF20: a_out = 16'hFF10; // NR10 37 | 16'hFF21: a_out = 16'hFF12; // NR12 38 | 16'hFF22: a_out = 16'hFF11; // NR11 39 | 16'hFF23: a_out = 16'hFF13; // NR13 40 | 16'hFF24: a_out = 16'hFF14; // NR14 41 | 16'hFF25: a_out = 16'hFF16; // NR21 42 | 16'hFF27: a_out = 16'hFF17; // NR22 43 | 16'hFF28: a_out = 16'hFF18; // NR23 44 | 16'hFF29: a_out = 16'hFF19; // NR24 45 | 16'hFF2A: a_out = 16'hFF1A; // NR30 46 | 16'hFF2B: a_out = 16'hFF1B; // NR31 47 | 16'hFF2C: a_out = 16'hFF1C; // NR32 48 | 16'hFF2D: a_out = 16'hFF1E; // NR34 49 | 16'hFF2E: a_out = 16'hFF1D; // NR33 50 | // The final 7 registers are after the audio ram 51 | 16'hFF40: a_out = 16'hFF20; // NR41 52 | 16'hFF41: a_out = 16'hFF22; // NR43 53 | 16'hFF42: a_out = 16'hFF21; // NR42 54 | 16'hFF43: a_out = 16'hFF23; // NR44 55 | 16'hFF44: a_out = 16'hFF24; // NR50 56 | 16'hFF45: a_out = 16'hFF26; // NR52 57 | 16'hFF46: a_out = 16'hFF25; // NR51 58 | default: a_out = a_in; 59 | endcase 60 | 61 | // Megaduck has reversed nybbles for some registers 62 | case (a_in[7:0]) 63 | // NR12, NR22, NR42, NR43 64 | 8'h21, 8'h27, 8'h42, 8'h41: begin 65 | snd_in_do = {snd_in_di[3:0], snd_in_di[7:4]}; 66 | snd_out_do = {snd_out_di[3:0], snd_out_di[7:4]}; 67 | end 68 | 69 | // NR32 swizzled volume bits 70 | // https://github.com/bbbbbr/hUGEDriver/commit/b7abcb10dae7b7ac6296568878474b35baee2bae 71 | // GB: Bits:6..5 : 00 = mute, 01 = 100%, 10 = 50%, 11 = 25% 72 | // MD: Bits:6..5 : 00 = mute, 11 = 100%, 10 = 50%, 01 = 25% 73 | 8'h2C: begin 74 | snd_in_do[6] = ^snd_in_di[6:5]; 75 | snd_out_do[6] = ^snd_out_di[6:5]; 76 | end 77 | 78 | default: ; 79 | endcase 80 | end 81 | end 82 | endmodule -------------------------------------------------------------------------------- /rtl/gb/reg_savestates.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.std_logic_1164.all; 3 | use IEEE.numeric_std.all; 4 | 5 | use work.pBus_savestates.all; 6 | 7 | package pReg_savestates is 8 | 9 | -- ( adr upper lower size default) 10 | 11 | -- cpu 12 | constant REG_SAVESTATE_GBSE : regmap_type := ( 0, 11, 0, 1, x"000000000000000F"); 13 | constant REG_SAVESTATE_CPUREGS : regmap_type := ( 1, 63, 0, 1, x"0000000000000000"); 14 | constant REG_SAVESTATE_T80_1 : regmap_type := ( 2, 61, 0, 1, x"0000000000000000"); 15 | constant REG_SAVESTATE_T80_2 : regmap_type := ( 3, 50, 0, 1, x"00000000FFFF0000"); 16 | constant REG_SAVESTATE_T80_3 : regmap_type := ( 4, 57, 0, 1, x"020000801FE0FFFF"); 17 | constant REG_SAVESTATE_T80_4 : regmap_type := ( 5, 52, 0, 1, x"0001000000000000"); 18 | 19 | -- components 20 | constant REG_SAVESTATE_Timer : regmap_type := ( 6, 46, 0, 1, x"0000000000000008"); 21 | 22 | constant REG_SAVESTATE_HDMA : regmap_type := ( 7, 44, 0, 1, x"0000000001FFFFF0"); 23 | 24 | constant REG_SAVESTATE_Link : regmap_type := ( 8, 16, 0, 1, x"0000000000000000"); 25 | 26 | constant REG_SAVESTATE_Video1 : regmap_type := ( 9, 60, 0, 1, x"0000000000000000"); 27 | constant REG_SAVESTATE_Video2 : regmap_type := ( 10, 63, 0, 1, x"00000000FFFFFC00"); 28 | constant REG_SAVESTATE_BPalette : regmap_type := ( 11, 63, 0, 8, x"0000000000000000"); 29 | constant REG_SAVESTATE_OPalette : regmap_type := ( 19, 63, 0, 8, x"0000000000000000"); 30 | constant REG_SAVESTATE_Video3 : regmap_type := ( 27, 63, 0, 1, x"0000000000000000"); 31 | 32 | constant REG_SAVESTATE_Sound1 : regmap_type := ( 28, 63, 0, 1, x"0000000000000000"); 33 | constant REG_SAVESTATE_Sound2 : regmap_type := ( 29, 54, 0, 1, x"0000000000000000"); 34 | constant REG_SAVESTATE_Sound3 : regmap_type := ( 30, 62, 0, 1, x"0000000000000000"); 35 | 36 | constant REG_SAVESTATE_Wave1 : regmap_type := ( 33, 63, 0, 1, x"C32987D2AA340448"); 37 | constant REG_SAVESTATE_Wave2 : regmap_type := ( 34, 63, 0, 1, x"ADE28B430B959506"); 38 | constant REG_SAVESTATE_Wave1_GBC : regmap_type := ( 35, 63, 0, 1, x"FF00FF00FF00FF00"); 39 | constant REG_SAVESTATE_Wave2_GBC : regmap_type := ( 36, 63, 0, 1, x"FF00FF00FF00FF00"); 40 | 41 | constant REG_SAVESTATE_Top : regmap_type := ( 31, 56, 0, 1, x"0000000000800001"); 42 | constant REG_SAVESTATE_Top2 : regmap_type := ( 38, 10, 0, 1, x"0000000000000000"); 43 | constant REG_SAVESTATE_Ext : regmap_type := ( 32, 15, 0, 1, x"0000000000000001"); 44 | constant REG_SAVESTATE_Ext2 : regmap_type := ( 37, 63, 0, 1, x"0000000000000000"); 45 | 46 | end package; 47 | -------------------------------------------------------------------------------- /rtl/gb/rtc_loader.sv: -------------------------------------------------------------------------------- 1 | module RTC_loader( 2 | input logic clk_sys, 3 | input logic reset, 4 | input logic external_reset_s, 5 | 6 | input logic cart_download, 7 | output logic loading_done, 8 | input logic RTC_valid, 9 | 10 | input logic [4:0] addr_in, 11 | input logic [15:0] data_in, 12 | input logic wr_in, 13 | 14 | output logic [16:0] addr_out, 15 | output logic [15:0] data_out, 16 | output logic wr_out 17 | ); 18 | 19 | assign addr_out = currRTCaddr; 20 | 21 | logic wr_in_old, rtc_loaded; 22 | 23 | always @(posedge clk_sys) begin 24 | wr_in_old <= wr_in; 25 | 26 | if(external_reset_s | cart_download) begin 27 | rtc_loaded <= 0; 28 | end else if(~wr_in_old & wr_in) begin 29 | rtc_loaded <= 1; 30 | end else begin 31 | rtc_loaded <= rtc_loaded; 32 | end 33 | end 34 | 35 | typedef enum { 36 | READ, 37 | WAIT, 38 | WRITE, 39 | INC, 40 | STOP 41 | } stateType; 42 | 43 | stateType currState, nextState; 44 | logic [16:0] currRTCaddr, nextRTCaddr; 45 | 46 | always_ff @(posedge clk_sys) begin 47 | if(reset) begin 48 | currState <= READ; 49 | currRTCaddr <= 0; 50 | 51 | end else begin 52 | currState <= nextState; 53 | currRTCaddr <= nextRTCaddr; 54 | end 55 | end 56 | 57 | always_comb begin 58 | nextState = currState; 59 | nextRTCaddr = currRTCaddr; 60 | wr_out = 0; 61 | loading_done = 0; 62 | 63 | case(currState) 64 | 65 | READ: begin 66 | if(rtc_loaded & RTC_valid) begin 67 | nextState = WAIT; 68 | end else begin 69 | nextState = STOP; 70 | end 71 | end 72 | 73 | WAIT: begin 74 | nextState = WRITE; 75 | end 76 | 77 | WRITE: begin 78 | wr_out = 1; 79 | nextState = INC; 80 | end 81 | 82 | INC: begin 83 | nextRTCaddr = currRTCaddr + 1; 84 | 85 | if(nextRTCaddr < 10) begin 86 | nextState = READ; 87 | end else begin 88 | nextState = STOP; 89 | end 90 | end 91 | 92 | STOP: begin 93 | loading_done = 1; 94 | end 95 | endcase 96 | end 97 | 98 | rtc_ram rtc_ram_inst ( 99 | .clock ( clk_sys ), 100 | .wraddress ( addr_in ), 101 | .data ( data_in ), 102 | .wren ( wr_in ), 103 | 104 | .rdaddress ( currRTCaddr ), 105 | .q ( data_out ) // 1 cycle delay after address set 106 | ); 107 | 108 | endmodule -------------------------------------------------------------------------------- /rtl/gb/rtc_ram/rtc_ram.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name IP_TOOL_NAME "RAM: 2-PORT" 2 | set_global_assignment -name IP_TOOL_VERSION "23.1" 3 | set_global_assignment -name IP_GENERATED_DEVICE_FAMILY "{Cyclone V}" 4 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "rtc_ram.v"] 5 | set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "rtc_ram_inst.v"] 6 | set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "rtc_ram_bb.v"] 7 | -------------------------------------------------------------------------------- /rtl/gb/rtc_ram/rtc_ram_inst.v: -------------------------------------------------------------------------------- 1 | rtc_ram rtc_ram_inst ( 2 | .clock ( clock_sig ), 3 | .data ( data_sig ), 4 | .rdaddress ( rdaddress_sig ), 5 | .wraddress ( wraddress_sig ), 6 | .wren ( wren_sig ), 7 | .q ( q_sig ) 8 | ); 9 | -------------------------------------------------------------------------------- /rtl/gb/rumbler.sv: -------------------------------------------------------------------------------- 1 | module rumbler ( 2 | input logic clk, 3 | input logic reset, 4 | 5 | input logic rumble_en, 6 | input logic rumbling, 7 | 8 | output logic cart_wr, 9 | output logic cart_rumble 10 | ); 11 | 12 | assign cart_rumble = currRumble; 13 | 14 | typedef enum { 15 | IDLE, 16 | FLIPBIT, 17 | DELAY_1, 18 | WRITE, 19 | DELAY_2 20 | } stateType; 21 | 22 | stateType currState, nextState; 23 | reg [31:0] currCount, nextCount; 24 | reg currRumble, nextRumble; 25 | 26 | always_ff @(posedge clk) begin 27 | if(reset) begin 28 | currState <= IDLE; 29 | currCount <= 0; 30 | currRumble <= 0; 31 | end else begin 32 | currState <= nextState; 33 | currCount <= nextCount; 34 | currRumble <= nextRumble; 35 | end 36 | end 37 | 38 | always_comb begin 39 | nextState = currState; 40 | nextCount = currCount; 41 | nextRumble = currRumble; 42 | cart_wr = 1'b1; 43 | 44 | case(currState) 45 | 46 | IDLE: begin 47 | if(rumble_en & rumbling) begin 48 | nextState = FLIPBIT; 49 | end 50 | end 51 | 52 | FLIPBIT: begin 53 | nextRumble = ~currRumble; 54 | nextState = DELAY_1; 55 | end 56 | 57 | DELAY_1: begin 58 | nextCount = currCount + 1; 59 | 60 | if(currCount > 7) begin 61 | nextState = WRITE; 62 | nextCount = 0; 63 | end 64 | end 65 | 66 | WRITE: begin 67 | cart_wr = 0; 68 | nextCount = currCount + 1; 69 | 70 | if(currCount > 7) begin 71 | nextState = DELAY_2; 72 | nextCount = 0; 73 | end 74 | end 75 | 76 | DELAY_2: begin 77 | nextCount = currCount + 1; 78 | 79 | if (currCount > 85190) begin 80 | if(rumble_en & rumbling) begin 81 | nextState = FLIPBIT; 82 | end else begin 83 | nextState = IDLE; 84 | end 85 | end 86 | end 87 | 88 | endcase 89 | end 90 | 91 | 92 | endmodule : rumbler -------------------------------------------------------------------------------- /rtl/gb/savestate_ui.sv: -------------------------------------------------------------------------------- 1 | module savestate_ui #(parameter INFO_TIMEOUT_BITS) 2 | ( 3 | input clk, 4 | input [10:0] ps2_key, 5 | input allow_ss, 6 | input joySS , 7 | input joyRight, 8 | input joyLeft , 9 | input joyDown , 10 | input joyUp , 11 | input joyStart, 12 | input joyRewind, 13 | input rewindEnable, 14 | input [1:0] status_slot, 15 | input [1:0] OSD_saveload, 16 | output reg ss_save, 17 | output reg ss_load, 18 | output reg ss_info_req, 19 | output reg [7:0] ss_info, 20 | output reg statusUpdate, 21 | output [1:0] selected_slot 22 | ); 23 | 24 | reg [1:0] ss_base = 0; 25 | 26 | reg lastRight = 1'b0; 27 | reg lastLeft = 1'b0; 28 | reg lastDown = 1'b0; 29 | reg lastUp = 1'b0; 30 | 31 | reg [(INFO_TIMEOUT_BITS-1):0] InfoWaitcnt = 0; 32 | 33 | reg slotswitched = 1'b0; 34 | reg [1:0] lastOSDsetting = 2'b0; 35 | 36 | assign selected_slot = ss_base; 37 | 38 | wire pressed = ps2_key[9]; 39 | 40 | always @(posedge clk) begin 41 | reg old_state; 42 | reg alt = 0; 43 | reg [1:0] old_st; 44 | 45 | old_state <= ps2_key[10]; 46 | 47 | lastRight <= joyRight; 48 | lastLeft <= joyLeft; 49 | lastDown <= joyDown; 50 | lastUp <= joyUp; 51 | 52 | slotswitched <= 1'b0; 53 | 54 | ss_save <= 1'b0; 55 | ss_load <= 1'b0; 56 | ss_info_req <= 1'b0; 57 | statusUpdate <= 1'b0; 58 | 59 | 60 | if(allow_ss) begin 61 | 62 | // keyboard 63 | if(old_state != ps2_key[10]) begin 64 | case(ps2_key[7:0]) 65 | 'h11: alt <= pressed; 66 | 'h05: begin ss_save <= pressed & alt; ss_load <= pressed & ~alt; ss_base <= 0; statusUpdate <= 1'b1; end // F1 67 | 'h06: begin ss_save <= pressed & alt; ss_load <= pressed & ~alt; ss_base <= 1; statusUpdate <= 1'b1; end // F2 68 | 'h04: begin ss_save <= pressed & alt; ss_load <= pressed & ~alt; ss_base <= 2; statusUpdate <= 1'b1; end // F3 69 | 'h0C: begin ss_save <= pressed & alt; ss_load <= pressed & ~alt; ss_base <= 3; statusUpdate <= 1'b1; end // F4 70 | endcase 71 | end 72 | 73 | lastOSDsetting <= status_slot; 74 | if (lastOSDsetting != status_slot) begin 75 | ss_base <= status_slot; 76 | statusUpdate <= 1'b1; 77 | end 78 | 79 | // gamepad 80 | if (joySS) begin 81 | // timeout with no button pressed -> help text 82 | InfoWaitcnt <= InfoWaitcnt + 1'b1; 83 | if (InfoWaitcnt[(INFO_TIMEOUT_BITS-1)]) begin 84 | ss_info <= 7'd1; 85 | ss_info_req <= 1'b1; 86 | InfoWaitcnt <= 25'b0; 87 | end 88 | // switch slot 89 | if (joyRight & ~lastRight & ss_base < 3) begin 90 | ss_base <= ss_base + 1'd1; 91 | statusUpdate <= 1'b1; 92 | slotswitched <= 1'b1; 93 | InfoWaitcnt <= 25'b0; 94 | end 95 | if (joyLeft & ~lastLeft & ss_base > 0) begin 96 | ss_base <= ss_base - 1'd1; 97 | statusUpdate <= 1'b1; 98 | slotswitched <= 1'b1; 99 | InfoWaitcnt <= 25'b0; 100 | end 101 | // save and load 102 | if (joyStart & joyDown & ~lastDown) begin 103 | ss_save <= 1'b1; 104 | InfoWaitcnt <= 25'b0; 105 | end 106 | // save and load 107 | if (joyStart & joyUp & ~lastUp) begin 108 | ss_load <= 1'b1; 109 | InfoWaitcnt <= 25'b0; 110 | end 111 | end else begin 112 | InfoWaitcnt <= 25'b0; 113 | end 114 | 115 | // OSD 116 | old_st <= OSD_saveload; 117 | if(old_st[0] ^ OSD_saveload[0]) ss_save <= OSD_saveload[0]; 118 | if(old_st[1] ^ OSD_saveload[1]) ss_load <= OSD_saveload[1]; 119 | 120 | // infotexts 121 | if (slotswitched) begin 122 | ss_info <= 7'd2 + ss_base; 123 | ss_info_req <= 1'b1; 124 | end 125 | 126 | if(ss_load | ss_save) begin 127 | ss_info <= 7'd6 + {ss_base, ss_load}; 128 | ss_info_req <= 1'b1; 129 | end 130 | 131 | // rewind info 132 | if (rewindEnable & joyRewind) begin 133 | ss_info_req <= 1'b1; 134 | ss_info <= 7'd14; 135 | end 136 | 137 | end 138 | end 139 | 140 | endmodule 141 | 142 | -------------------------------------------------------------------------------- /rtl/gb/sdram.sv: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // sdram controller implementation for the MiST/MiSTer boards 4 | // 5 | // Copyright (c) 2015 Till Harbaum 6 | // Copyright (c) 2017 Sorgelig 7 | // 8 | // This source file is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published 10 | // by the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This source file is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | // 21 | 22 | // 23 | // 24 | // This SDRAM module provides/writes the data in 8 cycles of clock. 25 | // So, with 64MHz of system clock, it can emulate 8MHz asynchronous DRAM. 26 | // 27 | // 28 | 29 | module sdram 30 | ( 31 | 32 | // interface to the MT48LC16M16 chip 33 | inout reg [15:0] sd_data, // 16 bit bidirectional data bus 34 | output reg [12:0] sd_addr, // 13 bit multiplexed address bus 35 | output [1:0] sd_dqm, // two byte masks 36 | output reg [1:0] sd_ba, // two banks 37 | output sd_cs, // a single chip select 38 | output sd_we, // write enable 39 | output sd_ras, // row address select 40 | output sd_cas, // columns address select 41 | output sd_clk, 42 | 43 | // cpu/chipset interface 44 | input init, // init signal after FPGA config to initialize RAM 45 | input clk, // sdram is accessed at 64MHz 46 | input sync, 47 | 48 | input [15:0] din, // data input from chipset/cpu 49 | output reg [15:0] dout, // data output to chipset/cpu 50 | input [23:0] addr, // 24 bit word address 51 | input [1:0] ds, // upper/lower data strobe 52 | input oe, // cpu/chipset requests read 53 | input we, // cpu/chipset requests write 54 | input autorefresh,// autorefresh when no read or write required 55 | input refresh // force refresh when core is paused or fastforward 56 | ); 57 | 58 | localparam RASCAS_DELAY = 3'd2; // tRCD=20ns -> 3 cycles@128MHz 59 | localparam BURST_LENGTH = 3'b000; // 000=1, 001=2, 010=4, 011=8 60 | localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved 61 | localparam CAS_LATENCY = 3'd2; // 2/3 allowed 62 | localparam OP_MODE = 2'b00; // only 00 (standard operation) allowed 63 | localparam NO_WRITE_BURST = 1'b1; // 0= write burst enabled, 1=only single access write 64 | 65 | localparam MODE = { 3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH}; 66 | 67 | 68 | // --------------------------------------------------------------------- 69 | // ------------------------ cycle state machine ------------------------ 70 | // --------------------------------------------------------------------- 71 | 72 | // The state machine runs at 128Mhz synchronous to the 8 Mhz chipset clock. 73 | // It wraps from T15 to T0 on the rising edge of clk_8 74 | 75 | localparam STATE_FIRST = 3'd0; // first state in cycle 76 | localparam STATE_CMD_START = 3'd1; // state in which a new command can be started 77 | localparam STATE_CMD_CONT = STATE_CMD_START + RASCAS_DELAY; // command can be continued 78 | localparam STATE_READ = STATE_CMD_CONT + CAS_LATENCY + 4'd1; 79 | localparam STATE_HIGHZ = STATE_READ - 4'd1; // disable output to prevent contention 80 | 81 | 82 | // --------------------------------------------------------------------- 83 | // --------------------------- startup/reset --------------------------- 84 | // --------------------------------------------------------------------- 85 | 86 | // wait 1ms (32 8Mhz cycles) after FPGA config is done before going 87 | // into normal operation. Initialize the ram in the last 16 reset cycles (cycles 15-0) 88 | reg [4:0] reset; 89 | always @(posedge clk) begin 90 | if(init) reset <= 5'h1f; 91 | else if((stage == STATE_FIRST) && (reset != 0)) 92 | reset <= reset - 5'd1; 93 | end 94 | 95 | // --------------------------------------------------------------------- 96 | // ------------------ generate ram control signals --------------------- 97 | // --------------------------------------------------------------------- 98 | 99 | // all possible commands 100 | localparam CMD_INHIBIT = 4'b1111; 101 | localparam CMD_NOP = 4'b0111; 102 | localparam CMD_ACTIVE = 4'b0011; 103 | localparam CMD_READ = 4'b0101; 104 | localparam CMD_WRITE = 4'b0100; 105 | localparam CMD_BURST_TERMINATE = 4'b0110; 106 | localparam CMD_PRECHARGE = 4'b0010; 107 | localparam CMD_AUTO_REFRESH = 4'b0001; 108 | localparam CMD_LOAD_MODE = 4'b0000; 109 | 110 | reg [3:0] sd_cmd; // current command sent to sd ram 111 | 112 | // drive control signals according to current command 113 | assign sd_cs = sd_cmd[3]; 114 | assign sd_ras = sd_cmd[2]; 115 | assign sd_cas = sd_cmd[1]; 116 | assign sd_we = sd_cmd[0]; 117 | 118 | assign sd_dqm = sd_addr[12:11]; 119 | 120 | reg [1:0] mode; 121 | reg [15:0] din_r; 122 | reg [2:0] stage; 123 | 124 | always @(posedge clk) begin 125 | reg [12:0] addr_r; 126 | reg old_sync; 127 | reg old_oe; 128 | 129 | if(|stage) stage <= stage + 1'd1; 130 | 131 | old_sync <= sync; 132 | old_oe <= oe; 133 | if(~old_sync && sync && autorefresh) stage <= 1; // normal operation with read/write and refresh 134 | if(refresh && stage == STATE_FIRST) stage <= 1; // forced refresh when paused or fastforward 135 | if(~old_oe && oe && ~autorefresh) stage <= 1; // react on request only with fastforward 136 | 137 | sd_cmd <= CMD_INHIBIT; // default: idle 138 | sd_data <= 16'hZZZZ; 139 | 140 | if(reset != 0) begin 141 | // initialization takes place at the end of the reset phase 142 | if(stage == STATE_CMD_START) begin 143 | 144 | if(reset == 13) begin 145 | sd_cmd <= CMD_PRECHARGE; 146 | sd_addr[10] <= 1'b1; // precharge all banks 147 | end 148 | 149 | if(reset == 2) begin 150 | sd_cmd <= CMD_LOAD_MODE; 151 | sd_addr <= MODE; 152 | end 153 | 154 | end 155 | mode <= 0; 156 | end else begin 157 | 158 | // normal operation 159 | if(stage == STATE_CMD_START) begin 160 | if(we || oe) begin 161 | 162 | mode <= {we, oe}; 163 | 164 | // RAS phase 165 | sd_cmd <= CMD_ACTIVE; 166 | sd_addr <= { 1'b0, addr[19:8] }; 167 | sd_ba <= addr[21:20]; 168 | 169 | din_r <= din; 170 | addr_r <= { we ? ~ds : 2'b00, 2'b10, addr[22], addr[7:0] }; // auto precharge 171 | end 172 | else if (autorefresh || refresh) begin 173 | sd_cmd <= CMD_AUTO_REFRESH; 174 | mode <= 0; 175 | end 176 | end 177 | 178 | // CAS phase 179 | if(stage == STATE_CMD_CONT && mode) begin 180 | sd_cmd <= mode[1] ? CMD_WRITE : CMD_READ; 181 | sd_addr <= addr_r; 182 | if(mode[1]) sd_data <= din_r; 183 | end 184 | 185 | if(stage == STATE_HIGHZ) begin 186 | sd_addr[12:11] <= 2'b11; // disable chip output 187 | mode[1] <= 0; // disable data output 188 | end 189 | 190 | if(stage == STATE_READ && mode) begin 191 | dout <= sd_data; 192 | end 193 | end 194 | end 195 | 196 | altddio_out 197 | #( 198 | .extend_oe_disable("OFF"), 199 | .intended_device_family("Cyclone V"), 200 | .invert_output("OFF"), 201 | .lpm_hint("UNUSED"), 202 | .lpm_type("altddio_out"), 203 | .oe_reg("UNREGISTERED"), 204 | .power_up_high("OFF"), 205 | .width(1) 206 | ) 207 | sdramclk_ddr 208 | ( 209 | .datain_h(1'b0), 210 | .datain_l(1'b1), 211 | .outclock(clk), 212 | .dataout(sd_clk), 213 | .aclr(1'b0), 214 | .aset(1'b0), 215 | .oe(1'b1), 216 | .outclocken(1'b1), 217 | .sclr(1'b0), 218 | .sset(1'b0) 219 | ); 220 | 221 | endmodule 222 | -------------------------------------------------------------------------------- /rtl/gb/speedcontrol.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.std_logic_1164.all; 3 | use IEEE.numeric_std.all; 4 | 5 | entity speedcontrol is 6 | port 7 | ( 8 | clk_sys : in std_logic; 9 | pause : in std_logic; 10 | speedup : in std_logic; 11 | cart_act : in std_logic; 12 | DMA_on : in std_logic; 13 | ce : out std_logic := '0'; 14 | ce_2x : buffer std_logic := '0'; 15 | refresh : out std_logic := '0'; 16 | ff_on : out std_logic := '0' 17 | ); 18 | end entity; 19 | 20 | architecture arch of speedcontrol is 21 | 22 | signal clkdiv : unsigned(2 downto 0) := (others => '0'); 23 | 24 | signal cart_act_1 : std_logic := '0'; 25 | 26 | signal unpause_cnt : integer range 0 to 15 := 0; 27 | signal fastforward_cnt : integer range 0 to 15 := 0; 28 | 29 | signal refreshcnt : integer range 0 to 127 := 0; 30 | signal sdram_busy : integer range 0 to 1 := 0; 31 | 32 | type tstate is 33 | ( 34 | NORMAL, 35 | PAUSED, 36 | FASTFORWARDSTART, 37 | FASTFORWARD, 38 | FASTFORWARDEND, 39 | RAMACCESS 40 | ); 41 | signal state : tstate := NORMAL; 42 | 43 | begin 44 | 45 | process(clk_sys) 46 | begin 47 | if falling_edge(clk_sys) then 48 | 49 | ce <= '0'; 50 | ce_2x <= '0'; 51 | refresh <= '0'; 52 | 53 | cart_act_1 <= cart_act; 54 | 55 | if (refreshcnt > 0) then 56 | refreshcnt <= refreshcnt - 1; 57 | end if; 58 | 59 | case (state) is 60 | 61 | when NORMAL => 62 | if (pause = '1' and clkdiv = "111" and cart_act = '0') then 63 | state <= PAUSED; 64 | unpause_cnt <= 0; 65 | elsif (speedup = '1' and pause = '0' and DMA_on = '0' and clkdiv = "000") then 66 | state <= FASTFORWARDSTART; 67 | fastforward_cnt <= 0; 68 | else 69 | clkdiv <= clkdiv + 1; 70 | if (clkdiv = "000") then 71 | ce <= '1'; 72 | end if; 73 | if (clkdiv(1 downto 0) = "00") then 74 | ce_2x <= '1'; 75 | end if; 76 | end if; 77 | 78 | when PAUSED => 79 | if (unpause_cnt = 0) then 80 | refresh <= '1'; 81 | end if; 82 | 83 | if (pause = '0') then 84 | if (unpause_cnt = 15) then 85 | state <= NORMAL; 86 | else 87 | unpause_cnt <= unpause_cnt + 1; 88 | end if; 89 | end if; 90 | 91 | when FASTFORWARDSTART => 92 | if (fastforward_cnt = 15) then 93 | state <= FASTFORWARD; 94 | ff_on <= '1'; 95 | else 96 | fastforward_cnt <= fastforward_cnt + 1; 97 | end if; 98 | 99 | when FASTFORWARD => 100 | if (pause = '1' or speedup = '0' or DMA_on = '1') then 101 | state <= FASTFORWARDEND; 102 | fastforward_cnt <= 0; 103 | if (clkdiv(0) = '1') then 104 | clkdiv <= "100"; 105 | end if; 106 | elsif (cart_act = '1' and cart_act_1 = '0') then 107 | state <= RAMACCESS; 108 | sdram_busy <= 1; 109 | elsif (cart_act = '0' and refreshcnt = 0) then 110 | refreshcnt <= 127; 111 | refresh <= '1'; 112 | state <= RAMACCESS; 113 | sdram_busy <= 1; 114 | else 115 | clkdiv(0) <= not clkdiv(0); 116 | if (clkdiv(0) = '0') then 117 | ce <= '1'; 118 | end if; 119 | ce_2x <= '1'; 120 | end if; 121 | 122 | when FASTFORWARDEND => 123 | if (fastforward_cnt = 15) then 124 | state <= NORMAL; 125 | ff_on <= '0'; 126 | else 127 | fastforward_cnt <= fastforward_cnt + 1; 128 | end if; 129 | 130 | when RAMACCESS => 131 | if (sdram_busy > 0) then 132 | sdram_busy <= sdram_busy - 1; 133 | else 134 | state <= FASTFORWARD; 135 | end if; 136 | 137 | end case; 138 | 139 | end if; 140 | end process; 141 | 142 | 143 | 144 | end architecture; 145 | -------------------------------------------------------------------------------- /rtl/gb/spram.vhd: -------------------------------------------------------------------------------- 1 | LIBRARY ieee; 2 | USE ieee.std_logic_1164.all; 3 | 4 | LIBRARY altera_mf; 5 | USE altera_mf.altera_mf_components.all; 6 | 7 | ENTITY spram IS 8 | generic ( 9 | addr_width : integer := 8; 10 | data_width : integer := 8 11 | ); 12 | PORT 13 | ( 14 | clock : IN STD_LOGIC; 15 | clken : IN STD_LOGIC := '1'; 16 | address : IN STD_LOGIC_VECTOR (addr_width-1 DOWNTO 0); 17 | data : IN STD_LOGIC_VECTOR (data_width-1 DOWNTO 0); 18 | wren : IN STD_LOGIC := '0'; 19 | q : OUT STD_LOGIC_VECTOR (data_width-1 DOWNTO 0) 20 | ); 21 | END spram; 22 | 23 | 24 | ARCHITECTURE SYN OF spram IS 25 | 26 | BEGIN 27 | altsyncram_component : altsyncram 28 | GENERIC MAP ( 29 | clock_enable_input_a => "NORMAL", 30 | clock_enable_output_a => "BYPASS", 31 | intended_device_family => "Cyclone V", 32 | lpm_hint => "ENABLE_RUNTIME_MOD=NO", 33 | lpm_type => "altsyncram", 34 | numwords_a => 2**addr_width, 35 | operation_mode => "SINGLE_PORT", 36 | outdata_aclr_a => "NONE", 37 | outdata_reg_a => "UNREGISTERED", 38 | power_up_uninitialized => "FALSE", 39 | read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ", 40 | widthad_a => addr_width, 41 | width_a => data_width, 42 | width_byteena_a => 1 43 | ) 44 | PORT MAP ( 45 | address_a => address, 46 | clock0 => clock, 47 | clocken0 => clken, 48 | data_a => data, 49 | wren_a => wren, 50 | q_a => q 51 | ); 52 | 53 | 54 | 55 | END SYN; 56 | -------------------------------------------------------------------------------- /rtl/gb/sprites.v: -------------------------------------------------------------------------------- 1 | // 2 | // sprites.v 3 | // 4 | // Gameboy for the MIST board https://github.com/mist-devel 5 | // 6 | // Copyright (c) 2015 Till Harbaum 7 | // 8 | // This source file is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published 10 | // by the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This source file is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | // 21 | 22 | module sprites ( 23 | input clk, 24 | input ce, 25 | input ce_cpu, 26 | input size16, 27 | input isGBC, 28 | input sprite_en, 29 | 30 | input lcd_on, 31 | 32 | // pixel position input which the current pixel is generated for 33 | input [7:0] v_cnt, 34 | input [7:0] h_cnt, 35 | 36 | input sprite_fetch_done, 37 | output sprite_fetch, 38 | 39 | input oam_fetch, 40 | input oam_eval_reset, 41 | output oam_eval, 42 | 43 | output [10:0] sprite_addr, 44 | output reg [7:0] sprite_attr, 45 | output [3:0] sprite_index, 46 | 47 | output oam_eval_end, 48 | 49 | // oam memory interface 50 | input dma_active, 51 | input oam_wr, 52 | input [7:0] oam_addr_in, 53 | input [7:0] oam_di, 54 | output [7:0] oam_do, 55 | 56 | // savestates 57 | input [7:0] Savestate_OAMRAMAddr, 58 | input Savestate_OAMRAMRWrEn, 59 | input [7:0] Savestate_OAMRAMWriteData, 60 | output[7:0] Savestate_OAMRAMReadData 61 | ); 62 | 63 | localparam SPRITES_PER_LINE = 10; 64 | 65 | reg [7:0] oam_spr_addr; 66 | wire [7:0] oam_fetch_addr; 67 | reg [7:0] oam_q; 68 | 69 | reg oam_eval_en; 70 | assign oam_eval = lcd_on & ~oam_eval_end & oam_eval_en & ~oam_eval_reset; 71 | 72 | wire [7:0] oam_addr = dma_active ? oam_addr_in : 73 | oam_eval ? oam_spr_addr : 74 | oam_fetch ? oam_fetch_addr : 75 | oam_addr_in; 76 | 77 | wire valid_oam_addr = (oam_addr[7:4] < 4'hA); // $FEA0 - $FEFF unused range 78 | assign oam_do = dma_active ? 8'hFF : valid_oam_addr ? oam_q : 8'd0; 79 | 80 | 81 | dpram #(8) oam_data ( 82 | .clock_a (clk ), 83 | .address_a (oam_addr ), 84 | .wren_a (ce_cpu && oam_wr && valid_oam_addr), 85 | .data_a (oam_di ), 86 | .q_a (oam_q ), 87 | 88 | .clock_b (clk), 89 | .address_b (Savestate_OAMRAMAddr ), 90 | .wren_b (Savestate_OAMRAMRWrEn ), 91 | .data_b (Savestate_OAMRAMWriteData), 92 | .q_b (Savestate_OAMRAMReadData ) 93 | ); 94 | 95 | reg [7:0] sprite_x[0:SPRITES_PER_LINE-1]; 96 | reg [3:0] sprite_y[0:SPRITES_PER_LINE-1]; 97 | reg [5:0] sprite_no[0:SPRITES_PER_LINE-1]; 98 | 99 | // OAM evaluation. Get the first 10 sprites on the current line. 100 | reg [5:0] spr_index; // 40 sprites 101 | reg [3:0] sprite_cnt; 102 | reg sprite_cycle; 103 | 104 | reg [7:0] spr_y; 105 | wire [7:0] spr_height = size16 ? 8'd16 : 8'd8; 106 | wire sprite_on_line = (v_cnt + 8'd16 >= spr_y) && (v_cnt + 8'd16 < spr_y + spr_height); 107 | 108 | assign oam_eval_end = (spr_index == 6'd40); 109 | 110 | wire [0:9] sprite_x_matches; 111 | 112 | reg old_fetch_done; 113 | integer spr_i = 0; 114 | always @(posedge clk) begin 115 | if (ce) begin 116 | 117 | if (oam_eval_reset | ~lcd_on) begin 118 | sprite_cnt <= 0; 119 | spr_index <= ~lcd_on ? 6'd1 : 6'd0; 120 | sprite_cycle <= 0; 121 | oam_spr_addr <= 0; 122 | oam_eval_en <= oam_eval_reset ? 1'b1 : 1'b0; // OAM evaluation does not run on the first line after enabling the lcd 123 | for (spr_i=0; spr_i < SPRITES_PER_LINE; spr_i=spr_i+1) begin 124 | sprite_x[spr_i] <= 8'hFF; 125 | sprite_no[spr_i] <= 6'd0; 126 | end 127 | end else begin 128 | 129 | if (~oam_eval_end) begin 130 | if (sprite_cycle) spr_index <= spr_index + 1'b1; 131 | 132 | if (oam_eval_en && sprite_cnt < SPRITES_PER_LINE) begin 133 | if (~sprite_cycle) begin 134 | spr_y <= oam_do; 135 | oam_spr_addr <= {spr_index,2'b01}; 136 | end else begin 137 | if (sprite_on_line) begin 138 | sprite_no[sprite_cnt] <= spr_index; 139 | sprite_x[sprite_cnt] <= oam_do; 140 | sprite_y[sprite_cnt] <= v_cnt[3:0] - spr_y[3:0]; 141 | sprite_cnt <= sprite_cnt + 1'b1; 142 | end 143 | oam_spr_addr <= {spr_index+1'b1, 2'b00}; 144 | end 145 | end 146 | 147 | sprite_cycle <= ~sprite_cycle; 148 | end 149 | 150 | // Set X-position to FF after fetching the sprite to prevent fetching it again. 151 | old_fetch_done <= sprite_fetch_done; 152 | if (~old_fetch_done & sprite_fetch_done) begin 153 | if (sprite_x_matches[0]) sprite_x[0] <= 8'hFF; 154 | else if (sprite_x_matches[1]) sprite_x[1] <= 8'hFF; 155 | else if (sprite_x_matches[2]) sprite_x[2] <= 8'hFF; 156 | else if (sprite_x_matches[3]) sprite_x[3] <= 8'hFF; 157 | else if (sprite_x_matches[4]) sprite_x[4] <= 8'hFF; 158 | else if (sprite_x_matches[5]) sprite_x[5] <= 8'hFF; 159 | else if (sprite_x_matches[6]) sprite_x[6] <= 8'hFF; 160 | else if (sprite_x_matches[7]) sprite_x[7] <= 8'hFF; 161 | else if (sprite_x_matches[8]) sprite_x[8] <= 8'hFF; 162 | else if (sprite_x_matches[9]) sprite_x[9] <= 8'hFF; 163 | end 164 | 165 | end 166 | end 167 | end 168 | 169 | 170 | // Sprite fetching 171 | assign sprite_x_matches = { 172 | sprite_x[0] == h_cnt, 173 | sprite_x[1] == h_cnt, 174 | sprite_x[2] == h_cnt, 175 | sprite_x[3] == h_cnt, 176 | sprite_x[4] == h_cnt, 177 | sprite_x[5] == h_cnt, 178 | sprite_x[6] == h_cnt, 179 | sprite_x[7] == h_cnt, 180 | sprite_x[8] == h_cnt, 181 | sprite_x[9] == h_cnt 182 | }; 183 | 184 | assign sprite_fetch = |sprite_x_matches & oam_fetch & (isGBC | sprite_en); 185 | 186 | wire [3:0] active_sprite = 187 | sprite_x_matches[0] ? 4'd0 : 188 | sprite_x_matches[1] ? 4'd1 : 189 | sprite_x_matches[2] ? 4'd2 : 190 | sprite_x_matches[3] ? 4'd3 : 191 | sprite_x_matches[4] ? 4'd4 : 192 | sprite_x_matches[5] ? 4'd5 : 193 | sprite_x_matches[6] ? 4'd6 : 194 | sprite_x_matches[7] ? 4'd7 : 195 | sprite_x_matches[8] ? 4'd8 : 196 | 4'd9; 197 | assign sprite_index = active_sprite; 198 | 199 | wire [5:0] oam_fetch_index = sprite_no[active_sprite]; 200 | 201 | reg [3:0] row; 202 | reg [7:0] tile_no; 203 | reg oam_fetch_cycle; 204 | assign oam_fetch_addr = {oam_fetch_index, 1'b1, oam_fetch_cycle}; 205 | assign sprite_addr = size16 ? {tile_no[7:1],row} : {tile_no,row[2:0]}; 206 | 207 | always @(posedge clk) begin 208 | if (ce) begin 209 | if (sprite_fetch) begin 210 | 211 | if (~oam_fetch_cycle) begin 212 | tile_no <= oam_do; 213 | end else begin 214 | sprite_attr <= oam_do; 215 | row <= oam_do[6] ? ~sprite_y[active_sprite] : sprite_y[active_sprite]; 216 | end 217 | 218 | oam_fetch_cycle <= ~oam_fetch_cycle; 219 | end else begin 220 | oam_fetch_cycle <= 0; 221 | end 222 | end 223 | end 224 | 225 | endmodule -------------------------------------------------------------------------------- /rtl/gb/timer.v: -------------------------------------------------------------------------------- 1 | // 2 | // timer.v 3 | // 4 | // Gameboy for the MIST board https://github.com/mist-devel 5 | // 6 | // Copyright (c) 2015 Till Harbaum 7 | // 8 | // This source file is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published 10 | // by the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This source file is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | // 21 | 22 | module timer ( 23 | input reset, 24 | input clk_sys, 25 | input ce, // 4 Mhz cpu clock 26 | output reg irq, 27 | 28 | // cpu register interface 29 | input cpu_sel, 30 | input [1:0] cpu_addr, 31 | input cpu_wr, 32 | input [7:0] cpu_di, 33 | output [7:0] cpu_do, 34 | 35 | // savestates 36 | input [63:0] SaveStateBus_Din, 37 | input [9:0] SaveStateBus_Adr, 38 | input SaveStateBus_wren, 39 | input SaveStateBus_rst, 40 | output [63:0] SaveStateBus_Dout 41 | ); 42 | 43 | // savestates 44 | wire [46:0] SS_Timer; 45 | wire [46:0] SS_Timer_BACK; 46 | 47 | eReg_SavestateV #(0, 6, 46, 0, 64'h0000000000000008) iREG_SAVESTATE_Timer (clk_sys, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_Dout, SS_Timer_BACK, SS_Timer); 48 | 49 | // input: 4Mhz 50 | // clk_div[0] = 2Mhz 51 | // clk_div[1] = 1Mhz 52 | // clk_div[2] = 524khz 53 | // clk_div[3] = 262khz 54 | // clk_div[4] = 131khz 55 | // clk_div[5] = 65khz 56 | // clk_div[6] = 32khz 57 | // clk_div[7] = 16khz 58 | // clk_div[8] = 8khz 59 | // clk_div[9] = 4khz 60 | 61 | wire resetdiv = cpu_sel && cpu_wr && (cpu_addr == 2'b00); //resetdiv also resets internal counter 62 | 63 | reg [9:0] clk_div; 64 | reg clk_div_1_9; 65 | reg clk_div_1_3; 66 | reg clk_div_1_5; 67 | reg clk_div_1_7; 68 | always @(posedge clk_sys) 69 | if (reset) 70 | clk_div <= SS_Timer[9:0]; // 10'd8; 71 | else if(resetdiv) 72 | clk_div <= 10'd2; 73 | else if (ce) 74 | clk_div <= clk_div + 10'd1; 75 | 76 | reg [7:0] div; 77 | reg [7:0] tma; 78 | reg [7:0] tima; 79 | reg [2:0] tac; 80 | reg tima_overflow; 81 | reg tima_overflow_1; 82 | reg tima_overflow_2; 83 | reg tima_overflow_3; 84 | reg tima_overflow_4; 85 | 86 | assign SS_Timer_BACK[ 9: 0] = clk_div; 87 | assign SS_Timer_BACK[17:10] = tima; 88 | assign SS_Timer_BACK[25:18] = tma; 89 | assign SS_Timer_BACK[28:26] = tac; 90 | assign SS_Timer_BACK[29] = irq; 91 | assign SS_Timer_BACK[37:30] = div; 92 | assign SS_Timer_BACK[38] = clk_div_1_9; 93 | assign SS_Timer_BACK[39] = clk_div_1_3; 94 | assign SS_Timer_BACK[40] = clk_div_1_5; 95 | assign SS_Timer_BACK[41] = clk_div_1_7; 96 | assign SS_Timer_BACK[42] = tima_overflow; 97 | assign SS_Timer_BACK[43] = tima_overflow_1; 98 | assign SS_Timer_BACK[44] = tima_overflow_2; 99 | assign SS_Timer_BACK[45] = tima_overflow_3; 100 | assign SS_Timer_BACK[46] = tima_overflow_4; 101 | 102 | always @(posedge clk_sys) begin 103 | if(reset) begin 104 | tima <= SS_Timer[17:10]; // 0 105 | tma <= SS_Timer[25:18]; // 0 106 | tac <= SS_Timer[28:26]; // 0 107 | irq <= SS_Timer[29]; // 0 108 | div <= SS_Timer[37:30]; // 0 109 | clk_div_1_9 <= SS_Timer[38]; // 0 110 | clk_div_1_3 <= SS_Timer[39]; // 0 111 | clk_div_1_5 <= SS_Timer[40]; // 0 112 | clk_div_1_7 <= SS_Timer[41]; // 0 113 | tima_overflow <= SS_Timer[42]; // 0 114 | tima_overflow_1 <= SS_Timer[43]; // 0 115 | tima_overflow_2 <= SS_Timer[44]; // 0 116 | tima_overflow_3 <= SS_Timer[45]; // 0 117 | tima_overflow_4 <= SS_Timer[46]; // 0 118 | end else if (ce) begin 119 | irq <= 1'b0; 120 | 121 | tima_overflow <= 1'b0; 122 | tima_overflow_1 <= tima_overflow; 123 | tima_overflow_2 <= tima_overflow_1; 124 | tima_overflow_3 <= tima_overflow_2; 125 | tima_overflow_4 <= tima_overflow_3; 126 | 127 | if(clk_div[7:0] == 0) // 16kHz 128 | div <= div + 8'd1; 129 | 130 | clk_div_1_9 <= clk_div[9]; 131 | clk_div_1_3 <= clk_div[3]; 132 | clk_div_1_5 <= clk_div[5]; 133 | clk_div_1_7 <= clk_div[7]; 134 | 135 | // timer enabled? 136 | if(tac[2]) begin 137 | // timer frequency, count up when uppermost clk_div bit switches from 1 to 0 138 | if(((tac[1:0] == 2'b00) && (!clk_div[9] && clk_div_1_9)) || // 4 khz 139 | ((tac[1:0] == 2'b01) && (!clk_div[3] && clk_div_1_3)) || // 262 khz 140 | ((tac[1:0] == 2'b10) && (!clk_div[5] && clk_div_1_5)) || // 65 khz 141 | ((tac[1:0] == 2'b11) && (!clk_div[7] && clk_div_1_7))) begin // 16 khz 142 | 143 | tima <= tima + 8'd1; 144 | if(tima == 8'hff) begin 145 | tima_overflow <= 1'b1; 146 | end 147 | end 148 | end 149 | 150 | if (tima_overflow_3) begin // 1 cycle = 4 clock cycles after timer overflows 151 | irq <= 1'b1; // irq 152 | tima <= tma; // reload timer 153 | end 154 | 155 | if (tima_overflow_4) begin // delay 4 is required because cpu write takes place 1 clock cycle later(too late?) 156 | if(cpu_sel && cpu_wr && cpu_addr == 2'b10) begin // writing tma when loading tima has instant effect 157 | tima <= cpu_di; 158 | end 159 | end 160 | 161 | if(cpu_sel && cpu_wr) begin 162 | case(cpu_addr) 163 | 2'b00: div <= 8'h00; // writing clears counter 164 | 2'b01: begin 165 | if (!tima_overflow_4) begin 166 | tima <= cpu_di; 167 | end 168 | tima_overflow_1 <= 1'b0; // prevent irq and loading of tima with write to tima at the same time 169 | end 170 | 2'b10: tma <= cpu_di; 171 | 2'b11: tac <= cpu_di[2:0]; 172 | endcase 173 | end 174 | end 175 | end 176 | 177 | assign cpu_do = 178 | (cpu_addr == 2'b00)?div: 179 | (cpu_addr == 2'b01)?tima: 180 | (cpu_addr == 2'b10)?tma: 181 | {5'b11111, tac}; 182 | 183 | endmodule 184 | -------------------------------------------------------------------------------- /rtl/ui/char_rom.sv: -------------------------------------------------------------------------------- 1 | module char_rom ( 2 | input wire clk, 3 | 4 | input wire [6:0] character, 5 | input wire [2:0] row, 6 | output reg [7:0] data_out 7 | ); 8 | reg [7:0] rom[2 ** 10 - 1:0]; 9 | 10 | initial $readmemh("../assets/PixelOperatorMono.hex", rom); 11 | 12 | // Data loading 13 | always @(posedge clk) begin 14 | data_out <= rom[character*8+row]; 15 | end 16 | endmodule 17 | -------------------------------------------------------------------------------- /rtl/ui/ui.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "char_rom.sv"] 2 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "ui.sv"] 3 | 4 | -------------------------------------------------------------------------------- /rtl/ui/ui.sv: -------------------------------------------------------------------------------- 1 | // For anyone looking at this, this method of creating UI is kind of a hack, and pieced together from other, more 2 | // complete experiments of mine. I dislike all of this code, but it works for the purpose 3 | module ui ( 4 | input wire clk, 5 | input wire ce_pix, 6 | 7 | input wire [9:0] video_fetch_x, 8 | input wire [9:0] video_fetch_y, 9 | 10 | // Settings 11 | input wire [2:0] string_index, 12 | input wire [7:0] save_index_bcd, 13 | 14 | output wire active, 15 | output reg [23:0] vid_out 16 | ); 17 | localparam UI_SCALE = 1; 18 | localparam X_SCALE_BIT = UI_SCALE; 19 | localparam Y_SCALE_BIT = UI_SCALE; 20 | 21 | localparam DISPLAY_WIDTH = 160; 22 | localparam DISPLAY_HEIGHT = 144; 23 | localparam TEXT_PADDING_Y = 10; 24 | 25 | localparam TEXT_HEIGHT = UI_SCALE * 8 + 2 * TEXT_PADDING_Y; 26 | 27 | localparam START_Y = (DISPLAY_HEIGHT - TEXT_HEIGHT) / 2; 28 | 29 | localparam UI_START_Y = START_Y + TEXT_PADDING_Y; 30 | localparam UI_END_Y = UI_START_Y + 8 * UI_SCALE; 31 | localparam END_Y = START_Y + TEXT_PADDING_Y * 2 + 8 * UI_SCALE; 32 | 33 | // Strings 34 | // Not detected 35 | localparam NOT_DETECTED_TEXT_LENGTH = 19; 36 | localparam NOT_DETECTED_TEXT_WIDTH = NOT_DETECTED_TEXT_LENGTH * UI_SCALE * 8; 37 | 38 | localparam NOT_DETECTED_START_X = (DISPLAY_WIDTH - NOT_DETECTED_TEXT_WIDTH) / 2; 39 | localparam NOT_DETECTED_END_X = NOT_DETECTED_START_X + NOT_DETECTED_TEXT_WIDTH; 40 | 41 | reg [7:0] not_detected_text[NOT_DETECTED_TEXT_LENGTH] = '{ 42 | "C", 43 | "a", 44 | "m", 45 | "e", 46 | "r", 47 | "a", 48 | " ", 49 | "n", 50 | "o", 51 | "t", 52 | " ", 53 | "d", 54 | "e", 55 | "t", 56 | "e", 57 | "c", 58 | "t", 59 | "e", 60 | "d" 61 | }; 62 | 63 | // SRAM exporting 64 | localparam SRAM_EXPORTING_TEXT_LENGTH = 14; 65 | localparam SRAM_EXPORTING_TEXT_WIDTH = SRAM_EXPORTING_TEXT_LENGTH * UI_SCALE * 8; 66 | 67 | localparam SRAM_EXPORTING_START_X = (DISPLAY_WIDTH - SRAM_EXPORTING_TEXT_WIDTH) / 2; 68 | localparam SRAM_EXPORTING_END_X = SRAM_EXPORTING_START_X + SRAM_EXPORTING_TEXT_WIDTH; 69 | 70 | reg [7:0] sram_exporting_text[SRAM_EXPORTING_TEXT_LENGTH] = '{ 71 | "S", 72 | "R", 73 | "A", 74 | "M", 75 | " ", 76 | "e", 77 | "x", 78 | "p", 79 | "o", 80 | "r", 81 | "t", 82 | "i", 83 | "n", 84 | "g" 85 | }; 86 | 87 | // Saved to SRAM_ 88 | localparam SAVED_TO_TEXT_LENGTH = 12; 89 | localparam SAVED_TO_NUMBER_TEXT_LENGTH = 2; 90 | localparam SAVED_TO_SUFFIX_TEXT_LENGTH = 4; 91 | localparam SAVED_TO_TEXT_WIDTH = (SAVED_TO_TEXT_LENGTH + SAVED_TO_NUMBER_TEXT_LENGTH + SAVED_TO_SUFFIX_TEXT_LENGTH) * UI_SCALE * 8; 92 | 93 | localparam SAVED_TO_START_X = (DISPLAY_WIDTH - SAVED_TO_TEXT_WIDTH) / 2; 94 | localparam SAVED_TO_END_X = SAVED_TO_START_X + SAVED_TO_TEXT_WIDTH; 95 | 96 | reg [7:0] saved_to_text[SAVED_TO_TEXT_LENGTH] = '{ 97 | "S", 98 | "a", 99 | "v", 100 | "e", 101 | "d", 102 | ":", 103 | " ", 104 | "S", 105 | "R", 106 | "A", 107 | "M", 108 | "_" 109 | }; 110 | 111 | reg [7:0] saved_to_suffix_text[SAVED_TO_SUFFIX_TEXT_LENGTH] = '{".", "s", "a", "v"}; 112 | 113 | // Fail: No free slots 114 | localparam FAILED_EXPORT_TEXT_LENGTH = 19; 115 | localparam FAILED_EXPORT_TEXT_WIDTH = FAILED_EXPORT_TEXT_LENGTH * UI_SCALE * 8; 116 | 117 | localparam FAILED_EXPORT_START_X = (DISPLAY_WIDTH - FAILED_EXPORT_TEXT_WIDTH) / 2; 118 | localparam FAILED_EXPORT_END_X = FAILED_EXPORT_START_X + FAILED_EXPORT_TEXT_WIDTH; 119 | 120 | reg [7:0] failed_export_text[FAILED_EXPORT_TEXT_LENGTH] = '{ 121 | "F", 122 | "a", 123 | "i", 124 | "l", 125 | ":", 126 | " ", 127 | "N", 128 | "o", 129 | " ", 130 | "f", 131 | "r", 132 | "e", 133 | "e", 134 | " ", 135 | "s", 136 | "l", 137 | "o", 138 | "t", 139 | "s" 140 | }; 141 | 142 | // Comb 143 | reg [7:0] character = 0; 144 | 145 | reg [9:0] ui_start_x; 146 | reg [9:0] ui_end_x; 147 | 148 | always_comb begin 149 | reg [9:0] local_addr; 150 | character = 0; 151 | ui_start_x = 0; 152 | ui_end_x = 1; 153 | 154 | case (string_index) 155 | 0: begin 156 | ui_start_x = NOT_DETECTED_START_X; 157 | ui_end_x = NOT_DETECTED_END_X; 158 | 159 | if (character_addr < NOT_DETECTED_TEXT_LENGTH) begin 160 | character = not_detected_text[character_addr[4:0]]; 161 | end 162 | end 163 | 1: begin 164 | ui_start_x = SRAM_EXPORTING_START_X; 165 | ui_end_x = SRAM_EXPORTING_END_X; 166 | 167 | if (character_addr < SRAM_EXPORTING_TEXT_LENGTH) begin 168 | character = sram_exporting_text[character_addr[4:0]]; 169 | end 170 | end 171 | 2: begin 172 | ui_start_x = SAVED_TO_START_X; 173 | ui_end_x = SAVED_TO_END_X; 174 | 175 | if (character_addr < SAVED_TO_TEXT_LENGTH) begin 176 | character = saved_to_text[character_addr[4:0]]; 177 | end else if (character_addr == SAVED_TO_TEXT_LENGTH) begin 178 | character = {4'h0, save_index_bcd[7:4]} + 8'h30; 179 | end else if (character_addr == SAVED_TO_TEXT_LENGTH + 1) begin 180 | character = {4'h0, save_index_bcd[3:0]} + 8'h30; 181 | end else if (character_addr < SAVED_TO_TEXT_LENGTH + SAVED_TO_NUMBER_TEXT_LENGTH + SAVED_TO_SUFFIX_TEXT_LENGTH) begin 182 | character = saved_to_suffix_text[character_addr[4:0] - (SAVED_TO_TEXT_LENGTH + SAVED_TO_NUMBER_TEXT_LENGTH + SAVED_TO_SUFFIX_TEXT_LENGTH)]; 183 | end 184 | end 185 | 3: begin 186 | ui_start_x = FAILED_EXPORT_START_X; 187 | ui_end_x = FAILED_EXPORT_END_X; 188 | 189 | if (character_addr < FAILED_EXPORT_TEXT_LENGTH) begin 190 | character = failed_export_text[character_addr[4:0]]; 191 | end 192 | end 193 | endcase 194 | end 195 | 196 | wire x_active = video_fetch_x >= ui_start_x && video_fetch_x < ui_end_x; 197 | wire y_active = video_fetch_y >= UI_START_Y && video_fetch_y < UI_END_Y; 198 | 199 | assign active = video_fetch_y >= START_Y && video_fetch_y < END_Y; 200 | 201 | wire [9:0] x_offset = video_fetch_x - ui_start_x; 202 | wire [9:0] y_offset = video_fetch_y - UI_START_Y; 203 | 204 | wire [3:0] char_x_pixel = video_fetch_x >= ui_start_x ? x_offset[X_SCALE_BIT+1:0] : 0; 205 | wire [3:0] char_y_pixel = video_fetch_y >= UI_START_Y ? y_offset[X_SCALE_BIT+1:0] : 0; 206 | 207 | wire [2:0] character_row = char_y_pixel[Y_SCALE_BIT+1:Y_SCALE_BIT-1]; 208 | 209 | wire [9:0] x_offset_prefetch = x_offset + 1; 210 | // wire [9:0] character_addr = {4'b0, x_offset_prefetch[9:4]}; 211 | wire [9:0] character_addr = {5'b0, x_offset_prefetch[8:3]}; 212 | 213 | assign vid_out = x_active && y_active && font_line_reg[7-char_x_pixel[X_SCALE_BIT+1:X_SCALE_BIT-1]] ? 24'hFFFFFF : 0; 214 | 215 | reg [7:0] font_line_reg; 216 | wire [7:0] font_line; 217 | 218 | char_rom char_rom ( 219 | .clk(clk), 220 | 221 | .character(character - 32), 222 | .row(character_row), 223 | .data_out(font_line) 224 | ); 225 | 226 | always @(posedge clk) begin 227 | if (ce_pix) begin 228 | font_line_reg <= font_line; 229 | end 230 | end 231 | endmodule 232 | -------------------------------------------------------------------------------- /target/pocket/cart_pll.ppf: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /target/pocket/cart_pll/cart_pll_0002.qip: -------------------------------------------------------------------------------- 1 | set_instance_assignment -name PLL_COMPENSATION_MODE DIRECT -to "*cart_pll_0002*|altera_pll:altera_pll_i*|*" 2 | set_instance_assignment -name PLL_CHANNEL_SPACING "0.0 KHz" -to "*cart_pll_0002*|altera_pll:altera_pll_i*|*" 3 | set_instance_assignment -name PLL_AUTO_RESET OFF -to "*cart_pll_0002*|altera_pll:altera_pll_i*|*" 4 | set_instance_assignment -name PLL_BANDWIDTH_PRESET AUTO -to "*cart_pll_0002*|altera_pll:altera_pll_i*|*" 5 | -------------------------------------------------------------------------------- /target/pocket/cart_pll/cart_pll_0002.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/10ps 2 | module cart_pll_0002( 3 | 4 | // interface 'refclk' 5 | input wire refclk, 6 | 7 | // interface 'reset' 8 | input wire rst, 9 | 10 | // interface 'outclk0' 11 | output wire outclk_0, 12 | 13 | // interface 'locked' 14 | output wire locked 15 | ); 16 | 17 | altera_pll #( 18 | .fractional_vco_multiplier("true"), 19 | .reference_clock_frequency("74.25 MHz"), 20 | .operation_mode("direct"), 21 | .number_of_clocks(1), 22 | .output_clock_frequency0("6.285402 MHz"), 23 | .phase_shift0("0 ps"), 24 | .duty_cycle0(50), 25 | .output_clock_frequency1("0 MHz"), 26 | .phase_shift1("0 ps"), 27 | .duty_cycle1(50), 28 | .output_clock_frequency2("0 MHz"), 29 | .phase_shift2("0 ps"), 30 | .duty_cycle2(50), 31 | .output_clock_frequency3("0 MHz"), 32 | .phase_shift3("0 ps"), 33 | .duty_cycle3(50), 34 | .output_clock_frequency4("0 MHz"), 35 | .phase_shift4("0 ps"), 36 | .duty_cycle4(50), 37 | .output_clock_frequency5("0 MHz"), 38 | .phase_shift5("0 ps"), 39 | .duty_cycle5(50), 40 | .output_clock_frequency6("0 MHz"), 41 | .phase_shift6("0 ps"), 42 | .duty_cycle6(50), 43 | .output_clock_frequency7("0 MHz"), 44 | .phase_shift7("0 ps"), 45 | .duty_cycle7(50), 46 | .output_clock_frequency8("0 MHz"), 47 | .phase_shift8("0 ps"), 48 | .duty_cycle8(50), 49 | .output_clock_frequency9("0 MHz"), 50 | .phase_shift9("0 ps"), 51 | .duty_cycle9(50), 52 | .output_clock_frequency10("0 MHz"), 53 | .phase_shift10("0 ps"), 54 | .duty_cycle10(50), 55 | .output_clock_frequency11("0 MHz"), 56 | .phase_shift11("0 ps"), 57 | .duty_cycle11(50), 58 | .output_clock_frequency12("0 MHz"), 59 | .phase_shift12("0 ps"), 60 | .duty_cycle12(50), 61 | .output_clock_frequency13("0 MHz"), 62 | .phase_shift13("0 ps"), 63 | .duty_cycle13(50), 64 | .output_clock_frequency14("0 MHz"), 65 | .phase_shift14("0 ps"), 66 | .duty_cycle14(50), 67 | .output_clock_frequency15("0 MHz"), 68 | .phase_shift15("0 ps"), 69 | .duty_cycle15(50), 70 | .output_clock_frequency16("0 MHz"), 71 | .phase_shift16("0 ps"), 72 | .duty_cycle16(50), 73 | .output_clock_frequency17("0 MHz"), 74 | .phase_shift17("0 ps"), 75 | .duty_cycle17(50), 76 | .pll_type("General"), 77 | .pll_subtype("General") 78 | ) altera_pll_i ( 79 | .rst (rst), 80 | .outclk ({outclk_0}), 81 | .locked (locked), 82 | .fboutclk ( ), 83 | .fbclk (1'b0), 84 | .refclk (refclk) 85 | ); 86 | endmodule 87 | 88 | -------------------------------------------------------------------------------- /target/pocket/core.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "cart_pll.qip"] 2 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "core_top.sv"] 3 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "core_bridge_cmd.v"] 4 | set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) "core_constraints.sdc"] 5 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "data_loader.sv"] 6 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "data_unloader.sv"] 7 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "mf_pllbase.qip"] 8 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "psram.sv"] 9 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "sound_i2s.sv"] 10 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "sync_fifo.sv"] 11 | -------------------------------------------------------------------------------- /target/pocket/core_constraints.sdc: -------------------------------------------------------------------------------- 1 | # 2 | # user core constraints 3 | # 4 | # put your clock groups in here as well as any net assignments 5 | # 6 | 7 | set_clock_groups -asynchronous \ 8 | -group { bridge_spiclk } \ 9 | -group { clk_74a } \ 10 | -group { clk_74b } \ 11 | -group { ic|mp1|mf_pllbase_inst|altera_pll_i|general[0].gpll~PLL_OUTPUT_COUNTER|divclk } \ 12 | -group { ic|mp1|mf_pllbase_inst|altera_pll_i|general[1].gpll~PLL_OUTPUT_COUNTER|divclk } \ 13 | -group { ic|mp1|mf_pllbase_inst|altera_pll_i|general[2].gpll~PLL_OUTPUT_COUNTER|divclk } \ 14 | -group { ic|mp1|mf_pllbase_inst|altera_pll_i|general[3].gpll~PLL_OUTPUT_COUNTER|divclk } 15 | -------------------------------------------------------------------------------- /target/pocket/mf_pllbase.ppf: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /target/pocket/mf_pllbase/mf_pllbase_0002.qip: -------------------------------------------------------------------------------- 1 | set_instance_assignment -name PLL_COMPENSATION_MODE NORMAL -to "*mf_pllbase_0002*|altera_pll:altera_pll_i*|*" 2 | set_instance_assignment -name PLL_CHANNEL_SPACING "0.0 KHz" -to "*mf_pllbase_0002*|altera_pll:altera_pll_i*|*" 3 | set_instance_assignment -name PLL_AUTO_RESET OFF -to "*mf_pllbase_0002*|altera_pll:altera_pll_i*|*" 4 | set_instance_assignment -name PLL_BANDWIDTH_PRESET AUTO -to "*mf_pllbase_0002*|altera_pll:altera_pll_i*|*" 5 | -------------------------------------------------------------------------------- /target/pocket/mf_pllbase/mf_pllbase_0002.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/10ps 2 | module mf_pllbase_0002( 3 | 4 | // interface 'refclk' 5 | input wire refclk, 6 | 7 | // interface 'reset' 8 | input wire rst, 9 | 10 | // interface 'outclk0' 11 | output wire outclk_0, 12 | 13 | // interface 'outclk1' 14 | output wire outclk_1, 15 | 16 | // interface 'outclk2' 17 | output wire outclk_2, 18 | 19 | // interface 'outclk3' 20 | output wire outclk_3, 21 | 22 | // interface 'locked' 23 | output wire locked 24 | ); 25 | 26 | altera_pll #( 27 | .fractional_vco_multiplier("true"), 28 | .reference_clock_frequency("74.25 MHz"), 29 | .operation_mode("normal"), 30 | .number_of_clocks(4), 31 | .output_clock_frequency0("67.108860 MHz"), 32 | .phase_shift0("0 ps"), 33 | .duty_cycle0(50), 34 | .output_clock_frequency1("33.554430 MHz"), 35 | .phase_shift1("0 ps"), 36 | .duty_cycle1(50), 37 | .output_clock_frequency2("6.710885 MHz"), 38 | .phase_shift2("0 ps"), 39 | .duty_cycle2(50), 40 | .output_clock_frequency3("6.710885 MHz"), 41 | .phase_shift3("37253 ps"), 42 | .duty_cycle3(50), 43 | .output_clock_frequency4("0 MHz"), 44 | .phase_shift4("0 ps"), 45 | .duty_cycle4(50), 46 | .output_clock_frequency5("0 MHz"), 47 | .phase_shift5("0 ps"), 48 | .duty_cycle5(50), 49 | .output_clock_frequency6("0 MHz"), 50 | .phase_shift6("0 ps"), 51 | .duty_cycle6(50), 52 | .output_clock_frequency7("0 MHz"), 53 | .phase_shift7("0 ps"), 54 | .duty_cycle7(50), 55 | .output_clock_frequency8("0 MHz"), 56 | .phase_shift8("0 ps"), 57 | .duty_cycle8(50), 58 | .output_clock_frequency9("0 MHz"), 59 | .phase_shift9("0 ps"), 60 | .duty_cycle9(50), 61 | .output_clock_frequency10("0 MHz"), 62 | .phase_shift10("0 ps"), 63 | .duty_cycle10(50), 64 | .output_clock_frequency11("0 MHz"), 65 | .phase_shift11("0 ps"), 66 | .duty_cycle11(50), 67 | .output_clock_frequency12("0 MHz"), 68 | .phase_shift12("0 ps"), 69 | .duty_cycle12(50), 70 | .output_clock_frequency13("0 MHz"), 71 | .phase_shift13("0 ps"), 72 | .duty_cycle13(50), 73 | .output_clock_frequency14("0 MHz"), 74 | .phase_shift14("0 ps"), 75 | .duty_cycle14(50), 76 | .output_clock_frequency15("0 MHz"), 77 | .phase_shift15("0 ps"), 78 | .duty_cycle15(50), 79 | .output_clock_frequency16("0 MHz"), 80 | .phase_shift16("0 ps"), 81 | .duty_cycle16(50), 82 | .output_clock_frequency17("0 MHz"), 83 | .phase_shift17("0 ps"), 84 | .duty_cycle17(50), 85 | .pll_type("General"), 86 | .pll_subtype("General") 87 | ) altera_pll_i ( 88 | .rst (rst), 89 | .outclk ({outclk_3, outclk_2, outclk_1, outclk_0}), 90 | .locked (locked), 91 | .fboutclk ( ), 92 | .fbclk (1'b0), 93 | .refclk (refclk) 94 | ); 95 | endmodule 96 | 97 | -------------------------------------------------------------------------------- /target/pocket/pin_ddio_clk.ppf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /target/pocket/pin_ddio_clk.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name IP_TOOL_NAME "ALTDDIO_OUT" 2 | set_global_assignment -name IP_TOOL_VERSION "18.1" 3 | set_global_assignment -name IP_GENERATED_DEVICE_FAMILY "{Cyclone V}" 4 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "pin_ddio_clk.v"] 5 | set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "pin_ddio_clk_inst.v"] 6 | set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "pin_ddio_clk.ppf"] 7 | -------------------------------------------------------------------------------- /target/pocket/pin_ddio_clk.v: -------------------------------------------------------------------------------- 1 | // megafunction wizard: %ALTDDIO_OUT% 2 | // GENERATION: STANDARD 3 | // VERSION: WM1.0 4 | // MODULE: ALTDDIO_OUT 5 | 6 | // ============================================================ 7 | // File Name: pin_ddio_clk.v 8 | // Megafunction Name(s): 9 | // ALTDDIO_OUT 10 | // 11 | // Simulation Library Files(s): 12 | // altera_mf 13 | // ============================================================ 14 | // ************************************************************ 15 | // THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! 16 | // 17 | // 18.1.1 Build 646 04/11/2019 SJ Lite Edition 18 | // ************************************************************ 19 | 20 | 21 | //Copyright (C) 2019 Intel Corporation. All rights reserved. 22 | //Your use of Intel Corporation's design tools, logic functions 23 | //and other software and tools, and any partner logic 24 | //functions, and any output files from any of the foregoing 25 | //(including device programming or simulation files), and any 26 | //associated documentation or information are expressly subject 27 | //to the terms and conditions of the Intel Program License 28 | //Subscription Agreement, the Intel Quartus Prime License Agreement, 29 | //the Intel FPGA IP License Agreement, or other applicable license 30 | //agreement, including, without limitation, that your use is for 31 | //the sole purpose of programming logic devices manufactured by 32 | //Intel and sold by Intel or its authorized distributors. Please 33 | //refer to the applicable agreement for further details, at 34 | //https://fpgasoftware.intel.com/eula. 35 | 36 | 37 | // synopsys translate_off 38 | `timescale 1 ps / 1 ps 39 | // synopsys translate_on 40 | module pin_ddio_clk ( 41 | datain_h, 42 | datain_l, 43 | outclock, 44 | dataout); 45 | 46 | input [0:0] datain_h; 47 | input [0:0] datain_l; 48 | input outclock; 49 | output [0:0] dataout; 50 | 51 | wire [0:0] sub_wire0; 52 | wire [0:0] dataout = sub_wire0[0:0]; 53 | 54 | altddio_out ALTDDIO_OUT_component ( 55 | .datain_h (datain_h), 56 | .datain_l (datain_l), 57 | .outclock (outclock), 58 | .dataout (sub_wire0), 59 | .aclr (1'b0), 60 | .aset (1'b0), 61 | .oe (1'b1), 62 | .oe_out (), 63 | .outclocken (1'b1), 64 | .sclr (1'b0), 65 | .sset (1'b0)); 66 | defparam 67 | ALTDDIO_OUT_component.extend_oe_disable = "OFF", 68 | ALTDDIO_OUT_component.intended_device_family = "Cyclone V", 69 | ALTDDIO_OUT_component.invert_output = "OFF", 70 | ALTDDIO_OUT_component.lpm_hint = "UNUSED", 71 | ALTDDIO_OUT_component.lpm_type = "altddio_out", 72 | ALTDDIO_OUT_component.oe_reg = "UNREGISTERED", 73 | ALTDDIO_OUT_component.power_up_high = "OFF", 74 | ALTDDIO_OUT_component.width = 1; 75 | 76 | 77 | endmodule 78 | 79 | // ============================================================ 80 | // CNX file retrieval info 81 | // ============================================================ 82 | // Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all 83 | // Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone V" 84 | // Retrieval info: CONSTANT: EXTEND_OE_DISABLE STRING "OFF" 85 | // Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone V" 86 | // Retrieval info: CONSTANT: INVERT_OUTPUT STRING "OFF" 87 | // Retrieval info: CONSTANT: LPM_HINT STRING "UNUSED" 88 | // Retrieval info: CONSTANT: LPM_TYPE STRING "altddio_out" 89 | // Retrieval info: CONSTANT: OE_REG STRING "UNREGISTERED" 90 | // Retrieval info: CONSTANT: POWER_UP_HIGH STRING "OFF" 91 | // Retrieval info: CONSTANT: WIDTH NUMERIC "1" 92 | // Retrieval info: USED_PORT: datain_h 0 0 1 0 INPUT NODEFVAL "datain_h[0..0]" 93 | // Retrieval info: CONNECT: @datain_h 0 0 1 0 datain_h 0 0 1 0 94 | // Retrieval info: USED_PORT: datain_l 0 0 1 0 INPUT NODEFVAL "datain_l[0..0]" 95 | // Retrieval info: CONNECT: @datain_l 0 0 1 0 datain_l 0 0 1 0 96 | // Retrieval info: USED_PORT: dataout 0 0 1 0 OUTPUT NODEFVAL "dataout[0..0]" 97 | // Retrieval info: CONNECT: dataout 0 0 1 0 @dataout 0 0 1 0 98 | // Retrieval info: USED_PORT: outclock 0 0 0 0 INPUT_CLK_EXT NODEFVAL "outclock" 99 | // Retrieval info: CONNECT: @outclock 0 0 0 0 outclock 0 0 0 0 100 | // Retrieval info: GEN_FILE: TYPE_NORMAL pin_ddio_clk.v TRUE FALSE 101 | // Retrieval info: GEN_FILE: TYPE_NORMAL pin_ddio_clk.qip TRUE FALSE 102 | // Retrieval info: GEN_FILE: TYPE_NORMAL pin_ddio_clk.bsf FALSE TRUE 103 | // Retrieval info: GEN_FILE: TYPE_NORMAL pin_ddio_clk_inst.v TRUE TRUE 104 | // Retrieval info: GEN_FILE: TYPE_NORMAL pin_ddio_clk_bb.v FALSE TRUE 105 | // Retrieval info: GEN_FILE: TYPE_NORMAL pin_ddio_clk.inc FALSE TRUE 106 | // Retrieval info: GEN_FILE: TYPE_NORMAL pin_ddio_clk.cmp FALSE TRUE 107 | // Retrieval info: GEN_FILE: TYPE_NORMAL pin_ddio_clk.ppf TRUE FALSE 108 | // Retrieval info: LIB_FILE: altera_mf 109 | -------------------------------------------------------------------------------- /target/pocket/sound_i2s.sv: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Adam Gastineau 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | // A very simple audio i2s bridge to APF, based on their example code 26 | module sound_i2s #( 27 | parameter CHANNEL_WIDTH = 16, 28 | parameter SIGNED_INPUT = 0 29 | ) ( 30 | // input wire clk_74a, 31 | // input wire clk_audio, 32 | input wire audio_sclk, 33 | 34 | // Left and right audio channels. ~~Can be in an arbitrary `clk_audio` domain~~ 35 | input wire [CHANNEL_WIDTH - 1:0] audio_l, 36 | input wire [CHANNEL_WIDTH - 1:0] audio_r, 37 | 38 | // output reg audio_mclk, 39 | output reg audio_lrck, 40 | output reg audio_dac 41 | ); 42 | // 43 | // audio i2s generator 44 | // 45 | 46 | reg audgen_nextsamp; 47 | 48 | // generate MCLK = 12.288mhz with fractional accumulator 49 | // reg [21:0] audgen_accum = 0; 50 | // localparam [20:0] CYCLE_48KHZ = 21'd122880 * 2; 51 | // always @(posedge clk_74a) begin 52 | // audgen_accum <= audgen_accum + CYCLE_48KHZ; 53 | // if (audgen_accum >= 21'd742500) begin 54 | // audio_mclk <= ~audio_mclk; 55 | // audgen_accum <= audgen_accum - 21'd742500 + CYCLE_48KHZ; 56 | // end 57 | // end 58 | 59 | // // generate SCLK = 3.072mhz by dividing MCLK by 4 60 | // reg [1:0] aud_mclk_divider; 61 | // reg prev_audio_mclk; 62 | // wire audgen_sclk = aud_mclk_divider[1] /* synthesis keep*/; 63 | 64 | // always @(posedge clk_74a) begin 65 | // if (audio_mclk && ~prev_audio_mclk) begin 66 | // aud_mclk_divider <= aud_mclk_divider + 1'b1; 67 | // end 68 | 69 | // prev_audio_mclk <= audio_mclk; 70 | // end 71 | 72 | // shift out audio data as I2S 73 | // 32 total bits per channel, but only 16 active bits at the start and then 16 dummy bits 74 | // 75 | // synchronize audio samples coming from the core 76 | 77 | localparam CHANNEL_LEFT_HIGH = 16; 78 | localparam CHANNEL_RIGHT_HIGH = 16 + CHANNEL_LEFT_HIGH; 79 | 80 | // Width of channel with signed component 81 | localparam SIGNED_CHANNEL_WIDTH = SIGNED_INPUT ? CHANNEL_WIDTH : CHANNEL_WIDTH + 1; 82 | 83 | // Can center unsigned in signed interval by flipping high bit 84 | wire [CHANNEL_WIDTH - 1:0] sign_converted_audio_l = { 85 | audio_l[CHANNEL_WIDTH-1:CHANNEL_WIDTH-1], audio_l[CHANNEL_WIDTH-2:0] 86 | }; 87 | 88 | wire [CHANNEL_WIDTH - 1:0] sign_converted_audio_r = { 89 | audio_r[CHANNEL_WIDTH-1:CHANNEL_WIDTH-1], audio_r[CHANNEL_WIDTH-2:0] 90 | }; 91 | 92 | wire [31:0] audgen_sampdata; 93 | 94 | assign audgen_sampdata[CHANNEL_LEFT_HIGH-1:CHANNEL_LEFT_HIGH-CHANNEL_WIDTH] = SIGNED_INPUT ? audio_l : sign_converted_audio_l; 95 | assign audgen_sampdata[CHANNEL_RIGHT_HIGH-1:CHANNEL_RIGHT_HIGH-CHANNEL_WIDTH] = SIGNED_INPUT ? audio_r : sign_converted_audio_r; 96 | 97 | generate 98 | if (15 - SIGNED_CHANNEL_WIDTH > 0) begin 99 | assign audgen_sampdata[31-SIGNED_CHANNEL_WIDTH:16] = 0; 100 | assign audgen_sampdata[15-SIGNED_CHANNEL_WIDTH:0] = 0; 101 | end 102 | endgenerate 103 | 104 | // sync_fifo #( 105 | // .WIDTH(32) 106 | // ) sync_fifo ( 107 | // .clk_write(clk_audio), 108 | // .clk_read (audio_sclk), 109 | 110 | // .write_en(write_en), 111 | // .data(audgen_sampdata), 112 | // .data_s(audgen_sampdata_s) 113 | // ); 114 | 115 | // reg write_en = 0; 116 | // reg [CHANNEL_WIDTH - 1:0] prev_left; 117 | // reg [CHANNEL_WIDTH - 1:0] prev_right; 118 | 119 | // // Mark write when necessary 120 | // always @(posedge clk_audio) begin 121 | // prev_left <= audio_l; 122 | // prev_right <= audio_r; 123 | 124 | // write_en <= 0; 125 | 126 | // if (audio_l != prev_left || audio_r != prev_right) begin 127 | // write_en <= 1; 128 | // end 129 | // end 130 | 131 | // wire [31:0] audgen_sampdata_s; 132 | 133 | reg [31:0] audgen_sampshift; 134 | reg [ 4:0] audio_lrck_cnt; 135 | always @(posedge audio_sclk) begin 136 | // output the next bit 137 | audio_dac <= audgen_sampshift[31]; 138 | 139 | // 48khz * 64 140 | audio_lrck_cnt <= audio_lrck_cnt + 1'b1; 141 | if (audio_lrck_cnt == 31) begin 142 | // switch channels 143 | audio_lrck <= ~audio_lrck; 144 | 145 | // Reload sample shifter 146 | if (~audio_lrck) begin 147 | audgen_sampshift <= audgen_sampdata; 148 | end 149 | end else if (audio_lrck_cnt < 16) begin 150 | // only shift for 16 clocks per channel 151 | audgen_sampshift <= {audgen_sampshift[30:0], 1'b0}; 152 | end 153 | end 154 | 155 | initial begin 156 | // Verify parameters 157 | if (CHANNEL_WIDTH > 16) begin 158 | $error("CHANNEL_WIDTH must be <= 16. Received %d", CHANNEL_WIDTH); 159 | end 160 | 161 | if (SIGNED_INPUT != 0 && SIGNED_INPUT != 1) begin 162 | $error("SIGNED_INPUT must be 0 or 1. Received %d", SIGNED_INPUT); 163 | end 164 | end 165 | endmodule 166 | -------------------------------------------------------------------------------- /target/pocket/sync_fifo.sv: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Adam Gastineau 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | // An easily reusable method for synchronizing multiple bits across clock domains 26 | // Uses a shallow depth (4 entries) FIFO, so make sure to empty it quickly 27 | module sync_fifo #( 28 | parameter WIDTH = 2 29 | ) ( 30 | input wire clk_write, 31 | input wire clk_read, 32 | 33 | input wire write_en, 34 | input wire [WIDTH - 1:0] data, 35 | output reg [WIDTH - 1:0] data_s = 0, 36 | output reg write_en_s = 0 37 | ); 38 | 39 | reg read_req = 0; 40 | wire empty; 41 | 42 | wire [WIDTH - 1:0] fifo_out; 43 | 44 | dcfifo dcfifo_component ( 45 | .data(data), 46 | .rdclk(clk_read), 47 | .rdreq(read_req), 48 | .wrclk(clk_write), 49 | .wrreq(write_en), 50 | .q(fifo_out), 51 | .rdempty(empty), 52 | .aclr(), 53 | .eccstatus(), 54 | .rdfull(), 55 | .rdusedw(), 56 | .wrempty(), 57 | .wrfull(), 58 | .wrusedw() 59 | ); 60 | defparam dcfifo_component.intended_device_family = "Cyclone V", dcfifo_component.lpm_numwords = 4, 61 | dcfifo_component.lpm_showahead = "OFF", dcfifo_component.lpm_type = "dcfifo", 62 | dcfifo_component.lpm_width = WIDTH, dcfifo_component.lpm_widthu = 2, 63 | dcfifo_component.overflow_checking = "ON", dcfifo_component.rdsync_delaypipe = 5, 64 | dcfifo_component.underflow_checking = "ON", dcfifo_component.use_eab = "ON", 65 | dcfifo_component.wrsync_delaypipe = 5; 66 | 67 | reg [1:0] read_state = 0; 68 | 69 | localparam READ_DELAY = 1; 70 | localparam READ_WRITE = 2; 71 | 72 | always @(posedge clk_read) begin 73 | read_req <= 0; 74 | write_en_s <= 0; 75 | 76 | if (~empty) begin 77 | read_state <= READ_DELAY; 78 | read_req <= 1; 79 | end 80 | 81 | case (read_state) 82 | READ_DELAY: begin 83 | read_state <= READ_WRITE; 84 | end 85 | READ_WRITE: begin 86 | read_state <= 0; 87 | 88 | data_s <= fifo_out; 89 | write_en_s <= 1; 90 | end 91 | endcase 92 | end 93 | 94 | endmodule 95 | --------------------------------------------------------------------------------