├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── BUG_REPORT.yml │ └── QUESTION.yml ├── .gitignore ├── LICENSE ├── README.md ├── assets └── pce.aseprite ├── pkg ├── Assets │ └── pce │ │ └── common │ │ └── .gitkeep ├── Cores │ └── agg23.PC Engine │ │ ├── audio.json │ │ ├── chip32.bin │ │ ├── core.json │ │ ├── data.json │ │ ├── icon.bin │ │ ├── info.txt │ │ ├── input.json │ │ ├── interact.json │ │ ├── variants.json │ │ └── video.json └── Platforms │ ├── _images │ └── pce.bin │ └── pce.json ├── 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 ├── pce_pocket.qip ├── pce_pocket.qpf ├── pce_pocket.qsf └── pce_pocket.sdc ├── rtl ├── main.sv ├── pce.qip ├── pce │ ├── CEGen.vhd │ ├── HUC6280 │ │ ├── AddSubBCD.vhd │ │ ├── HUC6280.qip │ │ ├── HUC6280.vhd │ │ ├── HUC6280_AG.vhd │ │ ├── HUC6280_ALU.vhd │ │ ├── HUC6280_CPU.vhd │ │ ├── HUC6280_MC.vhd │ │ ├── HUC6280_PKG.vhd │ │ ├── psg.vhd │ │ └── voltab.mif │ ├── arcade.sv │ ├── cache_2way.sv │ ├── cd │ │ ├── CDDA_FIFO.vhd │ │ ├── MSM5205.vhd │ │ ├── SCSI.vhd │ │ ├── SCSI_FIFO.vhd │ │ ├── cd.qip │ │ └── cd.vhd │ ├── cheatcodes.sv │ ├── color_mix.sv │ ├── ddram.sv │ ├── dpram.vhd │ ├── hps_ext.v │ ├── huc6202.vhd │ ├── huc6260.vhd │ ├── huc6260_palette_init.mif │ ├── huc6270.vhd │ ├── mb128.sv │ ├── palette.mif │ ├── pce_top.vhd │ ├── sdram.sv │ └── xe1ap.v └── sys │ └── iir_filter.v ├── support └── chip32.asm └── target └── pocket ├── audio.sv ├── core.qip ├── core_bridge_cmd.v ├── core_constraints.sdc ├── core_top.v ├── data_loader.sv ├── data_unloader.sv ├── linebuffer.v ├── 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 ├── sound_i2s.sv ├── stp1.stp └── sync_fifo.sv /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: agg23 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.yml: -------------------------------------------------------------------------------- 1 | ##################################################################### 2 | # SPDX-License-Identifier: CC0-1.0 3 | # SPDX-FileType: OTHER 4 | # SPDX-FileCopyrightText: (c) 2022, OpenGateware authors and contributors, Adam Gastineau 5 | ##################################################################### 6 | name: "Bug Report" 7 | description: "Let us know about an unexpected error, a crash, or incorrect behavior." 8 | labels: 9 | - bug 10 | body: 11 | - type: markdown 12 | attributes: 13 | value: | 14 | Please note that the issue tracker is intended for bug reports. 15 | Make sure to [search for existing issues](https://github.com/agg23/openfpga-pcengine/issues) before filing a new one! 16 | 17 | - type: input 18 | id: version 19 | attributes: 20 | label: Version (or build number) 21 | placeholder: "0.1.0" 22 | description: | 23 | You can find the version in the About section, when you open the core. 24 | 25 | If you are not running the latest version, please try upgrading before reporting issues. 26 | validations: 27 | required: true 28 | 29 | - type: textarea 30 | id: steps 31 | attributes: 32 | label: Steps to reproduce 33 | description: | 34 | Please list the full steps required to reproduce the issue 35 | placeholder: | 36 | - Be precise 37 | - Include exact data used during testing for easy reference 38 | - The steps have to be in the exact order 39 | - Mention pre-requisites when applicable 40 | validations: 41 | required: false 42 | 43 | - type: textarea 44 | id: expected_behavior 45 | attributes: 46 | label: Expected Behavior 47 | description: If you want to include screenshots, paste them into the markdown editor below or follow up with a separate comment. 48 | placeholder: What were you expecting? 49 | validations: 50 | required: false 51 | 52 | - type: textarea 53 | id: actual_behavior 54 | attributes: 55 | label: Actual Behavior 56 | placeholder: What happened instead? 57 | validations: 58 | required: true 59 | 60 | - type: textarea 61 | id: bug_context 62 | attributes: 63 | label: Additional Context 64 | description: | 65 | Are there anything unusual about your situation that we should know? 66 | validations: 67 | required: false 68 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/QUESTION.yml: -------------------------------------------------------------------------------- 1 | ##################################################################### 2 | # SPDX-License-Identifier: CC0-1.0 3 | # SPDX-FileType: OTHER 4 | # SPDX-FileCopyrightText: (c) 2022, OpenGateware authors and contributors, Adam Gastineau 5 | ##################################################################### 6 | name: "Question" 7 | description: "Ask a question about the project." 8 | labels: 9 | - question 10 | body: 11 | - type: markdown 12 | attributes: 13 | value: | 14 | Please note that the issue tracker is intended for bug reports. 15 | Make sure to [search for existing issues](https://github.com/agg23/openfpga-pcengine/issues) before filing a new one! 16 | 17 | - type: textarea 18 | id: question 19 | attributes: 20 | label: Ask a question 21 | placeholder: | 22 | Ask your question here! Please keep the questions related to the core only. 23 | validations: 24 | required: true 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/**/chip32.bin 2 | support/*.bin 3 | 4 | *.zip 5 | *.rbf_r 6 | *.rev 7 | *.wlf 8 | *.vcd 9 | db 10 | greybox_tmp 11 | hps_isw_handoff 12 | incremental_db 13 | output_files 14 | PLLJ_PLLSPE_INFO.txt 15 | simulation 16 | vip 17 | .qsys_edit 18 | *_netlist 19 | *_sim 20 | *.bak 21 | *.bsf 22 | *.cdf 23 | *.cmp 24 | *.csv 25 | *.done 26 | *.f 27 | *.pin 28 | *.pof 29 | *.ptf.* 30 | *.qar 31 | *.qarlog 32 | *.qdf 33 | *.qws 34 | *.rbf 35 | *.rpt 36 | *.sip 37 | *.sld 38 | *.smsg 39 | *.sof 40 | *.sopc_builder 41 | *.sopcinfo 42 | *.spd 43 | *.summary 44 | *.xml 45 | *~ 46 | **/.DS_Store 47 | build_id.mif 48 | build_id.v 49 | c5_pin_model_dump.txt 50 | cr_ie_info.json 51 | # Gateman directories and files 52 | !.gateman/* 53 | !gateware.json 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PC Engine for Analogue Pocket 2 | 3 | Ported from the core originally developed by [Gregory Estrade](https://github.com/Torlus/FPGAPCE) and heavily modified by [@srg320](https://github.com/srg320) and [@greyrogue](https://github.com/greyrogue). Core icon based on TG-16 icon by [spiritualized1997](https://github.com/spiritualized1997). Latest upstream available at https://github.com/MiSTer-devel/TurboGrafx16_MiSTer 4 | 5 | Please report any issues encountered to this repo. Most likely any problems are a result of my port, not the original core. Issues will be upstreamed as necessary. 6 | 7 | ## Installation 8 | 9 | ### Easy mode 10 | 11 | I highly recommend the updater tools by [@mattpannella](https://github.com/mattpannella) and [@RetroDriven](https://github.com/RetroDriven). If you're running Windows, use [the RetroDriven GUI](https://github.com/RetroDriven/Pocket_Updater), or if you prefer the CLI, use [the mattpannella tool](https://github.com/mattpannella/pocket_core_autoupdate_net). Either of these will allow you to automatically download and install openFPGA cores onto your Analogue Pocket. Go donate to them if you can 12 | 13 | ### Manual mode 14 | Download the core by clicking Releases on the right side of this page, then download the `agg23.*.zip` file from the latest release. 15 | 16 | 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. 17 | 18 | ## Usage 19 | 20 | ROMs should be placed in `/Assets/pce/common` 21 | 22 | SuperGrafix games **_MUST_** have the `.sgx` extension, as otherwise there's no way for the core to tell that it uses the SuperGrafx hardware. 23 | 24 | Please note that CD games are not currently supported. Support will be added in a future update. 25 | 26 | ## Features 27 | 28 | ### Dock Support 29 | 30 | Core supports four players/controllers via the Analogue Dock. To enable four player mode, turn on `Use Turbo Tap` setting. 31 | 32 | ### 6 button controller 33 | 34 | Some games support a 6 button controller. For those games, enable the `Use 6 Button Ctrl` option in `Core Settings`. Please note that this option can break games that don't support the 6 button controller, so turn it off if you're not using it. 35 | 36 | ### Controller Turbo 37 | 38 | Like the original PC Engine controllers, this core supports multiple turbo modes. Adjust the `I` and `II` button turbo modes, and use the `X` and `Y` buttons (by default) as your turbo buttons. Note that the original PCE controllers had the turbo on the `I` and `II` buttons directly, rather than having separate buttons, but since the Pocket has more than just two, we use them for the turbo. 39 | 40 | ### Video Modes 41 | 42 | The PC Engine is unique in that it can arbitrarily decide what resolution to display at. The Pocket is more limited, requiring fixed resolutions at all times. I've tried to compromise and cover the most common resolutions output by the PCE, but some are better supported than others. You should see the video centered on the screen with surrounding black bars on some resolutions, but the aspect ratios should be correct. 43 | 44 | ### Video Options 45 | 46 | * `Extra Sprites` - Allows extra sprites to be displayed on each line. Will decrease flickering in some games 47 | * `Raw RGB Color` - Use the raw RGB color palette output by the HUC6260. If disabled, will use the composite color palette 48 | 49 | ### Audio Options 50 | 51 | The core can be quiet in some games, so there are options to boost the master audio (`Master Audio Boost`) and ADPCM channels (`PCM Audio Boost`). 52 | 53 | ### Memory Cards 54 | 55 | Instead of sharing a memory card (as you would in real life), each game gets its own save file and therefore memory card. Some games don't have the ability to initialize a memory card, so each newly created save file is pre-initialized for use. 56 | 57 | ## Licensing 58 | 59 | All source included in this project from me or the [MiSTer project](https://github.com/MiSTer-devel/TurboGrafx16_MiSTer) is licensed as GPLv2, unless otherwise noted. The original source for [FPGAPCE](https://github.com/Torlus/FPGAPCE), the project this core is based off of, is [public domain](https://twitter.com/Torlus/status/1582663978068893696). The contents of the public domain tweet are reproduced here: 60 | 61 | > Indeed. The main reason why I haven't provided a license is that I didn't know how to deal with the different licenses attached to parts of the cores. 62 | Anyway, consider *my own* source code as public domain, i.e do what you want with it, for any use you want. (1/2) 63 | 64 | [Additionally, he wrote](https://twitter.com/Torlus/status/1582664299973341184): 65 | 66 | > If stated otherwise in the comments at the beginning of a given source file, the license attached prevails. That applies to my FPGAPCE project (https://github.com/Torlus/FPGAPCE). -------------------------------------------------------------------------------- /assets/pce.aseprite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agg23/openfpga-pcengine/8810ed6283c7080013b69db843b5601f170c77c2/assets/pce.aseprite -------------------------------------------------------------------------------- /pkg/Assets/pce/common/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agg23/openfpga-pcengine/8810ed6283c7080013b69db843b5601f170c77c2/pkg/Assets/pce/common/.gitkeep -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/audio.json: -------------------------------------------------------------------------------- 1 | { 2 | "audio": { 3 | "magic": "APF_VER_1" 4 | } 5 | } -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/chip32.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agg23/openfpga-pcengine/8810ed6283c7080013b69db843b5601f170c77c2/pkg/Cores/agg23.PC Engine/chip32.bin -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/core.json: -------------------------------------------------------------------------------- 1 | { 2 | "core": { 3 | "magic": "APF_VER_1", 4 | "metadata": { 5 | "platform_ids": [ 6 | "pce" 7 | ], 8 | "shortname": "PC Engine", 9 | "description": "PC Engine (known as TurboGrafx-16 in the US) by NEC", 10 | "author": "agg23", 11 | "url": "https://github.com/agg23/openfpga-pcengine/", 12 | "version": "1.0.1", 13 | "date_release": "2024-09-16" 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": false, 25 | "cartridge_adapter": -1 26 | }, 27 | "chip32_vm": "chip32.bin" 28 | }, 29 | "cores": [ 30 | { 31 | "name": "default", 32 | "id": 0, 33 | "filename": "pce.rev" 34 | } 35 | ] 36 | } 37 | } -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "magic": "APF_VER_1", 4 | "data_slots": [ 5 | { 6 | "name": "Cartridge", 7 | "id": 0, 8 | "required": true, 9 | "parameters": "0x109", 10 | "extensions": ["pce", "sgx"], 11 | "address": "0x10000000" 12 | }, 13 | { 14 | "name": "Save", 15 | "id": 1, 16 | "required": false, 17 | "parameters": "0x84", 18 | "nonvolatile": true, 19 | "extensions": ["sav"], 20 | "address": "0x20000000", 21 | "size_maximum": "0x4000" 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agg23/openfpga-pcengine/8810ed6283c7080013b69db843b5601f170c77c2/pkg/Cores/agg23.PC Engine/icon.bin -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/info.txt: -------------------------------------------------------------------------------- 1 | Port by agg23. Core by Gregory Estrade, srg320, and greyrogue 2 | 3 | PC Engine (PCE), known as TurboGrafx-16 in the United States, was created by NEC and was the first 4th generation console to reach the market. Therefore, even though it came out in the late 80s, it has significantly better graphics than 8-bit systems that were being sold at the time, such as the NES. 4 | 5 | Currently supports standard PC Engine/TurboGrafx and SuperGrafx ROMs. CD support is coming sometime in the future. Please report all issues to agg23, as most likely any issues experienced are issues with the port, not the core. -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "magic": "APF_VER_1", 4 | "controllers": [ 5 | { 6 | "type": "default", 7 | "mappings": [ 8 | { 9 | "id": 0, 10 | "name": "I Button", 11 | "key": "pad_btn_a" 12 | }, 13 | { 14 | "id": 1, 15 | "name": "II Button", 16 | "key": "pad_btn_b" 17 | }, 18 | { 19 | "id": 2, 20 | "name": "III/Turbo I Button", 21 | "key": "pad_btn_x" 22 | }, 23 | { 24 | "id": 3, 25 | "name": "IV/Turbo II Button", 26 | "key": "pad_btn_y" 27 | }, 28 | { 29 | "id": 4, 30 | "name": "V Button", 31 | "key": "pad_trig_l" 32 | }, 33 | { 34 | "id": 5, 35 | "name": "VI Button", 36 | "key": "pad_trig_r" 37 | }, 38 | { 39 | "id": 20, 40 | "name": "Run", 41 | "key": "pad_btn_start" 42 | }, 43 | { 44 | "id": 21, 45 | "name": "Select", 46 | "key": "pad_btn_select" 47 | } 48 | ] 49 | } 50 | ] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/interact.json: -------------------------------------------------------------------------------- 1 | { 2 | "interact": { 3 | "magic": "APF_VER_1", 4 | "variables": [ 5 | { 6 | "name": "Reset core", 7 | "id": 1, 8 | "type": "action", 9 | "enabled": true, 10 | "address": "0x00000050", 11 | "value": 1 12 | }, 13 | { 14 | "name": "Use Turbo Tap", 15 | "id": 20, 16 | "type": "check", 17 | "enabled": true, 18 | "address": "0x00000100", 19 | "persist": true, 20 | "writeonly": true, 21 | "defaultval": 0, 22 | "value": 1 23 | }, 24 | { 25 | "name": "Use 6 Button Ctrl", 26 | "id": 21, 27 | "type": "check", 28 | "enabled": true, 29 | "address": "0x00000104", 30 | "persist": true, 31 | "writeonly": true, 32 | "defaultval": 0, 33 | "value": 1 34 | }, 35 | { 36 | "name": "Button I Turbo", 37 | "id": 22, 38 | "type": "slider_u32", 39 | "enabled": true, 40 | "address": "0x00000108", 41 | "persist": true, 42 | "writeonly": true, 43 | "defaultval": 0, 44 | "graphical": { 45 | "min": 0, 46 | "max": 2, 47 | "adjust_small": 1, 48 | "adjust_large": 1 49 | } 50 | }, 51 | { 52 | "name": "Button II Turbo", 53 | "id": 23, 54 | "type": "slider_u32", 55 | "enabled": true, 56 | "address": "0x0000010C", 57 | "persist": true, 58 | "writeonly": true, 59 | "defaultval": 0, 60 | "graphical": { 61 | "min": 0, 62 | "max": 2, 63 | "adjust_small": 1, 64 | "adjust_large": 1 65 | } 66 | }, 67 | { 68 | "name": "Extra Sprites", 69 | "id": 41, 70 | "type": "check", 71 | "enabled": true, 72 | "address": "0x00000204", 73 | "persist": true, 74 | "writeonly": true, 75 | "defaultval": 0, 76 | "value": 1 77 | }, 78 | { 79 | "name": "Raw RGB Color", 80 | "id": 42, 81 | "type": "check", 82 | "enabled": true, 83 | "address": "0x00000208", 84 | "persist": true, 85 | "writeonly": true, 86 | "defaultval": 0, 87 | "value": 1 88 | }, 89 | { 90 | "name": "Master Audio Boost", 91 | "id": 60, 92 | "type": "list", 93 | "enabled": true, 94 | "address": "0x00000300", 95 | "persist": true, 96 | "writeonly": true, 97 | "defaultval": 0, 98 | "options": [ 99 | { 100 | "name": "No Boost", 101 | "value": 0 102 | }, 103 | { 104 | "name": "2x Boost", 105 | "value": 1 106 | }, 107 | { 108 | "name": "4x Boost", 109 | "value": 2 110 | } 111 | ] 112 | }, 113 | { 114 | "name": "PCM Audio Boost", 115 | "id": 61, 116 | "type": "check", 117 | "enabled": true, 118 | "address": "0x00000304", 119 | "persist": true, 120 | "writeonly": true, 121 | "defaultval": 0, 122 | "value": 1 123 | } 124 | ], 125 | "messages": [] 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/variants.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "magic": "APF_VER_1", 4 | "variant_list": [] 5 | } 6 | } -------------------------------------------------------------------------------- /pkg/Cores/agg23.PC Engine/video.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": { 3 | "magic": "APF_VER_1", 4 | "scaler_modes": [ 5 | { 6 | "width": 256, 7 | "height": 240, 8 | "aspect_w": 128, 9 | "aspect_h": 105, 10 | "rotation": 0, 11 | "mirror": 0 12 | }, 13 | { 14 | "width": 256, 15 | "height": 224, 16 | "aspect_w": 64, 17 | "aspect_h": 49, 18 | "rotation": 0, 19 | "mirror": 0 20 | }, 21 | { 22 | "width": 360, 23 | "height": 240, 24 | "aspect_w": 128, 25 | "aspect_h": 105, 26 | "rotation": 0, 27 | "mirror": 0 28 | }, 29 | { 30 | "width": 360, 31 | "height": 224, 32 | "aspect_w": 64, 33 | "aspect_h": 49, 34 | "rotation": 0, 35 | "mirror": 0 36 | }, 37 | { 38 | "width": 512, 39 | "height": 240, 40 | "aspect_w": 128, 41 | "aspect_h": 105, 42 | "rotation": 0, 43 | "mirror": 0 44 | }, 45 | { 46 | "width": 512, 47 | "height": 224, 48 | "aspect_w": 64, 49 | "aspect_h": 49, 50 | "rotation": 0, 51 | "mirror": 0 52 | } 53 | ], 54 | "display_modes": [ 55 | { 56 | "id": "0x71" 57 | }, 58 | { 59 | "id": "0x72" 60 | }, 61 | { 62 | "id": "0x10" 63 | }, 64 | { 65 | "id": "0x20" 66 | }, 67 | { 68 | "id": "0x30" 69 | }, 70 | { 71 | "id": "0x40" 72 | }, 73 | { 74 | "id": "0xE0" 75 | }, 76 | { 77 | "id": "0xE1" 78 | } 79 | ] 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /pkg/Platforms/_images/pce.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agg23/openfpga-pcengine/8810ed6283c7080013b69db843b5601f170c77c2/pkg/Platforms/_images/pce.bin -------------------------------------------------------------------------------- /pkg/Platforms/pce.json: -------------------------------------------------------------------------------- 1 | { 2 | "platform": { 3 | "category": "Console", 4 | "name": "PC Engine", 5 | "year": 1987, 6 | "manufacturer": "NEC Home Electronics" 7 | } 8 | } -------------------------------------------------------------------------------- /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/build_id_gen.tcl: -------------------------------------------------------------------------------- 1 | # ================================================================================ 2 | # (c) 2011 Altera Corporation. All rights reserved. 3 | # Altera products are protected under numerous U.S. and foreign patents, maskwork 4 | # rights, copyrights and other intellectual property laws. 5 | # 6 | # This reference design file, and your use thereof, is subject to and governed 7 | # by the terms and conditions of the applicable Altera Reference Design License 8 | # Agreement (either as signed by you, agreed by you upon download or as a 9 | # "click-through" agreement upon installation andor found at www.altera.com). 10 | # By using this reference design file, you indicate your acceptance of such terms 11 | # and conditions between you and Altera Corporation. In the event that you do 12 | # not agree with such terms and conditions, you may not use the reference design 13 | # file and please promptly destroy any copies you have made. 14 | # 15 | # This reference design file is being provided on an "as-is" basis and as an 16 | # accommodation and therefore all warranties, representations or guarantees of 17 | # any kind (whether express, implied or statutory) including, without limitation, 18 | # warranties of merchantability, non-infringement, or fitness for a particular 19 | # purpose, are specifically disclaimed. By making this reference design file 20 | # available, Altera expressly does not recommend, suggest or require that this 21 | # reference design file be used in combination with any other product not 22 | # provided by Altera. 23 | # ================================================================================ 24 | # 25 | # Build ID Verilog Module Script 26 | # Jeff Wiencrot - 8/1/2011 27 | # 28 | # Generates a Verilog module that contains a timestamp, physical address, and host name 29 | # from the current build. These values are available from the build_date, build_time, 30 | # physical_address, and host_name output ports of the build_id module in the build_id.v 31 | # Verilog source file. 32 | # 33 | # The format for each value is as follows: 34 | # Date - 32-bit decimal number of the format mmddyyyy 35 | # Time - 32-bit decimal number of the format hhmmss 36 | # Phyiscal Address - 48-bit hexadecimal number 37 | # Host name - 120-bit hexadecimal number with pairs of digits equal to the 38 | # hexadecimal code for the first 15 ASCII characters of the host 39 | # name. For added clarity, host names that have fewer than 30 40 | # hexadecimal digits (15 characters) are padded on the left with 41 | # zeros. 42 | # 43 | # Usage: 44 | # 45 | # To manually execute this script, source this file using the following Tcl commands: 46 | # source build_id_verilog.tcl 47 | # 48 | # To have this script automatically execute each time your project is built, use the 49 | # following command (see: http://www.altera.com/support/examples/tcl/auto_processing.html): 50 | # set_global_assignment -name PRE_FLOW_SCRIPT_FILE quartus_sh:build_id_verilog.tcl 51 | # 52 | # Comment out the last line to prevent the process from automatically executing when 53 | # the file is sourced. The process can then be executed with the following command: 54 | # generateBuildID_Verilog 55 | # 56 | # 57 | # For more information, see "build_identification.pdf" 58 | # 59 | # ================================================================================ 60 | # 61 | # 2021-01-21 Analogue 62 | # 63 | # Only care about generating build date/time, so the rest was removed. 64 | # The original can be downloaded from the Intel resource page 65 | # 66 | 67 | proc generateBuildID_Verilog {} { 68 | 69 | # Get the timestamp (see: http://www.altera.com/support/examples/tcl/tcl-date-time-stamp.html) 70 | set buildDate [ clock format [ clock seconds ] -format %Y%m%d ] 71 | set buildTime [ clock format [ clock seconds ] -format %H%M%S ] 72 | 73 | # Create a Verilog file for output 74 | set outputFileName "../platform/pocket/build_id.v" 75 | set outputFile [open $outputFileName "w"] 76 | 77 | # Output the Verilog source 78 | puts $outputFile "// Build ID Verilog Module" 79 | puts $outputFile "//" 80 | puts $outputFile "// Note - these are stored as binary coded decimal" 81 | puts $outputFile "// Date: $buildDate" 82 | puts $outputFile "// Time: $buildTime" 83 | puts $outputFile "" 84 | puts $outputFile "module build_id" 85 | puts $outputFile "(" 86 | puts $outputFile " output \[31:0\] build_date," 87 | puts $outputFile " output \[31:0\] build_time" 88 | puts $outputFile ");" 89 | puts $outputFile "" 90 | puts $outputFile " assign build_date = 32'h$buildDate;" 91 | puts $outputFile " assign build_time = 32'h$buildTime;" 92 | puts $outputFile "" 93 | puts $outputFile "endmodule" 94 | close $outputFile 95 | 96 | 97 | 98 | # Send confirmation message to the Messages window 99 | #post_message "APF core build date/time generated: [pwd]/$outputFileName" 100 | #post_message "Date: $buildDate" 101 | #post_message "Time: $buildTime" 102 | } 103 | 104 | 105 | proc generateBuildID_MIF {} { 106 | 107 | # Get the timestamp (see: http://www.altera.com/support/examples/tcl/tcl-date-time-stamp.html) 108 | set buildDate [ clock format [ clock seconds ] -format %Y%m%d ] 109 | set buildTime [ clock format [ clock seconds ] -format %H%M%S ] 110 | set buildUnique [expr {int(rand()*(4294967295))}] 111 | 112 | set buildDateNoLeadingZeros [string trimleft $buildDate "0"] 113 | set buildTimeNoLeadingZeros [string trimleft $buildTime "0"] 114 | set buildDate4Byte [format "%08d" $buildDateNoLeadingZeros] 115 | set buildTime4Byte [format "%08d" $buildTimeNoLeadingZeros] 116 | set buildUnique4Byte [format "%08x" $buildUnique] 117 | 118 | #set buildDate4Byte \ 119 | [concat [string range $buildDate 0 1] \ 120 | [string range $buildDate 2 3] \ 121 | [string range $buildDate 4 5] \ 122 | [string range $buildDate 6 7] ] 123 | 124 | 125 | set buildDateNumBytes 4 126 | set buildTimeNumBytes 4 127 | 128 | # Calculate depth of the memory (8-bit) words 129 | set memoryDepth [expr $buildDateNumBytes + $buildTimeNumBytes] 130 | 131 | # Create a Memory Initialization File for output 132 | set outputFileName "../platform/pocket/build_id.mif" 133 | set outputFile [open $outputFileName "w"] 134 | 135 | # Output the MIF header (see: http://quartushelp.altera.com/current/mergedProjects/reference/glossary/def_mif.htm) 136 | puts $outputFile "-- Build ID Memory Initialization File" 137 | puts $outputFile "--" 138 | puts $outputFile "" 139 | puts $outputFile "DEPTH = 256;" 140 | puts $outputFile "WIDTH = 32;" 141 | puts $outputFile "ADDRESS_RADIX = HEX;" 142 | puts $outputFile "DATA_RADIX = HEX;" 143 | puts $outputFile "" 144 | puts $outputFile "CONTENT" 145 | puts $outputFile "BEGIN" 146 | puts $outputFile "" 147 | puts $outputFile " 0E0 : $buildDate4Byte;" 148 | puts $outputFile " 0E1 : $buildTime4Byte;" 149 | puts $outputFile " 0E2 : $buildUnique4Byte;" 150 | puts $outputFile "" 151 | puts $outputFile "END;" 152 | 153 | # Close file to complete write 154 | close $outputFile 155 | 156 | # Send confirmation message to the Messages window 157 | post_message "APF core build date/time generated: [pwd]/$outputFileName" 158 | } 159 | 160 | generateBuildID_MIF 161 | 162 | # 2021-01-21 Analogue 163 | # 164 | # There are some circumstances where you want all parts of a FPGA flow to be deterministic, especially 165 | # when trying to hash out timing issues. 166 | # You should comment this line out and temporarily bypass buildid generation so that synthesis/par 167 | # have consistent working input. MIF bram contents like above won't affect the random seed or trigger 168 | # recompilation. 169 | # Don't forget to re-enable before you release. 170 | # 171 | # generateBuildID_Verilog 172 | -------------------------------------------------------------------------------- /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/pce_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/pce_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 = "pce_pocket" 12 | -------------------------------------------------------------------------------- /projects/pce_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 pce_pocket.qip 61 | set_global_assignment -name SDC_FILE pce_pocket.sdc 62 | set_global_assignment -name QIP_FILE ../rtl/pce.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_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top -------------------------------------------------------------------------------- /projects/pce_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/pce.qip: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # Quartus Prime QIP Index File 3 | # ============================================================================== 4 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "pce/cd/cd.qip"] 5 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "pce/HUC6280/HUC6280.qip"] 6 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "pce/arcade.sv"] 7 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "pce/cache_2way.sv"] 8 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "pce/CEGen.vhd"] 9 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "pce/cheatcodes.sv"] 10 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "pce/color_mix.sv"] 11 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "pce/ddram.sv"] 12 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "pce/dpram.vhd"] 13 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "pce/hps_ext.v"] 14 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "pce/huc6202.vhd"] 15 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "pce/huc6260.vhd"] 16 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "pce/huc6270.vhd"] 17 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "pce/mb128.sv"] 18 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "pce/pce_top.vhd"] 19 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "pce/sdram.sv"] 20 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "pce/xe1ap.v"] 21 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "sys/iir_filter.v"] 22 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "main.sv"] -------------------------------------------------------------------------------- /rtl/pce/CEGen.vhd: -------------------------------------------------------------------------------- 1 | LIBRARY ieee; 2 | USE ieee.std_logic_1164.all; 3 | 4 | ENTITY CEGen IS 5 | PORT 6 | ( 7 | CLK : in STD_LOGIC; 8 | RST_N : in STD_LOGIC; 9 | 10 | IN_CLK : in integer; 11 | OUT_CLK : in integer; 12 | 13 | CE : out STD_LOGIC 14 | ); 15 | END CEGen; 16 | 17 | ARCHITECTURE SYN OF CEGen IS 18 | BEGIN 19 | process( RST_N, CLK ) 20 | variable CLK_SUM : integer; 21 | begin 22 | if RST_N = '0' then 23 | CLK_SUM := 0; 24 | CE <= '0'; 25 | elsif rising_edge(CLK) then 26 | CE <= '0'; 27 | CLK_SUM := CLK_SUM + OUT_CLK; 28 | if CLK_SUM >= IN_CLK then 29 | CLK_SUM := CLK_SUM - IN_CLK; 30 | CE <= '1'; 31 | end if; 32 | end if; 33 | end process; 34 | END SYN; 35 | -------------------------------------------------------------------------------- /rtl/pce/HUC6280/AddSubBCD.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.STD_LOGIC_1164.ALL; 3 | library STD; 4 | use IEEE.NUMERIC_STD.ALL; 5 | library work; 6 | 7 | entity bit_adder is 8 | port( 9 | A : in std_logic; 10 | B : in std_logic; 11 | CI : in std_logic; 12 | S : out std_logic; 13 | CO : out std_logic 14 | ); 15 | end bit_adder; 16 | 17 | architecture rtl of bit_adder is 18 | 19 | begin 20 | 21 | S <= (not A and not B and CI) or 22 | (not A and B and not CI) or 23 | ( A and not B and not CI) or 24 | ( A and B and CI); 25 | 26 | CO <= (not A and B and CI) or 27 | ( A and not B and CI) or 28 | ( A and B and not CI) or 29 | ( A and B and CI); 30 | 31 | end rtl; 32 | 33 | 34 | 35 | library IEEE; 36 | use IEEE.STD_LOGIC_1164.ALL; 37 | library STD; 38 | use IEEE.NUMERIC_STD.ALL; 39 | library work; 40 | 41 | entity adder4 is 42 | port( 43 | A : in std_logic_vector(3 downto 0); 44 | B : in std_logic_vector(3 downto 0); 45 | CI : in std_logic; 46 | S : out std_logic_vector(3 downto 0); 47 | CO : out std_logic 48 | ); 49 | end adder4; 50 | 51 | architecture rtl of adder4 is 52 | 53 | component bit_adder is 54 | port( 55 | A : in std_logic; 56 | B : in std_logic; 57 | CI : in std_logic; 58 | S : out std_logic; 59 | CO : out std_logic 60 | ); 61 | end component; 62 | 63 | signal CO0, CO1, CO2 : std_logic; 64 | 65 | begin 66 | 67 | b_add0: bit_adder port map (A(0), B(0), CI, S(0), CO0); 68 | b_add1: bit_adder port map (A(1), B(1), CO0, S(1), CO1); 69 | b_add2: bit_adder port map (A(2), B(2), CO1, S(2), CO2); 70 | b_add3: bit_adder port map (A(3), B(3), CO2, S(3), CO); 71 | 72 | end rtl; 73 | 74 | 75 | library IEEE; 76 | use IEEE.STD_LOGIC_1164.ALL; 77 | library STD; 78 | use IEEE.NUMERIC_STD.ALL; 79 | library work; 80 | 81 | entity BCDAdder is 82 | port( 83 | A : in std_logic_vector(3 downto 0); 84 | B : in std_logic_vector(3 downto 0); 85 | CI : in std_logic; 86 | 87 | S : out std_logic_vector(3 downto 0); 88 | CO : out std_logic; 89 | VO : out std_logic; 90 | 91 | ADD : in std_logic; 92 | BCD : in std_logic 93 | ); 94 | end BCDAdder; 95 | 96 | architecture rtl of BCDAdder is 97 | 98 | signal B2 : std_logic_vector(3 downto 0); 99 | signal BIN_S : std_logic_vector(3 downto 0); 100 | signal BIN_CO : std_logic; 101 | signal BCD_B : std_logic_vector(3 downto 0); 102 | signal BCD_CO : std_logic; 103 | 104 | signal CI2 : std_logic; 105 | 106 | begin 107 | 108 | B2 <= B xor (3 downto 0 => not ADD); 109 | 110 | bin_adder : entity work.adder4 111 | port map( 112 | A => A, 113 | B => B2, 114 | CI => CI, 115 | S => BIN_S, 116 | CO => BIN_CO 117 | ); 118 | 119 | BCD_CO <= (BIN_S(3) and BIN_S(2)) or (BIN_S(3) and BIN_S(1)) or (BIN_CO xor not ADD); 120 | BCD_B <= not ADD & ((BCD_CO and BCD) xor not ADD) & ((BCD_CO and BCD) xor not ADD) & not ADD; 121 | 122 | CI2 <= not ADD; 123 | bcd_corr_adder : entity work.adder4 124 | port map( 125 | A => BIN_S, 126 | B => BCD_B, 127 | CI => CI2, 128 | S => S 129 | ); 130 | 131 | CO <= BIN_CO when BCD = '0' else BCD_CO xor not ADD; 132 | VO <= (not (A(3) xor B2(3))) and (A(3) xor BIN_S(3)); 133 | 134 | end rtl; 135 | 136 | 137 | library IEEE; 138 | use IEEE.std_logic_1164.all; 139 | use ieee.numeric_std.all; 140 | library work; 141 | 142 | entity AddSubBCD is 143 | port( 144 | A : in std_logic_vector(7 downto 0); 145 | B : in std_logic_vector(7 downto 0); 146 | CI : in std_logic; 147 | ADD : in std_logic; 148 | BCD : in std_logic; 149 | S : out std_logic_vector(7 downto 0); 150 | CO : out std_logic; 151 | VO : out std_logic 152 | ); 153 | end AddSubBCD; 154 | 155 | architecture rtl of AddSubBCD is 156 | 157 | signal VO1 : std_logic; 158 | signal CO0, CO1 : std_logic; 159 | 160 | begin 161 | 162 | add0 : entity work.BCDAdder 163 | port map ( 164 | A => A(3 downto 0), 165 | B => B(3 downto 0), 166 | CI => CI, 167 | 168 | S => S(3 downto 0), 169 | CO => CO0, 170 | 171 | ADD => ADD, 172 | BCD => BCD 173 | ); 174 | 175 | add1 : entity work.BCDAdder 176 | port map ( 177 | A => A(7 downto 4), 178 | B => B(7 downto 4), 179 | CI => CO0, 180 | 181 | S => S(7 downto 4), 182 | CO => CO1, 183 | VO => VO1, 184 | 185 | ADD => ADD, 186 | BCD => BCD 187 | ); 188 | 189 | 190 | VO <= VO1; 191 | CO <= CO1; 192 | 193 | end rtl; -------------------------------------------------------------------------------- /rtl/pce/HUC6280/HUC6280.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) HUC6280.vhd ] 2 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) HUC6280_CPU.vhd ] 3 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) HUC6280_PKG.vhd ] 4 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) HUC6280_MC.vhd ] 5 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) HUC6280_ALU.vhd ] 6 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) HUC6280_AG.vhd ] 7 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) AddSubBCD.vhd ] 8 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) psg.vhd ] 9 | 10 | 11 | -------------------------------------------------------------------------------- /rtl/pce/HUC6280/HUC6280.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.std_logic_1164.all; 3 | use ieee.numeric_std.all; 4 | library work; 5 | use work.HUC6280_PKG.all; 6 | 7 | entity HUC6280 is 8 | port( 9 | CLK : in std_logic; 10 | RST_N : in std_logic; 11 | WAIT_N : in std_logic; 12 | 13 | A : out std_logic_vector(20 downto 0); 14 | DI : in std_logic_vector(7 downto 0); 15 | DO : out std_logic_vector(7 downto 0); 16 | WR_N : out std_logic; 17 | RD_N : out std_logic; 18 | RDY : in std_logic; 19 | NMI_N : in std_logic; 20 | IRQ1_N : in std_logic; 21 | IRQ2_N : in std_logic; 22 | 23 | CE : out std_logic; 24 | CEK_N : out std_logic; 25 | CE7_N : out std_logic; 26 | CER_N : out std_logic; 27 | PRE_RD : out std_logic; -- for MiSTer sdram/ddram read 28 | PRE_WR : out std_logic; 29 | 30 | HSM : out std_logic; 31 | 32 | O : out std_logic_vector(7 downto 0); 33 | K : in std_logic_vector(7 downto 0); 34 | 35 | VDCNUM : in std_logic; 36 | 37 | AUD_LDATA: out std_logic_vector(23 downto 0); 38 | AUD_RDATA: out std_logic_vector(23 downto 0) 39 | ); 40 | end HUC6280; 41 | 42 | architecture rtl of HUC6280 is 43 | 44 | signal CPU_CE : std_logic; 45 | signal CPU_CER : std_logic; 46 | signal IO_CE : std_logic; 47 | signal EN : std_logic; 48 | 49 | signal CPU_DI : std_logic_vector(7 downto 0); 50 | signal CPU_DO : std_logic_vector(7 downto 0); 51 | signal CPU_A : std_logic_vector(20 downto 0); 52 | signal CPU_WE_N : std_logic; 53 | signal CPU_CS : std_logic; 54 | signal CPU_MCYCLE : std_logic; 55 | signal CPU_IRQ1_N : std_logic; 56 | signal CPU_IRQ2_N : std_logic; 57 | signal CPU_IRQT_N : std_logic; 58 | signal CPU_RDY : std_logic; 59 | 60 | signal CPU_CLK_CNT : unsigned(4 downto 0); 61 | signal IO_CLK_CNT : unsigned(2 downto 0); 62 | signal VDC_SEL_OLD : std_logic; 63 | 64 | --IO 65 | signal IO_BUF : std_logic_vector(7 downto 0); 66 | signal RAM_SEL : std_logic; 67 | signal VDC_SEL : std_logic; 68 | signal VCE_SEL : std_logic; 69 | signal IOP_SEL : std_logic; 70 | signal PSG_SEL : std_logic; 71 | signal TMR_SEL : std_logic; 72 | signal INT_SEL : std_logic; 73 | signal IO_SEL : std_logic; 74 | 75 | signal INT_MASK_PRE : std_logic_vector(2 downto 0); 76 | signal INT_MASK : std_logic_vector(2 downto 0); 77 | signal TMR_PRE_CNT : unsigned(9 downto 0); 78 | signal TMR_VALUE : std_logic_vector(6 downto 0); 79 | signal TMR_LATCH : std_logic_vector(6 downto 0); 80 | signal TMR_EN : std_logic; 81 | signal TMR_RELOAD : std_logic; 82 | signal TMR_IRQ : std_logic; 83 | signal TMR_IRQ_ACK : std_logic; 84 | 85 | begin 86 | 87 | 88 | process(CLK, RST_N) 89 | begin 90 | if RST_N = '0' then 91 | CPU_CLK_CNT <= (others=>'0'); 92 | CPU_CE <= '0'; 93 | CPU_CER <= '0'; 94 | IO_CLK_CNT <= (others=>'0'); 95 | IO_CE <= '0'; 96 | elsif rising_edge(CLK) then 97 | CPU_CE <= '0'; 98 | if (CPU_CLK_CNT = 5 and CPU_CS = '1') or (CPU_CLK_CNT = 23 and CPU_CS = '0') then 99 | if WAIT_N = '1' then 100 | CPU_CLK_CNT <= (others=>'0'); 101 | CPU_CE <= '1'; 102 | end if; 103 | else 104 | CPU_CLK_CNT <= CPU_CLK_CNT + 1; 105 | end if; 106 | 107 | CPU_CER <= '0'; 108 | if CPU_CLK_CNT = 1 then 109 | CPU_CER <= '1'; 110 | end if; 111 | 112 | IO_CE <= '0'; 113 | IO_CLK_CNT <= IO_CLK_CNT + 1; 114 | if IO_CLK_CNT = 5 then 115 | IO_CLK_CNT <= (others=>'0'); 116 | IO_CE <= '1'; 117 | end if; 118 | end if; 119 | end process; 120 | 121 | CE <= CPU_CE and CPU_RDY; 122 | 123 | EN <= CPU_CE and CPU_RDY; 124 | 125 | 126 | CORE : entity work.HUC6280_CPU 127 | port map ( 128 | CLK => CLK, 129 | RST_N => RST_N, 130 | CE => CPU_CE, 131 | 132 | A_OUT => CPU_A, 133 | DI => CPU_DI, 134 | DO => CPU_DO, 135 | WE_N => CPU_WE_N, 136 | RDY => CPU_RDY, 137 | IRQ1_N => CPU_IRQ1_N, 138 | IRQ2_N => CPU_IRQ2_N, 139 | IRQT_N => CPU_IRQT_N, 140 | NMI_N => NMI_N, 141 | MCYCLE => CPU_MCYCLE, 142 | CS => CPU_CS, 143 | VDCNUM => VDCNUM 144 | ); 145 | 146 | CPU_IRQ1_N <= IRQ1_N or INT_MASK(1); 147 | CPU_IRQ2_N <= IRQ2_N or INT_MASK(0); 148 | CPU_IRQT_N <= not TMR_IRQ or INT_MASK(2); 149 | 150 | RAM_SEL <= '1' when CPU_A(20 downto 15) = "111110" else '0'; -- RAM : Page $F8 - $FB 151 | VDC_SEL <= '1' when CPU_A(20 downto 13) = x"FF" and CPU_A(12 downto 10) = "000" else '0'; -- VDC : $0000 - $03FF 152 | VCE_SEL <= '1' when CPU_A(20 downto 13) = x"FF" and CPU_A(12 downto 10) = "001" else '0'; -- VCE : $0400 - $07FF 153 | 154 | process(CLK, RST_N) 155 | begin 156 | if RST_N = '0' then 157 | WR_N <= '1'; 158 | RD_N <= '1'; 159 | CPU_RDY <= '1'; 160 | VDC_SEL_OLD <= '0'; 161 | elsif rising_edge(CLK) then 162 | if CPU_CER = '1' then 163 | if CPU_MCYCLE = '1' then 164 | WR_N <= CPU_WE_N; 165 | RD_N <= not CPU_WE_N; 166 | end if; 167 | 168 | VDC_SEL_OLD <= VDC_SEL or VCE_SEL; 169 | if (VDC_SEL = '1' or VCE_SEL = '1') and VDC_SEL_OLD = '0' then 170 | CPU_RDY <= '0'; 171 | end if; 172 | elsif CPU_CE = '1' then 173 | if CPU_RDY = '1' then 174 | WR_N <= '1'; 175 | RD_N <= '1'; 176 | end if; 177 | CPU_RDY <= RDY; 178 | end if; 179 | end if; 180 | end process; 181 | 182 | PRE_RD <= CPU_WE_N and CPU_MCYCLE and RST_N; 183 | PRE_WR <= not CPU_WE_N and CPU_MCYCLE and RST_N; 184 | 185 | A <= CPU_A; 186 | DO <= CPU_DO; 187 | CER_N <= not RAM_SEL; 188 | CE7_N <= not VDC_SEL; 189 | CEK_N <= not VCE_SEL; 190 | HSM <= CPU_CS; 191 | 192 | 193 | 194 | --KO port 195 | IOP_SEL <= '1' when CPU_A(20 downto 13) = x"FF" and CPU_A(12 downto 10) = "100" else '0'; -- IOP : $1000 - $13FF 196 | process(CLK, RST_N) 197 | begin 198 | if RST_N = '0' then 199 | O <= (others=>'0'); 200 | elsif rising_edge(CLK) then 201 | if EN = '1' then 202 | if IOP_SEL = '1' and CPU_WE_N = '0' then 203 | O <= CPU_DO; 204 | end if; 205 | end if; 206 | end if; 207 | end process; 208 | 209 | --Interrupts register 210 | INT_SEL <= '1' when CPU_A(20 downto 13) = x"FF" and CPU_A(12 downto 10) = "101" else '0'; -- INT : $1400 - $17FF 211 | process(CLK, RST_N) 212 | begin 213 | if RST_N = '0' then 214 | INT_MASK_PRE <= (others=>'0'); 215 | TMR_IRQ_ACK <= '0'; 216 | elsif rising_edge(CLK) then 217 | TMR_IRQ_ACK <= '0'; 218 | 219 | if CPU_CE = '1' then 220 | INT_MASK <= INT_MASK_PRE; -- Delay interrupt mask usage until 1 cycle after it is updated 221 | end if; 222 | 223 | if INT_SEL = '1' and CPU_CER = '1' then 224 | if CPU_WE_N = '0' then 225 | case CPU_A(1 downto 0) is 226 | when "10" => 227 | INT_MASK_PRE <= CPU_DO(2 downto 0); 228 | when "11" => 229 | TMR_IRQ_ACK <= '1'; 230 | when others => null; 231 | end case; 232 | end if; 233 | end if; 234 | end if; 235 | end process; 236 | 237 | 238 | -- Timer 239 | TMR_SEL <= '1' when CPU_A(20 downto 13) = x"FF" and CPU_A(12 downto 10) = "011" else '0'; -- TMR : $0C00 - $0FFF 240 | process( CLK, RST_N ) 241 | begin 242 | if RST_N = '0' then 243 | TMR_VALUE <= (others => '0'); 244 | TMR_PRE_CNT <= (others => '1'); 245 | TMR_LATCH <= (others => '0'); 246 | TMR_EN <= '0'; 247 | TMR_RELOAD <= '0'; 248 | TMR_IRQ <= '0'; 249 | elsif rising_edge(CLK) then 250 | if TMR_SEL = '1' and CPU_WE_N = '0' and CPU_CER = '1' then 251 | if CPU_A(0) = '0' then 252 | -- Timer latch 253 | TMR_LATCH <= CPU_DO(6 downto 0); 254 | else 255 | -- Timer enable 256 | TMR_EN <= CPU_DO(0); 257 | if TMR_EN = '0' and CPU_DO(0) = '1' then 258 | TMR_VALUE <= TMR_LATCH; 259 | TMR_PRE_CNT <= (others => '1'); 260 | end if; 261 | end if; 262 | end if; 263 | 264 | if TMR_IRQ_ACK = '1' then 265 | TMR_IRQ <= '0'; 266 | end if; 267 | 268 | if IO_CE = '1' then 269 | TMR_RELOAD <= '0'; 270 | if TMR_EN = '1' then 271 | TMR_PRE_CNT <= TMR_PRE_CNT - 1; 272 | if TMR_PRE_CNT = 0 then 273 | TMR_VALUE <= std_logic_vector( unsigned(TMR_VALUE) - 1 ); 274 | if TMR_VALUE = "0000000" then 275 | TMR_RELOAD <= '1'; 276 | TMR_IRQ <= '1'; 277 | end if; 278 | end if; 279 | end if; 280 | 281 | if TMR_RELOAD = '1' then 282 | TMR_VALUE <= TMR_LATCH; 283 | end if; 284 | end if; 285 | end if; 286 | end process; 287 | 288 | -- PSG 289 | PSG_SEL <= '1' when CPU_A(20 downto 13) = x"FF" and CPU_A(12 downto 10) = "010" else '0'; -- PSG : $0800 - $0BFF 290 | PSG : entity work.psg port map ( 291 | CLK => CLK, 292 | CLKEN => IO_CE, -- 7.16 Mhz clock 293 | RESET_N => RST_N, 294 | 295 | DI => CPU_DO, 296 | A => CPU_A(3 downto 0), 297 | WE => not CPU_WE_N and EN and PSG_SEL, 298 | 299 | DAC_LATCH=> '1', 300 | LDATA => AUD_LDATA, 301 | RDATA => AUD_RDATA 302 | ); 303 | 304 | IO_SEL <= IOP_SEL or INT_SEL or TMR_SEL or PSG_SEL; 305 | process(CLK, RST_N) 306 | begin 307 | if RST_N = '0' then 308 | IO_BUF <= (others=>'1'); 309 | elsif rising_edge(CLK) then 310 | if EN = '1' then 311 | if IO_SEL = '1' then 312 | if CPU_WE_N = '0' then 313 | IO_BUF <= CPU_DO; 314 | else 315 | IO_BUF <= CPU_DI; 316 | end if; 317 | end if; 318 | end if; 319 | end if; 320 | end process; 321 | 322 | process(CLK) 323 | begin 324 | if rising_edge(CLK) then 325 | if IO_SEL = '0' then 326 | CPU_DI <= DI; 327 | elsif PSG_SEL = '1' then 328 | CPU_DI <= x"00"; 329 | elsif IOP_SEL = '1' then 330 | CPU_DI <= K; 331 | elsif INT_SEL = '1' then 332 | case CPU_A(1 downto 0) is 333 | when "10" => 334 | CPU_DI <= IO_BUF(7 downto 3) & INT_MASK; 335 | when "11" => 336 | CPU_DI <= IO_BUF(7 downto 3) & TMR_IRQ & not IRQ1_N & not IRQ2_N; 337 | when others => 338 | CPU_DI <= IO_BUF; 339 | end case; 340 | elsif TMR_SEL = '1' then 341 | CPU_DI <= IO_BUF(7) & TMR_VALUE; 342 | else 343 | CPU_DI <= IO_BUF; 344 | end if; 345 | end if; 346 | end process; 347 | 348 | end rtl; 349 | -------------------------------------------------------------------------------- /rtl/pce/HUC6280/HUC6280_AG.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.std_logic_1164.all; 3 | use ieee.numeric_std.all; 4 | library work; 5 | 6 | entity HUC6280_AG is 7 | port( 8 | CLK : in std_logic; 9 | RST_N : in std_logic; 10 | CE : in std_logic; 11 | PC_CTRL : in std_logic_vector(2 downto 0); 12 | ADDR_CTRL : in std_logic_vector(5 downto 0); 13 | GOT_INT : in std_logic; 14 | DI : in std_logic_vector(7 downto 0); 15 | X : in std_logic_vector(7 downto 0); 16 | Y : in std_logic_vector(7 downto 0); 17 | DR : in std_logic_vector(7 downto 0); 18 | 19 | PC : out std_logic_vector(15 downto 0); 20 | AA : out std_logic_vector(15 downto 0) 21 | ); 22 | end HUC6280_AG; 23 | 24 | architecture rtl of HUC6280_AG is 25 | 26 | signal AAL, AAH : std_logic_vector(7 downto 0); 27 | signal SavedCarry : std_logic; 28 | 29 | signal NewAAL : std_logic_vector(8 downto 0); 30 | signal NewAAH : std_logic_vector(7 downto 0); 31 | 32 | signal PCr: std_logic_vector(15 downto 0); 33 | signal NextPC, NewPCWithOffset: std_logic_vector(15 downto 0); 34 | 35 | begin 36 | 37 | NewPCWithOffset <= std_logic_vector(unsigned(PCr) + unsigned((7 downto 0 => AAL(7)) & AAL)); 38 | 39 | process(CLK, RST_N, PC_CTRL, PCr, DI, DR, GOT_INT, NewPCWithOffset, AAH, AAL ) 40 | begin 41 | case PC_CTRL is 42 | when "000" => 43 | NextPC <= PCr; 44 | when "001" => 45 | if GOT_INT = '0' then 46 | NextPC <= std_logic_vector(unsigned(PCr) + 1); 47 | else 48 | NextPC <= PCr; 49 | end if; 50 | when "010"=> 51 | NextPC <= PCr(15 downto 8) & DI; 52 | when "011" => 53 | NextPC <= DI & PCr(7 downto 0); 54 | when "100" => 55 | NextPC <= NewPCWithOffset; 56 | when "110" => 57 | NextPC <= AAH & AAL; 58 | when others => 59 | NextPC <= PCr; 60 | end case; 61 | 62 | if RST_N = '0' then 63 | PCr <= (others=>'0'); 64 | elsif rising_edge(CLK) then 65 | if CE = '1' then 66 | PCr <= NextPC; 67 | end if; 68 | end if; 69 | end process; 70 | 71 | process(ADDR_CTRL, AAL, AAH, X, Y, DI, DR, SavedCarry) 72 | begin 73 | case ADDR_CTRL(5 downto 3) is 74 | when "000"=> 75 | NewAAL <= "0" & AAL; 76 | when "001"=> 77 | NewAAL <= "0" & DI; 78 | when "010" => 79 | NewAAL <= std_logic_vector(unsigned("0" & AAL) + 1); 80 | when "011" => 81 | NewAAL <= std_logic_vector(unsigned("0" & AAL) + unsigned("0" & X)); 82 | when "100" => 83 | NewAAL <= std_logic_vector(unsigned("0" & AAL) + unsigned("0" & Y)); 84 | when "101" => 85 | NewAAL <= "0" & DR; 86 | when "110" => 87 | NewAAL <= std_logic_vector(unsigned("0" & DR) + unsigned("0" & Y)); 88 | when others => 89 | NewAAL <= "0" & X; 90 | end case; 91 | 92 | case ADDR_CTRL(2 downto 0) is 93 | when "000"=> 94 | NewAAH <= AAH; 95 | when "001"=> 96 | NewAAH <= DI; 97 | when "010" => 98 | NewAAH <= std_logic_vector(unsigned(AAH) + 1); 99 | when "011" => 100 | NewAAH <= std_logic_vector(unsigned(AAH) + ("0000000"&SavedCarry)); 101 | when others => 102 | NewAAH <= AAH; 103 | end case; 104 | end process; 105 | 106 | 107 | process(CLK, RST_N) 108 | begin 109 | if RST_N = '0' then 110 | AAL <= (others=>'0'); 111 | AAH <= (others=>'0'); 112 | SavedCarry <= '0'; 113 | elsif rising_edge(CLK) then 114 | if CE = '1' then 115 | AAL <= NewAAL(7 downto 0); 116 | SavedCarry <= NewAAL(8); 117 | AAH <= NewAAH(7 downto 0); 118 | end if; 119 | end if; 120 | end process; 121 | 122 | AA <= AAH & AAL; 123 | PC <= PCr; 124 | 125 | end rtl; -------------------------------------------------------------------------------- /rtl/pce/HUC6280/HUC6280_ALU.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.std_logic_1164.all; 3 | use ieee.numeric_std.all; 4 | library work; 5 | use work.HUC6280_PKG.all; 6 | 7 | entity ALU is 8 | port( 9 | CLK : in std_logic; 10 | EN : in std_logic; 11 | L : in std_logic_vector(7 downto 0); 12 | R : in std_logic_vector(7 downto 0); 13 | CTRL : in ALUCtrl_r; 14 | BCD : in std_logic; 15 | CI : in std_logic; 16 | VI : in std_logic; 17 | NI : in std_logic; 18 | CO : out std_logic; 19 | VO : out std_logic; 20 | NO : out std_logic; 21 | ZO : out std_logic; 22 | RES : out std_logic_vector(7 downto 0) 23 | ); 24 | end ALU; 25 | 26 | architecture rtl of ALU is 27 | 28 | signal IntL : std_logic_vector(7 downto 0); 29 | signal IntR : std_logic_vector(7 downto 0); 30 | signal CR : std_logic; 31 | signal ADDIn : std_logic; 32 | signal BCDIn : std_logic; 33 | signal CIIn : std_logic; 34 | signal SavedC : std_logic; 35 | 36 | signal AddS : std_logic_vector(7 downto 0); 37 | signal AddCO : std_logic; 38 | signal AddVO : std_logic; 39 | signal Result : std_logic_vector(7 downto 0); 40 | 41 | begin 42 | 43 | process(CTRL, CI, L, R, BCD, SavedC) 44 | begin 45 | CR <= CI; 46 | BCDIn <= '0'; 47 | case CTRL.fstOp is 48 | when "000" => 49 | CR <= L(7); 50 | IntL <= L(6 downto 0) & "0"; 51 | IntR <= x"00"; 52 | when "001"=> 53 | CR <= L(7); 54 | IntL <= L(6 downto 0) & CI; 55 | IntR <= x"00"; 56 | when "010" => 57 | CR <= L(0); 58 | IntL <= "0" & L(7 downto 1); 59 | IntR <= x"00"; 60 | when "011" => 61 | CR <= L(0); 62 | IntL <= CI & L(7 downto 1); 63 | IntR <= x"00"; 64 | when "100" => 65 | IntL <= L; 66 | IntR <= R; 67 | BCDIn <= BCD and CTRL.secOp(1) and CTRL.secOp(0); 68 | when "101" => 69 | IntL <= L; 70 | IntR <= R; 71 | when "110" => -- INC/DEC LSB 72 | IntL <= L; 73 | IntR <= x"01"; 74 | CR <= CTRL.secOp(2); 75 | when "111" => -- INC/DEC MSB 76 | IntL <= L; 77 | IntR <= x"00"; 78 | CR <= SavedC; 79 | when others => null; 80 | end case; 81 | end process; 82 | 83 | CIIn <= CR or not CTRL.secOp(0); 84 | ADDIn <= not CTRL.secOp(2); 85 | 86 | AddSub: entity work.AddSubBCD 87 | port map ( 88 | A => IntL, 89 | B => IntR, 90 | CI => CIIn, 91 | ADD => ADDIn, 92 | BCD => BCDIn, 93 | S => AddS, 94 | CO => AddCO, 95 | VO => AddVO 96 | ); 97 | 98 | process(CLK) 99 | begin 100 | if rising_edge(CLK) then 101 | if EN = '1' then 102 | SavedC <= AddCO; 103 | end if; 104 | end if; 105 | end process; 106 | 107 | process(CTRL, CR, IntL, IntR, AddCO, AddS) 108 | begin 109 | case CTRL.secOp is 110 | when "000" => 111 | CO <= CR; 112 | Result <= IntL or IntR; 113 | when "001"=> 114 | CO <= CR; 115 | Result <= IntL and IntR; 116 | when "010" => 117 | CO <= CR; 118 | Result <= IntL xor IntR; 119 | when "011" | "110" | "111" => 120 | CO <= AddCO; 121 | Result <= AddS; 122 | when "100" => 123 | CO <= CR; 124 | Result <= IntL; 125 | when "101" => --TRB,TSB 126 | CO <= CR; 127 | if CTRL.fc = '0' then 128 | Result <= IntR and (not IntL); 129 | else 130 | Result <= IntR or IntL; 131 | end if; 132 | when others => null; 133 | end case; 134 | end process; 135 | 136 | process(CTRL, VI, NI, IntR, Result, AddVO, BCDIn) 137 | begin 138 | VO <= VI; 139 | NO <= Result(7); 140 | case CTRL.secOp is 141 | when "001" => --AND 142 | if CTRL.fc = '1' then --BIT 143 | VO <= IntR(6); 144 | NO <= IntR(7); 145 | end if; 146 | when "011" => -- ADC 147 | if BCDIn = '0' then 148 | VO <= AddVO; 149 | end if; 150 | when "101" => --TRB,TSB 151 | VO <= IntR(6); 152 | NO <= IntR(7); 153 | when "111" => -- SBC 154 | if BCDIn = '0' then 155 | VO <= AddVO; 156 | end if; 157 | when others => null; 158 | end case; 159 | end process; 160 | 161 | 162 | ZO <= '1' when Result = x"00" else '0'; 163 | 164 | RES <= Result; 165 | 166 | end rtl; -------------------------------------------------------------------------------- /rtl/pce/HUC6280/HUC6280_PKG.vhd: -------------------------------------------------------------------------------- 1 | library IEEE; 2 | use IEEE.std_logic_1164.all; 3 | use ieee.numeric_std.all; 4 | 5 | package HUC6280_PKG is 6 | 7 | type MicroInst_r is record 8 | STATE_CTRL : std_logic_vector(1 downto 0); 9 | ADDR_BUS : std_logic_vector(2 downto 0); 10 | LOAD_SDLH : std_logic_vector(1 downto 0); 11 | LOAD_P : std_logic_vector(2 downto 0); 12 | LOAD_T : std_logic_vector(2 downto 0); 13 | ADDR_CTRL : std_logic_vector(5 downto 0); 14 | LOAD_PC : std_logic_vector(2 downto 0); 15 | LOAD_SP : std_logic_vector(2 downto 0); 16 | AXY_CTRL : std_logic_vector(2 downto 0); 17 | ALUBUS_CTRL : std_logic_vector(3 downto 0); 18 | ALUCtrl : std_logic_vector(4 downto 0); 19 | OUT_BUS : std_logic_vector(3 downto 0); 20 | MEM_CYCLE : std_logic; 21 | end record; 22 | 23 | type ALUCtrl_r is record 24 | fstOp : std_logic_vector(2 downto 0); 25 | secOp : std_logic_vector(2 downto 0); 26 | fc : std_logic; 27 | end record; 28 | 29 | type MCode_r is record 30 | STATE_CTRL : std_logic_vector(1 downto 0); 31 | ADDR_BUS : std_logic_vector(2 downto 0); 32 | LOAD_SDLH : std_logic_vector(1 downto 0); 33 | LOAD_P : std_logic_vector(2 downto 0); 34 | LOAD_T : std_logic_vector(2 downto 0); 35 | ADDR_CTRL : std_logic_vector(5 downto 0); 36 | LOAD_PC : std_logic_vector(2 downto 0); 37 | LOAD_SP : std_logic_vector(2 downto 0); 38 | AXY_CTRL : std_logic_vector(2 downto 0); 39 | ALUBUS_CTRL : std_logic_vector(3 downto 0); 40 | OUT_BUS : std_logic_vector(3 downto 0); 41 | MEM_CYCLE : std_logic; 42 | ALU_CTRL : ALUCtrl_r; 43 | end record; 44 | 45 | constant FLAG_C : integer := 0; 46 | constant FLAG_Z : integer := 1; 47 | constant FLAG_I : integer := 2; 48 | constant FLAG_D : integer := 3; 49 | constant FLAG_B : integer := 4; 50 | constant FLAG_T : integer := 5; 51 | constant FLAG_V : integer := 6; 52 | constant FLAG_N : integer := 7; 53 | 54 | type MPR_t is array(0 to 7) of std_logic_vector(7 downto 0); 55 | 56 | end HUC6280_PKG; 57 | 58 | package body HUC6280_PKG is 59 | 60 | 61 | end package body HUC6280_PKG; 62 | -------------------------------------------------------------------------------- /rtl/pce/arcade.sv: -------------------------------------------------------------------------------- 1 | // 2 | // Arcade Card 3 | // Copyright (c) 2020 Alexey Melnikov 4 | // 5 | // This source file is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published 7 | // by the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This source file is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | // 18 | 19 | module ARCADE_CARD 20 | ( 21 | input CLK, 22 | input RST_N, 23 | 24 | input EN, 25 | input WR_N, 26 | input RD_N, 27 | input [20:0] A, 28 | input [7:0] DI, 29 | output [7:0] DO, 30 | output SEL_N, 31 | 32 | output RAM_CS_N, 33 | output [20:0] RAM_A 34 | ); 35 | 36 | 37 | typedef struct packed 38 | { 39 | reg [23:0] base; 40 | reg [15:0] offset; 41 | reg [15:0] increment; 42 | reg [6:0] control; 43 | reg [20:0] addr; 44 | } port_t; 45 | 46 | port_t port[4]; 47 | wire [1:0] p; 48 | 49 | reg ena; 50 | reg [31:0] shift_latch; 51 | reg [7:0] shift_bits; 52 | reg [7:0] rotate_bits; 53 | 54 | assign SEL_N = ~(EN && &A[20:13] && (A[12:8] == 'h1A)); 55 | assign RAM_A = port[p].addr; 56 | 57 | always_comb begin 58 | DO = 8'hFF; 59 | RAM_CS_N = 1; 60 | 61 | if(A[20:15] == 16) begin // pages 0x40-0x43 62 | p = A[14:13]; 63 | RAM_CS_N = ~EN | ~ena; 64 | end 65 | else begin 66 | p = A[5:4]; 67 | 68 | if(!A[7]) begin 69 | 70 | case(A[3:0]) 71 | 0,1: RAM_CS_N = SEL_N; 72 | 73 | 2: DO = port[p].base[7:0]; 74 | 3: DO = port[p].base[15:8]; 75 | 4: DO = port[p].base[23:16]; 76 | 5: DO = port[p].offset[7:0]; 77 | 6: DO = port[p].offset[15:8]; 78 | 7: DO = port[p].increment[7:0]; 79 | 8: DO = port[p].increment[15:8]; 80 | 9: DO = port[p].control; 81 | default:; 82 | endcase 83 | end 84 | else if(&A[6:5]) begin 85 | 86 | case (A[4:0]) 87 | 0: DO = shift_latch[7:0]; 88 | 1: DO = shift_latch[15:8]; 89 | 2: DO = shift_latch[23:16]; 90 | 3: DO = shift_latch[31:24]; 91 | 4: DO = shift_bits; 92 | 5: DO = rotate_bits; 93 | 94 | 'h1C: DO = 0; 95 | 'h1D: DO = 0; 96 | 97 | 'h1E: DO = 8'h10; 98 | 'h1F: DO = 8'h51; 99 | default:; 100 | endcase 101 | end 102 | end 103 | end 104 | 105 | always @(posedge CLK) begin 106 | for(int i=0; i<4; i++) begin 107 | port[i].addr = port[i].base[20:0] + (port[i].control[1] ? {{5{port[i].control[3]}}, port[i].offset} : 21'd0); 108 | end 109 | end 110 | 111 | wire [3:0] rot = DI[3] ? (4'd8 - DI[2:0]) : DI[2:0]; 112 | wire acc = ~(WR_N & RD_N); 113 | 114 | always @(posedge CLK) begin 115 | reg old_acc; 116 | 117 | old_acc <= acc; 118 | 119 | if(~RST_N) begin 120 | for(int i=0; i<4; i++) begin 121 | port[i].base <= 0; 122 | port[i].offset <= 0; 123 | port[i].increment <= 0; 124 | port[i].control <= 0; 125 | end 126 | ena <= 0; 127 | shift_latch <= 0; 128 | shift_bits <= 0; 129 | rotate_bits <= 0; 130 | end 131 | else if(~old_acc & acc) begin 132 | 133 | if(~SEL_N & ~WR_N) begin 134 | if(!A[7]) begin 135 | 136 | ena <= 1; 137 | case(A[3:0]) 138 | 2: port[p].base[7:0] <= DI; 139 | 3: port[p].base[15:8] <= DI; 140 | 4: port[p].base[23:16] <= DI; 141 | 5: begin 142 | port[p].offset[7:0] <= DI; 143 | if(port[p].control[6:5] == 1) port[p].base <= port[p].base + {{8{port[p].control[3]}}, port[p].offset[15:8], DI}; 144 | end 145 | 6: begin 146 | port[p].offset[15:8] <= DI; 147 | if(port[p].control[6:5] == 2) port[p].base <= port[p].base + {{8{port[p].control[3]}}, DI, port[p].offset[7:0]}; 148 | end 149 | 7: port[p].increment[7:0] <= DI; 150 | 8: port[p].increment[15:8] <= DI; 151 | 9: port[p].control <= DI[6:0]; 152 | 10: if(port[p].control[6:5] == 3) port[p].base <= port[p].base + {{8{port[p].control[3]}}, port[p].offset}; 153 | endcase 154 | end 155 | else if(&A[6:5]) begin 156 | 157 | case (A[4:0]) 158 | 0: shift_latch[7:0] <= DI; 159 | 1: shift_latch[15:8] <= DI; 160 | 2: shift_latch[23:16] <= DI; 161 | 3: shift_latch[31:24] <= DI; 162 | 4: begin 163 | shift_bits <= DI[3:0]; 164 | shift_latch <= DI[3] ? (shift_latch >> (8 - DI[2:0])) : (shift_latch << DI[2:0]); 165 | end 166 | 5: begin 167 | rotate_bits <= DI[3:0]; 168 | if(DI[3]) shift_latch <= (shift_latch >> rot) | (shift_latch << (32 - rot)); 169 | else shift_latch <= (shift_latch << rot) | ((shift_latch >> (32 - rot)) & ((32'd1 << rot) - 1'd1)); 170 | end 171 | endcase 172 | end 173 | end 174 | 175 | if(~RAM_CS_N & port[p].control[0]) begin 176 | if(port[p].control[4]) port[p].base <= port[p].base + port[p].increment; 177 | else port[p].offset <= port[p].offset + port[p].increment; 178 | end 179 | end 180 | end 181 | 182 | endmodule 183 | -------------------------------------------------------------------------------- /rtl/pce/cd/CDDA_FIFO.vhd: -------------------------------------------------------------------------------- 1 | -- megafunction wizard: %FIFO% 2 | -- GENERATION: STANDARD 3 | -- VERSION: WM1.0 4 | -- MODULE: scfifo 5 | 6 | -- ============================================================ 7 | -- File Name: CDDA_FIFO.vhd 8 | -- Megafunction Name(s): 9 | -- scfifo 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 | -- 17.0.0 Build 595 04/25/2017 SJ Lite Edition 18 | -- ************************************************************ 19 | 20 | 21 | --Copyright (C) 2017 Intel Corporation. All rights reserved. 22 | --Your use of Intel Corporation's design tools, logic functions 23 | --and other software and tools, and its AMPP 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 MegaCore Function License Agreement, or other 30 | --applicable license agreement, including, without limitation, 31 | --that your use is for the sole purpose of programming logic 32 | --devices manufactured by Intel and sold by Intel or its 33 | --authorized distributors. Please refer to the applicable 34 | --agreement for further details. 35 | 36 | 37 | LIBRARY ieee; 38 | USE ieee.std_logic_1164.all; 39 | 40 | LIBRARY altera_mf; 41 | USE altera_mf.all; 42 | 43 | ENTITY CDDA_FIFO IS 44 | PORT 45 | ( 46 | clock : IN STD_LOGIC ; 47 | data : IN STD_LOGIC_VECTOR (31 DOWNTO 0); 48 | rdreq : IN STD_LOGIC ; 49 | sclr : IN STD_LOGIC ; 50 | wrreq : IN STD_LOGIC ; 51 | empty : OUT STD_LOGIC ; 52 | full : OUT STD_LOGIC ; 53 | q : OUT STD_LOGIC_VECTOR (31 DOWNTO 0) 54 | ); 55 | END CDDA_FIFO; 56 | 57 | 58 | ARCHITECTURE SYN OF cdda_fifo IS 59 | 60 | SIGNAL sub_wire0 : STD_LOGIC ; 61 | SIGNAL sub_wire1 : STD_LOGIC ; 62 | SIGNAL sub_wire2 : STD_LOGIC_VECTOR (31 DOWNTO 0); 63 | 64 | 65 | 66 | COMPONENT scfifo 67 | GENERIC ( 68 | add_ram_output_register : STRING; 69 | intended_device_family : STRING; 70 | lpm_numwords : NATURAL; 71 | lpm_showahead : STRING; 72 | lpm_type : STRING; 73 | lpm_width : NATURAL; 74 | lpm_widthu : NATURAL; 75 | overflow_checking : STRING; 76 | underflow_checking : STRING; 77 | use_eab : STRING 78 | ); 79 | PORT ( 80 | clock : IN STD_LOGIC ; 81 | data : IN STD_LOGIC_VECTOR (31 DOWNTO 0); 82 | rdreq : IN STD_LOGIC ; 83 | sclr : IN STD_LOGIC ; 84 | wrreq : IN STD_LOGIC ; 85 | empty : OUT STD_LOGIC ; 86 | full : OUT STD_LOGIC ; 87 | q : OUT STD_LOGIC_VECTOR (31 DOWNTO 0) 88 | ); 89 | END COMPONENT; 90 | 91 | BEGIN 92 | empty <= sub_wire0; 93 | full <= sub_wire1; 94 | q <= sub_wire2(31 DOWNTO 0); 95 | 96 | scfifo_component : scfifo 97 | GENERIC MAP ( 98 | add_ram_output_register => "OFF", 99 | intended_device_family => "Cyclone V", 100 | lpm_numwords => 4096, 101 | lpm_showahead => "ON", 102 | lpm_type => "scfifo", 103 | lpm_width => 32, 104 | lpm_widthu => 12, 105 | overflow_checking => "ON", 106 | underflow_checking => "ON", 107 | use_eab => "ON" 108 | ) 109 | PORT MAP ( 110 | clock => clock, 111 | data => data, 112 | rdreq => rdreq, 113 | sclr => sclr, 114 | wrreq => wrreq, 115 | empty => sub_wire0, 116 | full => sub_wire1, 117 | q => sub_wire2 118 | ); 119 | 120 | 121 | 122 | END SYN; 123 | 124 | -- ============================================================ 125 | -- CNX file retrieval info 126 | -- ============================================================ 127 | -- Retrieval info: PRIVATE: AlmostEmpty NUMERIC "0" 128 | -- Retrieval info: PRIVATE: AlmostEmptyThr NUMERIC "-1" 129 | -- Retrieval info: PRIVATE: AlmostFull NUMERIC "0" 130 | -- Retrieval info: PRIVATE: AlmostFullThr NUMERIC "-1" 131 | -- Retrieval info: PRIVATE: CLOCKS_ARE_SYNCHRONIZED NUMERIC "1" 132 | -- Retrieval info: PRIVATE: Clock NUMERIC "0" 133 | -- Retrieval info: PRIVATE: Depth NUMERIC "4096" 134 | -- Retrieval info: PRIVATE: Empty NUMERIC "1" 135 | -- Retrieval info: PRIVATE: Full NUMERIC "1" 136 | -- Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone V" 137 | -- Retrieval info: PRIVATE: LE_BasedFIFO NUMERIC "0" 138 | -- Retrieval info: PRIVATE: LegacyRREQ NUMERIC "0" 139 | -- Retrieval info: PRIVATE: MAX_DEPTH_BY_9 NUMERIC "0" 140 | -- Retrieval info: PRIVATE: OVERFLOW_CHECKING NUMERIC "0" 141 | -- Retrieval info: PRIVATE: Optimize NUMERIC "2" 142 | -- Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0" 143 | -- Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0" 144 | -- Retrieval info: PRIVATE: UNDERFLOW_CHECKING NUMERIC "0" 145 | -- Retrieval info: PRIVATE: UsedW NUMERIC "0" 146 | -- Retrieval info: PRIVATE: Width NUMERIC "32" 147 | -- Retrieval info: PRIVATE: dc_aclr NUMERIC "0" 148 | -- Retrieval info: PRIVATE: diff_widths NUMERIC "1" 149 | -- Retrieval info: PRIVATE: msb_usedw NUMERIC "0" 150 | -- Retrieval info: PRIVATE: output_width NUMERIC "32" 151 | -- Retrieval info: PRIVATE: rsEmpty NUMERIC "1" 152 | -- Retrieval info: PRIVATE: rsFull NUMERIC "0" 153 | -- Retrieval info: PRIVATE: rsUsedW NUMERIC "0" 154 | -- Retrieval info: PRIVATE: sc_aclr NUMERIC "0" 155 | -- Retrieval info: PRIVATE: sc_sclr NUMERIC "1" 156 | -- Retrieval info: PRIVATE: wsEmpty NUMERIC "0" 157 | -- Retrieval info: PRIVATE: wsFull NUMERIC "1" 158 | -- Retrieval info: PRIVATE: wsUsedW NUMERIC "0" 159 | -- Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all 160 | -- Retrieval info: CONSTANT: ADD_RAM_OUTPUT_REGISTER STRING "OFF" 161 | -- Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone V" 162 | -- Retrieval info: CONSTANT: LPM_NUMWORDS NUMERIC "4096" 163 | -- Retrieval info: CONSTANT: LPM_SHOWAHEAD STRING "ON" 164 | -- Retrieval info: CONSTANT: LPM_TYPE STRING "scfifo" 165 | -- Retrieval info: CONSTANT: LPM_WIDTH NUMERIC "32" 166 | -- Retrieval info: CONSTANT: LPM_WIDTHU NUMERIC "12" 167 | -- Retrieval info: CONSTANT: OVERFLOW_CHECKING STRING "ON" 168 | -- Retrieval info: CONSTANT: UNDERFLOW_CHECKING STRING "ON" 169 | -- Retrieval info: CONSTANT: USE_EAB STRING "ON" 170 | -- Retrieval info: USED_PORT: clock 0 0 0 0 INPUT NODEFVAL "clock" 171 | -- Retrieval info: USED_PORT: data 0 0 32 0 INPUT NODEFVAL "data[31..0]" 172 | -- Retrieval info: USED_PORT: empty 0 0 0 0 OUTPUT NODEFVAL "empty" 173 | -- Retrieval info: USED_PORT: full 0 0 0 0 OUTPUT NODEFVAL "full" 174 | -- Retrieval info: USED_PORT: q 0 0 32 0 OUTPUT NODEFVAL "q[31..0]" 175 | -- Retrieval info: USED_PORT: rdreq 0 0 0 0 INPUT NODEFVAL "rdreq" 176 | -- Retrieval info: USED_PORT: sclr 0 0 0 0 INPUT NODEFVAL "sclr" 177 | -- Retrieval info: USED_PORT: wrreq 0 0 0 0 INPUT NODEFVAL "wrreq" 178 | -- Retrieval info: CONNECT: @clock 0 0 0 0 clock 0 0 0 0 179 | -- Retrieval info: CONNECT: @data 0 0 32 0 data 0 0 32 0 180 | -- Retrieval info: CONNECT: @rdreq 0 0 0 0 rdreq 0 0 0 0 181 | -- Retrieval info: CONNECT: @sclr 0 0 0 0 sclr 0 0 0 0 182 | -- Retrieval info: CONNECT: @wrreq 0 0 0 0 wrreq 0 0 0 0 183 | -- Retrieval info: CONNECT: empty 0 0 0 0 @empty 0 0 0 0 184 | -- Retrieval info: CONNECT: full 0 0 0 0 @full 0 0 0 0 185 | -- Retrieval info: CONNECT: q 0 0 32 0 @q 0 0 32 0 186 | -- Retrieval info: GEN_FILE: TYPE_NORMAL CDDA_FIFO.vhd TRUE 187 | -- Retrieval info: GEN_FILE: TYPE_NORMAL CDDA_FIFO.inc FALSE 188 | -- Retrieval info: GEN_FILE: TYPE_NORMAL CDDA_FIFO.cmp FALSE 189 | -- Retrieval info: GEN_FILE: TYPE_NORMAL CDDA_FIFO.bsf FALSE 190 | -- Retrieval info: GEN_FILE: TYPE_NORMAL CDDA_FIFO_inst.vhd FALSE 191 | -- Retrieval info: LIB_FILE: altera_mf 192 | -------------------------------------------------------------------------------- /rtl/pce/cd/MSM5205.vhd: -------------------------------------------------------------------------------- 1 | library STD; 2 | use STD.TEXTIO.ALL; 3 | library IEEE; 4 | use IEEE.STD_LOGIC_1164.ALL; 5 | use IEEE.STD_LOGIC_TEXTIO.all; 6 | use IEEE.NUMERIC_STD.ALL; 7 | 8 | entity MSM5205 is 9 | port( 10 | CLK : in std_logic; 11 | RST_N : in std_logic; 12 | 13 | XTI : in std_logic; 14 | D : in std_logic_vector(3 downto 0); 15 | VCK_R : out std_logic; 16 | VCK_F : out std_logic; 17 | 18 | SOUT : out signed(15 downto 0) 19 | ); 20 | end MSM5205; 21 | 22 | architecture rtl of MSM5205 is 23 | 24 | type DeltaTable_t is array (0 to 391) of unsigned(11 downto 0); 25 | constant DT : DeltaTable_t := 26 | (x"002", x"006", x"00A", x"00E", x"012", x"016", x"01A", x"01E", 27 | x"002", x"006", x"00A", x"00E", x"013", x"017", x"01B", x"01F", 28 | x"002", x"006", x"00B", x"00F", x"015", x"019", x"01E", x"022", 29 | x"002", x"007", x"00C", x"011", x"017", x"01C", x"021", x"026", 30 | x"002", x"007", x"00D", x"012", x"019", x"01E", x"024", x"029", 31 | x"003", x"009", x"00F", x"015", x"01C", x"022", x"028", x"02E", 32 | x"003", x"00A", x"011", x"018", x"01F", x"026", x"02D", x"034", 33 | x"003", x"00A", x"012", x"019", x"022", x"029", x"031", x"038", 34 | x"004", x"00C", x"015", x"01D", x"026", x"02E", x"037", x"03F", 35 | x"004", x"00D", x"016", x"01F", x"029", x"032", x"03B", x"044", 36 | x"005", x"00F", x"019", x"023", x"02E", x"038", x"042", x"04C", 37 | x"005", x"010", x"01B", x"026", x"032", x"03D", x"048", x"053", 38 | x"006", x"012", x"01F", x"02B", x"038", x"044", x"051", x"05D", 39 | x"006", x"013", x"021", x"02E", x"03D", x"04A", x"058", x"065", 40 | x"007", x"016", x"025", x"034", x"043", x"052", x"061", x"070", 41 | x"008", x"018", x"029", x"039", x"04A", x"05A", x"06B", x"07B", 42 | x"009", x"01B", x"02D", x"03F", x"052", x"064", x"076", x"088", 43 | x"00A", x"01E", x"032", x"046", x"05A", x"06E", x"082", x"096", 44 | x"00B", x"021", x"037", x"04D", x"063", x"079", x"08F", x"0A5", 45 | x"00C", x"024", x"03C", x"054", x"06D", x"085", x"09D", x"0B5", 46 | x"00D", x"027", x"042", x"05C", x"078", x"092", x"0AD", x"0C7", 47 | x"00E", x"02B", x"049", x"066", x"084", x"0A1", x"0BF", x"0DC", 48 | x"010", x"030", x"051", x"071", x"092", x"0B2", x"0D3", x"0F3", 49 | x"011", x"034", x"058", x"07B", x"0A0", x"0C3", x"0E7", x"10A", 50 | x"013", x"03A", x"061", x"088", x"0B0", x"0D7", x"0FE", x"125", 51 | x"015", x"040", x"06B", x"096", x"0C2", x"0ED", x"118", x"143", 52 | x"017", x"046", x"076", x"0A5", x"0D5", x"104", x"134", x"163", 53 | x"01A", x"04E", x"082", x"0B6", x"0EB", x"11F", x"153", x"187", 54 | x"01C", x"055", x"08F", x"0C8", x"102", x"13B", x"175", x"1AE", 55 | x"01F", x"05E", x"09D", x"0DC", x"11C", x"15B", x"19A", x"1D9", 56 | x"022", x"067", x"0AD", x"0F2", x"139", x"17E", x"1C4", x"209", 57 | x"026", x"072", x"0BF", x"10B", x"159", x"1A5", x"1F2", x"23E", 58 | x"02A", x"07E", x"0D2", x"126", x"17B", x"1CF", x"223", x"277", 59 | x"02E", x"08A", x"0E7", x"143", x"1A1", x"1FD", x"25A", x"2B6", 60 | x"033", x"099", x"0FF", x"165", x"1CB", x"231", x"297", x"2FD", 61 | x"038", x"0A8", x"118", x"188", x"1F9", x"269", x"2D9", x"349", 62 | x"03D", x"0B8", x"134", x"1AF", x"22B", x"2A6", x"322", x"39D", 63 | x"044", x"0CC", x"154", x"1DC", x"264", x"2EC", x"374", x"3FC", 64 | x"04A", x"0DF", x"175", x"20A", x"2A0", x"335", x"3CB", x"460", 65 | x"052", x"0F6", x"19B", x"23F", x"2E4", x"388", x"42D", x"4D1", 66 | x"05A", x"10F", x"1C4", x"279", x"32E", x"3E3", x"498", x"54D", 67 | x"063", x"12A", x"1F1", x"2B8", x"37F", x"446", x"50D", x"5D4", 68 | x"06D", x"148", x"223", x"2FE", x"3D9", x"4B4", x"58F", x"66A", 69 | x"078", x"168", x"259", x"349", x"43B", x"52B", x"61C", x"70C", 70 | x"084", x"18D", x"296", x"39F", x"4A8", x"5B1", x"6BA", x"7C3", 71 | x"091", x"1B4", x"2D8", x"3FB", x"51F", x"642", x"766", x"889", 72 | x"0A0", x"1E0", x"321", x"461", x"5A2", x"6E2", x"823", x"963", 73 | x"0B0", x"210", x"371", x"4D1", x"633", x"793", x"8F4", x"A54", 74 | x"0C2", x"246", x"3CA", x"54E", x"6D2", x"856", x"9DA", x"B5E"); 75 | 76 | type StepIndexTable_t is array (0 to 3) of unsigned(3 downto 0); 77 | constant SIT : StepIndexTable_t := ("0010","0100","0110","1000"); 78 | 79 | 80 | signal CLK_CNT : unsigned(5 downto 0); 81 | signal SAMPLE_RCE : std_logic; 82 | signal SAMPLE_FCE : std_logic; 83 | 84 | signal DEC_DATA : unsigned(3 downto 0); 85 | signal DEC_EXEC : std_logic; 86 | 87 | signal SAMPLE : unsigned(15 downto 0); 88 | signal STEP : unsigned(5 downto 0); 89 | 90 | begin 91 | 92 | process( CLK ) 93 | begin 94 | if rising_edge(CLK) then 95 | SAMPLE_RCE <= '0'; 96 | SAMPLE_FCE <= '0'; 97 | if XTI = '1' then 98 | CLK_CNT <= CLK_CNT + 1; 99 | if CLK_CNT = 24-1 then 100 | SAMPLE_RCE <= '1'; 101 | end if; 102 | if CLK_CNT = 48-1 then 103 | CLK_CNT <= (others => '0'); 104 | SAMPLE_FCE <= '1'; 105 | end if; 106 | end if; 107 | end if; 108 | end process; 109 | 110 | VCK_R <= SAMPLE_RCE; 111 | VCK_F <= SAMPLE_FCE; 112 | 113 | process( RST_N, CLK ) 114 | variable DELTA : unsigned(11 downto 0); 115 | variable NEXT_STEP : unsigned(6 downto 0); 116 | variable NEXT_SAMPLE : unsigned(15 downto 0); 117 | begin 118 | if RST_N = '0' then 119 | DEC_DATA <= (others => '0'); 120 | DEC_EXEC <= '0'; 121 | STEP <= (others => '0'); 122 | SAMPLE <= x"0000"; 123 | elsif rising_edge(CLK) then 124 | DEC_EXEC <= '0'; 125 | if SAMPLE_FCE = '1' then 126 | DEC_DATA <= unsigned(D); 127 | DEC_EXEC <= '1'; 128 | end if; 129 | 130 | if DEC_EXEC = '1' then 131 | DELTA := DT(to_integer(STEP&DEC_DATA(2 downto 0))); 132 | if DEC_DATA(3) = '1' then 133 | NEXT_SAMPLE := SAMPLE - DELTA; 134 | else 135 | NEXT_SAMPLE := SAMPLE + DELTA; 136 | end if; 137 | 138 | if NEXT_SAMPLE(12 downto 11) = "01" then 139 | NEXT_SAMPLE := x"07FF"; 140 | elsif NEXT_SAMPLE(12 downto 11) = "10" then 141 | NEXT_SAMPLE := x"F800"; 142 | end if; 143 | 144 | SAMPLE <= NEXT_SAMPLE; 145 | 146 | if DEC_DATA(2) = '1' then 147 | NEXT_STEP := ("0"&STEP) + SIT(to_integer(DEC_DATA(1 downto 0))); 148 | else 149 | NEXT_STEP := ("0"&STEP) - 1; 150 | end if; 151 | 152 | if NEXT_STEP(6) = '1' then 153 | STEP <= "000000"; 154 | elsif NEXT_STEP > 48 then 155 | STEP <= "110000"; 156 | else 157 | STEP <= NEXT_STEP(5 downto 0); 158 | end if; 159 | end if; 160 | end if; 161 | end process; 162 | 163 | SOUT <= signed(SAMPLE(11 downto 0)) & x"0"; 164 | 165 | end rtl; -------------------------------------------------------------------------------- /rtl/pce/cd/SCSI.vhd: -------------------------------------------------------------------------------- 1 | library STD; 2 | use STD.TEXTIO.ALL; 3 | library IEEE; 4 | use IEEE.STD_LOGIC_1164.ALL; 5 | use IEEE.STD_LOGIC_TEXTIO.all; 6 | use IEEE.NUMERIC_STD.ALL; 7 | 8 | entity SCSI is 9 | port( 10 | RESET_N : in std_logic; 11 | CLK : in std_logic; 12 | 13 | DBI : in std_logic_vector(7 downto 0); 14 | DBO : out std_logic_vector(7 downto 0); 15 | SEL_N : in std_logic; 16 | ACK_N : in std_logic; 17 | RST_N : in std_logic; 18 | BSY_N : out std_logic; 19 | REQ_N : out std_logic; 20 | MSG_N : out std_logic; 21 | CD_N : out std_logic; 22 | IO_N : out std_logic; 23 | 24 | STATUS : in std_logic_vector(7 downto 0); 25 | MESSAGE : in std_logic_vector(7 downto 0); 26 | STAT_GET : in std_logic; 27 | 28 | COMMAND : out std_logic_vector(95 downto 0); 29 | COMM_SEND : out std_logic; 30 | 31 | DOUT_REQ : in std_logic; 32 | DOUT : out std_logic_vector(79 downto 0); 33 | DOUT_SEND : out std_logic; 34 | 35 | CD_DATA : in std_logic_vector(7 downto 0); 36 | CD_WR : in std_logic; 37 | CD_DATA_END : out std_logic; 38 | STOP_CD_SND : out std_logic; 39 | 40 | DBG_DATAIN_CNT: out unsigned(15 downto 0) 41 | ); 42 | end SCSI; 43 | 44 | architecture rtl of SCSI is 45 | 46 | type SCSIPhase_t is ( 47 | SP_FREE, 48 | SP_COMM_START, 49 | SP_COMM_END, 50 | SP_STAT_START, 51 | SP_STAT_END, 52 | SP_MSGIN_START, 53 | SP_MSGIN_END, 54 | SP_DATAIN_START, 55 | SP_DATAIN_END, 56 | SP_DATAOUT_START, 57 | SP_DATAOUT_END 58 | ); 59 | signal SP : SCSIPhase_t; 60 | 61 | signal BSY_Nr : std_logic; 62 | signal MSG_Nr : std_logic; 63 | signal CD_Nr : std_logic; 64 | signal IO_Nr : std_logic; 65 | signal REQ_Nr : std_logic; 66 | -- signal TR_DONE : std_logic; 67 | -- signal TR_RDY : std_logic; 68 | 69 | type CommBuf_t is array (0 to 11) of std_logic_vector(7 downto 0); 70 | signal COMM : CommBuf_t; 71 | signal COMM_POS : unsigned(3 downto 0); 72 | signal COMM_OUT : std_logic; 73 | type CommLen_t is array (0 to 15) of unsigned(3 downto 0); 74 | constant COMM_LEN : CommLen_t := 75 | ("0110", "0110", "1010", "1010", "1010", "1010", "1010", "1010", "1010", "1010", "1100", "1100", "1010", "1010", "1010", "1010"); 76 | 77 | type DataBuf_t is array (0 to 9) of std_logic_vector(7 downto 0); 78 | signal DATA_BUF : DataBuf_t; 79 | signal DATA_POS : unsigned(3 downto 0); 80 | signal DATA_OUT : std_logic; 81 | 82 | signal FULL : std_logic; 83 | signal EMPTY : std_logic; 84 | signal FIFO_RD_REQ: std_logic; 85 | signal FIFO_WR_REQ: std_logic; 86 | signal FIFO_D : std_logic_vector(7 downto 0); 87 | signal FIFO_Q : std_logic_vector(7 downto 0); 88 | signal CD_WR_OLD : std_logic; 89 | signal STAT_PEND : std_logic; 90 | signal DOUT_PEND: std_logic; 91 | 92 | signal DATAIN_CNT : unsigned(15 downto 0); 93 | 94 | signal STAT_COUNT : unsigned(15 downto 0); 95 | 96 | begin 97 | 98 | process( RESET_N, CLK ) 99 | begin 100 | if RESET_N = '0' then 101 | FIFO_D <= (others => '0'); 102 | FIFO_WR_REQ <= '0'; 103 | --CD_WR_OLD <= '0'; 104 | elsif rising_edge(CLK) then 105 | FIFO_WR_REQ <= '0'; 106 | -- if EN = '1' then 107 | CD_WR_OLD <= CD_WR; 108 | if CD_WR = '1' and CD_WR_OLD = '0' then 109 | FIFO_D <= CD_DATA; 110 | if FULL = '0' then 111 | FIFO_WR_REQ <= '1'; 112 | end if; 113 | end if; 114 | -- end if; 115 | end if; 116 | end process; 117 | 118 | 119 | FIFO : entity work.SCSI_FIFO 120 | port map( 121 | aclr => not RESET_N, 122 | 123 | wrclk => CLK, 124 | data => FIFO_D, 125 | wrreq => FIFO_WR_REQ, 126 | wrfull => FULL, 127 | 128 | rdclk => CLK, 129 | rdreq => FIFO_RD_REQ, 130 | rdempty => EMPTY, 131 | q => FIFO_Q 132 | ); 133 | 134 | process( CLK, RESET_N ) begin 135 | if RESET_N = '0' then 136 | DBO <= (others => '0'); 137 | BSY_Nr <= '1'; 138 | MSG_Nr <= '1'; 139 | CD_Nr <= '1'; 140 | IO_Nr <= '1'; 141 | REQ_Nr <= '1'; 142 | COMM <= (others => (others => '0')); 143 | COMM_POS <= (others => '0'); 144 | DATA_BUF <= (others => (others => '0')); 145 | DATA_POS <= (others => '0'); 146 | SP <= SP_FREE; 147 | STOP_CD_SND <= '0'; 148 | 149 | COMM_OUT <= '0'; 150 | DATA_OUT <= '0'; 151 | CD_DATA_END <= '0'; 152 | STAT_PEND <= '0'; 153 | DOUT_PEND <= '0'; 154 | FIFO_RD_REQ <= '0'; 155 | 156 | STAT_COUNT <= (others => '0'); 157 | 158 | DATAIN_CNT <= (others => '0'); 159 | 160 | elsif rising_edge( CLK ) then 161 | if STAT_GET = '1' then 162 | STAT_PEND <= '1'; 163 | end if; 164 | 165 | if DOUT_REQ = '1' then 166 | DOUT_PEND <= '1'; 167 | end if; 168 | 169 | 170 | COMM_OUT <= '0'; 171 | DATA_OUT <= '0'; 172 | CD_DATA_END <= '0'; 173 | FIFO_RD_REQ <= '0'; 174 | 175 | if RST_N = '0' then 176 | BSY_Nr <= '1'; 177 | MSG_Nr <= '1'; 178 | CD_Nr <= '1'; 179 | IO_Nr <= '1'; 180 | REQ_Nr <= '1'; 181 | else 182 | case SP is 183 | when SP_FREE => 184 | if SEL_N = '0' then 185 | BSY_Nr <= '0'; 186 | MSG_Nr <= '1'; 187 | CD_Nr <= '0'; 188 | IO_Nr <= '1'; 189 | REQ_Nr <= '0'; 190 | SP <= SP_COMM_START; 191 | DATAIN_CNT <= (others => '0'); 192 | elsif STAT_PEND = '1' then 193 | STAT_COUNT <= STAT_COUNT + 1; 194 | 195 | if (STAT_COUNT = 45000) then -- CLK is 42.95 MHz; this gives ~1.05 millisec delay. 196 | -- this is empirical and may not be correct but it solves 197 | -- the Sailor Moon hang issue 198 | STAT_COUNT <= (others => '0'); 199 | STAT_PEND <= '0'; 200 | DBO <= STATUS; 201 | BSY_Nr <= '0'; 202 | MSG_Nr <= '1'; 203 | CD_Nr <= '0'; 204 | IO_Nr <= '0'; 205 | REQ_Nr <= '0'; 206 | SP <= SP_STAT_START; 207 | end if; 208 | elsif EMPTY = '0' then 209 | DBO <= FIFO_Q; 210 | BSY_Nr <= '0'; 211 | MSG_Nr <= '1'; 212 | CD_Nr <= '1'; 213 | IO_Nr <= '0'; 214 | REQ_Nr <= '0'; 215 | FIFO_RD_REQ <= '1'; 216 | SP <= SP_DATAIN_START; 217 | elsif DOUT_PEND = '1' then 218 | DOUT_PEND <= '0'; 219 | BSY_Nr <= '0'; 220 | MSG_Nr <= '1'; 221 | CD_Nr <= '1'; 222 | IO_Nr <= '1'; 223 | REQ_Nr <= '0'; 224 | SP <= SP_DATAOUT_START; 225 | end if; 226 | 227 | when SP_COMM_START => 228 | if REQ_Nr = '0' and ACK_N = '0' then 229 | REQ_Nr <= '1'; 230 | COMM(to_integer(COMM_POS)) <= DBI; 231 | COMM_POS <= COMM_POS + 1; 232 | SP <= SP_COMM_END; 233 | end if; 234 | 235 | when SP_COMM_END => 236 | if REQ_Nr = '1' and ACK_N = '1' then 237 | if COMM_POS = COMM_LEN(to_integer(unsigned(COMM(0)(7 downto 4)))) then 238 | COMM_POS <= (others => '0'); 239 | COMM_OUT <= '1'; 240 | CD_Nr <= '1'; 241 | SP <= SP_FREE; 242 | if ((COMM(0) = x"08") or (COMM(0) = x"DA")) then -- READ6 and PAUSE commands should mute sound, but still drain FIFO 243 | STOP_CD_SND <= '1'; 244 | end if; 245 | if ((COMM(0) = x"D8") or (COMM(0) = x"D9")) then -- SAPSP and SAPEP commands should unmute sound (FIFO should be empty by now) 246 | STOP_CD_SND <= '0'; 247 | end if; 248 | else 249 | REQ_Nr <= '0'; 250 | SP <= SP_COMM_START; 251 | end if; 252 | end if; 253 | 254 | when SP_STAT_START => 255 | if REQ_Nr = '0' and ACK_N = '0' then 256 | REQ_Nr <= '1'; 257 | SP <= SP_STAT_END; 258 | end if; 259 | 260 | when SP_STAT_END => 261 | if REQ_Nr = '1' and ACK_N = '1' then 262 | DBO <= MESSAGE; 263 | BSY_Nr <= '0'; 264 | MSG_Nr <= '0'; 265 | CD_Nr <= '0'; 266 | IO_Nr <= '0'; 267 | REQ_Nr <= '0'; 268 | SP <= SP_MSGIN_START; 269 | end if; 270 | 271 | when SP_MSGIN_START => 272 | if REQ_Nr = '0' and ACK_N = '0' then 273 | REQ_Nr <= '1'; 274 | SP <= SP_MSGIN_END; 275 | end if; 276 | 277 | when SP_MSGIN_END => 278 | if REQ_Nr = '1' and ACK_N = '1' then 279 | BSY_Nr <= '1'; 280 | MSG_Nr <= '1'; 281 | CD_Nr <= '1'; 282 | IO_Nr <= '1'; 283 | REQ_Nr <= '1'; 284 | SP <= SP_FREE; 285 | end if; 286 | 287 | when SP_DATAIN_START => 288 | if REQ_Nr = '0' and ACK_N = '0' then 289 | REQ_Nr <= '1'; 290 | SP <= SP_DATAIN_END; 291 | STOP_CD_SND <= '0'; -- unmute 292 | end if; 293 | 294 | when SP_DATAIN_END => 295 | if REQ_Nr = '1' and ACK_N = '1' then 296 | if EMPTY = '0' then 297 | DBO <= FIFO_Q; 298 | REQ_Nr <= '0'; 299 | FIFO_RD_REQ <= '1'; 300 | SP <= SP_DATAIN_START; 301 | else 302 | CD_DATA_END <= '1'; 303 | SP <= SP_FREE; 304 | end if; 305 | DATAIN_CNT <= DATAIN_CNT + 1; 306 | end if; 307 | 308 | when SP_DATAOUT_START => 309 | if REQ_Nr = '0' and ACK_N = '0' then 310 | REQ_Nr <= '1'; 311 | DATA_BUF(to_integer(DATA_POS)) <= DBI; 312 | DATA_POS <= DATA_POS + 1; 313 | SP <= SP_DATAOUT_END; 314 | end if; 315 | 316 | when SP_DATAOUT_END => 317 | if REQ_Nr = '1' and ACK_N = '1' then 318 | if DATA_POS = 10 then 319 | DATA_POS <= (others => '0'); 320 | DATA_OUT <= '1'; 321 | SP <= SP_FREE; 322 | else 323 | REQ_Nr <= '0'; 324 | SP <= SP_DATAOUT_START; 325 | end if; 326 | end if; 327 | 328 | when others => null; 329 | end case; 330 | end if; 331 | end if; 332 | end process; 333 | 334 | BSY_N <= BSY_Nr; 335 | MSG_N <= MSG_Nr; 336 | CD_N <= CD_Nr; 337 | IO_N <= IO_Nr; 338 | REQ_N <= REQ_Nr; 339 | 340 | COMMAND <= COMM(11) & COMM(10) & COMM(9) & COMM(8) & COMM(7) & COMM(6) & COMM(5) & COMM(4) & COMM(3) & COMM(2) & COMM(1) & COMM(0); 341 | COMM_SEND <= COMM_OUT; 342 | 343 | DOUT <= DATA_BUF(9) & DATA_BUF(8) & DATA_BUF(7) & DATA_BUF(6) & DATA_BUF(5) & DATA_BUF(4) & DATA_BUF(3) & DATA_BUF(2) & DATA_BUF(1) & DATA_BUF(0); 344 | DOUT_SEND <= DATA_OUT; 345 | 346 | DBG_DATAIN_CNT <= DATAIN_CNT; 347 | 348 | end rtl; 349 | -------------------------------------------------------------------------------- /rtl/pce/cd/SCSI_FIFO.vhd: -------------------------------------------------------------------------------- 1 | -- megafunction wizard: %FIFO% 2 | -- GENERATION: STANDARD 3 | -- VERSION: WM1.0 4 | -- MODULE: dcfifo_mixed_widths 5 | 6 | -- ============================================================ 7 | -- File Name: SCSI_FIFO.vhd 8 | -- Megafunction Name(s): 9 | -- dcfifo_mixed_widths 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 | -- 13.1.4 Build 182 03/12/2014 SJ Full Version 18 | -- ************************************************************ 19 | 20 | 21 | --Copyright (C) 1991-2014 Altera Corporation 22 | --Your use of Altera Corporation's design tools, logic functions 23 | --and other software and tools, and its AMPP 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 Altera Program License 28 | --Subscription Agreement, Altera MegaCore Function License 29 | --Agreement, or other applicable license agreement, including, 30 | --without limitation, that your use is for the sole purpose of 31 | --programming logic devices manufactured by Altera and sold by 32 | --Altera or its authorized distributors. Please refer to the 33 | --applicable agreement for further details. 34 | 35 | 36 | LIBRARY ieee; 37 | USE ieee.std_logic_1164.all; 38 | 39 | LIBRARY altera_mf; 40 | USE altera_mf.all; 41 | 42 | ENTITY SCSI_FIFO IS 43 | PORT 44 | ( 45 | aclr : IN STD_LOGIC := '0'; 46 | data : IN STD_LOGIC_VECTOR (7 DOWNTO 0); 47 | rdclk : IN STD_LOGIC ; 48 | rdreq : IN STD_LOGIC ; 49 | wrclk : IN STD_LOGIC ; 50 | wrreq : IN STD_LOGIC ; 51 | q : OUT STD_LOGIC_VECTOR (7 DOWNTO 0); 52 | rdempty : OUT STD_LOGIC ; 53 | wrfull : OUT STD_LOGIC 54 | ); 55 | END SCSI_FIFO; 56 | 57 | 58 | ARCHITECTURE SYN OF scsi_fifo IS 59 | 60 | SIGNAL sub_wire0 : STD_LOGIC ; 61 | SIGNAL sub_wire1 : STD_LOGIC_VECTOR (7 DOWNTO 0); 62 | SIGNAL sub_wire2 : STD_LOGIC ; 63 | 64 | 65 | 66 | COMPONENT dcfifo_mixed_widths 67 | GENERIC ( 68 | intended_device_family : STRING; 69 | lpm_numwords : NATURAL; 70 | lpm_showahead : STRING; 71 | lpm_type : STRING; 72 | lpm_width : NATURAL; 73 | lpm_widthu : NATURAL; 74 | lpm_widthu_r : NATURAL; 75 | lpm_width_r : NATURAL; 76 | overflow_checking : STRING; 77 | rdsync_delaypipe : NATURAL; 78 | read_aclr_synch : STRING; 79 | underflow_checking : STRING; 80 | use_eab : STRING; 81 | write_aclr_synch : STRING; 82 | wrsync_delaypipe : NATURAL 83 | ); 84 | PORT ( 85 | aclr : IN STD_LOGIC ; 86 | data : IN STD_LOGIC_VECTOR (7 DOWNTO 0); 87 | rdclk : IN STD_LOGIC ; 88 | rdreq : IN STD_LOGIC ; 89 | wrfull : OUT STD_LOGIC ; 90 | q : OUT STD_LOGIC_VECTOR (7 DOWNTO 0); 91 | rdempty : OUT STD_LOGIC ; 92 | wrclk : IN STD_LOGIC ; 93 | wrreq : IN STD_LOGIC 94 | ); 95 | END COMPONENT; 96 | 97 | BEGIN 98 | wrfull <= sub_wire0; 99 | q <= sub_wire1(7 DOWNTO 0); 100 | rdempty <= sub_wire2; 101 | 102 | dcfifo_mixed_widths_component : dcfifo_mixed_widths 103 | GENERIC MAP ( 104 | intended_device_family => "Cyclone V", 105 | lpm_numwords => 4096, 106 | lpm_showahead => "ON", 107 | lpm_type => "dcfifo_mixed_widths", 108 | lpm_width => 8, 109 | lpm_widthu => 12, 110 | lpm_widthu_r => 12, 111 | lpm_width_r => 8, 112 | overflow_checking => "ON", 113 | rdsync_delaypipe => 4, 114 | read_aclr_synch => "OFF", 115 | underflow_checking => "ON", 116 | use_eab => "ON", 117 | write_aclr_synch => "OFF", 118 | wrsync_delaypipe => 4 119 | ) 120 | PORT MAP ( 121 | aclr => aclr, 122 | data => data, 123 | rdclk => rdclk, 124 | rdreq => rdreq, 125 | wrclk => wrclk, 126 | wrreq => wrreq, 127 | wrfull => sub_wire0, 128 | q => sub_wire1, 129 | rdempty => sub_wire2 130 | ); 131 | 132 | 133 | 134 | END SYN; 135 | 136 | -- ============================================================ 137 | -- CNX file retrieval info 138 | -- ============================================================ 139 | -- Retrieval info: PRIVATE: AlmostEmpty NUMERIC "0" 140 | -- Retrieval info: PRIVATE: AlmostEmptyThr NUMERIC "-1" 141 | -- Retrieval info: PRIVATE: AlmostFull NUMERIC "0" 142 | -- Retrieval info: PRIVATE: AlmostFullThr NUMERIC "-1" 143 | -- Retrieval info: PRIVATE: CLOCKS_ARE_SYNCHRONIZED NUMERIC "0" 144 | -- Retrieval info: PRIVATE: Clock NUMERIC "4" 145 | -- Retrieval info: PRIVATE: Depth NUMERIC "4096" 146 | -- Retrieval info: PRIVATE: Empty NUMERIC "1" 147 | -- Retrieval info: PRIVATE: Full NUMERIC "1" 148 | -- Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone V" 149 | -- Retrieval info: PRIVATE: LE_BasedFIFO NUMERIC "0" 150 | -- Retrieval info: PRIVATE: LegacyRREQ NUMERIC "0" 151 | -- Retrieval info: PRIVATE: MAX_DEPTH_BY_9 NUMERIC "0" 152 | -- Retrieval info: PRIVATE: OVERFLOW_CHECKING NUMERIC "0" 153 | -- Retrieval info: PRIVATE: Optimize NUMERIC "0" 154 | -- Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0" 155 | -- Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0" 156 | -- Retrieval info: PRIVATE: UNDERFLOW_CHECKING NUMERIC "0" 157 | -- Retrieval info: PRIVATE: UsedW NUMERIC "0" 158 | -- Retrieval info: PRIVATE: Width NUMERIC "8" 159 | -- Retrieval info: PRIVATE: dc_aclr NUMERIC "1" 160 | -- Retrieval info: PRIVATE: diff_widths NUMERIC "1" 161 | -- Retrieval info: PRIVATE: msb_usedw NUMERIC "0" 162 | -- Retrieval info: PRIVATE: output_width NUMERIC "8" 163 | -- Retrieval info: PRIVATE: rsEmpty NUMERIC "1" 164 | -- Retrieval info: PRIVATE: rsFull NUMERIC "0" 165 | -- Retrieval info: PRIVATE: rsUsedW NUMERIC "0" 166 | -- Retrieval info: PRIVATE: sc_aclr NUMERIC "0" 167 | -- Retrieval info: PRIVATE: sc_sclr NUMERIC "0" 168 | -- Retrieval info: PRIVATE: wsEmpty NUMERIC "0" 169 | -- Retrieval info: PRIVATE: wsFull NUMERIC "1" 170 | -- Retrieval info: PRIVATE: wsUsedW NUMERIC "0" 171 | -- Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all 172 | -- Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone V" 173 | -- Retrieval info: CONSTANT: LPM_NUMWORDS NUMERIC "4096" 174 | -- Retrieval info: CONSTANT: LPM_SHOWAHEAD STRING "ON" 175 | -- Retrieval info: CONSTANT: LPM_TYPE STRING "dcfifo_mixed_widths" 176 | -- Retrieval info: CONSTANT: LPM_WIDTH NUMERIC "8" 177 | -- Retrieval info: CONSTANT: LPM_WIDTHU NUMERIC "12" 178 | -- Retrieval info: CONSTANT: LPM_WIDTHU_R NUMERIC "12" 179 | -- Retrieval info: CONSTANT: LPM_WIDTH_R NUMERIC "8" 180 | -- Retrieval info: CONSTANT: OVERFLOW_CHECKING STRING "ON" 181 | -- Retrieval info: CONSTANT: RDSYNC_DELAYPIPE NUMERIC "4" 182 | -- Retrieval info: CONSTANT: READ_ACLR_SYNCH STRING "OFF" 183 | -- Retrieval info: CONSTANT: UNDERFLOW_CHECKING STRING "ON" 184 | -- Retrieval info: CONSTANT: USE_EAB STRING "ON" 185 | -- Retrieval info: CONSTANT: WRITE_ACLR_SYNCH STRING "OFF" 186 | -- Retrieval info: CONSTANT: WRSYNC_DELAYPIPE NUMERIC "4" 187 | -- Retrieval info: USED_PORT: aclr 0 0 0 0 INPUT GND "aclr" 188 | -- Retrieval info: USED_PORT: data 0 0 8 0 INPUT NODEFVAL "data[7..0]" 189 | -- Retrieval info: USED_PORT: q 0 0 8 0 OUTPUT NODEFVAL "q[7..0]" 190 | -- Retrieval info: USED_PORT: rdclk 0 0 0 0 INPUT NODEFVAL "rdclk" 191 | -- Retrieval info: USED_PORT: rdempty 0 0 0 0 OUTPUT NODEFVAL "rdempty" 192 | -- Retrieval info: USED_PORT: rdreq 0 0 0 0 INPUT NODEFVAL "rdreq" 193 | -- Retrieval info: USED_PORT: wrclk 0 0 0 0 INPUT NODEFVAL "wrclk" 194 | -- Retrieval info: USED_PORT: wrfull 0 0 0 0 OUTPUT NODEFVAL "wrfull" 195 | -- Retrieval info: USED_PORT: wrreq 0 0 0 0 INPUT NODEFVAL "wrreq" 196 | -- Retrieval info: CONNECT: @aclr 0 0 0 0 aclr 0 0 0 0 197 | -- Retrieval info: CONNECT: @data 0 0 8 0 data 0 0 8 0 198 | -- Retrieval info: CONNECT: @rdclk 0 0 0 0 rdclk 0 0 0 0 199 | -- Retrieval info: CONNECT: @rdreq 0 0 0 0 rdreq 0 0 0 0 200 | -- Retrieval info: CONNECT: @wrclk 0 0 0 0 wrclk 0 0 0 0 201 | -- Retrieval info: CONNECT: @wrreq 0 0 0 0 wrreq 0 0 0 0 202 | -- Retrieval info: CONNECT: q 0 0 8 0 @q 0 0 8 0 203 | -- Retrieval info: CONNECT: rdempty 0 0 0 0 @rdempty 0 0 0 0 204 | -- Retrieval info: CONNECT: wrfull 0 0 0 0 @wrfull 0 0 0 0 205 | -- Retrieval info: GEN_FILE: TYPE_NORMAL SCSI_FIFO.vhd TRUE 206 | -- Retrieval info: GEN_FILE: TYPE_NORMAL SCSI_FIFO.inc FALSE 207 | -- Retrieval info: GEN_FILE: TYPE_NORMAL SCSI_FIFO.cmp FALSE 208 | -- Retrieval info: GEN_FILE: TYPE_NORMAL SCSI_FIFO.bsf FALSE 209 | -- Retrieval info: GEN_FILE: TYPE_NORMAL SCSI_FIFO_inst.vhd FALSE 210 | -- Retrieval info: LIB_FILE: altera_mf 211 | -------------------------------------------------------------------------------- /rtl/pce/cd/cd.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) cd.vhd ] 2 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) CDDA_FIFO.vhd ] 3 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) SCSI.vhd ] 4 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) SCSI_FIFO.vhd ] 5 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) MSM5205.vhd ] 6 | -------------------------------------------------------------------------------- /rtl/pce/cheatcodes.sv: -------------------------------------------------------------------------------- 1 | // Cheat Code handling by Kitrinx 2 | // Apr 21, 2019 3 | 4 | // Code layout: 5 | // {clock bit, 32'bcode 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 | 34 | reg [ENA_F_S:0] codes[MAX_CODES]; 35 | 36 | wire [ADDR_WIDTH-1: 0] code_addr = code[64+:ADDR_WIDTH]; 37 | wire [DATA_WIDTH-1: 0] code_compare = code[32+:DATA_WIDTH]; 38 | wire [DATA_WIDTH-1: 0] code_data = code[0+:DATA_WIDTH]; 39 | wire code_comp_f = code[96]; 40 | 41 | wire [COMP_F_S:0] code_trimmed = {code_comp_f, code_addr, code_compare, code_data}; 42 | 43 | reg [INDEX_SIZE:0] index = '0; 44 | 45 | assign available = |index; 46 | 47 | reg code_change; 48 | always_ff @(posedge clk) begin 49 | int x; 50 | if (reset) begin 51 | index <= 0; 52 | code_change <= 0; 53 | for (x = 0; x < MAX_CODES; x = x + 1) codes[x] <= '0; 54 | end else begin 55 | code_change <= code[128]; 56 | if (code[128] && ~code_change && (index < MAX_CODES)) begin // detect posedge 57 | codes[index] <= {1'b1, code_trimmed}; 58 | index <= index + 1'b1; 59 | end 60 | end 61 | end 62 | 63 | always_comb begin 64 | int x; 65 | genie_ovr = 0; 66 | genie_data = '0; 67 | 68 | if (enable) begin 69 | for (x = 0; x < MAX_CODES; x = x + 1) begin 70 | if (codes[x][ENA_F_S] && codes[x][ADDR_S-:ADDR_WIDTH] == addr_in) begin 71 | if (!codes[x][COMP_F_S] || (codes[x][COMP_S-:DATA_WIDTH] == data_in)) begin 72 | genie_ovr = 1; 73 | genie_data = codes[x][DATA_S-:DATA_WIDTH]; 74 | end 75 | end 76 | end 77 | end 78 | end 79 | 80 | endmodule 81 | -------------------------------------------------------------------------------- /rtl/pce/color_mix.sv: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // Copyright (c) 2018 Sorgelig 4 | // 5 | // This program is GPL v2+ Licensed. 6 | // 7 | // 8 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 9 | 10 | module color_mix 11 | ( 12 | input clk_vid, 13 | input ce_pix, 14 | input [2:0] mix, 15 | 16 | input [7:0] R_in, 17 | input [7:0] G_in, 18 | input [7:0] B_in, 19 | input HSync_in, 20 | input VSync_in, 21 | input HBlank_in, 22 | input VBlank_in, 23 | 24 | output reg [7:0] R_out, 25 | output reg [7:0] G_out, 26 | output reg [7:0] B_out, 27 | output reg HSync_out, 28 | output reg VSync_out, 29 | output reg HBlank_out, 30 | output reg VBlank_out 31 | ); 32 | 33 | 34 | reg [7:0] R,G,B; 35 | reg HBl, VBl, HS, VS; 36 | always @(posedge clk_vid) if(ce_pix) begin 37 | R <= R_in; 38 | G <= G_in; 39 | B <= B_in; 40 | HS <= HSync_in; 41 | VS <= VSync_in; 42 | HBl <= HBlank_in; 43 | VBl <= VBlank_in; 44 | end 45 | 46 | wire [15:0] px = R * 16'd054 + G * 16'd183 + B * 16'd018; 47 | 48 | always @(posedge clk_vid) if(ce_pix) begin 49 | {R_out, G_out, B_out} <= 0; 50 | 51 | case(mix) 52 | 0, 53 | 1: {R_out, G_out, B_out} <= {R, G, B }; // color 54 | 2: { G_out } <= { px[15:8] }; // green 55 | 3: {R_out, G_out } <= {px[15:8], px[15:8] - px[15:10]}; // amber 56 | 4: { G_out, B_out} <= { px[15:8], px[15:8] }; // cyan 57 | 5: {R_out, G_out, B_out} <= {px[15:8], px[15:8], px[15:8] }; // gray 58 | endcase 59 | 60 | HSync_out <= HS; 61 | VSync_out <= VS; 62 | HBlank_out <= HBl; 63 | VBlank_out <= VBl; 64 | end 65 | 66 | endmodule 67 | -------------------------------------------------------------------------------- /rtl/pce/ddram.sv: -------------------------------------------------------------------------------- 1 | // 2 | // ddram.v 3 | // Copyright (c) 2020 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 | 23 | module ddram 24 | ( 25 | input DDRAM_CLK, 26 | 27 | input DDRAM_BUSY, 28 | output [7:0] DDRAM_BURSTCNT, 29 | output [28:0] DDRAM_ADDR, 30 | input [63:0] DDRAM_DOUT, 31 | input DDRAM_DOUT_READY, 32 | output DDRAM_RD, 33 | output [63:0] DDRAM_DIN, 34 | output [7:0] DDRAM_BE, 35 | output DDRAM_WE, 36 | 37 | input clkref, 38 | 39 | input [27:0] wraddr, 40 | input [15:0] din, 41 | input we, 42 | output reg we_rdy, 43 | input we_req, 44 | output reg we_ack, 45 | 46 | input [27:0] rdaddr, 47 | output [7:0] dout, 48 | input rd, 49 | output reg rd_rdy 50 | ); 51 | 52 | assign DDRAM_BURSTCNT = ram_burst; 53 | assign DDRAM_BE = DDRAM_RD ? 8'hFF : ({6'd0,~b,1'b1} << {ram_addr[2:1],ram_addr[0] & b}); 54 | assign DDRAM_ADDR = {4'b0011, ram_addr[27:3]}; // RAM at 0x30000000 55 | assign DDRAM_DIN = ram_data; 56 | assign DDRAM_WE = ram_write; 57 | 58 | assign dout = data; 59 | 60 | reg [7:0] ram_burst; 61 | reg [63:0] ram_data; 62 | reg [27:0] ram_addr; 63 | reg [7:0] data; 64 | reg ram_write = 0; 65 | reg b; 66 | reg start; 67 | reg [1:0] state = 0; 68 | 69 | reg [27:0] addr; 70 | 71 | always @(posedge DDRAM_CLK) begin 72 | reg old_ref; 73 | reg[127:0] ram_q; 74 | 75 | old_ref <= clkref; 76 | start <= ~old_ref & clkref; 77 | 78 | if(start) begin 79 | if(we) we_rdy <= 0; 80 | else if(rd) rd_rdy <= 0; 81 | end 82 | 83 | ram_burst <= 1; 84 | addr <= rdaddr; 85 | 86 | if(!DDRAM_BUSY) begin 87 | ram_write <= 0; 88 | case(state) 89 | 0: begin 90 | we_rdy <= 1; 91 | rd_rdy <= 1; 92 | cache_cs <= 0; 93 | if(we_ack != we_req) begin 94 | we_ack <= we_req; 95 | ram_data <= {4{din}}; 96 | ram_addr <= wraddr; 97 | ram_write <= 1; 98 | b <= 0; 99 | end 100 | else if(start) begin 101 | if(we) begin 102 | we_rdy <= 0; 103 | ram_data <= {8{din[7:0]}}; 104 | ram_addr <= addr; 105 | ram_write <= 1; 106 | b <= 1; 107 | cache_cs <= 1; 108 | cache_we <= 1; 109 | state <= 1; 110 | end 111 | else if(rd) begin 112 | ram_addr <= addr; 113 | rd_rdy <= 0; 114 | cache_cs <= 1; 115 | cache_we <= 0; 116 | state <= 2; 117 | end 118 | end 119 | end 120 | 121 | 1: if(cache_wrack) begin 122 | cache_cs <= 0; 123 | we_rdy <= 1; 124 | state <= 0; 125 | end 126 | 127 | 2: if(cache_rdack) begin 128 | cache_cs <= 0; 129 | data <= ram_addr[0] ? cache_do[15:8] : cache_do[7:0]; 130 | rd_rdy <= 1; 131 | state <= 0; 132 | end 133 | endcase 134 | end 135 | end 136 | 137 | wire [15:0] cache_do; 138 | wire cache_rdack; 139 | wire cache_wrack; 140 | reg cache_cs; 141 | reg cache_we; 142 | 143 | cache_2way cache 144 | ( 145 | .clk(DDRAM_CLK), 146 | .rst(we_ack != we_req), 147 | 148 | .cache_enable(1), 149 | 150 | .cpu_cs(cache_cs), 151 | .cpu_adr(addr[27:1]), 152 | .cpu_bs({addr[0],~addr[0]}), 153 | .cpu_we(cache_we), 154 | .cpu_rd(~cache_we), 155 | .cpu_dat_w(ram_data[15:0]), 156 | .cpu_dat_r(cache_do), 157 | .cpu_ack(cache_rdack), 158 | .wb_en(cache_wrack), 159 | 160 | .mem_dat_r(DDRAM_DOUT), 161 | .mem_read_req(DDRAM_RD), 162 | .mem_read_ack(DDRAM_DOUT_READY) 163 | ); 164 | 165 | endmodule 166 | -------------------------------------------------------------------------------- /rtl/pce/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 | mem_init_file : string := " "; 12 | disable_value : std_logic := '1' 13 | ); 14 | PORT 15 | ( 16 | clock : in STD_LOGIC ; 17 | address_a : in STD_LOGIC_VECTOR (addr_width-1 DOWNTO 0); 18 | data_a : in STD_LOGIC_VECTOR (data_width-1 DOWNTO 0) := (others => '0'); 19 | enable_a : in STD_LOGIC := '1'; 20 | wren_a : in STD_LOGIC := '0'; 21 | q_a : out STD_LOGIC_VECTOR (data_width-1 DOWNTO 0); 22 | cs_a : in std_logic := '1'; 23 | 24 | address_b : in STD_LOGIC_VECTOR (addr_width-1 DOWNTO 0) := (others => '0'); 25 | data_b : in STD_LOGIC_VECTOR (data_width-1 DOWNTO 0) := (others => '0'); 26 | enable_b : in STD_LOGIC := '1'; 27 | wren_b : in STD_LOGIC := '0'; 28 | q_b : out STD_LOGIC_VECTOR (data_width-1 DOWNTO 0); 29 | cs_b : in std_logic := '1' 30 | ); 31 | end entity; 32 | 33 | 34 | ARCHITECTURE SYN OF dpram IS 35 | 36 | signal q0 : std_logic_vector((data_width - 1) downto 0); 37 | signal q1 : std_logic_vector((data_width - 1) downto 0); 38 | 39 | BEGIN 40 | q_a<= q0 when cs_a = '1' else (others => disable_value); 41 | q_b<= q1 when cs_b = '1' else (others => disable_value); 42 | 43 | altsyncram_component : altsyncram 44 | GENERIC MAP ( 45 | address_reg_b => "CLOCK1", 46 | clock_enable_input_a => "NORMAL", 47 | clock_enable_input_b => "NORMAL", 48 | clock_enable_output_a => "BYPASS", 49 | clock_enable_output_b => "BYPASS", 50 | indata_reg_b => "CLOCK1", 51 | intended_device_family => "Cyclone V", 52 | lpm_type => "altsyncram", 53 | numwords_a => 2**addr_width, 54 | numwords_b => 2**addr_width, 55 | operation_mode => "BIDIR_DUAL_PORT", 56 | outdata_aclr_a => "NONE", 57 | outdata_aclr_b => "NONE", 58 | outdata_reg_a => "UNREGISTERED", 59 | outdata_reg_b => "UNREGISTERED", 60 | power_up_uninitialized => "FALSE", 61 | read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ", 62 | read_during_write_mode_port_b => "NEW_DATA_NO_NBE_READ", 63 | init_file => mem_init_file, 64 | widthad_a => addr_width, 65 | widthad_b => addr_width, 66 | width_a => data_width, 67 | width_b => data_width, 68 | width_byteena_a => 1, 69 | width_byteena_b => 1, 70 | wrcontrol_wraddress_reg_b => "CLOCK1" 71 | ) 72 | PORT MAP ( 73 | address_a => address_a, 74 | address_b => address_b, 75 | clock0 => clock, 76 | clock1 => clock, 77 | clocken0 => enable_a, 78 | clocken1 => enable_b, 79 | data_a => data_a, 80 | data_b => data_b, 81 | wren_a => wren_a and cs_a, 82 | wren_b => wren_b and cs_b, 83 | q_a => q0, 84 | q_b => q1 85 | ); 86 | 87 | END SYN; 88 | 89 | LIBRARY ieee; 90 | USE ieee.std_logic_1164.all; 91 | 92 | LIBRARY altera_mf; 93 | USE altera_mf.altera_mf_components.all; 94 | 95 | entity dpram_difclk is 96 | generic ( 97 | addr_width_a : integer := 8; 98 | data_width_a : integer := 8; 99 | addr_width_b : integer := 8; 100 | data_width_b : integer := 8; 101 | mem_init_file : string := " " 102 | ); 103 | PORT 104 | ( 105 | clock0 : in STD_LOGIC; 106 | clock1 : in STD_LOGIC; 107 | 108 | address_a : in STD_LOGIC_VECTOR (addr_width_a-1 DOWNTO 0); 109 | data_a : in STD_LOGIC_VECTOR (data_width_a-1 DOWNTO 0) := (others => '0'); 110 | enable_a : in STD_LOGIC := '1'; 111 | wren_a : in STD_LOGIC := '0'; 112 | q_a : out STD_LOGIC_VECTOR (data_width_a-1 DOWNTO 0); 113 | cs_a : in std_logic := '1'; 114 | 115 | address_b : in STD_LOGIC_VECTOR (addr_width_b-1 DOWNTO 0) := (others => '0'); 116 | data_b : in STD_LOGIC_VECTOR (data_width_b-1 DOWNTO 0) := (others => '0'); 117 | enable_b : in STD_LOGIC := '1'; 118 | wren_b : in STD_LOGIC := '0'; 119 | q_b : out STD_LOGIC_VECTOR (data_width_b-1 DOWNTO 0); 120 | cs_b : in std_logic := '1' 121 | ); 122 | end entity; 123 | 124 | 125 | ARCHITECTURE SYN OF dpram_difclk IS 126 | 127 | signal q0 : std_logic_vector((data_width_a - 1) downto 0); 128 | signal q1 : std_logic_vector((data_width_b - 1) downto 0); 129 | 130 | BEGIN 131 | q_a<= q0 when cs_a = '1' else (others => '1'); 132 | q_b<= q1 when cs_b = '1' else (others => '1'); 133 | 134 | altsyncram_component : altsyncram 135 | GENERIC MAP ( 136 | address_reg_b => "CLOCK1", 137 | clock_enable_input_a => "NORMAL", 138 | clock_enable_input_b => "NORMAL", 139 | clock_enable_output_a => "BYPASS", 140 | clock_enable_output_b => "BYPASS", 141 | indata_reg_b => "CLOCK1", 142 | intended_device_family => "Cyclone V", 143 | lpm_type => "altsyncram", 144 | numwords_a => 2**addr_width_a, 145 | numwords_b => 2**addr_width_b, 146 | operation_mode => "BIDIR_DUAL_PORT", 147 | outdata_aclr_a => "NONE", 148 | outdata_aclr_b => "NONE", 149 | outdata_reg_a => "UNREGISTERED", 150 | outdata_reg_b => "UNREGISTERED", 151 | power_up_uninitialized => "FALSE", 152 | read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ", 153 | read_during_write_mode_port_b => "NEW_DATA_NO_NBE_READ", 154 | init_file => mem_init_file, 155 | widthad_a => addr_width_a, 156 | widthad_b => addr_width_b, 157 | width_a => data_width_a, 158 | width_b => data_width_b, 159 | width_byteena_a => 1, 160 | width_byteena_b => 1, 161 | wrcontrol_wraddress_reg_b => "CLOCK1" 162 | ) 163 | PORT MAP ( 164 | address_a => address_a, 165 | address_b => address_b, 166 | clock0 => clock0, 167 | clock1 => clock1, 168 | clocken0 => enable_a, 169 | clocken1 => enable_b, 170 | data_a => data_a, 171 | data_b => data_b, 172 | wren_a => wren_a and cs_a, 173 | wren_b => wren_b and cs_b, 174 | q_a => q0, 175 | q_b => q1 176 | ); 177 | 178 | END SYN; 179 | 180 | -------------------------------------------------------------- 181 | -- Single port Block RAM 182 | -------------------------------------------------------------- 183 | 184 | LIBRARY ieee; 185 | USE ieee.std_logic_1164.all; 186 | 187 | LIBRARY altera_mf; 188 | USE altera_mf.altera_mf_components.all; 189 | 190 | ENTITY spram IS 191 | generic ( 192 | addr_width : integer := 8; 193 | data_width : integer := 8; 194 | mem_init_file : string := " "; 195 | mem_name : string := "MEM" -- for InSystem Memory content editor. 196 | ); 197 | PORT 198 | ( 199 | clock : in STD_LOGIC; 200 | address : in STD_LOGIC_VECTOR (addr_width-1 DOWNTO 0); 201 | data : in STD_LOGIC_VECTOR (data_width-1 DOWNTO 0) := (others => '0'); 202 | enable : in STD_LOGIC := '1'; 203 | wren : in STD_LOGIC := '0'; 204 | q : out STD_LOGIC_VECTOR (data_width-1 DOWNTO 0); 205 | cs : in std_logic := '1' 206 | ); 207 | END ENTITY; 208 | 209 | ARCHITECTURE SYN OF spram IS 210 | signal q0 : std_logic_vector((data_width - 1) downto 0); 211 | BEGIN 212 | q<= q0 when cs = '1' else (others => '1'); 213 | 214 | altsyncram_component : altsyncram 215 | GENERIC MAP ( 216 | clock_enable_input_a => "BYPASS", 217 | clock_enable_output_a => "BYPASS", 218 | intended_device_family => "Cyclone V", 219 | lpm_hint => "ENABLE_RUNTIME_MOD=YES,INSTANCE_NAME="&mem_name, 220 | lpm_type => "altsyncram", 221 | numwords_a => 2**addr_width, 222 | operation_mode => "SINGLE_PORT", 223 | outdata_aclr_a => "NONE", 224 | outdata_reg_a => "UNREGISTERED", 225 | power_up_uninitialized => "FALSE", 226 | read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ", 227 | init_file => mem_init_file, 228 | widthad_a => addr_width, 229 | width_a => data_width, 230 | width_byteena_a => 1 231 | ) 232 | PORT MAP ( 233 | address_a => address, 234 | clock0 => clock, 235 | data_a => data, 236 | wren_a => wren and cs, 237 | q_a => q0 238 | ); 239 | 240 | END SYN; 241 | -------------------------------------------------------------------------------- /rtl/pce/hps_ext.v: -------------------------------------------------------------------------------- 1 | // 2 | // hps_ext for TurboGrafx-16 CD 3 | // 4 | // Copyright (c) 2020 Alexey Melnikov 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 | module hps_ext 22 | ( 23 | input clk_sys, 24 | inout [35:0] EXT_BUS, 25 | 26 | // CD interface 27 | input [112:0] cd_in, 28 | output reg [112:0] cd_out 29 | ); 30 | 31 | assign EXT_BUS[15:0] = io_dout; 32 | wire [15:0] io_din = EXT_BUS[31:16]; 33 | assign EXT_BUS[32] = dout_en; 34 | wire io_strobe = EXT_BUS[33]; 35 | wire io_enable = EXT_BUS[34]; 36 | 37 | localparam EXT_CMD_MIN = CD_GET; 38 | localparam EXT_CMD_MAX = CD_SET; 39 | 40 | localparam CD_GET = 'h34; 41 | localparam CD_SET = 'h35; 42 | 43 | reg [15:0] io_dout; 44 | reg dout_en = 0; 45 | reg [4:0] byte_cnt; 46 | 47 | always@(posedge clk_sys) begin 48 | reg [15:0] cmd; 49 | reg [7:0] cd_req = 0; 50 | reg old_cd = 0; 51 | 52 | old_cd <= cd_in[112]; 53 | if(old_cd ^ cd_in[112]) cd_req <= cd_req + 1'd1; 54 | 55 | if(~io_enable) begin 56 | dout_en <= 0; 57 | io_dout <= 0; 58 | byte_cnt <= 0; 59 | cmd <= 0; 60 | if(cmd == CD_SET) cd_out[112] <= ~cd_out[112]; 61 | end 62 | else if(io_strobe) begin 63 | 64 | io_dout <= 0; 65 | if(~&byte_cnt) byte_cnt <= byte_cnt + 1'd1; 66 | 67 | if(byte_cnt == 0) begin 68 | cmd <= io_din; 69 | dout_en <= (io_din >= EXT_CMD_MIN && io_din <= EXT_CMD_MAX); 70 | if(io_din == CD_GET) io_dout <= cd_req; 71 | end else begin 72 | 73 | case(cmd) 74 | 75 | CD_GET: case(byte_cnt) 76 | 1: io_dout <= cd_in[15:0]; 77 | 2: io_dout <= cd_in[31:16]; 78 | 3: io_dout <= cd_in[47:32]; 79 | 4: io_dout <= cd_in[63:48]; 80 | 5: io_dout <= cd_in[79:64]; 81 | 6: io_dout <= cd_in[95:80]; 82 | 7: io_dout <= cd_in[111:96]; 83 | endcase 84 | 85 | CD_SET: case(byte_cnt) 86 | 1: cd_out[15:0] <= io_din; 87 | 2: cd_out[31:16] <= io_din; 88 | 3: cd_out[47:32] <= io_din; 89 | 4: cd_out[63:48] <= io_din; 90 | 5: cd_out[79:64] <= io_din; 91 | 6: cd_out[95:80] <= io_din; 92 | 7: cd_out[111:96] <= io_din; 93 | endcase 94 | endcase 95 | end 96 | end 97 | end 98 | 99 | endmodule 100 | -------------------------------------------------------------------------------- /rtl/pce/huc6202.vhd: -------------------------------------------------------------------------------- 1 | --============================================================================ 2 | -- HUC6202 3 | -- Copyright (C) 2018 Sorgelig 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify it 6 | -- under the terms of the GNU General Public License as published by the Free 7 | -- Software Foundation; either version 2 of the License, or (at your option) 8 | -- any later version. 9 | -- 10 | -- This program is distributed in the hope that it will be useful, but WITHOUT 11 | -- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | -- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | -- more details. 14 | -- 15 | -- You should have received a copy of the GNU General Public License along 16 | -- with this program; if not, write to the Free Software Foundation, Inc., 17 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | --============================================================================ 19 | 20 | library IEEE; 21 | use IEEE.STD_LOGIC_1164.ALL; 22 | use IEEE.STD_LOGIC_UNSIGNED.ALL; 23 | use IEEE.NUMERIC_STD.ALL; 24 | 25 | entity huc6202 is 26 | port ( 27 | CLK : in std_logic; 28 | CLKEN : in std_logic; 29 | RESET_N : in std_logic; 30 | 31 | A : in std_logic_vector(2 downto 0); 32 | WR_N : in std_logic; 33 | DI : in std_logic_vector(7 downto 0); 34 | DO : out std_logic_vector(7 downto 0); 35 | 36 | HS_F : in std_logic; 37 | VDC0_IN : in std_logic_vector(8 downto 0); 38 | VDC1_IN : in std_logic_vector(8 downto 0); 39 | VDC_OUT : out std_logic_vector(8 downto 0); 40 | 41 | SGX : in std_logic; 42 | 43 | VDCNUM : out std_logic 44 | ); 45 | end huc6202; 46 | 47 | architecture rtl of huc6202 is 48 | 49 | signal PRI0 : std_logic_vector(7 downto 0); 50 | signal PRI1 : std_logic_vector(7 downto 0); 51 | signal WIN1 : std_logic_vector(9 downto 0); 52 | signal WIN2 : std_logic_vector(9 downto 0); 53 | signal X : std_logic_vector(9 downto 0); 54 | signal PRIN : std_logic_vector(1 downto 0); 55 | signal PRI : std_logic_vector(3 downto 0); 56 | 57 | signal VDC_PRI : std_logic_vector(8 downto 0); 58 | 59 | begin 60 | 61 | PRIN(0) <= '1' when WIN1 <= x"40" or X >= WIN1 else '0'; 62 | PRIN(1) <= '1' when WIN2 <= x"40" or X >= WIN2 else '0'; 63 | PRI <= PRI0(3 downto 0) when PRIN = "00" else 64 | PRI0(7 downto 4) when PRIN = "01" else 65 | PRI1(3 downto 0) when PRIN = "10" else 66 | PRI1(7 downto 4); 67 | 68 | VDC_PRI <= VDC0_IN when VDC0_IN(3 downto 0) /= "0000" else VDC1_IN; 69 | 70 | process( CLK ) 71 | variable VDCDATA : std_logic_vector(8 downto 0); 72 | begin 73 | if rising_edge(CLK) then 74 | if CLKEN = '1' then 75 | X <= X + 1; 76 | if HS_F = '1' then 77 | X <= (others => '0'); 78 | end if; 79 | 80 | case PRI(1 downto 0) is 81 | when "00" => 82 | VDCDATA := (others => '0'); 83 | when "01" => 84 | VDCDATA := VDC0_IN; 85 | when "10" => 86 | VDCDATA := VDC1_IN; 87 | when others => 88 | VDCDATA := VDC_PRI; 89 | case PRI(3 downto 2) is 90 | when "01" => 91 | if VDC1_IN(8) = '1' and VDC0_IN(8) = '0' and VDC1_IN(3 downto 0) /= "0000" then 92 | VDCDATA := VDC1_IN; 93 | end if; 94 | when "10" => 95 | if VDC1_IN(8) = '0' and VDC0_IN(8) = '1' and VDC1_IN(3 downto 0) /= "0000" then 96 | VDCDATA := VDC1_IN; 97 | end if; 98 | when others => null; 99 | end case; 100 | end case; 101 | 102 | VDC_OUT <= VDCDATA; 103 | end if; 104 | end if; 105 | end process; 106 | 107 | process( CLK ) begin 108 | if rising_edge(CLK) then 109 | if RESET_N = '0' then 110 | PRI0 <= "00010001"; 111 | PRI1 <= "00010001"; 112 | WIN1 <= (others => '0'); 113 | WIN2 <= (others => '0'); 114 | VDCNUM <= '0'; 115 | DO <= X"FF"; 116 | else 117 | if WR_N = '0' then 118 | case A is 119 | when "000" => PRI0 <= DI; 120 | when "001" => PRI1 <= DI; 121 | when "010" => WIN1(7 downto 0) <= DI; 122 | when "011" => WIN1(9 downto 8) <= DI(1 downto 0); 123 | when "100" => WIN2(7 downto 0) <= DI; 124 | when "101" => WIN2(9 downto 8) <= DI(1 downto 0); 125 | when "110" => VDCNUM <= DI(0); 126 | when others => null; 127 | end case; 128 | end if; 129 | case A is 130 | when "000" => DO <= PRI0; 131 | when "001" => DO <= PRI1; 132 | when "010" => DO <= WIN1(7 downto 0); 133 | when "011" => DO <= "000000" & WIN1(9 downto 8); 134 | when "100" => DO <= WIN2(7 downto 0); 135 | when "101" => DO <= "000000" & WIN2(9 downto 8); 136 | when others => DO <= X"00"; 137 | end case; 138 | end if; 139 | end if; 140 | end process; 141 | 142 | end rtl; 143 | -------------------------------------------------------------------------------- /rtl/pce/mb128.sv: -------------------------------------------------------------------------------- 1 | // MB128.v 2 | // 3 | // This executes a compatible protocol to the Memory Base 128 4 | // or Save-kun peripheral as used by the PC Engine 5 | // 6 | // (c) 2020 by David Shadoff 7 | // 8 | // 9 | 10 | module MB128 11 | ( 12 | input clk_sys, // system clock 13 | input reset, 14 | 15 | input i_Clk, // Joypad Clr/Reset line, clocks the SPI-like MB128 protocol 16 | input i_Data, // Joypad Sel line, provides data to the SPI-like MB128 protocol 17 | 18 | output o_Active, 19 | output [3:0] o_Data, 20 | 21 | input bk_clk, 22 | input [15:0] bk_address, 23 | input [15:0] bk_din, 24 | output [15:0] bk_dout, 25 | input bk_we, 26 | output bk_written 27 | ); 28 | 29 | // constants - STATEs 30 | // STATE GROUP 1 - Request identification 31 | localparam STATE_IDLE = 0; 32 | localparam STATE_A8_A1 = 1; 33 | localparam STATE_A8_A2 = 2; 34 | 35 | // STATE GROUP 2 - Synced; request infromation 36 | localparam STATE_REQ = 3; 37 | localparam STATE_ADDR = 4; 38 | localparam STATE_LEN = 5; 39 | 40 | // STATE GROUP 3 - Synced; in-transfer states 41 | localparam STATE_READ = 6; 42 | localparam STATE_READ_TRAIL = 7; 43 | localparam STATE_WRITE = 8; 44 | localparam STATE_WRITE_TRAIL = 9; 45 | 46 | localparam CMD_WRITE = 0; 47 | localparam CMD_READ = 1; 48 | 49 | 50 | // registers 51 | reg [3:0] r_State = STATE_IDLE; 52 | reg [7:0] r_Register = 0; 53 | 54 | reg r_Req; 55 | reg [19:0] r_Bit_Count; 56 | reg [19:0] r_MB128_Addr; 57 | reg [19:0] r_MB128_Bits; 58 | reg [3:0] r_Data; 59 | 60 | reg clk_prev; 61 | 62 | reg ram_din; 63 | reg ram_we; 64 | wire ram_dout; 65 | 66 | // 67 | // master storage - should be backed by permanent storage like SDCard 68 | // 69 | dpram_difclk #(20,1,16,16) back128_l 70 | ( 71 | // Port A for MB128 access 72 | // 73 | .clock0(clk_sys), 74 | .address_a(r_MB128_Addr), 75 | .data_a(ram_din), 76 | .wren_a(ram_we), 77 | .q_a(ram_dout), 78 | 79 | // Port B save/load 80 | // 81 | .clock1(bk_clk), 82 | .address_b(bk_address), 83 | .data_b(bk_din), 84 | .wren_b(bk_we), 85 | .q_b(bk_dout) 86 | ); 87 | 88 | 89 | always @(posedge clk_sys) begin 90 | 91 | if(bk_address[15:10] == 2) bk_written <= 0; 92 | 93 | ram_we <= 0; 94 | if (ram_we) r_MB128_Addr <= r_MB128_Addr + 1'b1; 95 | 96 | clk_prev <= i_Clk; 97 | 98 | if (reset) begin 99 | r_State <= STATE_IDLE; 100 | r_Bit_Count <= 0; 101 | end 102 | else if (~clk_prev & i_Clk) begin // drive the SPI-like protocol based on this signal's positive edge 103 | 104 | r_Data <= 0; 105 | 106 | case (r_State) 107 | STATE_IDLE: 108 | begin 109 | if (r_Bit_Count <= 7) r_Bit_Count <= r_Bit_Count + 1'b1; 110 | r_Register <= {i_Data, r_Register[7:1]}; 111 | if (({i_Data, r_Register[7:1]} == 8'hA8) && (r_Bit_Count >= 7)) r_State <= STATE_A8_A1; 112 | end 113 | 114 | STATE_A8_A1: 115 | begin 116 | r_State <= STATE_A8_A2; 117 | end 118 | 119 | STATE_A8_A2: 120 | begin 121 | // Note that IDENT actually takes the value sent in data 122 | r_Data[2] <= i_Data; 123 | r_State <= STATE_REQ; 124 | end 125 | 126 | STATE_REQ: 127 | begin 128 | r_Req <= i_Data; 129 | 130 | r_MB128_Addr <= 0; 131 | r_MB128_Bits <= 0; 132 | r_Bit_Count <= 0; 133 | 134 | r_State <= STATE_ADDR; 135 | end 136 | 137 | STATE_ADDR: 138 | begin 139 | // 10 address bits come in LSB signifies 128 bytes of offset 140 | r_MB128_Addr <= {i_Data, r_MB128_Addr[19:1]}; 141 | 142 | r_Bit_Count <= r_Bit_Count + 1'b1; 143 | if (r_Bit_Count == 9) begin 144 | r_Bit_Count <= 0; 145 | r_State <= STATE_LEN; 146 | end 147 | end 148 | 149 | STATE_LEN: 150 | begin 151 | // 20 bits come in identifying # of bits 152 | r_MB128_Bits <= {i_Data, r_MB128_Bits[19:1]}; 153 | 154 | r_Bit_Count <= r_Bit_Count + 1'b1; 155 | if (r_Bit_Count == 19) begin 156 | 157 | r_Data[0] <= r_Req; 158 | r_State <= (r_Req == CMD_WRITE) ? STATE_WRITE : STATE_READ; 159 | r_Bit_Count <= 1; 160 | 161 | if (!{i_Data, r_MB128_Bits[19:1]}) begin 162 | r_Bit_Count <= 0; 163 | r_State <= (r_Req == CMD_WRITE) ? STATE_WRITE_TRAIL : STATE_READ_TRAIL; 164 | end 165 | end 166 | end 167 | 168 | STATE_READ: 169 | begin 170 | r_Bit_Count <= r_Bit_Count + 1'b1; 171 | r_Data[0] <= ram_dout; 172 | r_MB128_Addr <= r_MB128_Addr + 1'b1; 173 | 174 | if (r_Bit_Count == r_MB128_Bits) begin 175 | r_Bit_Count <= 0; 176 | r_State <= STATE_READ_TRAIL; 177 | end 178 | end 179 | 180 | STATE_READ_TRAIL: 181 | begin 182 | r_Bit_Count <= r_Bit_Count + 1'b1; 183 | if (r_Bit_Count == 2) begin 184 | r_Bit_Count <= 0; 185 | r_State <= STATE_IDLE; 186 | end 187 | end 188 | 189 | STATE_WRITE: 190 | begin 191 | r_Bit_Count <= r_Bit_Count + 1'b1; 192 | ram_din <= i_Data; 193 | ram_we <= 1; 194 | bk_written <= 1; 195 | 196 | if (r_Bit_Count == r_MB128_Bits) begin 197 | r_Bit_Count <= 0; 198 | r_State <= STATE_WRITE_TRAIL; 199 | end 200 | end 201 | 202 | STATE_WRITE_TRAIL: 203 | begin 204 | r_Bit_Count <= r_Bit_Count + 1'b1; 205 | 206 | if (r_Bit_Count == 4) begin 207 | r_Bit_Count <= 0; 208 | r_State <= STATE_IDLE; 209 | end 210 | end 211 | endcase 212 | end 213 | end 214 | 215 | assign o_Active = r_State != STATE_IDLE; 216 | assign o_Data = r_Data; 217 | 218 | endmodule 219 | -------------------------------------------------------------------------------- /rtl/pce/sdram.sv: -------------------------------------------------------------------------------- 1 | // 2 | // sdram.v 3 | // 4 | // sdram controller implementation 5 | // Copyright (c) 2018 Sorgelig 6 | // 7 | // Based on sdram module by Till Harbaum 8 | // 9 | // This source file is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published 11 | // by the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // This source file is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with this program. If not, see . 21 | // 22 | 23 | module sdram 24 | ( 25 | 26 | // interface to the MT48LC16M16 chip 27 | inout reg [15:0] SDRAM_DQ, // 16 bit bidirectional data bus 28 | output reg [12:0] SDRAM_A, // 13 bit multiplexed address bus 29 | output SDRAM_DQML, // byte mask 30 | output SDRAM_DQMH, // byte mask 31 | output reg [1:0] SDRAM_BA, // two banks 32 | output SDRAM_nCS, // a single chip select 33 | output reg SDRAM_nWE, // write enable 34 | output reg SDRAM_nRAS, // row address select 35 | output reg SDRAM_nCAS, // columns address select 36 | output SDRAM_CLK, 37 | output SDRAM_CKE, 38 | 39 | // cpu/chipset interface 40 | input init, // init signal after FPGA config to initialize RAM 41 | input clk, // sdram is accessed at up to 128MHz 42 | input clkref, // reference clock to sync to 43 | 44 | input [24:0] raddr, // 25 bit byte address 45 | input rd, // cpu/chipset requests read 46 | output reg rd_rdy = 0, 47 | output reg [7:0] dout, // data output to chipset/cpu 48 | 49 | input [24:0] waddr, // 25 bit byte address 50 | input [15:0] din, // data input from chipset/cpu 51 | input we, // cpu/chipset write 52 | input we_req, // cpu/chipset requests write 53 | output reg we_ack = 0 54 | ); 55 | 56 | assign SDRAM_nCS = 0; 57 | assign SDRAM_CKE = 1; 58 | assign {SDRAM_DQMH,SDRAM_DQML} = SDRAM_A[12:11]; 59 | 60 | // no burst configured 61 | localparam RASCAS_DELAY = 3'd2; // tRCD=20ns -> 3 cycles@128MHz 62 | localparam BURST_LENGTH = 3'b000; // 000=1, 001=2, 010=4, 011=8 63 | localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved 64 | localparam CAS_LATENCY = 3'd2; // 2/3 allowed 65 | localparam OP_MODE = 2'b00; // only 00 (standard operation) allowed 66 | localparam NO_WRITE_BURST = 1'b1; // 0= write burst enabled, 1=only single access write 67 | 68 | localparam MODE = { 3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH}; 69 | 70 | localparam STATE_IDLE = 4'd0; // first state in cycle 71 | localparam STATE_START = 4'd1; // state in which a new command can be started 72 | localparam STATE_CONT = STATE_START+RASCAS_DELAY; // 4 command can be continued 73 | localparam STATE_LAST = 4'd7; // last state in cycle 74 | localparam STATE_READY = STATE_CONT+CAS_LATENCY+2; 75 | 76 | 77 | reg [3:0] q; 78 | reg [22:0] a; 79 | reg [1:0] bank; 80 | reg [15:0] data; 81 | reg wr; 82 | reg b; 83 | reg ram_req=0; 84 | 85 | // access manager 86 | always @(posedge clk) begin 87 | reg old_ref; 88 | 89 | old_ref<=clkref; 90 | 91 | if(q==STATE_IDLE) begin 92 | rd_rdy <= 1; 93 | ram_req <= 0; 94 | wr <= 0; 95 | 96 | if(we_ack != we_req || we) begin 97 | ram_req <= 1; 98 | wr <= 1; 99 | {bank,a} <= waddr; 100 | data <= din; 101 | b <= we; 102 | end 103 | else 104 | if(rd) begin 105 | rd_rdy <= 0; 106 | ram_req <= 1; 107 | wr <= 0; 108 | {bank,a} <= raddr; 109 | b <= 0; 110 | end 111 | end 112 | 113 | if (q == STATE_READY && ram_req) begin 114 | if(wr) we_ack <= we_req; 115 | else rd_rdy <= 1; 116 | end 117 | 118 | q <= q + 1'd1; 119 | if(~old_ref & clkref) q <= 0; 120 | end 121 | 122 | localparam MODE_NORMAL = 2'b00; 123 | localparam MODE_RESET = 2'b01; 124 | localparam MODE_LDM = 2'b10; 125 | localparam MODE_PRE = 2'b11; 126 | 127 | // initialization 128 | reg [1:0] mode; 129 | always @(posedge clk) begin 130 | reg [4:0] reset=5'h1f; 131 | reg init_old=0; 132 | init_old <= init; 133 | 134 | if(init_old & ~init) reset <= 5'h1f; 135 | else if(q == STATE_LAST) begin 136 | if(reset != 0) begin 137 | reset <= reset - 5'd1; 138 | if(reset == 14) mode <= MODE_PRE; 139 | else if(reset == 3) mode <= MODE_LDM; 140 | else mode <= MODE_RESET; 141 | end 142 | else mode <= MODE_NORMAL; 143 | end 144 | end 145 | 146 | localparam CMD_NOP = 3'b111; 147 | localparam CMD_BURST_TERMINATE = 3'b110; 148 | localparam CMD_READ = 3'b101; 149 | localparam CMD_WRITE = 3'b100; 150 | localparam CMD_ACTIVE = 3'b011; 151 | localparam CMD_PRECHARGE = 3'b010; 152 | localparam CMD_AUTO_REFRESH = 3'b001; 153 | localparam CMD_LOAD_MODE = 3'b000; 154 | 155 | // SDRAM state machines 156 | always @(posedge clk) begin 157 | reg [15:0] data_reg; 158 | 159 | SDRAM_DQ <= 16'hZZZZ; 160 | casex({ram_req,wr,mode,q}) 161 | {2'b1X, MODE_NORMAL, STATE_START}: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_ACTIVE; 162 | {2'b11, MODE_NORMAL, STATE_CONT }: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE, SDRAM_DQ} <= {CMD_WRITE, data}; 163 | {2'b10, MODE_NORMAL, STATE_CONT }: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_READ; 164 | {2'b0X, MODE_NORMAL, STATE_START}: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_AUTO_REFRESH; 165 | 166 | // init 167 | {2'bXX, MODE_LDM, STATE_START}: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_LOAD_MODE; 168 | {2'bXX, MODE_PRE, STATE_START}: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_PRECHARGE; 169 | 170 | default: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_NOP; 171 | endcase 172 | 173 | if(mode == MODE_NORMAL) begin 174 | casex(q) 175 | STATE_START: SDRAM_A <= a[21:9]; 176 | STATE_CONT: SDRAM_A <= {~a[0]&wr&b,a[0]&wr&b,2'b10, a[22], a[8:1]}; 177 | endcase; 178 | end 179 | else if(mode == MODE_LDM && q == STATE_START) SDRAM_A <= MODE; 180 | else if(mode == MODE_PRE && q == STATE_START) SDRAM_A <= 13'b0010000000000; 181 | else SDRAM_A <= 0; 182 | 183 | data_reg <= SDRAM_DQ; 184 | if(q == STATE_START) SDRAM_BA <= (mode == MODE_NORMAL) ? bank : 2'b00; 185 | if(q == STATE_READY && ~wr && ram_req) dout <= a[0] ? data_reg[15:8] : data_reg[7:0]; 186 | end 187 | 188 | altddio_out 189 | #( 190 | .extend_oe_disable("OFF"), 191 | .intended_device_family("Cyclone V"), 192 | .invert_output("OFF"), 193 | .lpm_hint("UNUSED"), 194 | .lpm_type("altddio_out"), 195 | .oe_reg("UNREGISTERED"), 196 | .power_up_high("OFF"), 197 | .width(1) 198 | ) 199 | sdramclk_ddr 200 | ( 201 | .datain_h(1'b0), 202 | .datain_l(1'b1), 203 | .outclock(clk), 204 | .dataout(SDRAM_CLK), 205 | .aclr(1'b0), 206 | .aset(1'b0), 207 | .oe(1'b1), 208 | .outclocken(1'b1), 209 | .sclr(1'b0), 210 | .sset(1'b0) 211 | ); 212 | 213 | endmodule 214 | -------------------------------------------------------------------------------- /rtl/pce/xe1ap.v: -------------------------------------------------------------------------------- 1 | // Code your design here 2 | module XE1AP 3 | #(parameter CLKPERUSEC=50) // we need fixed time intervals in microseconds; 4 | // clk_sys may vary from core to core; this is 5 | // the number of clk_sys cycles in a microsecond 6 | (input clk_sys, 7 | input reset, 8 | input [31:0] joystick_0, // 3 = up, 2 = down, 1 = left, 0 = right 9 | // 7 = run, 6 = select, 5 = button 2, 4 - button 1 10 | input [15:0] joystick_l_analog_0, // [15:8] is up/down, range +/- 127 (up is minus) 11 | // [7:0] is left/right, range +/- 127 (left is minus) 12 | input [15:0] joystick_r_analog_0, 13 | 14 | input req, // signal requesting response from XE-1AP (on return to high) 15 | // pin 8 on original 9-pin connector 16 | 17 | output reg trg1, // pin 6 on original 9-pin connector 18 | output reg trg2, // pin 7 on original 9-pin connector 19 | 20 | output reg [3:0] data, // Data[3] = pin 4 on original 9-pin connector 21 | // Data[2] = pin 3 on original 9-pin connector 22 | // Data[1] = pin 2 on original 9-pin connector 23 | // Data[0] = pin 1 on original 9-pin connector 24 | 25 | output reg run_btn, // need to send back for the XHE-3 PC Engine attachment 26 | output reg select_btn // need to send back for the XHE-3 PC Engine attachment 27 | ); 28 | 29 | // Note that output data is sent 4 bits at a time, with trg2 == LOW signalling "data ready" 30 | // 31 | // The sequence of data (and bit-order) is as follows (from original joystick): 32 | // (Note that not all buttons are currently mapped for PC-Engine implementation) 33 | // 34 | // All values are low when pressed, high when not pressed 35 | // 36 | // 1: Buttons A, B, C, D - (Note: A is pressed if either A or A' is pressed; same with B or B') 37 | // 2: Buttons E1, E2, Start(F), Select (G) 38 | // 3: Top 4 bits of 'channel 0' (Y-axis; limit up = 0x00, limit down = 0xFF) 39 | // 4: Top 4 bits of 'channel 1' (X-axis; limit left = 0x00, limit right = 0xFF) 40 | // 5: Top 4 bits of 'channel 2' (Throttle; limit up = 0xFF, limit down = 0x00) 41 | // 6: 0000 (unused) 42 | // 7: Bottom 4 bits of 'channel 0' (Y-axis) 43 | // 8: Bottom 4 bits of 'channel 1' (X-axis) 44 | // 9: Bottom 4 bits of 'channel 2' (Throttle) 45 | // 10: 0000 (unused) 46 | // 11: Buttons A, B, A', B' (This can differentiate between the buttons, whereas scan #1 merges them) 47 | // 12: 1111 (all high) 48 | // 49 | 50 | // registers 51 | 52 | reg [6:0] clks_per_usec = CLKPERUSEC; 53 | 54 | reg [6:0] clk_counter = 0; 55 | reg [6:0] usec_counter = 0; 56 | reg [6:0] usec_counter_ff = 0; 57 | reg [47:0] shift_output = 48'h0; 58 | 59 | reg active = 1'b0; 60 | reg req_ff = 1'b1; 61 | reg req_fff = 1'b1; 62 | 63 | reg [2:0] cycle_count = 3'b0; // 0 = wait before pulse train 64 | // 1-6 = normal pulse cycles 65 | // 7 = cycle train completed 66 | 67 | always @(posedge clk_sys) 68 | begin 69 | 70 | req_ff <= req; 71 | req_fff <= req_ff; 72 | 73 | if (reset == 1'b1) begin 74 | active <= 1'b0; 75 | trg1 <= 1'b0; 76 | trg2 <= 1'b1; 77 | cycle_count <= 3'b0; 78 | clk_counter <= 0; 79 | usec_counter <= 0; 80 | end 81 | 82 | if (active == 1'b0) begin 83 | trg1 <= 1'b0; 84 | trg2 <= 1'b1; 85 | if ((req_fff == 0) && (req_ff == 1)) 86 | begin 87 | active <= 1'b1; 88 | cycle_count <= 3'b0; 89 | clk_counter <= 0; 90 | usec_counter <= 0; 91 | run_btn <= ~joystick_0[7]; 92 | select_btn <= ~joystick_0[6]; 93 | 94 | shift_output <= { 4'b1111, // Need to put first nybble as least-significant 95 | ~joystick_0[4], ~joystick_0[5], 2'b11, // A, B, A', B' 96 | 4'b0000, 97 | ~joystick_r_analog_0[11:8], // throttle[3:0] 98 | joystick_l_analog_0[3:0], // x[3:0] 99 | joystick_l_analog_0[11:8], // y[3:0] 100 | 4'b0000, 101 | joystick_r_analog_0[15], ~joystick_r_analog_0[14:12], // throttle[7:4] 102 | ~joystick_l_analog_0[7], joystick_l_analog_0[6:4], // x[7:4] 103 | ~joystick_l_analog_0[15], joystick_l_analog_0[14:12], // y[7:4] 104 | 2'b11, ~joystick_0[7], ~joystick_0[6], // E1, E2, start, select 105 | ~joystick_0[4], ~joystick_0[5], 2'b11 }; // A, B, C, D 106 | end 107 | end 108 | 109 | else if (active == 1) begin 110 | usec_counter_ff <= usec_counter; 111 | clk_counter <= clk_counter + 1; 112 | if (clk_counter == clks_per_usec) 113 | begin 114 | clk_counter <= 0; 115 | usec_counter <= usec_counter + 1; 116 | end 117 | 118 | if (cycle_count == 0) begin // first cycle needs 68 microseconds until output 119 | if ((usec_counter > usec_counter_ff) && (usec_counter == 68)) begin 120 | data[3:0] <= shift_output[3:0]; 121 | shift_output[43:0] <= shift_output[47:4]; 122 | trg2 <= 1'b0; 123 | cycle_count <= 1; 124 | usec_counter <= 0; 125 | end 126 | end 127 | 128 | else if ((cycle_count >= 1) && (cycle_count <= 6)) begin // normal 6 cycles of data output 129 | if ((usec_counter > usec_counter_ff) && (usec_counter == 13)) begin 130 | trg1 <= 1'b1; 131 | trg2 <= 1'b1; 132 | end 133 | 134 | else if ((usec_counter > usec_counter_ff) && (usec_counter == 17)) begin 135 | data[3:0] <= shift_output[3:0]; 136 | shift_output[43:0] <= shift_output[47:4]; 137 | trg2 <= 1'b0; 138 | end 139 | 140 | else if ((usec_counter > usec_counter_ff) && (usec_counter == 30)) begin 141 | trg2 <= 1'b1; 142 | end 143 | 144 | else if ((usec_counter > usec_counter_ff) && (usec_counter == 34)) begin 145 | trg1 <= 1'b0; 146 | if (cycle_count == 6) 147 | begin 148 | cycle_count <= 7; 149 | end 150 | end 151 | 152 | else if ((usec_counter > usec_counter_ff) && (usec_counter == 50)) begin 153 | data[3:0] <= shift_output[3:0]; 154 | shift_output[43:0] <= shift_output[47:4]; 155 | trg2 <= 1'b0; 156 | cycle_count <= cycle_count + 1; 157 | usec_counter <= 0; 158 | end 159 | end 160 | 161 | else if (cycle_count == 7) // Data train completed; ready to reset 162 | begin 163 | active <= 1'b0; 164 | trg1 <= 1'b0; 165 | trg2 <= 1'b1; 166 | data[3:0] <= 4'b1111; 167 | cycle_count <= 3'b0; 168 | clk_counter <= 0; 169 | usec_counter <= 0; 170 | end 171 | 172 | end 173 | end 174 | 175 | endmodule 176 | -------------------------------------------------------------------------------- /rtl/sys/iir_filter.v: -------------------------------------------------------------------------------- 1 | 2 | // 3-tap IIR filter for 2 channels. 3 | // Copyright (C) 2020 Sorgelig 4 | // 5 | // This program is free software; you can redistribute it and/or modify it 6 | // under the terms of the GNU General Public License as published by the Free 7 | // Software Foundation; either version 2 of the License, or (at your option) 8 | // any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, but WITHOUT 11 | // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | // more details. 14 | // 15 | // You should have received a copy of the GNU General Public License along 16 | // with this program; if not, write to the Free Software Foundation, Inc., 17 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | 19 | // 20 | // Can be converted to 2-tap (coeff_x2 = 0, coeff_y2 = 0) or 1-tap (coeff_x1,2 = 0, coeff_y1,2 = 0) 21 | // 22 | module IIR_filter 23 | #( 24 | parameter use_params = 1, // set to 1 to use following parameters, 0 for input port variables. 25 | parameter stereo = 1, // 0 for mono (input_l) 26 | 27 | parameter coeff_x = 0.00000774701983513660, // Base gain value for X. Float. Range: 0.0 ... 0.999(9) 28 | parameter coeff_x0 = 3, // Gain scale factor for X0. Integer. Range -7 ... +7 29 | parameter coeff_x1 = 3, // Gain scale factor for X1. Integer. Range -7 ... +7 30 | parameter coeff_x2 = 1, // Gain scale factor for X2. Integer. Range -7 ... +7 31 | parameter coeff_y0 = -2.96438150626551080000, // Coefficient for Y0. Float. Range -3.999(9) ... 3.999(9) 32 | parameter coeff_y1 = 2.92939452735121100000, // Coefficient for Y1. Float. Range -3.999(9) ... 3.999(9) 33 | parameter coeff_y2 = -0.96500747158831091000 // Coefficient for Y2. Float. Range -3.999(9) ... 3.999(9) 34 | ) 35 | ( 36 | input clk, 37 | input reset, 38 | 39 | input ce, // must be double of calculated rate for stereo! 40 | input sample_ce, // desired output sample rate 41 | 42 | input [39:0] cx, 43 | input [7:0] cx0, 44 | input [7:0] cx1, 45 | input [7:0] cx2, 46 | input [23:0] cy0, 47 | input [23:0] cy1, 48 | input [23:0] cy2, 49 | 50 | input [15:0] input_l, input_r, // signed samples 51 | output [15:0] output_l, output_r // signed samples 52 | ); 53 | 54 | localparam [39:0] pcoeff_x = coeff_x * 40'h8000000000; 55 | localparam [31:0] pcoeff_y0 = coeff_y0 * 24'h200000; 56 | localparam [31:0] pcoeff_y1 = coeff_y1 * 24'h200000; 57 | localparam [31:0] pcoeff_y2 = coeff_y2 * 24'h200000; 58 | 59 | wire [39:0] vcoeff = use_params ? pcoeff_x : cx; 60 | wire [23:0] vcoeff_y0 = use_params ? pcoeff_y0[23:0] : cy0; 61 | wire [23:0] vcoeff_y1 = use_params ? pcoeff_y1[23:0] : cy1; 62 | wire [23:0] vcoeff_y2 = use_params ? pcoeff_y2[23:0] : cy2; 63 | 64 | wire [59:0] inp_mul = $signed(inp) * $signed(vcoeff); 65 | 66 | wire [39:0] x = inp_mul[59:20]; 67 | wire [39:0] y = x + tap0; 68 | 69 | wire [39:0] tap0; 70 | iir_filter_tap iir_tap_0 71 | ( 72 | .clk(clk), 73 | .reset(reset), 74 | .ce(ce), 75 | .ch(ch), 76 | .cx(use_params ? coeff_x0[7:0] : cx0), 77 | .cy(vcoeff_y0), 78 | .x(x), 79 | .y(y), 80 | .z(tap1), 81 | .tap(tap0) 82 | ); 83 | 84 | wire [39:0] tap1; 85 | iir_filter_tap iir_tap_1 86 | ( 87 | .clk(clk), 88 | .reset(reset), 89 | .ce(ce), 90 | .ch(ch), 91 | .cx(use_params ? coeff_x1[7:0] : cx1), 92 | .cy(vcoeff_y1), 93 | .x(x), 94 | .y(y), 95 | .z(tap2), 96 | .tap(tap1) 97 | ); 98 | 99 | wire [39:0] tap2; 100 | iir_filter_tap iir_tap_2 101 | ( 102 | .clk(clk), 103 | .reset(reset), 104 | .ce(ce), 105 | .ch(ch), 106 | .cx(use_params ? coeff_x2[7:0] : cx2), 107 | .cy(vcoeff_y2), 108 | .x(x), 109 | .y(y), 110 | .z(0), 111 | .tap(tap2) 112 | ); 113 | 114 | wire [15:0] y_clamp = (~y[39] & |y[38:35]) ? 16'h7FFF : (y[39] & ~&y[38:35]) ? 16'h8000 : y[35:20]; 115 | 116 | reg ch = 0; 117 | reg [15:0] out_l, out_r, out_m; 118 | reg [15:0] inp, inp_m; 119 | always @(posedge clk) if (ce) begin 120 | if(!stereo) begin 121 | ch <= 0; 122 | inp <= input_l; 123 | out_l <= y_clamp; 124 | out_r <= y_clamp; 125 | end 126 | else begin 127 | ch <= ~ch; 128 | if(ch) begin 129 | out_m <= y_clamp; 130 | inp <= inp_m; 131 | end 132 | else begin 133 | out_l <= out_m; 134 | out_r <= y_clamp; 135 | inp <= input_l; 136 | inp_m <= input_r; 137 | end 138 | end 139 | end 140 | 141 | reg [31:0] out; 142 | always @(posedge clk) if (sample_ce) out <= {out_l, out_r}; 143 | 144 | assign {output_l, output_r} = out; 145 | 146 | endmodule 147 | 148 | module iir_filter_tap 149 | ( 150 | input clk, 151 | input reset, 152 | 153 | input ce, 154 | input ch, 155 | 156 | input [7:0] cx, 157 | input [23:0] cy, 158 | 159 | input [39:0] x, 160 | input [39:0] y, 161 | input [39:0] z, 162 | output [39:0] tap 163 | ); 164 | 165 | wire signed [60:0] y_mul = $signed(y[36:0]) * $signed(cy); 166 | 167 | function [39:0] x_mul; 168 | input [39:0] x; 169 | begin 170 | x_mul = 0; 171 | if(cx[0]) x_mul = x_mul + {{4{x[39]}}, x[39:4]}; 172 | if(cx[1]) x_mul = x_mul + {{3{x[39]}}, x[39:3]}; 173 | if(cx[2]) x_mul = x_mul + {{2{x[39]}}, x[39:2]}; 174 | if(cx[7]) x_mul = ~x_mul; //cheap NEG 175 | end 176 | endfunction 177 | 178 | (* ramstyle = "logic" *) reg [39:0] intreg[2]; 179 | always @(posedge clk, posedge reset) begin 180 | if(reset) {intreg[0],intreg[1]} <= 80'd0; 181 | else if(ce) intreg[ch] <= x_mul(x) - y_mul[60:21] + z; 182 | end 183 | 184 | assign tap = intreg[ch]; 185 | 186 | endmodule 187 | 188 | // simplified IIR 1-tap. 189 | module DC_blocker 190 | ( 191 | input clk, 192 | input ce, // 48/96 KHz 193 | input mute, 194 | 195 | input sample_rate, 196 | input [15:0] din, 197 | output [15:0] dout 198 | ); 199 | 200 | wire [39:0] x = {din[15], din, 23'd0}; 201 | wire [39:0] x0 = x - (sample_rate ? {{11{x[39]}}, x[39:11]} : {{10{x[39]}}, x[39:10]}); 202 | wire [39:0] y1 = y - (sample_rate ? {{10{y[39]}}, y[39:10]} : {{09{y[39]}}, y[39:09]}); 203 | wire [39:0] y0 = x0 - x1 + y1; 204 | 205 | reg [39:0] x1, y; 206 | always @(posedge clk) if(ce) begin 207 | x1 <= x0; 208 | y <= ^y0[39:38] ? {{2{y0[39]}},{38{y0[38]}}} : y0; 209 | end 210 | 211 | assign dout = mute ? 16'd0 : y[38:23]; 212 | 213 | endmodule 214 | -------------------------------------------------------------------------------- /support/chip32.asm: -------------------------------------------------------------------------------- 1 | architecture chip32.vm 2 | output "chip32.bin", create 3 | 4 | // we will put data into here that we're working on. It's the last 1K of the 8K chip32 memory 5 | constant rambuf = 0x1b00 6 | 7 | constant rom_dataslot = 0 8 | constant save_dataslot = 1 9 | 10 | // Host init command 11 | constant host_init = 0x4002 12 | 13 | // Error vector (0x0) 14 | jp error_handler 15 | 16 | // Init vector (0x2) 17 | // Choose core 18 | ld r0,#0 19 | core r0 20 | 21 | ld r1,#rom_dataslot // populate data slot 22 | ld r2,#rambuf // get ram buf position 23 | getext r1,r2 24 | ld r1,#ext_sgx 25 | test r2,r1 26 | jp z,set_sgx // Set sgx 27 | 28 | dont_set_sgx: 29 | ld r3,#0 30 | jp start_load 31 | 32 | set_sgx: 33 | ld r3,#1 34 | 35 | start_load: 36 | ld r1,#8 37 | pmpw r1,r3 // Write is_sgx = 1 38 | 39 | ld r1,#0 // Set address for write 40 | ld r2,#1 // Downloading start 41 | pmpw r1,r2 // Write ioctl_download = 1 42 | 43 | ld r1,#rom_dataslot 44 | ld r14,#load_err_msg 45 | loadf r1 // Load ROM 46 | jp nz,print_error_and_exit 47 | 48 | ld r1,#0 // Set address for write 49 | ld r2,#0 // Downloading end 50 | pmpw r1,r2 // Write ioctl_download = 0 51 | 52 | ld r1,#4 // Set address for write 53 | ld r2,#1 // Downloading start 54 | pmpw r1,r2 // Write save_download = 1 55 | 56 | ld r1,#save_dataslot 57 | loadf r1 // Load save 58 | 59 | ld r1,#4 // Set address for write 60 | ld r2,#0 // Downloading end 61 | pmpw r1,r2 // Write save_download = 0 62 | 63 | // Start core 64 | ld r0,#host_init 65 | host r0,r0 66 | 67 | exit 0 68 | 69 | // Error handling 70 | error_handler: 71 | ld r14,#test_err_msg 72 | 73 | print_error_and_exit: 74 | printf r14 75 | exit 1 76 | 77 | ext_sgx: 78 | db "SGX",0 79 | 80 | test_err_msg: 81 | db "Error",0 82 | 83 | load_err_msg: 84 | db "Could not load ROM",0 -------------------------------------------------------------------------------- /target/pocket/audio.sv: -------------------------------------------------------------------------------- 1 | module pce_audio ( 2 | input wire clk_sys_42_95, 3 | 4 | // Settings 5 | input wire cd_audio_boost, 6 | input wire adpcm_audio_boost, 7 | input wire [1:0] master_audio_boost, 8 | 9 | input wire [15:0] cdda_sl, 10 | input wire [15:0] cdda_sr, 11 | input wire [15:0] adpcm_s, 12 | input wire [15:0] psg_sl, 13 | input wire [15:0] psg_sr, 14 | 15 | output wire [15:0] audio_l, 16 | output wire [15:0] audio_r 17 | ); 18 | wire PSG_EN = 1; 19 | wire CDDA_EN = 1; 20 | wire ADPCM_EN = 1; 21 | 22 | localparam [3:0] comp_f1 = 4; 23 | localparam [3:0] comp_a1 = 2; 24 | localparam comp_x1 = ((32767 * (comp_f1 - 1)) / ((comp_f1 * comp_a1) - 1)) + 1; // +1 to make sure it won't overflow 25 | localparam comp_b1 = comp_x1 * comp_a1; 26 | 27 | localparam [3:0] comp_f2 = 8; 28 | localparam [3:0] comp_a2 = 4; 29 | localparam comp_x2 = ((32767 * (comp_f2 - 1)) / ((comp_f2 * comp_a2) - 1)) + 1; // +1 to make sure it won't overflow 30 | localparam comp_b2 = comp_x2 * comp_a2; 31 | 32 | function [15:0] compr; 33 | input [15:0] inp; 34 | reg [15:0] v, v1, v2; 35 | begin 36 | v = inp[15] ? (~inp) + 1'd1 : inp; 37 | v1 = (v < comp_x1[15:0]) ? (v * comp_a1) : (((v - comp_x1[15:0]) / comp_f1) + comp_b1[15:0]); 38 | v2 = (v < comp_x2[15:0]) ? (v * comp_a2) : (((v - comp_x2[15:0]) / comp_f2) + comp_b2[15:0]); 39 | v = master_audio_boost[1] ? v2 : v1; 40 | compr = inp[15] ? ~(v - 1'd1) : v; 41 | end 42 | endfunction 43 | 44 | reg [17:0] audio_l_int, audio_r_int; 45 | reg [15:0] cmp_l, cmp_r; 46 | 47 | logic [4:0] div_audio; 48 | logic adpcm_ce, psg_ce; 49 | 50 | logic [15:0] adpcm_filt, psg_l_filt, psg_r_filt; 51 | 52 | always @(posedge clk_sys_42_95) begin 53 | // 2684650 and 1342323 54 | div_audio <= div_audio + 1'd1; 55 | 56 | adpcm_ce <= &div_audio[4:0]; 57 | psg_ce <= &div_audio[3:0]; 58 | end 59 | 60 | IIR_filter #( 61 | .coeff_x (0.00200339512841342642), 62 | .coeff_x0(2), 63 | .coeff_x1(1), 64 | .coeff_x2(0), 65 | .coeff_y0(-1.95511712863912712201), 66 | .coeff_y1(0.95667938324280066276), 67 | .coeff_y2(0), 68 | .stereo (1) 69 | ) psg_filter ( 70 | .clk (clk_sys_42_95), 71 | .ce (psg_ce), // (1342323 * 2) 72 | .sample_ce(1), 73 | .input_l (psg_sl), 74 | .input_r (psg_sr), 75 | .output_l (psg_l_filt), 76 | .output_r (psg_r_filt) 77 | ); 78 | 79 | IIR_filter #( 80 | .coeff_x (0.00002488367092441635), 81 | .coeff_x0(3), 82 | .coeff_x1(3), 83 | .coeff_x2(1), 84 | .coeff_y0(-2.94383188882174362533), 85 | .coeff_y1(2.88923013608993572987), 86 | .coeff_y2(-0.94537670406128904155), 87 | .stereo (0) 88 | ) adpcm_filter ( 89 | .clk (clk_sys_42_95), 90 | .ce (adpcm_ce), // 1342323 91 | .sample_ce(1), 92 | .input_l (adpcm_s), 93 | .output_l (adpcm_filt) 94 | ); 95 | 96 | always @(posedge clk_sys_42_95) begin 97 | reg [17:0] pre_l, pre_r; 98 | reg signed [16:0] adpcm_boost; 99 | adpcm_boost <= $signed( 100 | {adpcm_filt[15], adpcm_filt} 101 | ) + $signed( 102 | (adpcm_audio_boost ? {{3{adpcm_filt[15]}}, adpcm_filt[15:2]} : 17'd0) 103 | ); 104 | 105 | pre_l <= ( CDDA_EN ? {{2{cdda_sl[15]}}, cdda_sl} : 18'd0) 106 | + ((CDDA_EN && cd_audio_boost) ? {{2{cdda_sl[15]}}, cdda_sl} : 18'd0) 107 | + ( PSG_EN ? {{2{psg_l_filt[15]}}, psg_l_filt} : 18'd0) 108 | + ( ADPCM_EN ? {adpcm_boost[16], adpcm_boost} : 18'd0); 109 | 110 | pre_r <= ( CDDA_EN ? {{2{cdda_sr[15]}}, cdda_sr} : 18'd0) 111 | + ((CDDA_EN && cd_audio_boost) ? {{2{cdda_sr[15]}}, cdda_sr} : 18'd0) 112 | + ( PSG_EN ? {{2{psg_r_filt[15]}}, psg_r_filt} : 18'd0) 113 | + ( ADPCM_EN ? {adpcm_boost[16], adpcm_boost} : 18'd0); 114 | 115 | if (~cd_audio_boost) begin 116 | // 3/4 + 1/4 to cover the whole range. 117 | audio_l_int <= $signed(pre_l) + ($signed(pre_l) >>> 2); 118 | audio_r_int <= $signed(pre_r) + ($signed(pre_r) >>> 2); 119 | end else begin 120 | audio_l_int <= pre_l; 121 | audio_r_int <= pre_r; 122 | end 123 | 124 | cmp_l <= compr(audio_l_int[17:2]); 125 | cmp_r <= compr(audio_r_int[17:2]); 126 | end 127 | 128 | assign audio_l = master_audio_boost > 0 ? cmp_l : audio_l_int[17:2]; 129 | assign audio_r = master_audio_boost > 0 ? cmp_r : audio_r_int[17:2]; 130 | 131 | endmodule 132 | -------------------------------------------------------------------------------- /target/pocket/core.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) "audio.sv"] 2 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "core_top.v"] 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 VERILOG_FILE [file join $::quartus(qip_path) "linebuffer.v"] 8 | set_global_assignment -name QIP_FILE [file join $::quartus(qip_path) "mf_pllbase.qip"] 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 | ic|mp1|mf_pllbase_inst|altera_pll_i|general[1].gpll~PLL_OUTPUT_COUNTER|divclk \ 13 | ic|mp1|mf_pllbase_inst|altera_pll_i|general[2].gpll~PLL_OUTPUT_COUNTER|divclk } 14 | 15 | set_multicycle_path -from {ic|pce|sdram|*} -to [get_clocks {*|mp1|mf_pllbase_inst|altera_pll_i|*[1].*|divclk}] -start -setup 2 16 | set_multicycle_path -from {ic|pce|*} -to [get_clocks {*|mp1|mf_pllbase_inst|altera_pll_i|*[1].*|divclk}] -start -hold 1 17 | set_multicycle_path -from {ic|pce|*} -to [get_clocks {*|mp1|mf_pllbase_inst|altera_pll_i|*[1].*|divclk}] -start -setup 2 18 | 19 | set_multicycle_path -from {ic|pce|pce_audio|psg_filter|*} -setup 3 20 | set_multicycle_path -from {ic|pce|pce_audio|psg_filter|*} -hold 2 21 | set_multicycle_path -from {ic|pce|pce_audio|adpcm_filter|*} -setup 3 22 | set_multicycle_path -from {ic|pce|pce_audio|adpcm_filter|*} -hold 2 23 | 24 | set_multicycle_path -from {ic|pce|color_mix|*} -to {isco|*} -start -hold 1 25 | set_multicycle_path -from {ic|pce|color_mix|*} -to {isco|*} -start -setup 2 26 | set_multicycle_path -from {ic|pce|color_mix|*} -to {iscc|*} -start -hold 1 27 | set_multicycle_path -from {ic|pce|color_mix|*} -to {iscc|*} -start -setup 2 28 | -------------------------------------------------------------------------------- /target/pocket/data_loader.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 data loader for consuming APF bridge writes and directing them to some storage medium 26 | // 27 | // This takes the 32 bit words from APF, and splits it into four / OUTPUT_WORD_SIZE words (4 separate bytes, or 2 16-bit words). 28 | // You can configure the cycle delay by setting WRITE_MEM_CLOCK_DELAY 29 | module data_loader #( 30 | // Upper 4 bits of address 31 | parameter ADDRESS_MASK_UPPER_4 = 0, 32 | parameter ADDRESS_SIZE = 28, 33 | 34 | // Number of clk_memory cycles to delay each write output 35 | // Min 4. Component will assert this value is within the valid range 36 | // Be aware that APF sends data every ~75 74MHz cycles, so you cannot send data slower than this 37 | parameter WRITE_MEM_CLOCK_DELAY = 4, 38 | 39 | // Number of clk_memory cycles to hold the write_en signal high 40 | // Min 1. Component will assert this value is within the valid range 41 | parameter WRITE_MEM_EN_CYCLE_LENGTH = 1, 42 | 43 | // Word size in number of bytes. Can either be 1 (output 8 bits), or 2 (output 16 bits) 44 | // Component will assert this value is within the valid range 45 | parameter OUTPUT_WORD_SIZE = 1 46 | ) ( 47 | input wire clk_74a, 48 | input wire clk_memory, 49 | 50 | input wire bridge_wr, 51 | input wire bridge_endian_little, 52 | input wire [31:0] bridge_addr, 53 | input wire [31:0] bridge_wr_data, 54 | 55 | // These outputs are synced to the memory clock 56 | output reg write_en = 0, 57 | output reg [ADDRESS_SIZE-1:0] write_addr = 0, 58 | output reg [8 * OUTPUT_WORD_SIZE - 1:0] write_data = 0 59 | ); 60 | 61 | `define MAX(x, y) ((x > y) ? x : y) 62 | 63 | localparam WORD_SIZE = 8 * OUTPUT_WORD_SIZE; 64 | 65 | // Only use the lower 28 bits of the address 66 | localparam FIFO_SIZE = WORD_SIZE + 28; 67 | 68 | wire mem_empty; 69 | 70 | wire [FIFO_SIZE - 1:0] fifo_out; 71 | 72 | reg read_req = 0; 73 | reg write_req = 0; 74 | reg [31:0] shift_data; 75 | reg [27:0] buff_bridge_addr; 76 | 77 | wire [FIFO_SIZE - 1:0] fifo_in = {shift_data[WORD_SIZE-1:0], buff_bridge_addr[27:0]}; 78 | 79 | dcfifo dcfifo_component ( 80 | .data(fifo_in), 81 | .rdclk(clk_memory), 82 | .rdreq(read_req), 83 | .wrclk(clk_74a), 84 | .wrreq(write_req), 85 | .q(fifo_out), 86 | .rdempty(mem_empty) 87 | // .wrempty(), 88 | // .aclr(), 89 | // .eccstatus(), 90 | // .rdfull(), 91 | // .rdusedw(), 92 | // .wrfull(), 93 | // .wrusedw() 94 | ); 95 | defparam dcfifo_component.clocks_are_synchronized = "FALSE", 96 | dcfifo_component.intended_device_family = "Cyclone V", dcfifo_component.lpm_numwords = 4, 97 | dcfifo_component.lpm_showahead = "OFF", dcfifo_component.lpm_type = "dcfifo", 98 | dcfifo_component.lpm_width = FIFO_SIZE, dcfifo_component.lpm_widthu = 2, 99 | dcfifo_component.overflow_checking = "OFF", dcfifo_component.rdsync_delaypipe = 5, 100 | dcfifo_component.underflow_checking = "OFF", dcfifo_component.use_eab = "OFF", 101 | dcfifo_component.wrsync_delaypipe = 5; 102 | 103 | /// APF to Mem clock 104 | 105 | reg prev_bridge_wr = 0; 106 | reg [2:0] write_count = 0; 107 | reg [2:0] write_state = 0; 108 | 109 | localparam WRITE_START = 1; 110 | localparam WRITE_REQ_SHIFT = 2; 111 | 112 | // Receive APF writes and buffer them into the memory clock domain 113 | always @(posedge clk_74a) begin 114 | prev_bridge_wr <= bridge_wr; 115 | 116 | if (~prev_bridge_wr && bridge_wr && bridge_addr[31:28] == ADDRESS_MASK_UPPER_4) begin 117 | // Beginning APF write to core 118 | write_state <= WRITE_REQ_SHIFT; 119 | write_req <= 1; 120 | write_count <= 0; 121 | 122 | shift_data <= bridge_endian_little ? bridge_wr_data : { 123 | bridge_wr_data[7:0], bridge_wr_data[15:8], bridge_wr_data[23:16], bridge_wr_data[31:24] 124 | }; 125 | 126 | buff_bridge_addr <= bridge_addr[27:0]; 127 | end 128 | 129 | case (write_state) 130 | WRITE_START: begin 131 | write_req <= 1; 132 | 133 | write_state <= WRITE_REQ_SHIFT; 134 | end 135 | WRITE_REQ_SHIFT: begin 136 | write_req <= 0; 137 | 138 | // We will be writing again in the next cycle 139 | shift_data <= {8'h0, shift_data[31:WORD_SIZE]}; 140 | buff_bridge_addr <= buff_bridge_addr + OUTPUT_WORD_SIZE; 141 | 142 | write_count <= write_count + 1; 143 | 144 | if (write_count == (4 / OUTPUT_WORD_SIZE) - 1) begin 145 | // Finished write 146 | write_state <= 0; 147 | end else begin 148 | write_state <= WRITE_START; 149 | end 150 | end 151 | endcase 152 | end 153 | 154 | /// Mem clock to core 155 | 156 | reg [5:0] read_state = 0; 157 | 158 | localparam READ_DELAY = 1; 159 | localparam READ_WRITE = 2; 160 | localparam READ_WRITE_EN_CYCLE_OFF = READ_WRITE + WRITE_MEM_EN_CYCLE_LENGTH; 161 | localparam READ_WRITE_END_DEFAULT = WRITE_MEM_CLOCK_DELAY - 1; 162 | // Must use max to prevent READ_WRITE_END from being the same as READ_WRITE_EN_CYCLE_OFF 163 | localparam READ_WRITE_END = 164 | `MAX(READ_WRITE_END_DEFAULT, READ_WRITE_EN_CYCLE_OFF + 1); 165 | localparam HAS_DELAY = READ_WRITE_END_DEFAULT > READ_WRITE_EN_CYCLE_OFF; 166 | 167 | always @(posedge clk_memory) begin 168 | if (read_state != 0) begin 169 | read_state <= read_state + 1; 170 | end else if (~mem_empty) begin 171 | // Start read 172 | read_state <= READ_DELAY; 173 | read_req <= 1; 174 | end 175 | 176 | case (read_state) 177 | READ_DELAY: begin 178 | read_req <= 0; 179 | write_en <= 0; 180 | end 181 | READ_WRITE: begin 182 | // Read data is available 183 | write_en <= 1; 184 | 185 | // Lowest 28 bits are the address 186 | write_addr <= fifo_out[27:0]; 187 | 188 | write_data <= fifo_out[WORD_SIZE+27:28]; 189 | 190 | read_req <= 0; 191 | end 192 | READ_WRITE_EN_CYCLE_OFF: begin 193 | write_en <= 0; 194 | 195 | if (!HAS_DELAY) begin 196 | // No extra delay, immediately go back to start 197 | read_state <= 0; 198 | end 199 | end 200 | READ_WRITE_END: begin 201 | read_state <= 0; 202 | end 203 | endcase 204 | end 205 | 206 | initial begin 207 | // Verify parameters 208 | if (WRITE_MEM_CLOCK_DELAY < 4) begin 209 | $error("WRITE_MEM_CLOCK_DELAY has a minimum value of 4. Received %d", WRITE_MEM_CLOCK_DELAY); 210 | end 211 | 212 | if (WRITE_MEM_EN_CYCLE_LENGTH < 1 || WRITE_MEM_EN_CYCLE_LENGTH >= WRITE_MEM_CLOCK_DELAY - 2) begin 213 | $error( 214 | "WRITE_MEM_EN_CYCLE_LENGTH must be between 1 and %d (inclusive, based off of WRITE_MEM_CLOCK_DELAY). Received %d", 215 | WRITE_MEM_CLOCK_DELAY - 2 - 1, WRITE_MEM_EN_CYCLE_LENGTH); 216 | end 217 | 218 | if (OUTPUT_WORD_SIZE < 1 || OUTPUT_WORD_SIZE > 2) begin 219 | $error("OUTPUT_WORD_SIZE must be 1 or 2. Received %d", OUTPUT_WORD_SIZE); 220 | end 221 | end 222 | 223 | endmodule 224 | -------------------------------------------------------------------------------- /target/pocket/data_unloader.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 data unloader for consuming APF bridge reads, reading from some underlying memory, and supplying that data to APF 26 | // 27 | // This consumes four / OUTPUT_WORD_SIZE words (4 separate bytes, or 2 16-bit words) and sends APF 32 bit words. 28 | // You can configure the cycle delay by setting READ_MEM_CLOCK_DELAY 29 | module data_unloader #( 30 | // Upper 4 bits of address 31 | parameter ADDRESS_MASK_UPPER_4 = 0, 32 | parameter ADDRESS_SIZE = 28, 33 | 34 | // Number of memory clock cycles it takes for a read to complete 35 | parameter READ_MEM_CLOCK_DELAY = 1, 36 | 37 | // Word size in number of bytes. Can either be 1 (input 8 bits), or 2 (input 16 bits) 38 | parameter INPUT_WORD_SIZE = 1 39 | ) ( 40 | input wire clk_74a, 41 | input wire clk_memory, 42 | 43 | input wire bridge_rd, 44 | input wire bridge_endian_little, 45 | input wire [31:0] bridge_addr, 46 | output reg [31:0] bridge_rd_data = 0, 47 | 48 | // These outputs are synced to the memory clock 49 | output reg read_en = 0, 50 | output reg [ADDRESS_SIZE-1:0] read_addr = 0, 51 | input wire [8 * INPUT_WORD_SIZE - 1:0] read_data 52 | ); 53 | 54 | localparam WORD_SIZE = 8 * INPUT_WORD_SIZE; 55 | 56 | // APF address to memory FIFO 57 | reg [27:0] fifo_address_in = 0; 58 | reg address_read_req = 0; 59 | reg address_write_req = 0; 60 | wire address_empty; 61 | 62 | wire [27:0] fifo_address_out; 63 | 64 | dcfifo fifo_address_req ( 65 | .data(fifo_address_in), 66 | .rdclk(clk_memory), 67 | .rdreq(address_read_req), 68 | .wrclk(clk_74a), 69 | .wrreq(address_write_req), 70 | .q(fifo_address_out), 71 | .rdempty(address_empty) 72 | // .wrempty(), 73 | // .aclr(), 74 | // .eccstatus(), 75 | // .rdfull(), 76 | // .rdusedw(), 77 | // .wrfull(), 78 | // .wrusedw() 79 | ); 80 | defparam fifo_address_req.clocks_are_synchronized = "FALSE", 81 | fifo_address_req.intended_device_family = "Cyclone V", fifo_address_req.lpm_numwords = 4, 82 | fifo_address_req.lpm_showahead = "OFF", fifo_address_req.lpm_type = "dcfifo", 83 | fifo_address_req.lpm_width = 28, fifo_address_req.lpm_widthu = 2, 84 | fifo_address_req.overflow_checking = "OFF", fifo_address_req.rdsync_delaypipe = 5, 85 | fifo_address_req.underflow_checking = "OFF", fifo_address_req.use_eab = "OFF", 86 | fifo_address_req.wrsync_delaypipe = 5; 87 | 88 | // Memory output to APF FIFO 89 | reg [WORD_SIZE - 1:0] fifo_data_in = 0; 90 | reg data_read_req = 0; 91 | reg data_write_req = 0; 92 | wire data_empty; 93 | 94 | wire [WORD_SIZE - 1:0] fifo_data_out; 95 | 96 | dcfifo fifo_data_response ( 97 | .data(fifo_data_in), 98 | .rdclk(clk_74a), 99 | .rdreq(data_read_req), 100 | .wrclk(clk_memory), 101 | .wrreq(data_write_req), 102 | .q(fifo_data_out), 103 | .rdempty(data_empty) 104 | // .wrempty(), 105 | // .aclr(), 106 | // .eccstatus(), 107 | // .rdfull(), 108 | // .rdusedw(), 109 | // .wrfull(), 110 | // .wrusedw() 111 | ); 112 | defparam fifo_data_response.clocks_are_synchronized = "FALSE", 113 | fifo_data_response.intended_device_family = "Cyclone V", fifo_data_response.lpm_numwords = 4, 114 | fifo_data_response.lpm_showahead = "OFF", fifo_data_response.lpm_type = "dcfifo", 115 | fifo_data_response.lpm_width = WORD_SIZE, fifo_data_response.lpm_widthu = 2, 116 | fifo_data_response.overflow_checking = "OFF", fifo_data_response.rdsync_delaypipe = 5, 117 | fifo_data_response.underflow_checking = "OFF", fifo_data_response.use_eab = "OFF", 118 | fifo_data_response.wrsync_delaypipe = 5; 119 | 120 | /// APF side 121 | 122 | reg prev_bridge_rd = 0; 123 | reg [2:0] addr_count = 0; 124 | reg [2:0] addr_state = 0; 125 | 126 | localparam ADDR_START = 1; 127 | localparam ADDR_REQ = 2; 128 | 129 | // Receive APF read addresses and buffer them into the memory clock domain 130 | always @(posedge clk_74a) begin 131 | prev_bridge_rd <= bridge_rd; 132 | 133 | if (~prev_bridge_rd && bridge_rd && bridge_addr[31:28] == ADDRESS_MASK_UPPER_4) begin 134 | // Beginning APF read from core 135 | addr_state <= ADDR_REQ; 136 | address_write_req <= 1; 137 | addr_count <= 0; 138 | 139 | fifo_address_in <= bridge_addr[27:0]; 140 | end 141 | 142 | case (addr_state) 143 | ADDR_START: begin 144 | address_write_req <= 1; 145 | 146 | addr_state <= ADDR_REQ; 147 | end 148 | ADDR_REQ: begin 149 | address_write_req <= 0; 150 | 151 | fifo_address_in <= fifo_address_in + INPUT_WORD_SIZE; 152 | 153 | addr_count <= addr_count + 1; 154 | 155 | if (addr_count == (4 / INPUT_WORD_SIZE) - 1) begin 156 | // Finished write 157 | addr_count <= 0; 158 | addr_state <= 0; 159 | end else begin 160 | addr_state <= ADDR_START; 161 | end 162 | end 163 | endcase 164 | end 165 | 166 | reg [2:0] data_send_state = 0; 167 | reg [2:0] apf_data_count = 0; 168 | reg [31:0] apf_bridge_write_data = 0; 169 | 170 | wire [31:0] apf_final_data = {fifo_data_out, apf_bridge_write_data[31-WORD_SIZE:0]}; 171 | 172 | localparam READ_DATA_DELAY = 1; 173 | localparam READ_DATA_WRITE = 2; 174 | 175 | // Receive data from memory and write to APF bridge 176 | always @(posedge clk_74a) begin 177 | if (data_send_state != 0) begin 178 | data_send_state <= data_send_state + 1; 179 | end else if (~data_empty) begin 180 | // Start data read 181 | data_send_state <= READ_DATA_DELAY; 182 | data_read_req <= 1; 183 | 184 | apf_data_count <= 0; 185 | end 186 | 187 | case (data_send_state) 188 | READ_DATA_DELAY: begin 189 | data_read_req <= 0; 190 | 191 | // Shift current APF data 192 | apf_bridge_write_data <= apf_bridge_write_data >> WORD_SIZE; 193 | end 194 | READ_DATA_WRITE: begin 195 | // Data from memory is available 196 | if (apf_data_count == (4 / INPUT_WORD_SIZE) - 1) begin 197 | // We have all of the data we need, send to APF 198 | bridge_rd_data <= bridge_endian_little ? apf_final_data : 199 | {apf_final_data[7:0], apf_final_data[15:8], apf_final_data[23:16], apf_final_data[31:24]}; 200 | 201 | data_send_state <= 0; 202 | end else begin 203 | apf_bridge_write_data <= apf_final_data; 204 | 205 | data_read_req <= 1; 206 | data_send_state <= READ_DATA_DELAY; 207 | 208 | apf_data_count <= apf_data_count + 1; 209 | end 210 | end 211 | endcase 212 | end 213 | 214 | /// Mem side 215 | 216 | reg [5:0] data_read_state = 0; 217 | 218 | localparam READ_ADDRESS_DELAY = 1; 219 | localparam READ_MEM_START = 2; 220 | localparam READ_MEM_COMPLETE = READ_MEM_START + READ_MEM_CLOCK_DELAY; 221 | localparam READ_ADDRESS_END = READ_MEM_COMPLETE + 1; 222 | 223 | always @(posedge clk_memory) begin 224 | if (data_read_state != 0) begin 225 | data_read_state <= data_read_state + 1; 226 | end else if (~address_empty) begin 227 | // Start address read 228 | data_read_state <= READ_ADDRESS_DELAY; 229 | address_read_req <= 1; 230 | end 231 | 232 | case (data_read_state) 233 | READ_ADDRESS_DELAY: begin 234 | address_read_req <= 0; 235 | end 236 | READ_MEM_START: begin 237 | // Address read data is available 238 | read_en <= 1; 239 | 240 | read_addr <= fifo_address_out[ADDRESS_SIZE-1:0]; 241 | end 242 | READ_MEM_COMPLETE: begin 243 | // We have data to send to APF 244 | read_en <= 0; 245 | 246 | data_write_req <= 1; 247 | fifo_data_in <= read_data; 248 | end 249 | READ_ADDRESS_END: begin 250 | data_write_req <= 0; 251 | 252 | data_read_state <= 0; 253 | end 254 | endcase 255 | end 256 | 257 | endmodule 258 | -------------------------------------------------------------------------------- /target/pocket/linebuffer.v: -------------------------------------------------------------------------------- 1 | module linebuffer ( 2 | input wire clk_vid, 3 | 4 | input wire vsync_in, 5 | input wire hsync_in, 6 | 7 | input wire ce_pix, 8 | input wire disable_pix, 9 | input wire [23:0] rgb_in, 10 | 11 | output wire vsync_out, 12 | output wire hsync_out, 13 | 14 | output reg de, 15 | output reg [23:0] rgb_out 16 | ); 17 | 18 | // If 0, outputting bank 0, writing bank 1 19 | // If 1, outputting bank 1, writing bank 0 20 | reg output_bank_select = 0; 21 | 22 | reg bank_read_ack; 23 | reg bank_write; 24 | 25 | wire [23:0] bank0_q; 26 | wire [23:0] bank1_q; 27 | 28 | wire [9:0] bank0_used; 29 | wire [9:0] bank1_used; 30 | 31 | wire bank0_empty; 32 | wire bank1_empty; 33 | 34 | linebuffer_bank bank0 ( 35 | .clk(clk_vid), 36 | 37 | .data(rgb_in), 38 | .read_ack(bank_read_ack && ~output_bank_select), 39 | .write_req(bank_write && output_bank_select), 40 | 41 | .q(bank0_q), 42 | .empty(bank0_empty), 43 | .used(bank0_used) 44 | ); 45 | 46 | linebuffer_bank bank1 ( 47 | .clk(clk_vid), 48 | 49 | .data(rgb_in), 50 | .read_ack(bank_read_ack && output_bank_select), 51 | .write_req(bank_write && ~output_bank_select), 52 | 53 | .q(bank1_q), 54 | .empty(bank1_empty), 55 | .used(bank1_used) 56 | ); 57 | 58 | wire bank_empty = output_bank_select ? bank1_empty : bank0_empty; 59 | wire [23:0] bank_q = output_bank_select ? bank1_q : bank0_q; 60 | wire [9:0] bank_line_width = ~output_bank_select ? bank1_used : bank0_used; 61 | 62 | // Incoming video data 63 | reg prev_hsync_in = 0; 64 | reg prev_vsync_in = 0; 65 | reg prev_disable_pix = 0; 66 | 67 | /// The number of pixels drawn in the last line (latched) 68 | reg [9:0] output_line_width; 69 | /// The number of lines drawn (only content, not empty) 70 | reg [9:0] output_line; 71 | 72 | reg [3:0] enable_delay = 0; 73 | reg [3:0] border_delay = 0; 74 | 75 | reg line_224 = 0; 76 | reg [9:0] expected_line_count = 0; 77 | 78 | always @(posedge clk_vid) begin 79 | prev_hsync_in <= hsync_in; 80 | prev_vsync_in <= vsync_in; 81 | prev_disable_pix <= disable_pix; 82 | 83 | bank_write <= 0; 84 | 85 | if (vsync_in && ~prev_vsync_in) begin 86 | line_224 <= output_line < 231; 87 | expected_line_count <= output_line < 231 ? 224 : 240; 88 | end 89 | 90 | if (hsync_in && ~prev_hsync_in) begin 91 | // Hsync, switch banks 92 | output_bank_select <= ~output_bank_select; 93 | 94 | // Latch width of new output bank 95 | output_line_width <= bank_line_width > 0 ? bank_line_width : output_line_width; 96 | end 97 | 98 | // Handle the weird timing of borders 99 | if (~disable_pix && prev_disable_pix) begin 100 | // Falling edge of border 101 | enable_delay <= 3; 102 | end else if (disable_pix && ~prev_disable_pix) begin 103 | // Rising edge of border 104 | border_delay <= 4; 105 | end else if (ce_pix) begin 106 | if (border_delay > 0) begin 107 | border_delay <= border_delay - 1; 108 | end 109 | 110 | if (~disable_pix || border_delay > 0) begin 111 | if (enable_delay > 0) begin 112 | enable_delay <= enable_delay - 1; 113 | end else begin 114 | // Delay finished, draw pixel 115 | bank_write <= 1; 116 | end 117 | end 118 | end 119 | end 120 | 121 | // Outgoing video data 122 | reg prev_de = 0; 123 | 124 | reg [3:0] hs_delay = 0; 125 | reg [8:0] border_start_offset = 0; 126 | reg [8:0] border_end_offset = 0; 127 | reg line_started = 0; 128 | reg prev_line_started = 0; 129 | 130 | /// Whether or not this line is empty 131 | reg line_empty = 0; 132 | /// The number of "empty" (black) lines drawn 133 | reg [9:0] line_empty_count = 0; 134 | 135 | /// Total lines drawn to the screen (including empty black lines) 136 | reg [9:0] total_rendered_count; 137 | 138 | reg [9:0] expected_line_width; 139 | reg [2:0] slot; 140 | 141 | always @(*) begin 142 | if (output_line_width < 280) begin 143 | expected_line_width <= 10'd256; 144 | slot <= 0; 145 | end else if (output_line_width < 380) begin 146 | expected_line_width <= 10'd360; 147 | slot <= 1; 148 | end else begin 149 | expected_line_width <= 10'd512; 150 | slot <= 2; 151 | end 152 | end 153 | 154 | wire [9:0] width_diff = expected_line_width > output_line_width ? expected_line_width - output_line_width : 0 /* synthesis keep */; 155 | // Divide by 2 and round up 156 | wire [8:0] calculated_border = width_diff[0] ? width_diff[9:1] + 1 : width_diff[9:1] /* synthesis keep */; 157 | 158 | wire [23:0] video_slot_rgb = {7'b0, slot, line_224, 10'b0, 3'b0}; 159 | 160 | always @(posedge clk_vid) begin 161 | bank_read_ack <= 0; 162 | de <= 0; 163 | rgb_out <= 0; 164 | 165 | if (hs_delay > 0) begin 166 | hs_delay <= hs_delay - 1; 167 | end 168 | 169 | if (~prev_vsync_in && vsync_in) begin 170 | // Reset line_started 171 | line_started <= 0; 172 | 173 | prev_line_started <= 0; 174 | total_rendered_count <= 0; 175 | output_line <= 0; 176 | end else if (~prev_hsync_in && hsync_in) begin 177 | // HSync went high. Delay by 6 vid cycles to prevent overlapping with VSync 178 | hs_delay <= 15; 179 | line_started <= 0; 180 | 181 | prev_line_started <= line_started; 182 | 183 | border_start_offset <= 0; 184 | border_end_offset <= 0; 185 | 186 | line_empty <= 0; 187 | line_empty_count <= 0; 188 | end 189 | 190 | if (hs_delay == 1 && bank_empty && prev_line_started && total_rendered_count < expected_line_count) begin 191 | // Right before draw, and no pixels, and previous line drew. This line will be empty 192 | line_empty <= 1; 193 | total_rendered_count <= total_rendered_count + 1; 194 | end 195 | 196 | if (line_empty && line_empty_count < expected_line_width) begin 197 | // Empty line, draw pixels up until expected_line_width 198 | de <= 1; 199 | rgb_out <= 0; 200 | line_started <= 1; 201 | // Make sure we don't draw extra border pixels 202 | border_end_offset <= calculated_border; 203 | 204 | line_empty_count <= line_empty_count + 1; 205 | end else if (calculated_border == 0 && hs_delay == 1 && ~bank_empty) begin 206 | // If no border, set read ack high one pixel early 207 | bank_read_ack <= 1; 208 | total_rendered_count <= total_rendered_count + 1; 209 | output_line <= output_line + 1; 210 | end else if (hs_delay == 0 && ~bank_empty) begin 211 | // Write out video data 212 | de <= 1; 213 | 214 | // Track whether we've written pixels 215 | line_started <= 1; 216 | 217 | if (border_start_offset < calculated_border) begin 218 | // Draw black bars to center video 219 | if (border_start_offset == calculated_border - 1) begin 220 | // Set high one pixel early 221 | bank_read_ack <= 1; 222 | total_rendered_count <= total_rendered_count + 1; 223 | output_line <= output_line + 1; 224 | end 225 | border_start_offset <= border_start_offset + 1; 226 | 227 | rgb_out <= 0; 228 | end else begin 229 | bank_read_ack <= 1; 230 | 231 | rgb_out <= bank_q; 232 | end 233 | end else if (line_started && bank_empty && border_end_offset < calculated_border) begin 234 | // We've exhausted the line buffer, write black until we're done 235 | border_end_offset <= border_end_offset + 1; 236 | 237 | de <= 1; 238 | rgb_out <= 0; 239 | end else if (prev_de) begin 240 | // Falling edge of de 241 | rgb_out <= video_slot_rgb; 242 | end 243 | 244 | prev_de <= de; 245 | end 246 | 247 | // Hsync delayed by 6 cycles 248 | assign hsync_out = hs_delay == 15 - 6; 249 | assign vsync_out = vsync_in && ~prev_vsync_in; 250 | 251 | endmodule 252 | 253 | module linebuffer_bank ( 254 | input wire clk, 255 | 256 | input wire [23:0] data, 257 | input wire read_ack, 258 | input wire write_req, 259 | 260 | output wire [23:0] q, 261 | output wire empty, 262 | output wire [9:0] used 263 | ); 264 | scfifo bank ( 265 | .clock(clk), 266 | .data(data), 267 | .rdreq(read_ack), 268 | .wrreq(write_req), 269 | .empty(empty), 270 | // .full(sub_wire1), 271 | .q(q), 272 | .usedw(used) 273 | ); 274 | defparam bank.add_ram_output_register = "ON", bank.intended_device_family = "Cyclone V", 275 | bank.lpm_numwords = 1024, bank.lpm_showahead = "ON", bank.lpm_type = "scfifo", 276 | bank.lpm_width = 24, bank.lpm_widthu = 10, bank.overflow_checking = "ON", 277 | bank.underflow_checking = "ON", bank.use_eab = "ON"; 278 | 279 | endmodule 280 | -------------------------------------------------------------------------------- /target/pocket/mf_pllbase.ppf: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /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 'locked' 20 | output wire locked 21 | ); 22 | 23 | altera_pll #( 24 | .fractional_vco_multiplier("true"), 25 | .reference_clock_frequency("74.25 MHz"), 26 | .operation_mode("normal"), 27 | .number_of_clocks(3), 28 | .output_clock_frequency0("85.909090 MHz"), 29 | .phase_shift0("0 ps"), 30 | .duty_cycle0(50), 31 | .output_clock_frequency1("42.954545 MHz"), 32 | .phase_shift1("0 ps"), 33 | .duty_cycle1(50), 34 | .output_clock_frequency2("42.954545 MHz"), 35 | .phase_shift2("5820 ps"), 36 | .duty_cycle2(50), 37 | .output_clock_frequency3("0 MHz"), 38 | .phase_shift3("0 ps"), 39 | .duty_cycle3(50), 40 | .output_clock_frequency4("0 MHz"), 41 | .phase_shift4("0 ps"), 42 | .duty_cycle4(50), 43 | .output_clock_frequency5("0 MHz"), 44 | .phase_shift5("0 ps"), 45 | .duty_cycle5(50), 46 | .output_clock_frequency6("0 MHz"), 47 | .phase_shift6("0 ps"), 48 | .duty_cycle6(50), 49 | .output_clock_frequency7("0 MHz"), 50 | .phase_shift7("0 ps"), 51 | .duty_cycle7(50), 52 | .output_clock_frequency8("0 MHz"), 53 | .phase_shift8("0 ps"), 54 | .duty_cycle8(50), 55 | .output_clock_frequency9("0 MHz"), 56 | .phase_shift9("0 ps"), 57 | .duty_cycle9(50), 58 | .output_clock_frequency10("0 MHz"), 59 | .phase_shift10("0 ps"), 60 | .duty_cycle10(50), 61 | .output_clock_frequency11("0 MHz"), 62 | .phase_shift11("0 ps"), 63 | .duty_cycle11(50), 64 | .output_clock_frequency12("0 MHz"), 65 | .phase_shift12("0 ps"), 66 | .duty_cycle12(50), 67 | .output_clock_frequency13("0 MHz"), 68 | .phase_shift13("0 ps"), 69 | .duty_cycle13(50), 70 | .output_clock_frequency14("0 MHz"), 71 | .phase_shift14("0 ps"), 72 | .duty_cycle14(50), 73 | .output_clock_frequency15("0 MHz"), 74 | .phase_shift15("0 ps"), 75 | .duty_cycle15(50), 76 | .output_clock_frequency16("0 MHz"), 77 | .phase_shift16("0 ps"), 78 | .duty_cycle16(50), 79 | .output_clock_frequency17("0 MHz"), 80 | .phase_shift17("0 ps"), 81 | .duty_cycle17(50), 82 | .pll_type("General"), 83 | .pll_subtype("General") 84 | ) altera_pll_i ( 85 | .rst (rst), 86 | .outclk ({outclk_2, outclk_1, outclk_0}), 87 | .locked (locked), 88 | .fboutclk ( ), 89 | .fbclk (1'b0), 90 | .refclk (refclk) 91 | ); 92 | endmodule 93 | 94 | -------------------------------------------------------------------------------- /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 = 15, 28 | parameter SIGNED_INPUT = 0 29 | ) ( 30 | input wire clk_74a, 31 | input wire clk_audio, 32 | 33 | // Left and right audio channels. Can be in an arbitrary clock domain 34 | input wire [CHANNEL_WIDTH - 1:0] audio_l, 35 | input wire [CHANNEL_WIDTH - 1:0] audio_r, 36 | 37 | output reg audio_mclk, 38 | output reg audio_lrck, 39 | output reg audio_dac 40 | ); 41 | // 42 | // audio i2s generator 43 | // 44 | 45 | reg audgen_nextsamp; 46 | 47 | // generate MCLK = 12.288mhz with fractional accumulator 48 | reg [21:0] audgen_accum = 0; 49 | localparam [20:0] CYCLE_48KHZ = 21'd122880 * 2; 50 | always @(posedge clk_74a) begin 51 | audgen_accum <= audgen_accum + CYCLE_48KHZ; 52 | if (audgen_accum >= 21'd742500) begin 53 | audio_mclk <= ~audio_mclk; 54 | audgen_accum <= audgen_accum - 21'd742500 + CYCLE_48KHZ; 55 | end 56 | end 57 | 58 | // generate SCLK = 3.072mhz by dividing MCLK by 4 59 | reg [1:0] aud_mclk_divider; 60 | reg prev_audio_mclk; 61 | wire audgen_sclk = aud_mclk_divider[1] /* synthesis keep*/; 62 | 63 | always @(posedge clk_74a) begin 64 | if (audio_mclk && ~prev_audio_mclk) begin 65 | aud_mclk_divider <= aud_mclk_divider + 1'b1; 66 | end 67 | 68 | prev_audio_mclk <= audio_mclk; 69 | end 70 | 71 | // shift out audio data as I2S 72 | // 32 total bits per channel, but only 16 active bits at the start and then 16 dummy bits 73 | // 74 | // synchronize audio samples coming from the core 75 | 76 | localparam CHANNEL_LEFT_HIGH = SIGNED_INPUT ? 16 : 15; 77 | localparam CHANNEL_RIGHT_HIGH = 16 + CHANNEL_LEFT_HIGH; 78 | 79 | // Width of channel with signed component 80 | localparam SIGNED_CHANNEL_WIDTH = SIGNED_INPUT ? CHANNEL_WIDTH : CHANNEL_WIDTH + 1; 81 | 82 | wire [31:0] audgen_sampdata; 83 | 84 | assign audgen_sampdata[CHANNEL_LEFT_HIGH-1:CHANNEL_LEFT_HIGH-CHANNEL_WIDTH] = audio_l; 85 | assign audgen_sampdata[CHANNEL_RIGHT_HIGH-1:CHANNEL_RIGHT_HIGH-CHANNEL_WIDTH] = audio_r; 86 | 87 | generate 88 | if (!SIGNED_INPUT) begin 89 | // If not signed, make sure high bit is 0 90 | assign audgen_sampdata[31] = 0; 91 | assign audgen_sampdata[15] = 0; 92 | end 93 | endgenerate 94 | 95 | generate 96 | if (15 - SIGNED_CHANNEL_WIDTH > 0) begin 97 | assign audgen_sampdata[31-SIGNED_CHANNEL_WIDTH:16] = 0; 98 | assign audgen_sampdata[15-SIGNED_CHANNEL_WIDTH:0] = 0; 99 | end 100 | endgenerate 101 | 102 | sync_fifo #( 103 | .WIDTH(32) 104 | ) sync_fifo ( 105 | .clk_write(clk_audio), 106 | .clk_read (clk_74a), 107 | 108 | .write_en(write_en), 109 | .data_in (audgen_sampdata), 110 | .data_out(audgen_sampdata_s) 111 | ); 112 | 113 | reg write_en = 0; 114 | reg [CHANNEL_WIDTH - 1:0] prev_left; 115 | reg [CHANNEL_WIDTH - 1:0] prev_right; 116 | 117 | // Mark write when necessary 118 | always @(posedge clk_audio) begin 119 | prev_left <= audio_l; 120 | prev_right <= audio_r; 121 | 122 | write_en <= 0; 123 | 124 | if (audio_l != prev_left || audio_r != prev_right) begin 125 | write_en <= 1; 126 | end 127 | end 128 | 129 | wire [31:0] audgen_sampdata_s; 130 | 131 | reg [31:0] audgen_sampshift; 132 | reg [4:0] audio_lrck_cnt; 133 | reg prev_audgen_sclk; 134 | always @(posedge clk_74a) begin 135 | if (prev_audgen_sclk && ~audgen_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_s; 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 | prev_audgen_sclk <= audgen_sclk; 156 | end 157 | 158 | initial begin 159 | // Verify parameters 160 | if (CHANNEL_WIDTH > 16) begin 161 | $error("CHANNEL_WIDTH must be <= 16. Received %d", CHANNEL_WIDTH); 162 | end 163 | 164 | if (SIGNED_INPUT != 0 && SIGNED_INPUT != 1) begin 165 | $error("SIGNED_INPUT must be 0 or 1. Received %d", SIGNED_INPUT); 166 | end 167 | 168 | if (CHANNEL_WIDTH == 16 && SIGNED_INPUT == 0) begin 169 | $error("Cannot have CHANNEL_WIDTH of 16 and an unsigned input"); 170 | end 171 | end 172 | endmodule 173 | -------------------------------------------------------------------------------- /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_in, 35 | output reg [WIDTH - 1:0] data_out = 0 36 | ); 37 | 38 | reg read_req = 0; 39 | wire empty; 40 | 41 | wire [WIDTH - 1:0] fifo_out; 42 | 43 | dcfifo dcfifo_component ( 44 | .data(data_in), 45 | .rdclk(clk_read), 46 | .rdreq(read_req), 47 | .wrclk(clk_write), 48 | .wrreq(write_en), 49 | .q(fifo_out), 50 | .rdempty(empty), 51 | .aclr(), 52 | .eccstatus(), 53 | .rdfull(), 54 | .rdusedw(), 55 | .wrempty(), 56 | .wrfull(), 57 | .wrusedw() 58 | ); 59 | defparam dcfifo_component.intended_device_family = "Cyclone V", dcfifo_component.lpm_numwords = 4, 60 | dcfifo_component.lpm_showahead = "OFF", dcfifo_component.lpm_type = "dcfifo", 61 | dcfifo_component.lpm_width = 32, dcfifo_component.lpm_widthu = 2, 62 | dcfifo_component.overflow_checking = "ON", dcfifo_component.rdsync_delaypipe = 5, 63 | dcfifo_component.underflow_checking = "ON", dcfifo_component.use_eab = "ON", 64 | dcfifo_component.wrsync_delaypipe = 5; 65 | 66 | reg [1:0] read_state = 0; 67 | 68 | localparam READ_DELAY = 1; 69 | localparam READ_WRITE = 2; 70 | 71 | always @(posedge clk_read) begin 72 | read_req <= 0; 73 | 74 | if (~empty) begin 75 | read_state <= READ_DELAY; 76 | read_req <= 1; 77 | end 78 | 79 | case (read_state) 80 | READ_DELAY: begin 81 | read_state <= READ_WRITE; 82 | end 83 | READ_WRITE: begin 84 | read_state <= 0; 85 | 86 | data_out <= fifo_out; 87 | end 88 | endcase 89 | end 90 | 91 | endmodule 92 | --------------------------------------------------------------------------------