├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── test.yml ├── .gitignore ├── .mill-version ├── .vscode └── settings.json ├── Makefile ├── README.md ├── arcadia ├── src │ ├── PlayerIO.scala │ ├── RGB.scala │ ├── Util.scala │ ├── Vec2.scala │ ├── clk │ │ └── ClockDivider.scala │ ├── cpu │ │ ├── m68k │ │ │ ├── CPU.scala │ │ │ └── MemMap.scala │ │ └── z80 │ │ │ ├── CPU.scala │ │ │ ├── MemMap.scala │ │ │ └── RegisterFile.scala │ ├── gfx │ │ ├── VideoIO.scala │ │ └── VideoTiming.scala │ ├── mem │ │ ├── AsyncMemIO.scala │ │ ├── BurstMemIO.scala │ │ ├── Crossing.scala │ │ ├── DualClockFIFO.scala │ │ ├── DualPortRam.scala │ │ ├── EEPROM.scala │ │ ├── Line.scala │ │ ├── MemIO.scala │ │ ├── RegisterFile.scala │ │ ├── SerialIO.scala │ │ ├── SinglePortRam.scala │ │ ├── SinglePortRom.scala │ │ ├── TrueDualPortRam.scala │ │ ├── arbiter │ │ │ ├── AsyncMemArbiter.scala │ │ │ └── BurstMemArbiter.scala │ │ ├── buffer │ │ │ ├── BurstBuffer.scala │ │ │ └── Config.scala │ │ ├── cache │ │ │ ├── Address.scala │ │ │ ├── Cache.scala │ │ │ ├── Config.scala │ │ │ ├── Entry.scala │ │ │ └── ReadCache.scala │ │ ├── ddr │ │ │ ├── Config.scala │ │ │ └── DDR.scala │ │ ├── dma │ │ │ ├── BurstReadDMA.scala │ │ │ ├── BurstWriteDMA.scala │ │ │ └── Config.scala │ │ ├── request │ │ │ ├── ReadRequest.scala │ │ │ ├── ReadWriteRequest.scala │ │ │ ├── Request.scala │ │ │ └── WriteRequest.scala │ │ └── sdram │ │ │ ├── Address.scala │ │ │ ├── Config.scala │ │ │ ├── SDRAM.scala │ │ │ └── SDRAMIO.scala │ ├── mister │ │ ├── FrameBufferCtrlIO.scala │ │ ├── IOCTL.scala │ │ ├── LEDIO.scala │ │ └── OptionsIO.scala │ ├── pocket │ │ ├── Bridge.scala │ │ ├── BridgeIO.scala │ │ └── OptionsIO.scala │ ├── snd │ │ ├── Audio.scala │ │ ├── AudioMixer.scala │ │ ├── DAC.scala │ │ ├── JT5205.scala │ │ ├── JTOPL.scala │ │ ├── NMK112.scala │ │ ├── OKIM6295.scala │ │ ├── YMZ280B.scala │ │ └── ymz │ │ │ ├── ADPCM.scala │ │ │ ├── AudioPipeline.scala │ │ │ ├── AudioPipelineState.scala │ │ │ ├── ChannelController.scala │ │ │ ├── ChannelReg.scala │ │ │ ├── ChannelState.scala │ │ │ ├── LERP.scala │ │ │ └── UtilReg.scala │ └── util │ │ ├── Counter.scala │ │ ├── Format.scala │ │ └── PISO.scala └── test │ └── src │ ├── RGBTest.scala │ ├── UtilTest.scala │ ├── Vec2Test.scala │ ├── clk │ └── ClockDividerTest.scala │ ├── cpu │ └── MemMapTest.scala │ ├── dma │ ├── BurstReadDMATest.scala │ └── BurstWriteDMATest.scala │ ├── mem │ ├── DDRTest.scala │ ├── EEPROMTest.scala │ ├── RegisterFileTest.scala │ ├── arbiter │ │ ├── AsyncMemArbiterTest.scala │ │ └── BurstMemArbiterTest.scala │ ├── buffer │ │ └── BurstBufferTest.scala │ ├── cache │ │ ├── CacheTest.scala │ │ └── ReadCacheTest.scala │ └── sdram │ │ └── SDRAMTest.scala │ ├── mister │ └── IOCTLTest.scala │ ├── snd │ ├── AudioMixerTest.scala │ ├── NMK112Test.scala │ ├── YMZ280BTest.scala │ └── ymz │ │ ├── ADPCMTest.scala │ │ ├── AudioPipelineTest.scala │ │ ├── ChannelControllerTest.scala │ │ └── LERPTest.scala │ └── util │ ├── CounterTest.scala │ ├── FormatTest.scala │ └── PISOTest.scala ├── bin ├── mill ├── mill.bat └── reverse ├── build.sc ├── dist ├── Assets │ └── tecmo │ │ ├── common │ │ └── .gitkeep │ │ └── nullobject.tecmo │ │ ├── Gemini Wing (Japan).json │ │ ├── Gemini Wing (World).json │ │ ├── Rygar (Japan).json │ │ ├── Rygar (US set 1).json │ │ ├── Rygar (US set 2).json │ │ ├── Silkworm (Japan).json │ │ └── Silkworm (World).json ├── Cores │ └── nullobject.tecmo │ │ ├── audio.json │ │ ├── core.json │ │ ├── data.json │ │ ├── icon.bin │ │ ├── info.txt │ │ ├── input.json │ │ ├── interact.json │ │ ├── variants.json │ │ └── video.json └── Platforms │ ├── _images │ └── tecmo.bin │ └── tecmo.json ├── mra ├── Gemini Wing (Japan).mra ├── Gemini Wing (World).mra ├── Rygar (Japan).mra ├── Rygar (US set 1).mra ├── Rygar (US set 2).mra ├── Silkworm (Japan).mra └── Silkworm (World).mra ├── quartus ├── .gitignore ├── ap_core.qpf ├── ap_core.qsf ├── apf │ ├── apf.qip │ ├── apf_constraints.sdc │ ├── apf_top.v │ ├── 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 ├── arcadia │ ├── arcadia.qip │ ├── dual_clock_fifo.vhd │ ├── dual_port_ram.vhd │ ├── reset_ctrl.v │ ├── single_port_ram.vhd │ ├── single_port_rom.vhd │ └── true_dual_port_ram.vhd ├── core │ ├── core_bridge_cmd.v │ ├── core_constraints.sdc │ ├── core_top.v │ ├── i2s.v │ ├── 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 │ └── stp1.stp ├── jt5205 │ ├── jt5205.qip │ ├── jt5205.v │ ├── jt5205_adpcm.v │ ├── jt5205_interpol2x.v │ └── jt5205_timing.v ├── jtopl │ ├── LICENSE │ ├── jtopl.qip │ ├── jtopl.v │ ├── jtopl_acc.v │ ├── jtopl_csr.v │ ├── jtopl_div.v │ ├── jtopl_eg.v │ ├── jtopl_eg_cnt.v │ ├── jtopl_eg_comb.v │ ├── jtopl_eg_ctrl.v │ ├── jtopl_eg_final.v │ ├── jtopl_eg_pure.v │ ├── jtopl_eg_step.v │ ├── jtopl_exprom.v │ ├── jtopl_lfo.v │ ├── jtopl_logsin.v │ ├── jtopl_mmr.v │ ├── jtopl_noise.v │ ├── jtopl_op.v │ ├── jtopl_pg.v │ ├── jtopl_pg_comb.v │ ├── jtopl_pg_inc.v │ ├── jtopl_pg_rhy.v │ ├── jtopl_pg_sum.v │ ├── jtopl_pm.v │ ├── jtopl_reg.v │ ├── jtopl_reg_ch.v │ ├── jtopl_sh.v │ ├── jtopl_sh_rst.v │ ├── jtopl_single_acc.v │ ├── jtopl_slot_cnt.v │ └── jtopl_timers.v ├── roms │ ├── alpha.mif │ ├── char.mif │ ├── cpu1.mif │ ├── cpu2.mif │ ├── fg.mif │ └── sprite.mif └── t80 │ ├── T80.vhd │ ├── T80_ALU.vhd │ ├── T80_MCode.vhd │ ├── T80_Reg.vhd │ ├── T80pa.vhd │ ├── T80s.vhd │ └── t80.qip └── tecmo ├── src ├── ChiselApp.scala ├── Config.scala ├── GameConfig.scala ├── MemSys.scala ├── Tecmo.scala ├── gfx │ ├── ColorMixer.scala │ ├── DebugLayer.scala │ ├── FrameBuffer.scala │ ├── GPU.scala │ ├── GPUMemIO.scala │ ├── LayerCtrlIO.scala │ ├── LayerProcessor.scala │ ├── PaletteEntry.scala │ ├── Sprite.scala │ ├── SpriteBlitter.scala │ ├── SpriteCtrlIO.scala │ ├── SpriteProcessor.scala │ └── Tile.scala ├── main │ └── Main.scala ├── package.scala └── snd │ ├── PCMCounter.scala │ ├── Sound.scala │ └── SoundCtrlIO.scala └── test └── src ├── gpu ├── ColorMixerTest.scala ├── DebugLayerTest.scala ├── LayerProcessorTest.scala ├── SpriteBlitterTest.scala └── SpriteProcessorTest.scala └── snd └── PCMCounterTest.scala /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | indent_size = 2 5 | indent_style = space 6 | insert_final_newline = true 7 | max_line_length = 100 8 | tab_width = 2 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: nullobject 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set up JDK 11 18 | uses: actions/setup-java@v3 19 | with: 20 | java-version: '11' 21 | distribution: 'temurin' 22 | - name: Units tests 23 | run: ./bin/mill _.test 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bloop 2 | .bsp 3 | .idea 4 | .metals 5 | .vscode/* 6 | !.vscode/settings.json 7 | !.vscode/tasks.json 8 | !.vscode/launch.json 9 | !.vscode/extensions.json 10 | dist/**/*.rbf_r 11 | dist/**/*.zip 12 | mra/*.rom 13 | out 14 | test_run_dir 15 | -------------------------------------------------------------------------------- /.mill-version: -------------------------------------------------------------------------------- 1 | 0.10.5 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "makefile.extensionOutputFolder": "./.vscode", 3 | "files.watcherExclude": { 4 | "**/target": true 5 | } 6 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REVISION_NAME = ap_core 2 | CORE_NAME = nullobject.tecmo 3 | CORE_VERSION = $(shell jq ".core.metadata.version" -r dist/Cores/$(CORE_NAME)/core.json) 4 | 5 | .PHONY: build clean copy mra program release 6 | 7 | build: 8 | bin/mill tecmo.run 9 | cd quartus; quartus_sh --flow compile $(REVISION_NAME) 10 | bin/reverse quartus/output_files/$(REVISION_NAME).rbf dist/Cores/$(CORE_NAME)/bitstream.rbf_r 11 | 12 | mra: 13 | cd mra; mra -z ~/mame/roms *.mra 14 | 15 | copy: 16 | rsync -avh --progress --exclude \*.zip dist/ /media/josh/2470-BED0 && umount /media/josh/2470-BED0 17 | 18 | program: 19 | cd quartus; quartus_pgm -m jtag -c USB-Blaster -o "p;output_files/$(REVISION_NAME).sof@1" 20 | 21 | release: 22 | cd dist; zip -rv pocket-tecmo-$(CORE_VERSION).zip * -x \*.gitkeep \*.zip 23 | 24 | clean: 25 | rm -rf dist/Cores/$(CORE_NAME)/out quartus/core/Tecmo.* quartus/db quartus/incremental_db quartus/output_files test_run_dir 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tecmo 2 | 3 | [![Test](https://github.com/nullobject/openfpga-tecmo/actions/workflows/test.yml/badge.svg)](https://github.com/nullobject/openfpga-tecmo/actions/workflows/test.yml) 4 | 5 | ## Install 6 | 7 | * Download and unzip the latest release 8 | * Copy the release files to your Pocket 9 | * Unzip ROMs and copy them to the `Assets/tecmo/common` directory (e.g. `Assets/tecmo/common/rygar`) 10 | 11 | Disclaimer: please don't ask me where to get ROMs, they are not provided with this project. 12 | 13 | ## Legal 14 | 15 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 16 | -------------------------------------------------------------------------------- /arcadia/src/PlayerIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia 34 | 35 | import chisel3._ 36 | 37 | /** A bundle that contains the player input signals. */ 38 | class PlayerIO extends Bundle { 39 | /** Up */ 40 | val up = Bool() 41 | /** Down */ 42 | val down = Bool() 43 | /** Left */ 44 | val left = Bool() 45 | /** Right */ 46 | val right = Bool() 47 | /** Buttons */ 48 | val buttons = Bits(PlayerIO.BUTTON_COUNT.W) 49 | /** Start */ 50 | val start = Bool() 51 | /** Coin */ 52 | val coin = Bool() 53 | } 54 | 55 | object PlayerIO { 56 | /** The number of buttons */ 57 | val BUTTON_COUNT = 4 58 | 59 | def apply() = new PlayerIO 60 | } 61 | -------------------------------------------------------------------------------- /arcadia/src/RGB.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia 34 | 35 | import chisel3._ 36 | import chisel3.internal.firrtl.Width 37 | 38 | /** 39 | * Represents a RGB color. 40 | * 41 | * @param width The width of the channels. 42 | */ 43 | sealed class RGB private[arcadia](width: Width) extends Bundle { 44 | /** Red channel */ 45 | val r = UInt(width) 46 | /** Green channel */ 47 | val g = UInt(width) 48 | /** Blue channel */ 49 | val b = UInt(width) 50 | 51 | /** Bitwise AND operator. */ 52 | def &(that: RGB): RGB = { 53 | RGB(this.r & that.r, this.g & that.g, this.b & that.b) 54 | } 55 | 56 | /** Bitwise OR operator. */ 57 | def |(that: RGB): RGB = { 58 | RGB(this.r | that.r, this.g | that.g, this.b | that.b) 59 | } 60 | } 61 | 62 | object RGB { 63 | /** 64 | * Creates a RGB bundle. 65 | * 66 | * @param width The channel width. 67 | * @return A bundle. 68 | */ 69 | def apply(width: Width): RGB = new RGB(width) 70 | 71 | /** 72 | * Constructs a RGB color from red, green, and blue values. 73 | * 74 | * @param r The red channel value. 75 | * @param g The green channel value. 76 | * @param b The blue channel value. 77 | * @return A RGB color. 78 | */ 79 | def apply(r: Bits, g: Bits, b: Bits): RGB = { 80 | val width = Seq(r.getWidth, g.getWidth, b.getWidth).max 81 | val rgb = Wire(new RGB(width.W)) 82 | rgb.r := r 83 | rgb.g := g 84 | rgb.b := b 85 | rgb 86 | } 87 | 88 | /** 89 | * Constructs a RGB color with all channels set to the same value. 90 | * 91 | * @param value The channel value. 92 | * @return A RGB color. 93 | */ 94 | def apply(value: Bits): RGB = { 95 | val rgb = Wire(new RGB(value.getWidth.W)) 96 | rgb.r := value 97 | rgb.g := value 98 | rgb.b := value 99 | rgb 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /arcadia/src/clk/ClockDivider.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.clk 34 | 35 | import chisel3._ 36 | 37 | private class ClockDivider(d: Double, width: Int) { 38 | val n = ((1 << width) / d).round 39 | val next = Wire(UInt((width + 1).W)) 40 | val counter = RegNext(next.tail(1)) 41 | val clockEnable = RegNext(next.head(1).asBool) 42 | next := counter +& n.U 43 | 44 | // Debug 45 | if (sys.env.get("DEBUG").contains("1")) { 46 | printf(p"ClockDivider(counter: 0x${Hexadecimal(counter)}, cen: $clockEnable )\n") 47 | } 48 | } 49 | 50 | object ClockDivider { 51 | /** 52 | * Constructs a fractional clock divider. 53 | * 54 | * @param d The divisor. 55 | * @param width The width of the counter. 56 | * @return A clock enable signal. 57 | */ 58 | def apply(d: Double, width: Int = 16): Bool = { 59 | val clockDivider = new ClockDivider(d, width) 60 | clockDivider.clockEnable 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /arcadia/src/cpu/z80/RegisterFile.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.cpu.z80 34 | 35 | import chisel3._ 36 | 37 | /** Represents the registers in the CPU. */ 38 | class RegisterFile extends Bundle { 39 | val iy = Bits(16.W) 40 | val hl_ = Bits(16.W) 41 | val de_ = Bits(16.W) 42 | val bc_ = Bits(16.W) 43 | val ix = Bits(16.W) 44 | val hl = Bits(16.W) 45 | val de = Bits(16.W) 46 | val bc = Bits(16.W) 47 | val pc = UInt(16.W) 48 | val sp = UInt(16.W) 49 | val r = Bits(8.W) 50 | val i = Bits(8.W) 51 | val f_ = Bits(8.W) 52 | val a_ = Bits(8.W) 53 | val f = Bits(8.W) 54 | val a = Bits(8.W) 55 | } 56 | -------------------------------------------------------------------------------- /arcadia/src/gfx/VideoIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.gfx 34 | 35 | import arcadia.UVec2 36 | import chisel3._ 37 | 38 | /** An interface that contains analog video timing signals. */ 39 | class VideoIO extends Bundle { 40 | /** Asserted when the video clock is enabled */ 41 | val clockEnable = Bool() 42 | /** Asserted when the video position is in the display region */ 43 | val displayEnable = Bool() 44 | /** Video position */ 45 | val pos = UVec2(9.W) 46 | /** Horizontal sync */ 47 | val hSync = Bool() 48 | /** Vertical sync */ 49 | val vSync = Bool() 50 | /** Horizontal blank */ 51 | val hBlank = Bool() 52 | /** Vertical blank */ 53 | val vBlank = Bool() 54 | } 55 | 56 | object VideoIO { 57 | def apply() = new VideoIO 58 | } 59 | -------------------------------------------------------------------------------- /arcadia/src/mem/DualClockFIFO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem 34 | 35 | import chisel3._ 36 | import chisel3.util._ 37 | 38 | /** 39 | * A dual-clock FIFO. 40 | * 41 | * The write port runs in the default clock domain, and the read port runs in the read clock domain. 42 | * 43 | * @param t The input data type. 44 | * @param depth The depth of the FIFO. 45 | */ 46 | class DualClockFIFO[T <: Data](t: T, depth: Int) extends Module { 47 | val io = IO(new Bundle { 48 | /** Read clock */ 49 | val readClock = Input(Clock()) 50 | /** Read port */ 51 | val deq = Flipped(DeqIO(t)) 52 | /** Write port */ 53 | val enq = Flipped(EnqIO(t)) 54 | }) 55 | 56 | class WrappedDualClockFIFO extends BlackBox(Map( 57 | "DATA_WIDTH" -> t.getWidth, 58 | "DEPTH" -> depth 59 | )) { 60 | val io = IO(new Bundle { 61 | val data = Input(Bits(t.getWidth.W)) 62 | val rdclk = Input(Clock()) 63 | val rdreq = Input(Bool()) 64 | val wrclk = Input(Clock()) 65 | val wrreq = Input(Bool()) 66 | val q = Output(Bits(t.getWidth.W)) 67 | val rdempty = Output(Bool()) 68 | val wrfull = Output(Bool()) 69 | }) 70 | 71 | override def desiredName = "dual_clock_fifo" 72 | } 73 | 74 | val fifo = Module(new WrappedDualClockFIFO) 75 | 76 | // Write port 77 | fifo.io.wrclk := clock 78 | fifo.io.wrreq := io.enq.fire 79 | io.enq.ready := !fifo.io.wrfull // allow writing while the FIFO isn't full 80 | fifo.io.data := io.enq.bits.asUInt 81 | 82 | // Read port 83 | fifo.io.rdclk := io.readClock 84 | fifo.io.rdreq := io.deq.fire 85 | io.deq.valid := !fifo.io.rdempty // allow reading while the FIFO isn't empty 86 | io.deq.bits := fifo.io.q.asTypeOf(t) 87 | } 88 | -------------------------------------------------------------------------------- /arcadia/src/mem/RegisterFile.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem 34 | 35 | import chisel3._ 36 | import chisel3.util._ 37 | 38 | /** 39 | * A set of registers, presented as a synchronous read/write memory device. 40 | * 41 | * @param dataWidth The width of the data bus. 42 | * @param depth The number of registers in the register file. 43 | */ 44 | class RegisterFile(dataWidth: Int, depth: Int) extends Module { 45 | /** The width of the address bus. */ 46 | val ADDR_WIDTH = log2Up(depth) 47 | 48 | val io = IO(new Bundle { 49 | /** Memory port */ 50 | val mem = Flipped(MemIO(ADDR_WIDTH, dataWidth)) 51 | /** Registers port */ 52 | val regs: Vec[Bits] = Output(Vec(depth, Bits(dataWidth.W))) 53 | }) 54 | 55 | // Data registers 56 | val regs = Reg(Vec(depth, Bits(dataWidth.W))) 57 | 58 | // Alias the current data register 59 | val data = regs(io.mem.addr) 60 | 61 | // Split data register into a vector of bytes 62 | val bytes = data.asTypeOf(Vec(io.mem.maskWidth, Bits(8.W))) 63 | 64 | // Write masked bytes to the data register 65 | 0.until(io.mem.maskWidth).foreach { n => 66 | when(io.mem.wr && io.mem.mask(n)) { bytes(n) := io.mem.din((n + 1) * 8 - 1, n * 8) } 67 | } 68 | 69 | // Concatenate the bytes and update the data register 70 | data := bytes.asUInt 71 | 72 | // Outputs 73 | io.mem.dout := data 74 | io.regs := regs 75 | } 76 | -------------------------------------------------------------------------------- /arcadia/src/mem/SerialIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem 34 | 35 | import chisel3._ 36 | 37 | /** An interface for communicating with a serial I/O device. */ 38 | class SerialIO extends Bundle { 39 | /** Chip select */ 40 | val cs = Input(Bool()) 41 | /** Serial clock */ 42 | val sck = Input(Bool()) 43 | /** Serial data in */ 44 | val sdi = Input(Bool()) 45 | /** Serial data out */ 46 | val sdo = Output(Bool()) 47 | } 48 | -------------------------------------------------------------------------------- /arcadia/src/mem/SinglePortRam.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem 34 | 35 | import chisel3._ 36 | 37 | /** 38 | * This module wraps an external single-port RAM module. 39 | * 40 | * @param addrWidth The width of the address bus. 41 | * @param dataWidth The width of the data bus. 42 | * @param depth The optional memory depth (in words). 43 | * @param maskEnable A boolean value indicating whether byte masking is enabled. 44 | */ 45 | class SinglePortRam(addrWidth: Int, 46 | dataWidth: Int, 47 | depth: Option[Int] = None, 48 | maskEnable: Boolean = false) extends Module { 49 | val io = IO(Flipped(MemIO(addrWidth, dataWidth))) 50 | 51 | private val depth_ = depth.getOrElse(0) 52 | 53 | class WrappedSinglePortRam extends BlackBox(Map( 54 | "ADDR_WIDTH" -> addrWidth, 55 | "DATA_WIDTH" -> dataWidth, 56 | "DEPTH" -> depth_, 57 | "MASK_ENABLE" -> (if (maskEnable) "TRUE" else "FALSE"), 58 | )) { 59 | val io = IO(new Bundle { 60 | val clk = Input(Clock()) 61 | val rd = Input(Bool()) 62 | val wr = Input(Bool()) 63 | val addr = Input(UInt(addrWidth.W)) 64 | val mask = Input(Bits((dataWidth / 8).W)) 65 | val din = Input(Bits(dataWidth.W)) 66 | val dout = Output(Bits(dataWidth.W)) 67 | }) 68 | 69 | override def desiredName = "single_port_ram" 70 | } 71 | 72 | val ram = Module(new WrappedSinglePortRam) 73 | ram.io.clk := clock 74 | ram.io.rd := io.rd 75 | ram.io.wr := io.wr 76 | ram.io.addr := io.addr 77 | ram.io.mask := io.mask 78 | ram.io.din := io.din 79 | io.dout := ram.io.dout 80 | } 81 | -------------------------------------------------------------------------------- /arcadia/src/mem/SinglePortRom.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem 34 | 35 | import chisel3._ 36 | 37 | /** 38 | * This module wraps an external single-port ROM module. 39 | * 40 | * @param addrWidth The width of the address bus. 41 | * @param dataWidth The width of the data bus. 42 | */ 43 | class SinglePortRom(addrWidth: Int, dataWidth: Int, depth: Int, initFile: String) extends Module { 44 | val io = IO(Flipped(ReadMemIO(addrWidth, dataWidth))) 45 | 46 | class WrappedSinglePortRam extends BlackBox( 47 | Map( 48 | "ADDR_WIDTH" -> addrWidth, 49 | "DATA_WIDTH" -> dataWidth, 50 | "DEPTH" -> depth, 51 | "INIT_FILE" -> initFile 52 | ) 53 | ) { 54 | val io = IO(new Bundle { 55 | val clk = Input(Clock()) 56 | val rd = Input(Bool()) 57 | val addr = Input(UInt(addrWidth.W)) 58 | val dout = Output(Bits(dataWidth.W)) 59 | }) 60 | 61 | override def desiredName = "single_port_rom" 62 | } 63 | 64 | val rom = Module(new WrappedSinglePortRam) 65 | rom.io.clk := clock 66 | rom.io.rd := io.rd 67 | rom.io.addr := io.addr 68 | io.dout := rom.io.dout 69 | } 70 | -------------------------------------------------------------------------------- /arcadia/src/mem/buffer/Config.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.buffer 34 | 35 | import arcadia.mem.LineConfig 36 | 37 | /** 38 | * Represents a buffer configuration. 39 | * 40 | * @param inAddrWidth The width of the input address bus. 41 | * @param inDataWidth The width of the input data bus. 42 | * @param outAddrWidth The width of the output address bus. 43 | * @param outDataWidth The width of the output data bus. 44 | * @param burstLength The number of words to transfer during a burst. 45 | */ 46 | case class Config(override val inAddrWidth: Int, 47 | override val inDataWidth: Int, 48 | override val outAddrWidth: Int, 49 | override val outDataWidth: Int, 50 | burstLength: Int = 1) extends LineConfig(inAddrWidth, inDataWidth, outAddrWidth, outDataWidth, burstLength) 51 | -------------------------------------------------------------------------------- /arcadia/src/mem/cache/Address.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.cache 34 | 35 | import chisel3._ 36 | import chisel3.util.log2Ceil 37 | 38 | /** 39 | * Represents the location of a word stored in the cache. 40 | * 41 | * @param config The cache configuration. 42 | */ 43 | class Address(private val config: Config) extends Bundle { 44 | /** The most significant bits of the address */ 45 | val tag = UInt(config.tagWidth.W) 46 | /** The index of the cache entry within the cache */ 47 | val index = UInt(config.indexWidth.W) 48 | /** The offset of the word within the cache line */ 49 | val offset = UInt(config.offsetWidth.W) 50 | } 51 | 52 | object Address { 53 | /** 54 | * Creates a cache address. 55 | * 56 | * @param config The cache configuration. 57 | * @param tag The tag value. 58 | * @param index The index value. 59 | * @param offset The offset value. 60 | * @return A cache address. 61 | */ 62 | def apply(config: Config, tag: UInt, index: UInt, offset: UInt): Address = { 63 | val wire = Wire(new Address(config)) 64 | wire.tag := tag 65 | wire.index := index 66 | wire.offset := offset 67 | wire 68 | } 69 | 70 | /** 71 | * Creates a cache address from a byte address. 72 | * 73 | * @param config The cache configuration. 74 | * @param addr The byte address. 75 | * @return A cache address. 76 | */ 77 | def apply(config: Config, addr: UInt): Address = 78 | (addr >> log2Ceil(config.outBytes)).asTypeOf(new Address(config)) 79 | } 80 | -------------------------------------------------------------------------------- /arcadia/src/mem/cache/Config.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.cache 34 | 35 | import arcadia.mem.LineConfig 36 | import chisel3.util.log2Ceil 37 | 38 | /** 39 | * Represents a cache configuration. 40 | * 41 | * @param inAddrWidth The width of the input address bus. 42 | * @param inDataWidth The width of the input data bus. 43 | * @param outAddrWidth The width of the output address bus. 44 | * @param outDataWidth The width of the output data bus. 45 | * @param lineWidth The number of words in a cache line. 46 | * @param depth The number of entries in the cache. 47 | * @param wrapping A boolean indicating whether burst wrapping should be enabled for the 48 | * cache. When a wrapping burst reaches a burst boundary, the address wraps 49 | * back to the previous burst boundary. 50 | */ 51 | case class Config(override val inAddrWidth: Int, 52 | override val inDataWidth: Int, 53 | override val outAddrWidth: Int, 54 | override val outDataWidth: Int, 55 | override val lineWidth: Int, 56 | depth: Int, 57 | wrapping: Boolean = false) extends LineConfig(inAddrWidth, inDataWidth, outAddrWidth, outDataWidth, lineWidth) { 58 | /** The width of a cache address index */ 59 | val indexWidth = log2Ceil(depth) 60 | /** The width of a cache address offset */ 61 | val offsetWidth = log2Ceil(lineWidth) 62 | /** The width of a cache tag */ 63 | val tagWidth = inAddrWidth - indexWidth - offsetWidth 64 | } 65 | -------------------------------------------------------------------------------- /arcadia/src/mem/ddr/Config.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.ddr 34 | 35 | import arcadia.mem.BusConfig 36 | 37 | /** 38 | * Represents the DDR memory configuration. 39 | * 40 | * @param addrWidth The width of the address bus. 41 | * @param dataWidth The width of the data bus. 42 | */ 43 | case class Config(addrWidth: Int = 32, dataWidth: Int = 64) extends BusConfig 44 | -------------------------------------------------------------------------------- /arcadia/src/mem/dma/Config.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.dma 34 | 35 | import arcadia.mem.BusConfig 36 | 37 | /** 38 | * Represents a DMA configuration. 39 | * 40 | * @param addrWidth The width of the DMA address bus. 41 | * @param dataWidth The width of the DMA data bus. 42 | * @param depth The total number of words to transfer. 43 | * @param burstLength The number of words to transfer during a burst. 44 | */ 45 | case class Config(addrWidth: Int = 32, 46 | dataWidth: Int = 64, 47 | depth: Int, 48 | burstLength: Int) extends BusConfig { 49 | /** The width of the DMA data bus in bytes. */ 50 | val byteWidth = dataWidth / 8 51 | /** The number of bursts. */ 52 | val numBursts = depth / burstLength 53 | } 54 | -------------------------------------------------------------------------------- /arcadia/src/mem/request/ReadRequest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.request 34 | 35 | import chisel3._ 36 | 37 | /** Represents a memory read request. */ 38 | class ReadRequest[S <: Data](s: S) extends Bundle { 39 | /** Read enable */ 40 | val rd = Output(Bool()) 41 | /** Address bus */ 42 | val addr = Output(s) 43 | } 44 | 45 | object ReadRequest { 46 | /** 47 | * Creates a read request. 48 | * 49 | * @param rd Read enable. 50 | * @param addr The memory address. 51 | */ 52 | def apply[S <: Data](rd: Bool, addr: S): ReadRequest[S] = { 53 | val req = Wire(new ReadRequest(chiselTypeOf(addr))) 54 | req.rd := rd 55 | req.addr := addr 56 | req 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /arcadia/src/mem/request/ReadWriteRequest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.request 34 | 35 | import chisel3._ 36 | 37 | /** Represents a memory request. */ 38 | class ReadWriteRequest[S <: Data, T <: Data](s: S, t: T) extends Bundle { 39 | /** Read enable */ 40 | val rd = Output(Bool()) 41 | /** Write enable */ 42 | val wr = Output(Bool()) 43 | /** Address bus */ 44 | val addr = Output(s) 45 | /** Data bus */ 46 | val din = Output(t) 47 | /** Byte mask */ 48 | val mask = Output(UInt((t.getWidth / 8).W)) 49 | } 50 | 51 | object ReadWriteRequest { 52 | /** 53 | * Creates a read-write request. 54 | * 55 | * @param rd Read enable. 56 | * @param wr Write enable. 57 | * @param addr The memory address. 58 | * @param din The data value. 59 | * @param mask The byte mask. 60 | */ 61 | def apply[S <: Data, T <: Data](rd: Bool, wr: Bool, addr: S, din: T, mask: UInt): ReadWriteRequest[S, T] = { 62 | val req = Wire(new ReadWriteRequest(chiselTypeOf(addr), chiselTypeOf(din))) 63 | req.rd := rd 64 | req.wr := wr 65 | req.addr := addr 66 | req.din := din 67 | req.mask := mask 68 | req 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /arcadia/src/mem/request/Request.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.request 34 | 35 | import chisel3._ 36 | 37 | /** Represents a memory request. */ 38 | class Request[S <: Data, T <: Data](s: S, t: T) extends Bundle { 39 | /** Read enable */ 40 | val rd = Output(Bool()) 41 | /** Write enable */ 42 | val wr = Output(Bool()) 43 | /** Address bus */ 44 | val addr = Output(s) 45 | /** Data bus */ 46 | val din = Output(t) 47 | /** Byte mask */ 48 | val mask = Output(UInt((t.getWidth / 8).W)) 49 | } 50 | 51 | object Request { 52 | /** 53 | * Creates a read-write memory request. 54 | * 55 | * @param rd Read enable. 56 | * @param wr Write enable. 57 | * @param addr The memory address. 58 | * @param din The data value. 59 | * @param mask The byte mask. 60 | */ 61 | def apply[S <: Data, T <: Data](rd: Bool, wr: Bool, addr: S, din: T, mask: UInt): Request[S, T] = { 62 | val req = Wire(new Request(chiselTypeOf(addr), chiselTypeOf(din))) 63 | req.rd := rd 64 | req.wr := wr 65 | req.addr := addr 66 | req.din := din 67 | req.mask := mask 68 | req 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /arcadia/src/mem/request/WriteRequest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.request 34 | 35 | import arcadia.mem.WriteMemIO 36 | import chisel3._ 37 | 38 | /** Represents a memory write request. */ 39 | class WriteRequest[S <: Data, T <: Data](s: S, t: T) extends Bundle { 40 | /** Write enable */ 41 | val wr = Output(Bool()) 42 | /** Address bus */ 43 | val addr = Output(s) 44 | /** Data bus */ 45 | val din = Output(t) 46 | /** Byte mask */ 47 | val mask = Output(UInt((t.getWidth / 8).W)) 48 | } 49 | 50 | object WriteRequest { 51 | /** 52 | * Creates a write request. 53 | * 54 | * @param wr Write enable. 55 | * @param addr The memory address. 56 | * @param din The data value. 57 | * @param mask The byte mask. 58 | */ 59 | def apply[S <: Data, T <: Data](wr: Bool, addr: S, din: T, mask: UInt): WriteRequest[S, T] = { 60 | val req = Wire(new WriteRequest(chiselTypeOf(addr), chiselTypeOf(din))) 61 | req.wr := wr 62 | req.addr := addr 63 | req.din := din 64 | req.mask := mask 65 | req 66 | } 67 | 68 | /** 69 | * Creates a write request from a write interface. 70 | * 71 | * @param mem The write interface. 72 | * @return A request bundle. 73 | */ 74 | def apply(mem: WriteMemIO): WriteRequest[UInt, Bits] = { 75 | val req = Wire(new WriteRequest[UInt, Bits](UInt(mem.addrWidth.W), Bits(mem.dataWidth.W))) 76 | req.wr := mem.wr 77 | req.addr := mem.addr 78 | req.din := mem.din 79 | req.mask := mem.mask 80 | req 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /arcadia/src/mem/sdram/Address.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.sdram 34 | 35 | import chisel3._ 36 | import chisel3.util.log2Ceil 37 | 38 | /** Represents the address of a word stored in SDRAM. */ 39 | class Address(private val config: Config) extends Bundle { 40 | /** The bank address */ 41 | val bank = UInt(config.bankWidth.W) 42 | /** The row address */ 43 | val row = UInt(config.rowWidth.W) 44 | /** The column address */ 45 | val col = UInt(config.colWidth.W) 46 | } 47 | 48 | object Address { 49 | /** 50 | * Converts a byte address to a SDRAM address. 51 | * 52 | * @param config The SDRAM configuration. 53 | * @param addr The memory address. 54 | */ 55 | def fromByteAddress(config: Config, addr: UInt) = { 56 | val n = log2Ceil(config.dataWidth / 8) 57 | (addr >> n).asTypeOf(new Address(config)) 58 | } 59 | } -------------------------------------------------------------------------------- /arcadia/src/mem/sdram/SDRAMIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem.sdram 34 | 35 | import chisel3._ 36 | 37 | /** 38 | * An interface for controlling a SDRAM device. 39 | * 40 | * @param config The SDRAM configuration. 41 | */ 42 | class SDRAMIO(config: Config) extends Bundle { 43 | /** Clock enable */ 44 | val cke = Output(Bool()) 45 | /** Chip select (active-low) */ 46 | val cs_n = Output(Bool()) 47 | /** Row address strobe (active-low) */ 48 | val ras_n = Output(Bool()) 49 | /** Column address strobe (active-low) */ 50 | val cas_n = Output(Bool()) 51 | /** Write enable (active-low) */ 52 | val we_n = Output(Bool()) 53 | /** Output enable (active-low) */ 54 | val oe_n = Output(Bool()) 55 | /** Bank bus */ 56 | val bank = Output(UInt(config.bankWidth.W)) 57 | /** Address bus */ 58 | val addr = Output(UInt(config.rowWidth.W)) 59 | /** Data input bus */ 60 | val din = Output(Bits(config.dataWidth.W)) 61 | /** Data output bus */ 62 | val dout = Input(Bits(config.dataWidth.W)) 63 | } 64 | 65 | object SDRAMIO { 66 | def apply(config: Config) = new SDRAMIO(config) 67 | } 68 | -------------------------------------------------------------------------------- /arcadia/src/mister/LEDIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mister 34 | 35 | import chisel3._ 36 | 37 | /** An interface that contains the LED outputs. */ 38 | class LEDIO extends Bundle { 39 | /** Power LED */ 40 | val power = Output(Bool()) 41 | /** Disk LED */ 42 | val disk = Output(Bool()) 43 | /** User LED */ 44 | val user = Output(Bool()) 45 | } 46 | 47 | object LEDIO { 48 | def apply() = new LEDIO 49 | } 50 | -------------------------------------------------------------------------------- /arcadia/src/mister/OptionsIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mister 34 | 35 | import arcadia.SVec2 36 | import chisel3._ 37 | 38 | /** An interface that contains the user options. */ 39 | class OptionsIO extends Bundle { 40 | /** CRT offset */ 41 | val offset = Output(SVec2(OptionsIO.SCREEN_OFFSET_WIDTH.W)) 42 | /** Rotate the HDMI output 90 degrees */ 43 | val rotate = Output(Bool()) 44 | /** Flip the video output */ 45 | val flip = Output(Bool()) 46 | // /** Video compatibility (60Hz) mode */ 47 | // val compatibility = Output(Bool()) 48 | /** Service mode */ 49 | val service = Output(Bool()) 50 | /** Layer enable */ 51 | val layer = Output(Vec(3, Bool())) 52 | /** Sprite enable */ 53 | val sprite = Output(Bool()) 54 | /** Frame buffer enable flags */ 55 | // val frameBufferEnable = Output(new Bundle { 56 | // /** Enable the sprite frame buffer */ 57 | // val sprite = Output(Bool()) 58 | // /** Enable the system frame buffer */ 59 | // val system = Output(Bool()) 60 | // }) 61 | /** Game index */ 62 | val gameIndex = Output(UInt(OptionsIO.GAME_INDEX_WIDTH.W)) 63 | } 64 | 65 | object OptionsIO { 66 | /** The width of the screen offset value */ 67 | val SCREEN_OFFSET_WIDTH = 4 68 | /** The width of the game index */ 69 | val GAME_INDEX_WIDTH = 4 70 | 71 | def apply() = new OptionsIO 72 | } 73 | -------------------------------------------------------------------------------- /arcadia/src/pocket/BridgeIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.pocket 34 | 35 | import arcadia.mem._ 36 | import chisel3._ 37 | import chisel3.util._ 38 | 39 | /** A flow control interface used to download data into the core. */ 40 | class BridgeIO extends Bundle { 41 | /** Write enable */ 42 | val wr = Input(Bool()) 43 | /** Address bus */ 44 | val addr = Input(UInt(BridgeIO.ADDR_WIDTH.W)) 45 | /** Output data bus */ 46 | val dout = Input(Bits(BridgeIO.DATA_WIDTH.W)) 47 | /** Pause flag */ 48 | val pause = Input(Bool()) // FIXME: remove from bridge 49 | /** Done flag */ 50 | val done = Input(Bool()) // FIXME: remove from bridge 51 | 52 | /** Converts the bridge to an asynchronous write-only memory interface. */ 53 | def rom: WriteMemIO = { 54 | val mem = Wire(WriteMemIO(BridgeIO.ADDR_WIDTH, BridgeIO.DATA_WIDTH)) 55 | mem.wr := wr 56 | mem.addr := addr 57 | mem.mask := Fill(mem.maskWidth, 1.U) 58 | mem.din := dout 59 | mem 60 | } 61 | } 62 | 63 | object BridgeIO { 64 | /** The width of the address bus */ 65 | val ADDR_WIDTH = 32 66 | /** The width of the data bus */ 67 | val DATA_WIDTH = 32 68 | 69 | def apply() = new BridgeIO 70 | } 71 | -------------------------------------------------------------------------------- /arcadia/src/pocket/OptionsIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadida.pocket 34 | 35 | import chisel3._ 36 | 37 | /** An interface that contains the user options. */ 38 | class OptionsIO extends Bundle { 39 | /** Game index */ 40 | val gameIndex = UInt(32.W) 41 | /** Scaler mode */ 42 | val scalerMode = UInt(3.W) 43 | /** Enable debugging */ 44 | val debug = Bool() 45 | /** Layer enable */ 46 | val layer = Vec(3, Bool()) 47 | /** Sprite enable */ 48 | val sprite = Bool() 49 | /** Flip video output */ 50 | val flip = Bool() 51 | /** FM enable */ 52 | val fm = Bool() 53 | /** PCM enable */ 54 | val pcm = Bool() 55 | } 56 | 57 | object OptionsIO { 58 | def apply() = new OptionsIO 59 | } 60 | -------------------------------------------------------------------------------- /arcadia/src/snd/Audio.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd 34 | 35 | import arcadia.Util 36 | import chisel3._ 37 | import chisel3.internal.firrtl.Width 38 | 39 | /** Represents a stereo pair of audio samples. */ 40 | sealed class Audio private[arcadia](width: Width) extends Bundle { 41 | /** Left channel data */ 42 | val left = SInt(width) 43 | /** Right channel data */ 44 | val right = SInt(width) 45 | 46 | /** 47 | * Adds the given audio sample. 48 | * 49 | * @param that The audio sample. 50 | */ 51 | def +(that: Audio): Audio = Audio(this.left + that.left, this.right + that.right) 52 | 53 | /** 54 | * Clamps the sample between two given values. 55 | * 56 | * @param a The minimum value. 57 | * @param b The maximum value. 58 | */ 59 | def clamp(a: Int, b: Int): Audio = Audio(Util.clamp(left, a, b), Util.clamp(right, a, b)) 60 | } 61 | 62 | object Audio { 63 | /** 64 | * Creates an audio bundle. 65 | * 66 | * @param width The channel width. 67 | * @return An audio bundle. 68 | */ 69 | def apply(width: Width) = new Audio(width) 70 | 71 | /** 72 | * Creates an audio sample from the left and right channel values. 73 | * 74 | * @param left The left channel value. 75 | * @param right The right channel value. 76 | * @return An audio sample. 77 | */ 78 | def apply(left: SInt, right: SInt): Audio = { 79 | val sample = Wire(new Audio(left.getWidth.W)) 80 | sample.left := left 81 | sample.right := right 82 | sample 83 | } 84 | 85 | 86 | /** 87 | * Creates an audio sample with zero left and right channel values. 88 | * 89 | * @param width The channel width. 90 | * @return An audio sample. 91 | */ 92 | def zero(width: Width) = Audio(0.S(width), 0.S(width)) 93 | } 94 | -------------------------------------------------------------------------------- /arcadia/src/snd/DAC.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd 34 | 35 | import chisel3._ 36 | import chisel3.util._ 37 | 38 | /** A sigma-delta digital to analogue converter (DAC) */ 39 | class DAC(width: Int = 16) extends Module { 40 | val io = IO(new Bundle { 41 | /** Sample value */ 42 | val sample = Input(SInt(width.W)) 43 | /** Asserted when the signal is valid */ 44 | val valid = Input(Bool()) 45 | /** Output value */ 46 | val q = Output(Bits(1.W)) 47 | }) 48 | 49 | // Registers 50 | val accumulatorReg = Reg(UInt((width + 1).W)) 51 | val sampleReg = RegEnable(io.sample, 0.S, io.valid) 52 | 53 | // Flip sample bits 54 | val sample = ~sampleReg(15) ## sampleReg(14, 0) 55 | 56 | // Add the sample to the accumulator 57 | accumulatorReg := accumulatorReg(width - 1, 0) +& sample 58 | 59 | // Output 60 | io.q := accumulatorReg(width) 61 | } 62 | -------------------------------------------------------------------------------- /arcadia/src/snd/JT5205.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd 34 | 35 | import arcadia.clk.ClockDivider 36 | import chisel3._ 37 | import chisel3.util._ 38 | 39 | /** 40 | * The 5205 is a ADPCM sound chip. 41 | * 42 | * @param clockFreq The system clock frequency (Hz). 43 | * @param sampleFreq The sample clock frequency (Hz). 44 | * @note This module wraps jotego's JT5205 implementation. 45 | * @see https://github.com/jotego/jt5205 46 | */ 47 | class JT5205(clockFreq: Double, sampleFreq: Double) extends Module { 48 | val io = IO(new Bundle { 49 | /** Data input port */ 50 | val din = Input(Bits(4.W)) 51 | /** Audio output port */ 52 | val audio = ValidIO(SInt(12.W)) 53 | /** Sample clock */ 54 | val vclk = Output(Bool()) 55 | }) 56 | 57 | class JT5205_ extends BlackBox { 58 | val io = IO(new Bundle { 59 | val rst = Input(Bool()) 60 | val clk = Input(Bool()) 61 | val cen = Input(Bool()) 62 | val sel = Input(Bits(2.W)) 63 | val din = Input(Bits(4.W)) 64 | val sound = Output(SInt(12.W)) 65 | val sample = Output(Bool()) 66 | val vclk_o = Output(Bool()) 67 | }) 68 | 69 | override def desiredName = "jt5205" 70 | } 71 | 72 | val m = Module(new JT5205_) 73 | m.io.clk := clock.asBool 74 | m.io.rst := reset.asBool 75 | m.io.cen := ClockDivider(clockFreq / sampleFreq) 76 | m.io.sel := "b10".U // 8 kHz 77 | m.io.din := io.din 78 | io.audio.valid := m.io.sample 79 | io.audio.bits := m.io.sound 80 | io.vclk := m.io.vclk_o 81 | } 82 | -------------------------------------------------------------------------------- /arcadia/src/snd/JTOPL.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd 34 | 35 | import arcadia.clk.ClockDivider 36 | import arcadia.mem._ 37 | import chisel3._ 38 | import chisel3.util._ 39 | 40 | /** 41 | * The OPL is a FM sound synthesizer. 42 | * 43 | * @param clockFreq The system clock frequency (Hz). 44 | * @param sampleFreq The sample clock frequency (Hz). 45 | * @note This module wraps jotego's JTOPL implementation. 46 | * @see https://github.com/jotego/jtopl 47 | */ 48 | class JTOPL(clockFreq: Double, sampleFreq: Double) extends Module { 49 | val io = IO(new Bundle { 50 | /** CPU port */ 51 | val cpu = Flipped(MemIO(1, 8)) 52 | /** IRQ */ 53 | val irq = Output(Bool()) 54 | /** Audio output port */ 55 | val audio = ValidIO(SInt(16.W)) 56 | }) 57 | 58 | class JTOPL_ extends BlackBox { 59 | val io = IO(new Bundle { 60 | val rst = Input(Bool()) 61 | val clk = Input(Bool()) 62 | val cen = Input(Bool()) 63 | val din = Input(Bits(8.W)) 64 | val addr = Input(Bool()) 65 | val cs_n = Input(Bool()) 66 | val wr_n = Input(Bool()) 67 | val dout = Output(Bits(8.W)) 68 | val irq_n = Output(Bool()) 69 | val snd = Output(SInt(16.W)) 70 | val sample = Output(Bool()) 71 | }) 72 | 73 | override def desiredName = "jtopl" 74 | } 75 | 76 | val m = Module(new JTOPL_) 77 | m.io.rst := reset.asBool 78 | m.io.clk := clock.asBool 79 | m.io.cen := ClockDivider(clockFreq / sampleFreq) 80 | m.io.cs_n := false.B 81 | m.io.wr_n := !io.cpu.wr 82 | m.io.addr := io.cpu.addr(0) 83 | m.io.din := io.cpu.din 84 | io.cpu.dout := m.io.dout 85 | io.irq := !m.io.irq_n 86 | io.audio.valid := m.io.sample 87 | io.audio.bits := m.io.snd 88 | } 89 | -------------------------------------------------------------------------------- /arcadia/src/snd/ymz/ADPCM.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd.ymz 34 | 35 | import arcadia.Util 36 | import chisel3._ 37 | 38 | /** 39 | * Decodes adaptive differential pulse-code modulation (ADPCM) samples. 40 | * 41 | * @param sampleWidth The width of the sample words. 42 | * @param dataWidth The width of the ADPCM data. 43 | * @see https://en.wikipedia.org/wiki/Adaptive_differential_pulse-code_modulation 44 | */ 45 | class ADPCM(sampleWidth: Int = 16, dataWidth: Int = 4) extends Module { 46 | /** Calculate the step size for the current ADPCM value */ 47 | val STEP_LUT = VecInit(230.S, 230.S, 230.S, 230.S, 307.S, 409.S, 512.S, 614.S) 48 | 49 | /** Calculate the delta for the current sample value */ 50 | val DELTA_LUT = VecInit.tabulate(16) { n => 51 | val value = (n & 7) * 2 + 1 52 | if (n >= 8) (-value).S else value.S 53 | } 54 | 55 | val io = IO(new Bundle { 56 | /** ADPCM data */ 57 | val data = Input(Bits(dataWidth.W)) 58 | /** Input port */ 59 | val in = Input(new Bundle { 60 | /** ADPCM step value */ 61 | val step = SInt(sampleWidth.W) 62 | /** ADPCM sample value */ 63 | val sample = SInt(sampleWidth.W) 64 | }) 65 | /** Output port */ 66 | val out = Output(new Bundle { 67 | /** ADPCM step value */ 68 | val step = SInt(sampleWidth.W) 69 | /** ADPCM sample value */ 70 | val sample = SInt(sampleWidth.W) 71 | }) 72 | }) 73 | 74 | // Calculate step value 75 | val step = ((io.in.step * STEP_LUT(io.data)) >> 8).asSInt 76 | io.out.step := Util.clamp(step, 127, 24576) 77 | 78 | // Calculate sample value 79 | val delta = ((io.in.step * DELTA_LUT(io.data)) >> 3).asSInt 80 | io.out.sample := Util.clamp(io.in.sample +& delta, -32768, 32767) 81 | } 82 | -------------------------------------------------------------------------------- /arcadia/src/snd/ymz/AudioPipelineState.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd.ymz 34 | 35 | import arcadia.snd.YMZ280BConfig 36 | import chisel3._ 37 | 38 | /** Represents the state of an audio pipeline. */ 39 | class AudioPipelineState(private val config: YMZ280BConfig) extends Bundle { 40 | /** Sample values */ 41 | val samples = Vec(2, SInt(config.sampleWidth.W)) 42 | /** Asserted when a new sample is required */ 43 | val underflow = Bool() 44 | /** ADPCM step value */ 45 | val adpcmStep = SInt(config.sampleWidth.W) 46 | /** Interpolation index */ 47 | val lerpIndex = UInt(config.lerpIndexWidth.W) 48 | /** Asserted when the loop step and sample values are cached */ 49 | val loopEnable = Bool() 50 | /** Cached loop ADPCM step value */ 51 | val loopStep = SInt(config.sampleWidth.W) 52 | /** Cached loop sample value */ 53 | val loopSample = SInt(config.sampleWidth.W) 54 | 55 | /** Updates the ADPCM state with the given step and sample values. */ 56 | def adpcm(step: SInt, sample: SInt) = { 57 | adpcmStep := step 58 | samples := VecInit(samples(1), sample) 59 | } 60 | 61 | /** Updates the interpolation index with the given pitch value. */ 62 | def interpolate(pitch: UInt) = { 63 | val index = lerpIndex + pitch + 1.U 64 | underflow := index.head(1) 65 | lerpIndex := index.tail(1) 66 | } 67 | } 68 | 69 | object AudioPipelineState { 70 | /** Returns the default audio pipeline state. */ 71 | def default(config: YMZ280BConfig): AudioPipelineState = { 72 | val state = Wire(new AudioPipelineState(config)) 73 | state.samples := VecInit(0.S, 0.S) 74 | state.adpcmStep := 127.S 75 | state.lerpIndex := 0.U 76 | state.underflow := true.B 77 | state.loopEnable := false.B 78 | state.loopStep := 0.S 79 | state.loopSample := 0.S 80 | state 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /arcadia/src/snd/ymz/LERP.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd.ymz 34 | 35 | import chisel3._ 36 | 37 | /** 38 | * Interpolates sample values. 39 | * 40 | * @param sampleWidth The width of the sample words. 41 | * @param indexWidth The width of the interpolation index. 42 | */ 43 | class LERP(sampleWidth: Int = 16, indexWidth: Int = 9) extends Module { 44 | val io = IO(new Bundle { 45 | /** Input sample values */ 46 | val samples = Input(Vec(2, SInt(sampleWidth.W))) 47 | /** Interpolation index */ 48 | val index = Input(UInt(indexWidth.W)) 49 | /** Output sample value */ 50 | val out = Output(SInt(sampleWidth.W)) 51 | }) 52 | 53 | // Calculate interpolated sample value 54 | val slope = io.samples(1) -& io.samples(0) 55 | val offset = io.index * slope 56 | io.out := offset(sampleWidth + indexWidth - 2, indexWidth - 1).asSInt + io.samples(0) 57 | } 58 | -------------------------------------------------------------------------------- /arcadia/src/snd/ymz/UtilReg.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd.ymz 34 | 35 | import chisel3._ 36 | import chisel3.util._ 37 | 38 | /** Represents the utility register. */ 39 | class UtilReg extends Bundle { 40 | /** IRQ mask */ 41 | val irqMask = Bits(8.W) 42 | /** Flags */ 43 | val flags = new Bundle { 44 | /** Key on enable */ 45 | val keyOnEnable = Bool() 46 | /** Memory enable */ 47 | val memEnable = Bool() 48 | /** IRQ enable */ 49 | val irqEnable = Bool() 50 | } 51 | } 52 | 53 | object UtilReg { 54 | /** 55 | * Decodes a utility register from the given register file. 56 | * 57 | * @param registerFile The register file. 58 | */ 59 | def fromRegisterFile(registerFile: Vec[UInt]): UtilReg = { 60 | Cat( 61 | registerFile(0xfe), // IRQ mask 62 | registerFile(0xff)(7), // key on enable 63 | registerFile(0xff)(6), // memory enable 64 | registerFile(0xff)(4), // IRQ enable 65 | ).asTypeOf(new UtilReg) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /arcadia/src/util/PISO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.util 34 | 35 | import chisel3._ 36 | 37 | /** 38 | * A PISO (parallel-in, serial-out) used to buffer data. 39 | * 40 | * @param n The number of elements. 41 | * @param t The input data type. 42 | * @param threshold The threshold at which the almost empty signal will be asserted. 43 | */ 44 | class PISO[T <: Data](n: Int, t: T, threshold: Int = 1) extends Module { 45 | val io = IO(new Bundle { 46 | /** Read enable */ 47 | val rd = Input(Bool()) 48 | /** Write enable */ 49 | val wr = Input(Bool()) 50 | /** Asserted when the PISO is empty */ 51 | val isEmpty = Output(Bool()) 52 | /** Asserted when the PISO is almost empty */ 53 | val isAlmostEmpty = Output(Bool()) 54 | /** Data input port */ 55 | val din = Input(Vec(n, t)) 56 | /** Data output port */ 57 | val dout = Output(t) 58 | }) 59 | 60 | // Registers 61 | val pisoReg = Reg(Vec(n, t)) 62 | val pisoCounterReg = RegInit(0.U) 63 | 64 | // Control signals 65 | val pisoEmpty = pisoCounterReg === 0.U 66 | val pisoAlmostEmpty = pisoCounterReg === threshold.U 67 | 68 | // Shift data in/out of PISO 69 | when(io.wr) { 70 | pisoReg := io.din 71 | pisoCounterReg := n.U 72 | }.elsewhen(io.rd && !pisoEmpty) { 73 | pisoReg := pisoReg.tail :+ pisoReg.head 74 | pisoCounterReg := pisoCounterReg - 1.U 75 | } 76 | 77 | // Outputs 78 | io.isEmpty := pisoEmpty 79 | io.isAlmostEmpty := pisoAlmostEmpty 80 | io.dout := pisoReg.head 81 | } 82 | -------------------------------------------------------------------------------- /arcadia/test/src/RGBTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia 34 | 35 | import chisel3._ 36 | import chiseltest._ 37 | import org.scalatest.flatspec.AnyFlatSpec 38 | import org.scalatest.matchers.should.Matchers 39 | 40 | class RGBTest extends AnyFlatSpec with ChiselScalatestTester with Matchers { 41 | it should "create a new RGB" in { 42 | test(new Module { 43 | val io = IO(new Bundle { 44 | val a = Output(RGB(4.W)) 45 | }) 46 | io.a := RGB(1.U, 2.U, 3.U) 47 | }) { dut => 48 | dut.io.a.r.expect(1) 49 | dut.io.a.g.expect(2) 50 | dut.io.a.b.expect(3) 51 | } 52 | } 53 | 54 | it should "AND two RGB values" in { 55 | test(new Module { 56 | val io = IO(new Bundle { 57 | val a = Input(RGB(4.W)) 58 | val b = Input(RGB(4.W)) 59 | val c = Output(RGB(4.W)) 60 | }) 61 | io.c := io.a & io.b 62 | }) { dut => 63 | dut.io.a.r.poke(1) 64 | dut.io.a.g.poke(2) 65 | dut.io.a.b.poke(4) 66 | dut.io.b.r.poke(1) 67 | dut.io.b.g.poke(2) 68 | dut.io.b.b.poke(5) 69 | dut.io.c.r.expect(1) 70 | dut.io.c.g.expect(2) 71 | dut.io.c.b.expect(4) 72 | } 73 | } 74 | 75 | it should "OR two RGB values" in { 76 | test(new Module { 77 | val io = IO(new Bundle { 78 | val a = Input(RGB(4.W)) 79 | val b = Input(RGB(4.W)) 80 | val c = Output(RGB(4.W)) 81 | }) 82 | io.c := io.a | io.b 83 | }) { dut => 84 | dut.io.a.r.poke(1) 85 | dut.io.a.g.poke(2) 86 | dut.io.a.b.poke(4) 87 | dut.io.b.r.poke(1) 88 | dut.io.b.g.poke(2) 89 | dut.io.b.b.poke(5) 90 | dut.io.c.r.expect(1) 91 | dut.io.c.g.expect(2) 92 | dut.io.c.b.expect(5) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /arcadia/test/src/clk/ClockDividerTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.clk 34 | 35 | import chisel3._ 36 | import chiseltest._ 37 | import org.scalatest._ 38 | import flatspec.AnyFlatSpec 39 | import matchers.should.Matchers 40 | 41 | class ClockDividerTest extends AnyFlatSpec with ChiselScalatestTester with Matchers { 42 | it should "divide the clock by 2" in { 43 | test(new Module { 44 | val io = IO(new Bundle { 45 | val clockEnable = Output(Bool()) 46 | }) 47 | io.clockEnable := ClockDivider(2) 48 | }) { dut => 49 | dut.io.clockEnable.expect(false) 50 | dut.clock.step() 51 | dut.io.clockEnable.expect(true) 52 | dut.clock.step() 53 | dut.io.clockEnable.expect(false) 54 | dut.clock.step() 55 | dut.io.clockEnable.expect(true) 56 | } 57 | } 58 | 59 | it should "divide the clock by 3" in { 60 | test(new Module { 61 | val io = IO(new Bundle { 62 | val clockEnable = Output(Bool()) 63 | }) 64 | io.clockEnable := ClockDivider(3) 65 | }) { dut => 66 | dut.io.clockEnable.expect(false) 67 | dut.clock.step() 68 | dut.io.clockEnable.expect(false) 69 | dut.clock.step() 70 | dut.io.clockEnable.expect(false) 71 | dut.clock.step() 72 | dut.io.clockEnable.expect(true) 73 | dut.clock.step() 74 | dut.io.clockEnable.expect(false) 75 | dut.clock.step() 76 | dut.io.clockEnable.expect(false) 77 | dut.clock.step() 78 | dut.io.clockEnable.expect(true) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /arcadia/test/src/mem/RegisterFileTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.mem 34 | 35 | import chisel3._ 36 | import chiseltest._ 37 | import org.scalatest.flatspec.AnyFlatSpec 38 | import org.scalatest.matchers.should.Matchers 39 | 40 | class RegisterFileTest extends AnyFlatSpec with ChiselScalatestTester with Matchers { 41 | it should "allow writing masked bytes" in { 42 | test(new RegisterFile(16, 3)) { dut => 43 | dut.io.mem.wr.poke(true) 44 | 45 | // write 46 | dut.io.mem.mask.poke(0) 47 | dut.io.mem.din.poke(0x1234) 48 | dut.clock.step() 49 | dut.io.mem.dout.expect(0x0000.U) 50 | 51 | // write 52 | dut.io.mem.mask.poke(1) 53 | dut.io.mem.din.poke(0x1234) 54 | dut.clock.step() 55 | dut.io.mem.dout.expect(0x0034.U) 56 | 57 | // write 58 | dut.io.mem.mask.poke(2) 59 | dut.io.mem.din.poke(0x5678) 60 | dut.clock.step() 61 | dut.io.mem.dout.expect(0x5634.U) 62 | 63 | // write 64 | dut.io.mem.mask.poke(3) 65 | dut.io.mem.din.poke(0xabcd) 66 | dut.clock.step() 67 | dut.io.mem.dout.expect(0xabcd.U) 68 | } 69 | } 70 | 71 | it should "output the registers" in { 72 | test(new RegisterFile(16, 3)) { dut => 73 | dut.io.mem.wr.poke(true) 74 | dut.io.mem.mask.poke(3) 75 | 76 | // write 77 | dut.io.mem.addr.poke(0) 78 | dut.io.mem.din.poke(0x1234) 79 | dut.clock.step() 80 | dut.io.mem.addr.poke(1) 81 | dut.io.mem.din.poke(0x5678) 82 | dut.clock.step() 83 | dut.io.mem.addr.poke(2) 84 | dut.io.mem.din.poke(0xabcd) 85 | dut.clock.step() 86 | 87 | // registers 88 | dut.io.regs(0).expect(0x1234.U) 89 | dut.io.regs(1).expect(0x5678.U) 90 | dut.io.regs(2).expect(0xabcd.U) 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /arcadia/test/src/snd/AudioMixerTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd 34 | 35 | import chiseltest._ 36 | import org.scalatest._ 37 | import flatspec.AnyFlatSpec 38 | import matchers.should.Matchers 39 | 40 | class AudioMixerTest extends AnyFlatSpec with ChiselScalatestTester with Matchers { 41 | it should "sum the audio inputs" in { 42 | test(new AudioMixer(16, Seq(ChannelConfig(16, 1), ChannelConfig(16, 0.5), ChannelConfig(16, 0.25)))) { dut => 43 | dut.io.in(0).poke(1) 44 | dut.io.in(1).poke(2) 45 | dut.io.in(2).poke(4) 46 | dut.clock.step() 47 | dut.io.out.expect(3) 48 | } 49 | } 50 | 51 | it should "clip the audio output (8-bit)" in { 52 | test(new AudioMixer(8, Seq(ChannelConfig(8, 1), ChannelConfig(8, 1)))) { dut => 53 | dut.io.in(0).poke(127) 54 | dut.io.in(1).poke(1) 55 | dut.clock.step() 56 | dut.io.out.expect(127) 57 | 58 | dut.io.in(0).poke(-128) 59 | dut.io.in(1).poke(-1) 60 | dut.clock.step() 61 | dut.io.out.expect(-128) 62 | } 63 | } 64 | 65 | it should "clip the audio output (16-bit)" in { 66 | test(new AudioMixer(16, Seq(ChannelConfig(16, 1), ChannelConfig(16, 1)))) { dut => 67 | dut.io.in(0).poke(32767) 68 | dut.io.in(1).poke(1) 69 | dut.clock.step() 70 | dut.io.out.expect(32767) 71 | 72 | dut.io.in(0).poke(-32768) 73 | dut.io.in(1).poke(-1) 74 | dut.clock.step() 75 | dut.io.out.expect(-32768) 76 | } 77 | } 78 | 79 | it should "convert all samples to the output width" in { 80 | test(new AudioMixer(9, Seq(ChannelConfig(8, 1)))) { dut => 81 | dut.io.in(0).poke(127) 82 | dut.clock.step() 83 | dut.io.out.expect(254) 84 | 85 | dut.io.in(0).poke(-128) 86 | dut.clock.step() 87 | dut.io.out.expect(-256) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /arcadia/test/src/snd/ymz/ADPCMTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd.ymz 34 | 35 | import chisel3._ 36 | import chiseltest._ 37 | import org.scalatest._ 38 | import org.scalatest.flatspec.AnyFlatSpec 39 | import org.scalatest.matchers.should.Matchers 40 | 41 | class ADPCMTest extends AnyFlatSpec with ChiselScalatestTester with Matchers { 42 | it should "decode sample values" in { 43 | test(new ADPCM) { dut => 44 | dut.io.data.poke(8) 45 | dut.io.in.step.poke(127) 46 | dut.io.in.sample.poke(0) 47 | dut.clock.step() 48 | dut.io.out.step.expect(127.S) 49 | dut.io.out.sample.expect(-16.S) 50 | 51 | dut.io.data.poke(7) 52 | dut.io.in.step.poke(127) 53 | dut.io.in.sample.poke(-16) 54 | dut.clock.step() 55 | dut.io.out.step.expect(304.S) 56 | dut.io.out.sample.expect(222.S) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /arcadia/test/src/snd/ymz/LERPTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package arcadia.snd.ymz 34 | 35 | import chisel3._ 36 | import chiseltest._ 37 | import org.scalatest._ 38 | import org.scalatest.flatspec.AnyFlatSpec 39 | import org.scalatest.matchers.should.Matchers 40 | 41 | class LERPTest extends AnyFlatSpec with ChiselScalatestTester with Matchers { 42 | it should "interpolate sample values" in { 43 | test(new LERP) { dut => 44 | dut.io.samples(0).poke(0) 45 | dut.io.samples(1).poke(16) 46 | 47 | dut.io.index.poke(0) 48 | dut.io.out.expect(0.S) 49 | 50 | dut.io.index.poke(64) 51 | dut.io.out.expect(4.S) 52 | 53 | dut.io.index.poke(128) 54 | dut.io.out.expect(8.S) 55 | 56 | dut.io.index.poke(192) 57 | dut.io.out.expect(12.S) 58 | 59 | dut.io.index.poke(256) 60 | dut.io.out.expect(16.S) 61 | } 62 | } 63 | 64 | it should "handle min/max sample values" in { 65 | test(new LERP) { dut => 66 | dut.io.samples(0).poke(-32767) 67 | dut.io.samples(1).poke(32767) 68 | 69 | dut.io.index.poke(0) 70 | dut.io.out.expect(-32767.S) 71 | 72 | dut.io.index.poke(128) 73 | dut.io.out.expect(0.S) 74 | 75 | dut.io.index.poke(256) 76 | dut.io.out.expect(32767.S) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /bin/reverse: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullobject/openfpga-tecmo/29e5a85398d8a6a7fffa4afc9a13009126342f9c/bin/reverse -------------------------------------------------------------------------------- /build.sc: -------------------------------------------------------------------------------- 1 | import mill._, scalalib._ 2 | import mill.scalalib.TestModule.ScalaTest 3 | 4 | trait CommonModule extends ScalaModule { m => 5 | def scalaVersion = "2.13.8" 6 | 7 | override def scalacOptions = Seq( 8 | "-language:reflectiveCalls", 9 | "-deprecation", 10 | "-feature", 11 | "-Xcheckinit", 12 | "-P:chiselplugin:genBundleElements" 13 | ) 14 | 15 | override def ivyDeps = Agg( 16 | ivy"edu.berkeley.cs::chisel3:3.5.4", 17 | ) 18 | 19 | override def scalacPluginIvyDeps = Agg( 20 | ivy"edu.berkeley.cs:::chisel3-plugin:3.5.4", 21 | ) 22 | 23 | object test extends Tests with ScalaTest { 24 | override def ivyDeps = m.ivyDeps() ++ Agg( 25 | ivy"edu.berkeley.cs::chiseltest:0.5.4" 26 | ) 27 | } 28 | } 29 | 30 | object arcadia extends CommonModule 31 | object tecmo extends CommonModule { 32 | override def moduleDeps = Seq(arcadia) 33 | } 34 | -------------------------------------------------------------------------------- /dist/Assets/tecmo/common/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullobject/openfpga-tecmo/29e5a85398d8a6a7fffa4afc9a13009126342f9c/dist/Assets/tecmo/common/.gitkeep -------------------------------------------------------------------------------- /dist/Assets/tecmo/nullobject.tecmo/Gemini Wing (Japan).json: -------------------------------------------------------------------------------- 1 | { 2 | "instance": { 3 | "magic": "APF_VER_1", 4 | "variant_select" : { 5 | "id" : 777, 6 | "select" : false 7 | }, 8 | "data_slots": [ 9 | { 10 | "id": 1, 11 | "filename": "geminij.rom" 12 | } 13 | ], 14 | "memory_writes": [ 15 | { 16 | "address": "0xf9000000", 17 | "data": 1 18 | }, 19 | { 20 | "address": "0xf9000004", 21 | "data": 1 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dist/Assets/tecmo/nullobject.tecmo/Gemini Wing (World).json: -------------------------------------------------------------------------------- 1 | { 2 | "instance": { 3 | "magic": "APF_VER_1", 4 | "variant_select" : { 5 | "id" : 777, 6 | "select" : false 7 | }, 8 | "data_slots": [ 9 | { 10 | "id": 1, 11 | "filename": "gemini.rom" 12 | } 13 | ], 14 | "memory_writes": [ 15 | { 16 | "address": "0xf9000000", 17 | "data": 1 18 | }, 19 | { 20 | "address": "0xf9000004", 21 | "data": 1 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dist/Assets/tecmo/nullobject.tecmo/Rygar (Japan).json: -------------------------------------------------------------------------------- 1 | { 2 | "instance": { 3 | "magic": "APF_VER_1", 4 | "variant_select" : { 5 | "id" : 777, 6 | "select" : false 7 | }, 8 | "data_slots": [ 9 | { 10 | "id": 1, 11 | "filename": "rygarj.rom" 12 | } 13 | ], 14 | "memory_writes": [ 15 | { 16 | "address": "0xf9000000", 17 | "data": 0 18 | }, 19 | { 20 | "address": "0xf9000004", 21 | "data": 0 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dist/Assets/tecmo/nullobject.tecmo/Rygar (US set 1).json: -------------------------------------------------------------------------------- 1 | { 2 | "instance": { 3 | "magic": "APF_VER_1", 4 | "variant_select" : { 5 | "id" : 777, 6 | "select" : false 7 | }, 8 | "data_slots": [ 9 | { 10 | "id": 1, 11 | "filename": "rygar.rom" 12 | } 13 | ], 14 | "memory_writes": [ 15 | { 16 | "address": "0xf9000000", 17 | "data": 0 18 | }, 19 | { 20 | "address": "0xf9000004", 21 | "data": 0 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dist/Assets/tecmo/nullobject.tecmo/Rygar (US set 2).json: -------------------------------------------------------------------------------- 1 | { 2 | "instance": { 3 | "magic": "APF_VER_1", 4 | "variant_select" : { 5 | "id" : 777, 6 | "select" : false 7 | }, 8 | "data_slots": [ 9 | { 10 | "id": 1, 11 | "filename": "rygar2.rom" 12 | } 13 | ], 14 | "memory_writes": [ 15 | { 16 | "address": "0xf9000000", 17 | "data": 0 18 | }, 19 | { 20 | "address": "0xf9000004", 21 | "data": 0 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dist/Assets/tecmo/nullobject.tecmo/Silkworm (Japan).json: -------------------------------------------------------------------------------- 1 | { 2 | "instance": { 3 | "magic": "APF_VER_1", 4 | "variant_select" : { 5 | "id" : 777, 6 | "select" : false 7 | }, 8 | "data_slots": [ 9 | { 10 | "id": 1, 11 | "filename": "silkwrmj.rom" 12 | } 13 | ], 14 | "memory_writes": [ 15 | { 16 | "address": "0xf9000000", 17 | "data": 2 18 | }, 19 | { 20 | "address": "0xf9000004", 21 | "data": 0 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dist/Assets/tecmo/nullobject.tecmo/Silkworm (World).json: -------------------------------------------------------------------------------- 1 | { 2 | "instance": { 3 | "magic": "APF_VER_1", 4 | "variant_select" : { 5 | "id" : 777, 6 | "select" : false 7 | }, 8 | "data_slots": [ 9 | { 10 | "id": 1, 11 | "filename": "silkworm.rom" 12 | } 13 | ], 14 | "memory_writes": [ 15 | { 16 | "address": "0xf9000000", 17 | "data": 2 18 | }, 19 | { 20 | "address": "0xf9000004", 21 | "data": 0 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dist/Cores/nullobject.tecmo/audio.json: -------------------------------------------------------------------------------- 1 | { 2 | "audio": { 3 | "magic": "APF_VER_1" 4 | } 5 | } -------------------------------------------------------------------------------- /dist/Cores/nullobject.tecmo/core.json: -------------------------------------------------------------------------------- 1 | { 2 | "core": { 3 | "magic": "APF_VER_1", 4 | "metadata": { 5 | "platform_ids": ["tecmo"], 6 | "shortname": "tecmo", 7 | "description": "Tecmo arcade core.", 8 | "author": "nullobject", 9 | "url": "https://github.com/nullobject/pocket-tecmo", 10 | "version": "2.0", 11 | "date_release": "2022-08-26" 12 | }, 13 | "framework": { 14 | "target_product": "Analogue Pocket", 15 | "version_required": "1.1", 16 | "sleep_supported": false, 17 | "dock": { 18 | "supported": true, 19 | "analog_output": false 20 | }, 21 | "hardware": { 22 | "link_port": false, 23 | "cartridge_adapter": -1 24 | } 25 | }, 26 | "cores": [ 27 | { 28 | "name": "default", 29 | "id": 0, 30 | "filename": "bitstream.rbf_r" 31 | } 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /dist/Cores/nullobject.tecmo/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "magic": "APF_VER_1", 4 | "data_slots": [ 5 | { 6 | "name": "Game JSON Setup", 7 | "id": 0, 8 | "required": true, 9 | "parameters": "0x12", 10 | "extensions": [ 11 | "json" 12 | ] 13 | }, 14 | { 15 | "name": "ROM", 16 | "id": 1, 17 | "required": true, 18 | "parameters": 0, 19 | "extensions": [ 20 | "rom" 21 | ], 22 | "address": "0x00000000" 23 | } 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /dist/Cores/nullobject.tecmo/icon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullobject/openfpga-tecmo/29e5a85398d8a6a7fffa4afc9a13009126342f9c/dist/Cores/nullobject.tecmo/icon.bin -------------------------------------------------------------------------------- /dist/Cores/nullobject.tecmo/info.txt: -------------------------------------------------------------------------------- 1 | Made with love by Josh Bassett, 2022. 2 | https://patreon.com/nullobject 3 | 4 | Huge thanks to all my Patreon supporters. If you like this core, then please consider supporting the project. 5 | 6 | Enjoy! 7 | -------------------------------------------------------------------------------- /dist/Cores/nullobject.tecmo/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "magic": "APF_VER_1", 4 | "controllers": [] 5 | } 6 | } -------------------------------------------------------------------------------- /dist/Cores/nullobject.tecmo/interact.json: -------------------------------------------------------------------------------- 1 | { 2 | "interact": { 3 | "magic": "APF_VER_1", 4 | "variables": [ 5 | { 6 | "name": "Debug", 7 | "id": 1, 8 | "type": "check", 9 | "enabled": true, 10 | "address": "0xf9000008", 11 | "writeonly": true, 12 | "defaultval": 0, 13 | "value": 1 14 | }, 15 | { 16 | "name": "Layer 0", 17 | "id": 2, 18 | "type": "check", 19 | "enabled": true, 20 | "address": "0xf900000c", 21 | "writeonly": true, 22 | "defaultval": 1, 23 | "value": 1 24 | }, 25 | { 26 | "name": "Layer 1", 27 | "id": 3, 28 | "type": "check", 29 | "enabled": true, 30 | "address": "0xf9000010", 31 | "writeonly": true, 32 | "defaultval": 1, 33 | "value": 1 34 | }, 35 | { 36 | "name": "Layer 2", 37 | "id": 4, 38 | "type": "check", 39 | "enabled": true, 40 | "address": "0xf9000014", 41 | "writeonly": true, 42 | "defaultval": 1, 43 | "value": 1 44 | }, 45 | { 46 | "name": "Sprites", 47 | "id": 5, 48 | "type": "check", 49 | "enabled": true, 50 | "address": "0xf9000018", 51 | "writeonly": true, 52 | "defaultval": 1, 53 | "value": 1 54 | }, 55 | { 56 | "name": "FM", 57 | "id": 6, 58 | "type": "check", 59 | "enabled": true, 60 | "address": "0xf900001c", 61 | "writeonly": true, 62 | "defaultval": 1, 63 | "value": 1 64 | }, 65 | { 66 | "name": "PCM", 67 | "id": 7, 68 | "type": "check", 69 | "enabled": true, 70 | "address": "0xf9000020", 71 | "writeonly": true, 72 | "defaultval": 1, 73 | "value": 1 74 | } 75 | ] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /dist/Cores/nullobject.tecmo/variants.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "magic": "APF_VER_1", 4 | "variant_list": [] 5 | } 6 | } -------------------------------------------------------------------------------- /dist/Cores/nullobject.tecmo/video.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": { 3 | "magic": "APF_VER_1", 4 | "scaler_modes": [ 5 | { 6 | "width": 256, 7 | "height": 224, 8 | "aspect_w": 4, 9 | "aspect_h": 3, 10 | "rotation": 0, 11 | "mirror": 0 12 | }, 13 | { 14 | "width": 256, 15 | "height": 224, 16 | "aspect_w": 4, 17 | "aspect_h": 3, 18 | "rotation": 90, 19 | "mirror": 0 20 | } 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /dist/Platforms/_images/tecmo.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullobject/openfpga-tecmo/29e5a85398d8a6a7fffa4afc9a13009126342f9c/dist/Platforms/_images/tecmo.bin -------------------------------------------------------------------------------- /dist/Platforms/tecmo.json: -------------------------------------------------------------------------------- 1 | { 2 | "platform": { 3 | "category": "Arcade", 4 | "name": "Tecmo", 5 | "year": 1986, 6 | "manufacturer": "Tecmo" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /mra/Gemini Wing (Japan).mra: -------------------------------------------------------------------------------- 1 | 2 | Gemini Wing (Japan) 3 | Tecmo 4 | 1987 5 | Tecmo 6 | Shooter - Vertical 7 | 8 | geminij 9 | gemini 10 | 0220 11 | tecmo 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 00 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /mra/Gemini Wing (World).mra: -------------------------------------------------------------------------------- 1 | 2 | Gemini Wing 3 | Tecmo 4 | 1987 5 | Tecmo 6 | Shooter - Vertical 7 | 8 | gemini 9 | gemini 10 | 0220 11 | tecmo 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 00 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /mra/Rygar (Japan).mra: -------------------------------------------------------------------------------- 1 | 2 | Rygar (Japan) 3 | Tecmo 4 | 1986 5 | Tecmo 6 | Platform 7 | 8 | rygarj 9 | rygar 10 | 0220 11 | tecmo 12 | 13 | 14 | 15 | 16 | 17 | 00 18 | 19 | 00 20 | 21 | 22 | 00 23 | 24 | 25 | 00 26 | 27 | 28 | 00 29 | 30 | 31 | 32 | 33 | 34 | 00 35 | 36 | 37 | 38 | 39 | 40 | 00 41 | 42 | 43 | 44 | 45 | 46 | 00 47 | 48 | 49 | -------------------------------------------------------------------------------- /mra/Rygar (US set 1).mra: -------------------------------------------------------------------------------- 1 | 2 | Rygar 3 | Tecmo 4 | 1986 5 | Tecmo 6 | Platform 7 | 8 | rygar 9 | rygar 10 | 0220 11 | tecmo 12 | 13 | 14 | 15 | 16 | 17 | 00 18 | 19 | 00 20 | 21 | 22 | 00 23 | 24 | 25 | 00 26 | 27 | 28 | 00 29 | 30 | 31 | 32 | 33 | 34 | 00 35 | 36 | 37 | 38 | 39 | 40 | 00 41 | 42 | 43 | 44 | 45 | 46 | 00 47 | 48 | 49 | -------------------------------------------------------------------------------- /mra/Rygar (US set 2).mra: -------------------------------------------------------------------------------- 1 | 2 | Rygar (US set 2) 3 | Tecmo 4 | 1986 5 | Tecmo 6 | Platform 7 | 8 | rygar2 9 | rygar 10 | 0220 11 | tecmo 12 | 13 | 14 | 15 | 16 | 17 | 00 18 | 19 | 00 20 | 21 | 22 | 00 23 | 24 | 25 | 00 26 | 27 | 28 | 00 29 | 30 | 31 | 32 | 33 | 34 | 00 35 | 36 | 37 | 38 | 39 | 40 | 00 41 | 42 | 43 | 44 | 45 | 46 | 00 47 | 48 | 49 | -------------------------------------------------------------------------------- /mra/Silkworm (Japan).mra: -------------------------------------------------------------------------------- 1 | 2 | Silkworm (Japan) 3 | Tecmo 4 | 1988 5 | Tecmo 6 | Shooter - Horizontal 7 | 8 | silkwormj 9 | silkworm 10 | 0220 11 | tecmo 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 00 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /mra/Silkworm (World).mra: -------------------------------------------------------------------------------- 1 | 2 | Silkworm 3 | Tecmo 4 | 1988 5 | Tecmo 6 | Shooter - Horizontal 7 | 8 | silkworm 9 | silkworm 10 | 0220 11 | tecmo 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 00 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /quartus/.gitignore: -------------------------------------------------------------------------------- 1 | *.qws 2 | *.qdf 3 | build_id.mif 4 | c5_pin_model_dump.txt 5 | core/Tecmo.* 6 | db 7 | greybox_tmp 8 | incremental_db 9 | jtag.cdf 10 | output_files 11 | -------------------------------------------------------------------------------- /quartus/ap_core.qpf: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------- # 2 | # 3 | # Copyright (C) 2019 Intel Corporation. All rights reserved. 4 | # Your use of Intel Corporation's design tools, logic functions 5 | # and other software and tools, and any partner logic 6 | # functions, and any output files from any of the foregoing 7 | # (including device programming or simulation files), and any 8 | # associated documentation or information are expressly subject 9 | # to the terms and conditions of the Intel Program License 10 | # Subscription Agreement, the Intel Quartus Prime License Agreement, 11 | # the Intel FPGA IP License Agreement, or other applicable license 12 | # agreement, including, without limitation, that your use is for 13 | # the sole purpose of programming logic devices manufactured by 14 | # Intel and sold by Intel or its authorized distributors. Please 15 | # refer to the applicable agreement for further details, at 16 | # https://fpgasoftware.intel.com/eula. 17 | # 18 | # -------------------------------------------------------------------------- # 19 | # 20 | # Quartus Prime 21 | # Version 18.1.1 Build 646 04/11/2019 SJ Lite Edition 22 | # Date created = 21:31:36 January 22, 2020 23 | # 24 | # -------------------------------------------------------------------------- # 25 | 26 | QUARTUS_VERSION = "18.1" 27 | DATE = "21:31:36 January 22, 2020" 28 | 29 | # Revisions 30 | 31 | PROJECT_REVISION = "ap_core" 32 | -------------------------------------------------------------------------------- /quartus/apf/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 | -------------------------------------------------------------------------------- /quartus/apf/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 | 14 | 15 | # io constraints go here 16 | # 17 | 18 | 19 | # load in user constraints 20 | read_sdc "core/core_constraints.sdc" -------------------------------------------------------------------------------- /quartus/apf/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 | -------------------------------------------------------------------------------- /quartus/apf/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 | -------------------------------------------------------------------------------- /quartus/arcadia/arcadia.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) reset_ctrl.v] 2 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) dual_clock_fifo.vhd] 3 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) dual_port_ram.vhd] 4 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) single_port_ram.vhd] 5 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) single_port_rom.vhd] 6 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) true_dual_port_ram.vhd] 7 | -------------------------------------------------------------------------------- /quartus/arcadia/reset_ctrl.v: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2021 Josh Bassett 18 | * 19 | * Permission is hereby granted, free of charge, to any person obtaining a copy 20 | * of this software and associated documentation files (the "Software"), to deal 21 | * in the Software without restriction, including without limitation the rights 22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | * copies of the Software, and to permit persons to whom the Software is 24 | * furnished to do so, subject to the following conditions: 25 | * 26 | * The above copyright notice and this permission notice shall be included in all 27 | * copies or substantial portions of the Software. 28 | * 29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35 | * SOFTWARE. 36 | */ 37 | 38 | // Synchronizes an asynchronous reset signal to a target clock domain. 39 | module reset_ctrl ( 40 | input clk, 41 | input rst_i, 42 | output rst_o 43 | ); 44 | 45 | (* altera_attribute = {"-name SYNCHRONIZER_IDENTIFICATION FORCED_IF_ASYNCHRONOUS"} *) reg r1 = 1'b1; 46 | (* altera_attribute = {"-name SYNCHRONIZER_IDENTIFICATION FORCED_IF_ASYNCHRONOUS"} *) reg r2 = 1'b1; 47 | 48 | always @(posedge clk) begin 49 | r1 <= rst_i; 50 | r2 <= r1; 51 | end 52 | 53 | assign rst_o = r2; 54 | 55 | endmodule 56 | -------------------------------------------------------------------------------- /quartus/core/core_constraints.sdc: -------------------------------------------------------------------------------- 1 | # 2 | # user core constraints 3 | # 4 | # put your clock groups in here as well as any net assignments 5 | # 6 | 7 | set_clock_groups -asynchronous \ 8 | -group { bridge_spiclk } \ 9 | -group { clk_74a } \ 10 | -group { clk_74b } \ 11 | -group { ic|mp1|mf_pllbase_inst|altera_pll_i|general[0].gpll~PLL_OUTPUT_COUNTER|divclk } \ 12 | -group { ic|mp1|mf_pllbase_inst|altera_pll_i|general[1].gpll~PLL_OUTPUT_COUNTER|divclk } \ 13 | -group { ic|mp1|mf_pllbase_inst|altera_pll_i|general[2].gpll~PLL_OUTPUT_COUNTER|divclk } \ 14 | -group { ic|mp1|mf_pllbase_inst|altera_pll_i|general[3].gpll~PLL_OUTPUT_COUNTER|divclk } 15 | -------------------------------------------------------------------------------- /quartus/core/i2s.v: -------------------------------------------------------------------------------- 1 | // 2 | // audio i2s generator 3 | // 4 | module i2s ( 5 | input clk_74a, 6 | input [15:0] left_audio, 7 | input [15:0] right_audio, 8 | 9 | output audio_mclk, 10 | output audio_dac, 11 | output audio_lrck 12 | ); 13 | 14 | assign audio_mclk = audgen_mclk; 15 | assign audio_dac = audgen_dac; 16 | assign audio_lrck = audgen_lrck; 17 | 18 | reg audgen_nextsamp; 19 | 20 | // generate MCLK = 12.288mhz with fractional accumulator 21 | reg [21:0] audgen_accum; 22 | reg audgen_mclk; 23 | parameter [20:0] CYCLE_48KHZ = 21'd122880 * 2;//21'd122880 * 2; 24 | always @(posedge clk_74a) begin 25 | audgen_accum <= audgen_accum + CYCLE_48KHZ; 26 | if(audgen_accum >= 21'd742500) begin 27 | audgen_mclk <= ~audgen_mclk; 28 | audgen_accum <= audgen_accum - 21'd742500 + CYCLE_48KHZ; 29 | end 30 | end 31 | 32 | // generate SCLK = 3.072mhz by dividing MCLK by 4 33 | reg [1:0] aud_mclk_divider; 34 | wire audgen_sclk = aud_mclk_divider[1] /* synthesis keep*/; 35 | reg audgen_lrck_1; 36 | always @(posedge audgen_mclk) begin 37 | aud_mclk_divider <= aud_mclk_divider + 1'b1; 38 | // rising edge 39 | if(audgen_lrck & ~audgen_lrck_1) begin 40 | //aud_mclk_divider <= 1; 41 | end 42 | end 43 | 44 | // shift out audio data as I2S 45 | // 32 total bits per channel, but only 16 active bits at the start and then 16 dummy bits 46 | // 47 | // synchronize audio samples coming from the ram readout 48 | wire [31:0] audgen_sampdata_s; 49 | synch_3 #(.WIDTH(32)) s5({left_audio, right_audio}, audgen_sampdata_s, audgen_sclk); 50 | //reg [31:0] audgen_sampdata = 32'hF0008000; 51 | reg [31:0] audgen_sampshift; 52 | reg [4:0] audgen_lrck_cnt; 53 | reg audgen_lrck; 54 | reg audgen_dac; 55 | always @(negedge audgen_sclk) begin 56 | audgen_nextsamp <= 0; 57 | 58 | // output the next bit 59 | audgen_dac <= audgen_sampshift[31]; 60 | 61 | // 48khz * 64 62 | audgen_lrck_cnt <= audgen_lrck_cnt + 1'b1; 63 | if(audgen_lrck_cnt == 31) begin 64 | // switch channels 65 | audgen_lrck <= ~audgen_lrck; 66 | 67 | if(audgen_lrck) begin 68 | // load new sample 69 | audgen_nextsamp <= 1; 70 | // RIFF wave data is stored as 16bit little endian signed, so byteswap 16-bit 71 | audgen_sampshift <= {audgen_sampdata_s}; 72 | end 73 | end else begin 74 | // only shift for 16 clocks per channel 75 | if(audgen_lrck_cnt < 16) begin 76 | audgen_sampshift <= {audgen_sampshift[30:0], 1'b0}; 77 | end 78 | 79 | end 80 | end 81 | 82 | endmodule -------------------------------------------------------------------------------- /quartus/core/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 | -------------------------------------------------------------------------------- /quartus/core/mf_pllbase/mf_pllbase_0002.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/10ps 2 | module mf_pllbase_0002( 3 | 4 | // interface 'refclk' 5 | input wire refclk, 6 | 7 | // interface 'reset' 8 | input wire rst, 9 | 10 | // interface 'outclk0' 11 | output wire outclk_0, 12 | 13 | // interface 'outclk1' 14 | output wire outclk_1, 15 | 16 | // interface 'outclk2' 17 | output wire outclk_2, 18 | 19 | // interface 'outclk3' 20 | output wire outclk_3, 21 | 22 | // interface 'locked' 23 | output wire locked 24 | ); 25 | 26 | altera_pll #( 27 | .fractional_vco_multiplier("true"), 28 | .reference_clock_frequency("74.25 MHz"), 29 | .operation_mode("normal"), 30 | .number_of_clocks(4), 31 | .output_clock_frequency0("96.000000 MHz"), 32 | .phase_shift0("0 ps"), 33 | .duty_cycle0(50), 34 | .output_clock_frequency1("12.000000 MHz"), 35 | .phase_shift1("0 ps"), 36 | .duty_cycle1(50), 37 | .output_clock_frequency2("6.000000 MHz"), 38 | .phase_shift2("0 ps"), 39 | .duty_cycle2(50), 40 | .output_clock_frequency3("6.000000 MHz"), 41 | .phase_shift3("41667 ps"), 42 | .duty_cycle3(50), 43 | .output_clock_frequency4("0 MHz"), 44 | .phase_shift4("0 ps"), 45 | .duty_cycle4(50), 46 | .output_clock_frequency5("0 MHz"), 47 | .phase_shift5("0 ps"), 48 | .duty_cycle5(50), 49 | .output_clock_frequency6("0 MHz"), 50 | .phase_shift6("0 ps"), 51 | .duty_cycle6(50), 52 | .output_clock_frequency7("0 MHz"), 53 | .phase_shift7("0 ps"), 54 | .duty_cycle7(50), 55 | .output_clock_frequency8("0 MHz"), 56 | .phase_shift8("0 ps"), 57 | .duty_cycle8(50), 58 | .output_clock_frequency9("0 MHz"), 59 | .phase_shift9("0 ps"), 60 | .duty_cycle9(50), 61 | .output_clock_frequency10("0 MHz"), 62 | .phase_shift10("0 ps"), 63 | .duty_cycle10(50), 64 | .output_clock_frequency11("0 MHz"), 65 | .phase_shift11("0 ps"), 66 | .duty_cycle11(50), 67 | .output_clock_frequency12("0 MHz"), 68 | .phase_shift12("0 ps"), 69 | .duty_cycle12(50), 70 | .output_clock_frequency13("0 MHz"), 71 | .phase_shift13("0 ps"), 72 | .duty_cycle13(50), 73 | .output_clock_frequency14("0 MHz"), 74 | .phase_shift14("0 ps"), 75 | .duty_cycle14(50), 76 | .output_clock_frequency15("0 MHz"), 77 | .phase_shift15("0 ps"), 78 | .duty_cycle15(50), 79 | .output_clock_frequency16("0 MHz"), 80 | .phase_shift16("0 ps"), 81 | .duty_cycle16(50), 82 | .output_clock_frequency17("0 MHz"), 83 | .phase_shift17("0 ps"), 84 | .duty_cycle17(50), 85 | .pll_type("General"), 86 | .pll_subtype("General") 87 | ) altera_pll_i ( 88 | .rst (rst), 89 | .outclk ({outclk_3, outclk_2, outclk_1, outclk_0}), 90 | .locked (locked), 91 | .fboutclk ( ), 92 | .fbclk (1'b0), 93 | .refclk (refclk) 94 | ); 95 | endmodule 96 | 97 | -------------------------------------------------------------------------------- /quartus/core/pin_ddio_clk.ppf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /quartus/core/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 | -------------------------------------------------------------------------------- /quartus/jt5205/jt5205.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jt5205.v ] 2 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jt5205_adpcm.v ] 3 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jt5205_timing.v ] 4 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jt5205_interpol2x.v ] 5 | -------------------------------------------------------------------------------- /quartus/jt5205/jt5205.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JT5205. 2 | JT5205 program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | JT5205 program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with JT5205. If not, see . 14 | 15 | Author: Jose Tejada Gomez. Twitter: @topapate 16 | Version: 1.0 17 | Date: 30-10-2019 */ 18 | 19 | module jt5205( 20 | input rst, 21 | input clk, 22 | input cen /* direct_enable */, 23 | input [ 1:0] sel, // s pin 24 | input [ 3:0] din, 25 | output signed [11:0] sound, 26 | output sample, 27 | // This output pin is not part of MSM5205 I/O 28 | // It helps integrating the system as it produces 29 | // a strobe 30 | // at the internal clock divider pace 31 | output irq, 32 | output vclk_o 33 | `ifdef JT5205_DEBUG 34 | , 35 | output signed [11:0] debug_raw, 36 | output debug_cen_lo 37 | `endif 38 | ); 39 | 40 | // Enabling the interpolator changes the sound of Chun Li's beat in 41 | // SF2 too much. So I decided to disable it 42 | parameter INTERPOL=1, // 1 for simple linear interpolation. 0 for raw output 43 | VCLK_CEN=1; // 1 for using vclk_o as an output clock enable 44 | // 0 for keeping vclk_o duty cycle as 50% 45 | 46 | wire cen_lo, cen_mid; 47 | wire signed [11:0] raw; 48 | 49 | assign irq=cen_lo; // Notice that irq is active even if rst is high. This is 50 | // important for games such as Tora e no michi. 51 | 52 | `ifdef JT5205_DEBUG 53 | assign debug_raw = raw; 54 | assign debug_cen_lo = cen_lo; 55 | `endif 56 | 57 | 58 | jt5205_timing #(VCLK_CEN) u_timing( 59 | .clk ( clk ), 60 | .cen ( cen ), 61 | .sel ( sel ), 62 | .cen_lo ( cen_lo ), 63 | .cen_mid( cen_mid ), 64 | .cenb_lo( ), 65 | .vclk_o (vclk_o ) 66 | ); 67 | 68 | jt5205_adpcm u_adpcm( 69 | .rst ( rst ), 70 | .clk ( clk ), 71 | .cen_lo ( cen_lo ), 72 | .cen_hf ( cen ), 73 | .din ( din ), 74 | .sound ( raw ) 75 | ); 76 | 77 | generate 78 | if( INTERPOL == 1 ) begin 79 | jt5205_interpol2x u_interpol( 80 | .rst ( rst ), 81 | .clk ( clk ), 82 | .cen_mid( cen_mid ), 83 | .din ( raw ), 84 | .dout ( sound ) 85 | ); 86 | assign sample=cen_mid; // 2x the original sampling freq. because of interpolator 87 | end else begin 88 | assign sound = raw; 89 | assign sample = cen_lo; 90 | end 91 | endgenerate 92 | 93 | 94 | endmodule 95 | -------------------------------------------------------------------------------- /quartus/jt5205/jt5205_interpol2x.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JT5205. 2 | JT5205 program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | JT5205 program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with JT5205. If not, see . 14 | 15 | Author: Jose Tejada Gomez. Twitter: @topapate 16 | Version: 1.0 17 | Date: 30-12-2019 */ 18 | 19 | // Simple 2x interpolator 20 | // Reduces HF content without altering too much the 21 | // original sound 22 | 23 | module jt5205_interpol2x( 24 | input rst, 25 | input clk, 26 | (* direct_enable *) input cen_mid, 27 | input signed [11:0] din, 28 | output reg signed [11:0] dout 29 | ); 30 | 31 | reg signed [11:0] last; 32 | 33 | always @(posedge clk, posedge rst) begin 34 | if(rst) begin 35 | last <= 12'd0; 36 | dout <= 12'd0; 37 | end else if(cen_mid) begin 38 | last <= din; 39 | dout <= (last>>>1)+(din>>>1); 40 | end 41 | end 42 | 43 | endmodule 44 | -------------------------------------------------------------------------------- /quartus/jt5205/jt5205_timing.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JT5205. 2 | JT5205 program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | JT5205 program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with JT5205. If not, see . 14 | 15 | Author: Jose Tejada Gomez. Twitter: @topapate 16 | Version: 1.0 17 | Date: 30-10-2019 */ 18 | 19 | module jt5205_timing( 20 | input clk, 21 | (* direct_enable *) input cen, 22 | input [ 1:0] sel, // s pin 23 | output cen_lo, // sample rate 24 | output cenb_lo, // sample rate (opposite phase) 25 | output cen_mid, // 2x sample rate 26 | output reg vclk_o 27 | ); 28 | 29 | parameter VCLK_CEN=1; 30 | 31 | reg [6:0] cnt=0; 32 | reg pre=0, preb=0; 33 | reg [6:0] lim; 34 | 35 | always @(posedge clk) begin 36 | case(sel) 37 | 0: lim <= 95; 38 | 1: lim <= 63; 39 | 2: lim <= 47; 40 | 3: lim <= 1; 41 | endcase 42 | end 43 | 44 | always @(posedge clk) begin 45 | if(sel==3) begin 46 | cnt <= 0; 47 | vclk_o <= 0; 48 | end 49 | if(cen) begin 50 | if(sel!=3) cnt <= cnt + 7'd1; 51 | 52 | pre <= 0; 53 | preb <= 0; 54 | if(cnt==lim) begin 55 | vclk_o <= 1; 56 | cnt <= 0; 57 | pre <= 1; 58 | end 59 | if(cnt==(lim>>1)) begin 60 | preb <= 1; 61 | vclk_o <= 0; 62 | end 63 | end else if(VCLK_CEN) begin 64 | vclk_o <= 0; 65 | end 66 | end 67 | 68 | assign cen_lo = pre &cen; 69 | assign cenb_lo = preb&cen; 70 | assign cen_mid = (pre|preb)&cen; 71 | 72 | endmodule -------------------------------------------------------------------------------- /quartus/jtopl/jtopl.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl.v ] 2 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_acc.v ] 3 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_csr.v ] 4 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_div.v ] 5 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg.v ] 6 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_cnt.v ] 7 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_comb.v ] 8 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_ctrl.v ] 9 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_final.v ] 10 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_pure.v ] 11 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_eg_step.v ] 12 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_exprom.v ] 13 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_lfo.v ] 14 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_logsin.v ] 15 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_mmr.v ] 16 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_noise.v ] 17 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_op.v ] 18 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg.v ] 19 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg_comb.v ] 20 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg_inc.v ] 21 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg_rhy.v ] 22 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pg_sum.v ] 23 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_pm.v ] 24 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_reg.v ] 25 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_reg_ch.v ] 26 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_sh.v ] 27 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_sh_rst.v ] 28 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_single_acc.v] 29 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_slot_cnt.v ] 30 | set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) jtopl_timers.v ] 31 | -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_acc.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | 4 | JTOPL program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | JTOPL program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with JTOPL. If not, see . 16 | 17 | Author: Jose Tejada Gomez. Twitter: @topapate 18 | Version: 1.0 19 | Date: 20-6-2020 20 | 21 | */ 22 | 23 | module jtopl_acc( 24 | input rst, 25 | input clk, 26 | input cenop, 27 | input signed [12:0] op_result, 28 | input zero, 29 | input op, // 0 for modulator operators 30 | input con, // 0 for modulated connection 31 | output signed [15:0] snd 32 | ); 33 | 34 | wire sum_en; 35 | 36 | assign sum_en = op | con; 37 | 38 | // Continuous output 39 | jtopl_single_acc u_acc( 40 | .clk ( clk ), 41 | .cenop ( cenop ), 42 | .op_result ( op_result ), 43 | .sum_en ( sum_en ), 44 | .zero ( zero ), 45 | .snd ( snd ) 46 | ); 47 | 48 | endmodule 49 | -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_csr.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | 4 | JTOPL program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | JTOPL program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with JTOPL. If not, see . 16 | 17 | Author: Jose Tejada Gomez. Twitter: @topapate 18 | Version: 1.0 19 | Date: 17-6-2020 20 | 21 | */ 22 | 23 | module jtopl_csr #( 24 | parameter LEN=18, W=34 25 | ) ( // Circular Shift Register + input mux 26 | input rst, 27 | input clk, 28 | input cen, 29 | input [ 7:0] din, 30 | output [W-1:0] shift_out, 31 | 32 | input up_mult, 33 | input up_ksl_tl, 34 | input up_ar_dr, 35 | input up_sl_rr, 36 | input up_wav, 37 | input update_op_I, 38 | input update_op_II, 39 | input update_op_IV 40 | ); 41 | 42 | 43 | wire [W-1:0] regop_in; 44 | 45 | jtopl_sh_rst #(.width(W),.stages(LEN)) u_regch( 46 | .clk ( clk ), 47 | .cen ( cen ), 48 | .rst ( rst ), 49 | .din ( regop_in ), 50 | .drop ( shift_out ) 51 | ); 52 | 53 | wire up_mult_I = up_mult & update_op_I; 54 | wire up_mult_II = up_mult & update_op_II; 55 | wire up_mult_IV = up_mult & update_op_IV; 56 | wire up_ksl_tl_IV = up_ksl_tl & update_op_IV; 57 | wire up_ar_dr_op = up_ar_dr & update_op_I; 58 | wire up_sl_rr_op = up_sl_rr & update_op_I; 59 | wire up_wav_I = up_wav & update_op_I; 60 | 61 | assign regop_in[31:0] = { // 4 bytes: 62 | up_mult_IV ? din[7] : shift_out[31], // AM enable 63 | up_mult_I ? din[6:5] : shift_out[30:29], // Vib enable, EG type, KSR 64 | up_mult_II ? din[4:0] : shift_out[28:24], // KSR + Mult 65 | 66 | up_ksl_tl_IV? din : shift_out[23:16], // KSL + TL 67 | 68 | up_ar_dr_op ? din : shift_out[15: 8], 69 | 70 | up_sl_rr_op ? din : shift_out[ 7: 0] 71 | }; 72 | 73 | `ifdef JTOPL2 74 | assign regop_in[33:32] = up_wav_I ? din[1:0] : shift_out[33:32]; 75 | `endif 76 | 77 | endmodule // jtopl_reg -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_div.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 10-6-2020 19 | 20 | */ 21 | 22 | module jtopl_div( 23 | input rst, 24 | input clk, 25 | input cen, 26 | output reg cenop // clock enable at operator rate 27 | ); 28 | 29 | parameter OPL_TYPE=1; 30 | 31 | localparam W = 2; // OPL_TYPE==2 ? 1 : 2; 32 | 33 | reg [W-1:0] cnt; 34 | 35 | `ifdef SIMULATION 36 | initial cnt={W{1'b0}}; 37 | `endif 38 | 39 | always @(posedge clk) if(cen) begin 40 | cnt <= cnt+1'd1; 41 | end 42 | 43 | always @(posedge clk) begin 44 | cenop <= cen && (&cnt); 45 | end 46 | 47 | endmodule 48 | -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_eg_cnt.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 17-6-2020 19 | 20 | */ 21 | 22 | module jtopl_eg_cnt( 23 | input rst, 24 | input clk, 25 | input cen, 26 | input zero, 27 | output reg [14:0] eg_cnt 28 | ); 29 | 30 | always @(posedge clk, posedge rst) begin : envelope_counter 31 | if( rst ) begin 32 | eg_cnt <=15'd0; 33 | end 34 | else begin 35 | if( zero && cen ) begin 36 | // envelope counter increases at each zero input 37 | // This is different from OPN/M where it increased 38 | // once every three zero inputs 39 | eg_cnt <= eg_cnt + 1'b1; 40 | end 41 | end 42 | end 43 | 44 | endmodule -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_eg_ctrl.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 17-6-2020 19 | 20 | */ 21 | 22 | module jtopl_eg_ctrl( 23 | input keyon_now, 24 | input keyoff_now, 25 | input [2:0] state_in, 26 | input [9:0] eg, 27 | // envelope configuration 28 | input en_sus, // enable sustain 29 | input [3:0] arate, // attack rate 30 | input [3:0] drate, // decay rate 31 | input [3:0] rrate, 32 | input [3:0] sl, // sustain level 33 | 34 | output reg [4:0] base_rate, 35 | output reg [2:0] state_next, 36 | output reg pg_rst 37 | ); 38 | 39 | localparam ATTACK = 3'b001, 40 | DECAY = 3'b010, 41 | HOLD = 3'b100, 42 | RELEASE= 3'b000; // default state is release 43 | 44 | // wire is_decaying = state_in[1] | state_in[2]; 45 | 46 | wire [4:0] sustain = { &sl, sl}; //93dB if sl==4'hF 47 | 48 | always @(*) begin 49 | pg_rst = keyon_now; 50 | end 51 | 52 | always @(*) 53 | casez ( { keyoff_now, keyon_now, state_in} ) 54 | 5'b01_???: begin // key on 55 | base_rate = {arate,1'b0}; 56 | state_next = ATTACK; 57 | end 58 | {2'b00, ATTACK}: 59 | if( eg==10'd0 ) begin 60 | base_rate = {drate,1'b0}; 61 | state_next = DECAY; 62 | end 63 | else begin 64 | base_rate = {arate,1'b0}; 65 | state_next = ATTACK; 66 | end 67 | {2'b00, DECAY}: begin 68 | if( eg[9:5] >= sustain ) begin 69 | base_rate = en_sus ? 5'd0 : {rrate,1'b0}; 70 | state_next = en_sus ? HOLD : RELEASE; 71 | end else begin 72 | base_rate = {drate,1'b0}; 73 | state_next = DECAY; 74 | end 75 | end 76 | {2'b00, HOLD}: begin 77 | base_rate = 5'd0; 78 | state_next = HOLD; 79 | end 80 | default: begin // RELEASE, note that keyoff_now==1 will enter this state too 81 | base_rate = {rrate,1'b1}; 82 | state_next = RELEASE; // release 83 | end 84 | endcase 85 | 86 | 87 | endmodule // jtopl_eg_ctrl -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_eg_final.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 17-6-2020 19 | 20 | */ 21 | 22 | module jtopl_eg_final( 23 | input [3:0] lfo_mod, 24 | input [3:0] fnum, 25 | input [2:0] block, 26 | input amsen, 27 | input ams, 28 | input [5:0] tl, 29 | input [1:0] ksl, // level damped by pitch 30 | input [9:0] eg_pure_in, 31 | output reg [9:0] eg_limited 32 | ); 33 | 34 | reg [ 5:0] am_final; 35 | reg [11:0] sum_eg_tl; 36 | reg [11:0] sum_eg_tl_am; 37 | reg [ 8:0] ksl_dB; 38 | reg [ 6:0] ksl_lut[0:15]; 39 | reg [ 7:0] ksl_base; 40 | 41 | always @(*) begin 42 | ksl_base = {1'b0, ksl_lut[fnum]}- { 1'b0, 4'd8-{1'b0,block}, 3'b0 }; 43 | if( ksl_base[7] || ksl==2'b0 ) begin 44 | ksl_dB = 9'd0; 45 | end else begin 46 | ksl_dB = {ksl_base[6:0],2'b0} >> ~ksl; 47 | end 48 | end 49 | 50 | always @(*) begin 51 | am_final = amsen ? ( ams ? {lfo_mod, 2'b0} : {2'b0, lfo_mod} ) : 6'd0; 52 | sum_eg_tl = { 2'b0, tl, 3'd0 } + 53 | { 1'b0, ksl_dB, 1'd0 } + 54 | { 1'b0, eg_pure_in}; // leading zeros needed to compute correctly 55 | sum_eg_tl_am = sum_eg_tl + { 5'd0, am_final }; 56 | end 57 | 58 | always @(*) begin 59 | eg_limited = sum_eg_tl_am[11:10]==2'd0 ? sum_eg_tl_am[9:0] : 10'h3ff; 60 | end 61 | 62 | initial begin 63 | ksl_lut[ 0] = 7'd00; ksl_lut[ 1] = 7'd32; ksl_lut[ 2] = 7'd40; ksl_lut[ 3] = 7'd45; 64 | ksl_lut[ 4] = 7'd48; ksl_lut[ 5] = 7'd51; ksl_lut[ 6] = 7'd53; ksl_lut[ 7] = 7'd55; 65 | ksl_lut[ 8] = 7'd56; ksl_lut[ 9] = 7'd58; ksl_lut[10] = 7'd59; ksl_lut[11] = 7'd60; 66 | ksl_lut[12] = 7'd61; ksl_lut[13] = 7'd62; ksl_lut[14] = 7'd63; ksl_lut[15] = 7'd64; 67 | end 68 | 69 | endmodule -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_eg_pure.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 17-6-2020 19 | 20 | */ 21 | 22 | module jtopl_eg_pure( 23 | input attack, 24 | input step, 25 | input [ 5:1] rate, 26 | input [ 9:0] eg_in, 27 | input sum_up, 28 | output reg [9:0] eg_pure 29 | ); 30 | 31 | reg [ 3:0] dr_sum; 32 | reg [ 9:0] dr_adj; 33 | reg [10:0] dr_result; 34 | 35 | always @(*) begin : dr_calculation 36 | case( rate[5:2] ) 37 | 4'b1100: dr_sum = 4'h2; // 12 38 | 4'b1101: dr_sum = 4'h4; // 13 39 | 4'b1110: dr_sum = 4'h8; // 14 40 | 4'b1111: dr_sum = 4'hf;// 15 41 | default: dr_sum = { 2'b0, step, 1'b0 }; 42 | endcase 43 | // Decay rate attenuation is multiplied by 4 for SSG operation 44 | dr_adj = {6'd0, dr_sum}; 45 | dr_result = dr_adj + eg_in; 46 | end 47 | 48 | reg [ 7:0] ar_sum0; 49 | reg [ 8:0] ar_sum1; 50 | reg [10:0] ar_result; 51 | reg [ 9:0] ar_sum; 52 | 53 | always @(*) begin : ar_calculation 54 | casez( rate[5:2] ) 55 | default: ar_sum0 = {2'd0, eg_in[9:4]}; 56 | 4'b1011, 4'b1100: ar_sum0 = {1'd0, eg_in[9:3]}; // 'hb 57 | // 4'b1101: ar_sum0 = {1'd0, eg_in[9:3]}; // 'hd 58 | // 4'b111?: ar_sum0 = eg_in[9:2]; // 'he/f 59 | 4'b1101, 4'b111?: ar_sum0 = eg_in[9:2]; // 'he/f 60 | endcase 61 | ar_sum1 = ar_sum0+9'd1; 62 | if( rate[5:2] == 4'he ) 63 | ar_sum = { ar_sum1, 1'b0 }; 64 | else if( rate[5:2] > 4'hb ) 65 | ar_sum = step ? { ar_sum1, 1'b0 } : { 1'b0, ar_sum1 }; // adds ar_sum1*3/2 max 66 | // else if( rate[5:2] == 4'hb ) 67 | // ar_sum = step ? { ar_sum1, 1'b0 } : 10'd0; // adds ar_sum1 max 68 | else 69 | ar_sum = step ? { 1'b0, ar_sum1 } : 10'd0; // adds ar_sum1/2 max 70 | ar_result = eg_in-ar_sum; 71 | end 72 | 73 | /////////////////////////////////////////////////////////// 74 | // rate not used below this point 75 | reg [9:0] eg_pre_fastar; // pre fast attack rate 76 | always @(*) begin 77 | if(sum_up) begin 78 | if( attack ) 79 | eg_pre_fastar = ar_result[10] ? 10'd0: ar_result[9:0]; 80 | else 81 | eg_pre_fastar = dr_result[10] ? 10'h3FF : dr_result[9:0]; 82 | end 83 | else eg_pre_fastar = eg_in; 84 | eg_pure = (attack&rate[5:1]==5'h1F) ? 10'd0 : eg_pre_fastar; 85 | end 86 | 87 | endmodule -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_lfo.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 21-6-2020 19 | */ 20 | 21 | // Follows OPLL Reverse Engineering from Nuked 22 | // https://github.com/nukeykt/Nuked-OPLL 23 | 24 | // The AM logic renders a triangular waveform. The logic for it is rather 25 | // obscure, but apparently that's how the original was done 26 | 27 | module jtopl_lfo( 28 | input rst, 29 | input clk, 30 | input cenop, 31 | input [17:0] slot, 32 | output [ 2:0] vib_cnt, 33 | output reg [ 3:0] trem 34 | ); 35 | 36 | parameter [6:0] LIM=7'd60; 37 | 38 | reg [12:0] cnt; 39 | reg am_inc, am_incen, am_dir, am_step; 40 | reg [ 1:0] am_bit; 41 | reg am_carry; 42 | reg [ 8:0] am_cnt; 43 | 44 | wire [12:0] next = cnt+1'b1; 45 | 46 | assign vib_cnt = cnt[12:10]; 47 | 48 | always @(*) begin 49 | am_inc = (slot[0] | am_dir ) & am_step & am_incen; 50 | am_bit = {1'b0, am_cnt[0]} + {1'b0, am_inc} + {1'b0, am_carry & am_incen}; 51 | end 52 | 53 | always @(posedge clk) begin 54 | if( rst ) begin 55 | cnt <= 13'd0; 56 | am_incen <= 1; 57 | am_dir <= 0; 58 | am_carry <= 0; 59 | am_cnt <= 9'd0; 60 | am_step <= 0; 61 | end else if( cenop ) begin 62 | if( slot[17] ) begin 63 | cnt <= next; 64 | am_step <= &next[5:0]; 65 | am_incen <= 1; 66 | end 67 | else if(slot[8]) am_incen <= 0; 68 | am_cnt <= { am_bit[0], am_cnt[8:1] }; 69 | am_carry <= am_bit[1]; 70 | if( slot[0] ) begin 71 | if( am_dir && am_cnt[6:0]==7'd0 ) am_dir <= 0; 72 | else 73 | if( !am_dir && ( (am_cnt[6:0]&7'h69) == 7'h69) ) am_dir <= 1; 74 | end 75 | // output 76 | if( slot[0] ) trem <= am_cnt[6:3]; 77 | end 78 | end 79 | 80 | endmodule 81 | -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_noise.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 24-6-2020 19 | 20 | */ 21 | 22 | // Following research by Andete in 23 | // https://github.com/andete/ym2413 24 | // This has been research for the YM2413 (OPLL) 25 | // I assume other OPL chips use the same one 26 | 27 | module jtopl_noise( 28 | input rst, // rst should be at least 6 clk&cen cycles long 29 | input clk, // CPU clock 30 | input cen, // optional clock enable, it not needed leave as 1'b1 31 | output noise 32 | ); 33 | 34 | reg [22:0] poly; 35 | reg nbit; 36 | 37 | assign noise = poly[22] ^ poly[9] ^ poly[8] ^ poly[0]; 38 | 39 | always @(posedge clk, posedge rst) begin 40 | if( rst ) 41 | poly <= 1; 42 | else if(cen) begin 43 | poly <= poly==0 ? 23'd1 : { poly[21:0], noise }; 44 | end 45 | end 46 | 47 | endmodule -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_pg_comb.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 13-6-2020 19 | 20 | */ 21 | 22 | module jtopl_pg_comb ( 23 | input [ 2:0] block, 24 | input [ 9:0] fnum, 25 | // Phase Modulation 26 | input [ 2:0] vib_cnt, 27 | input vib_dep, 28 | input viben, 29 | 30 | output [ 3:0] keycode, 31 | // Phase increment 32 | output [16:0] phinc_out, 33 | // Phase add 34 | input [ 3:0] mul, 35 | input [18:0] phase_in, 36 | input pg_rst, 37 | // input signed [7:0] pm_in, 38 | input [16:0] phinc_in, 39 | // Rhythm 40 | input noise, 41 | input [ 9:0] hh, 42 | input hh_en, 43 | input tc_en, 44 | input sd_en, 45 | input rm_xor, 46 | 47 | output [18:0] phase_out, 48 | output [ 9:0] phase_op 49 | ); 50 | 51 | wire signed [3:0] pm_offset; 52 | wire [9:0] phase_pre; 53 | 54 | assign keycode = { block, fnum[9] }; 55 | 56 | /* pm and pg_inc operate in parallel */ 57 | jtopl_pm u_pm( 58 | .vib_cnt ( vib_cnt ), 59 | .fnum ( fnum ), 60 | .vib_dep ( vib_dep ), 61 | .viben ( viben ), 62 | .pm_offset ( pm_offset ) 63 | ); 64 | 65 | jtopl_pg_inc u_inc( 66 | .block ( block ), 67 | .fnum ( fnum ), 68 | .pm_offset ( pm_offset ), 69 | .phinc_pure ( phinc_out ) 70 | ); 71 | 72 | // pg_sum uses the output from the previous blocks 73 | 74 | jtopl_pg_sum u_sum( 75 | .mul ( mul ), 76 | .phase_in ( phase_in ), 77 | .pg_rst ( pg_rst ), 78 | .phinc_pure ( phinc_in ), 79 | .phase_out ( phase_out ), 80 | .phase_op ( phase_pre ) 81 | ); 82 | 83 | jtopl_pg_rhy u_rhy( 84 | .phase_pre ( phase_pre ), 85 | // Rhythm 86 | .noise ( noise ), 87 | .hh ( hh ), 88 | .hh_en ( hh_en ), 89 | .tc_en ( tc_en ), 90 | .sd_en ( sd_en ), 91 | .rm_xor ( rm_xor ), 92 | .phase_op ( phase_op ) 93 | ); 94 | 95 | endmodule // jtopl_pg_comb -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_pg_inc.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 13-6-2020 19 | 20 | */ 21 | 22 | module jtopl_pg_inc ( 23 | input [ 2:0] block, 24 | input [ 9:0] fnum, 25 | input signed [ 3:0] pm_offset, 26 | output reg [16:0] phinc_pure 27 | ); 28 | 29 | reg [16:0] freq; 30 | 31 | always @(*) begin 32 | freq = { 7'd0, fnum } + { {13{pm_offset[3]}}, pm_offset }; 33 | // Add PM here 34 | freq = freq << block; 35 | phinc_pure = freq >> 1; 36 | end 37 | 38 | endmodule // jtopl_pg_inc -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_pg_rhy.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 25-6-2020 19 | 20 | */ 21 | 22 | module jtopl_pg_rhy ( 23 | input [ 9:0] phase_pre, 24 | // Rhythm 25 | input noise, 26 | input [ 9:0] hh, 27 | input hh_en, 28 | input tc_en, 29 | input sd_en, 30 | input rm_xor, 31 | output reg [ 9:0] phase_op 32 | ); 33 | 34 | always @(*) begin 35 | if( hh_en ) begin 36 | phase_op = {rm_xor, 9'd0 }; 37 | if( rm_xor ^ noise ) 38 | phase_op = phase_op | 10'hd0; 39 | else 40 | phase_op = phase_op | 10'h34; 41 | end else if( sd_en ) begin 42 | phase_op = { hh[8], hh[8]^noise, 8'd0 }; 43 | end else if( tc_en ) begin 44 | phase_op = { rm_xor, 9'h80 }; 45 | end else 46 | phase_op = phase_pre; 47 | end 48 | 49 | endmodule // jtopl_pg_sum -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_pg_sum.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 13-6-2020 19 | 20 | */ 21 | 22 | // Original hardware uses an adder to do the multiplication 23 | // but I think it will take less resources of the FPGA to 24 | // use a real multiplier instead 25 | 26 | module jtopl_pg_sum ( 27 | input [ 3:0] mul, 28 | input [18:0] phase_in, 29 | input pg_rst, 30 | input [16:0] phinc_pure, 31 | 32 | output reg [18:0] phase_out, 33 | output reg [ 9:0] phase_op 34 | ); 35 | 36 | reg [21:0] phinc_mul; 37 | reg [ 4:0] factor[0:15]; 38 | 39 | always @(*) begin 40 | phinc_mul = { 5'b0, phinc_pure} * factor[mul]; 41 | phase_out = pg_rst ? 'd0 : (phase_in + phinc_mul[19:1]); 42 | phase_op = phase_out[18:9]; 43 | end 44 | 45 | initial begin 46 | factor[ 0] = 5'd01; factor[ 1] = 5'd02; factor[ 2] = 5'd04; factor[ 3] = 5'd06; 47 | factor[ 4] = 5'd08; factor[ 5] = 5'd10; factor[ 6] = 5'd12; factor[ 7] = 5'd14; 48 | factor[ 8] = 5'd16; factor[ 9] = 5'd18; factor[10] = 5'd20; factor[11] = 5'd20; 49 | factor[12] = 5'd24; factor[13] = 5'd24; factor[14] = 5'd30; factor[15] = 5'd30; 50 | end 51 | 52 | endmodule // jtopl_pg_sum -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_pm.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 21-6-2020 19 | */ 20 | 21 | // Based on Nuked's work on OPLL and OPL3 22 | 23 | module jtopl_pm ( 24 | input [ 2:0] vib_cnt, 25 | input [ 9:0] fnum, 26 | input vib_dep, 27 | input viben, 28 | output reg [ 3:0] pm_offset 29 | ); 30 | 31 | reg [2:0] range; 32 | 33 | always @(*) begin 34 | if( vib_cnt[1:0]==2'b00 ) 35 | range = 3'd0; 36 | else begin 37 | range = fnum[9:7]>>vib_cnt[0]; 38 | if(!vib_dep) range = range>>1; 39 | end 40 | if( vib_cnt[2] ) 41 | pm_offset = ~{1'b0, range } + 4'd1; 42 | else 43 | pm_offset = {1'b0, range }; 44 | if(!viben) pm_offset = 4'd0; 45 | end 46 | 47 | endmodule 48 | -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_sh.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 19-6-2020 19 | */ 20 | 21 | // stages must be greater than 2 22 | module jtopl_sh #(parameter width=5, stages=24 ) 23 | ( 24 | input clk, 25 | input cen, 26 | input [width-1:0] din, 27 | output [width-1:0] drop 28 | ); 29 | 30 | reg [stages-1:0] bits[width-1:0]; 31 | 32 | genvar i; 33 | generate 34 | for (i=0; i < width; i=i+1) begin: bit_shifter 35 | always @(posedge clk) if(cen) begin 36 | bits[i] <= {bits[i][stages-2:0], din[i]}; 37 | end 38 | assign drop[i] = bits[i][stages-1]; 39 | end 40 | endgenerate 41 | 42 | endmodule 43 | -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_sh_rst.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | JTOPL is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 13-6-2020 19 | */ 20 | 21 | // stages must be greater than 2 22 | module jtopl_sh_rst #(parameter width=5, stages=18, rstval=1'b0 ) 23 | ( 24 | input rst, 25 | input clk, 26 | input cen, 27 | input [width-1:0] din, 28 | output [width-1:0] drop 29 | ); 30 | 31 | reg [stages-1:0] bits[width-1:0]; 32 | 33 | genvar i; 34 | integer k; 35 | generate 36 | initial 37 | for (k=0; k < width; k=k+1) begin 38 | bits[k] = { stages{rstval}}; 39 | end 40 | endgenerate 41 | 42 | generate 43 | for (i=0; i < width; i=i+1) begin: bit_shifter 44 | always @(posedge clk, posedge rst) 45 | if( rst ) begin 46 | bits[i] <= {stages{rstval}}; 47 | end else if(cen) begin 48 | bits[i] <= {bits[i][stages-2:0], din[i]}; 49 | end 50 | assign drop[i] = bits[i][stages-1]; 51 | end 52 | endgenerate 53 | 54 | endmodule 55 | -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_single_acc.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL. 2 | 3 | 4 | JTOPL program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | JTOPL program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with JTOPL. If not, see . 16 | 17 | Author: Jose Tejada Gomez. Twitter: @topapate 18 | Version: 1.0 19 | Date: 20-6-2020 20 | 21 | */ 22 | 23 | // Accumulates an arbitrary number of inputs with saturation 24 | // restart the sum when input "zero" is high 25 | 26 | module jtopl_single_acc #(parameter 27 | INW=13, // input data width 28 | OUTW=16 // output data width 29 | )( 30 | input clk, 31 | input cenop, 32 | input [INW-1:0] op_result, 33 | input sum_en, 34 | input zero, 35 | output reg [OUTW-1:0] snd 36 | ); 37 | 38 | // for full resolution use INW=14, OUTW=16 39 | // for cut down resolution use INW=9, OUTW=12 40 | // OUTW-INW should be > 0 41 | 42 | reg signed [OUTW-1:0] next, acc, current; 43 | reg overflow; 44 | 45 | wire [OUTW-1:0] plus_inf = { 1'b0, {(OUTW-1){1'b1}} }; // maximum positive value 46 | wire [OUTW-1:0] minus_inf = { 1'b1, {(OUTW-1){1'b0}} }; // minimum negative value 47 | 48 | always @(*) begin 49 | current = sum_en ? { {(OUTW-INW){op_result[INW-1]}}, op_result } : {OUTW{1'b0}}; 50 | next = zero ? current : current + acc; 51 | overflow = !zero && 52 | (current[OUTW-1] == acc[OUTW-1]) && 53 | (acc[OUTW-1]!=next[OUTW-1]); 54 | end 55 | 56 | always @(posedge clk) if( cenop ) begin 57 | acc <= overflow ? (acc[OUTW-1] ? minus_inf : plus_inf) : next; 58 | if(zero) snd <= acc; 59 | end 60 | 61 | endmodule -------------------------------------------------------------------------------- /quartus/jtopl/jtopl_slot_cnt.v: -------------------------------------------------------------------------------- 1 | /* This file is part of JTOPL 2 | 3 | JTOPL program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | JTOPL program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with JTOPL. If not, see . 15 | 16 | Author: Jose Tejada Gomez. Twitter: @topapate 17 | Version: 1.0 18 | Date: 13-6-2020 19 | 20 | */ 21 | 22 | module jtopl_slot_cnt( 23 | input rst, 24 | input clk, 25 | input cen, 26 | 27 | // Pipeline order 28 | output zero, 29 | output reg [ 1:0] group, 30 | output reg op, // 0 for modulator operators 31 | output reg [ 2:0] subslot, 32 | output reg [17:0] slot // hot one encoding of active slot 33 | ); 34 | 35 | // Each group contains three channels 36 | // and each subslot contains six operators 37 | wire [2:0] next_sub = subslot==3'd5 ? 3'd0 : (subslot+3'd1); 38 | wire [1:0] next_group = subslot==3'd5 ? (group==2'b10 ? 2'b00 : group+2'b1) : group; 39 | 40 | `ifdef SIMULATION 41 | // These signals need to operate during rst 42 | // initial state is not relevant (or critical) in real life 43 | // but we need a clear value during simulation 44 | initial begin 45 | group = 2'd0; 46 | subslot = 3'd0; 47 | slot = 18'd1; 48 | end 49 | `endif 50 | 51 | assign zero = slot[0]; 52 | 53 | always @(posedge clk) begin : up_counter 54 | if( cen ) begin 55 | { group, subslot } <= { next_group, next_sub }; 56 | if( { next_group, next_sub }==5'd0 ) begin 57 | slot <= 18'd1; 58 | end else begin 59 | slot <= { slot[16:0], 1'b0 }; 60 | end 61 | op <= next_sub >= 3'd3; 62 | end 63 | end 64 | 65 | endmodule -------------------------------------------------------------------------------- /quartus/t80/t80.qip: -------------------------------------------------------------------------------- 1 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80pa.vhd ] 2 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80s.vhd ] 3 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80_Reg.vhd ] 4 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80_MCode.vhd] 5 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80_ALU.vhd ] 6 | set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) T80.vhd ] 7 | -------------------------------------------------------------------------------- /tecmo/src/ChiselApp.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package tecmo 34 | 35 | import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} 36 | 37 | object ChiselApp extends App { 38 | (new ChiselStage).execute( 39 | Array("--compiler", "verilog", "--target-dir", "quartus/core"), 40 | Seq(ChiselGeneratorAnnotation(() => new Tecmo)) 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /tecmo/src/gfx/GPUMemIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package tecmo.gfx 34 | 35 | import arcadia.UVec2 36 | import chisel3._ 37 | import tecmo._ 38 | 39 | /** A bundle that contains memory interfaces for the GPU. */ 40 | class GPUMemIO extends Bundle { 41 | /** Program counter (debug) */ 42 | val pc = Input(UInt(16.W)) 43 | /** Layer port */ 44 | val layer = Vec(3, new Bundle { 45 | /** Scroll position */ 46 | val scroll = Input(UVec2(9.W)) 47 | /** VRAM port */ 48 | val vram = new LayerRamIO 49 | }) 50 | /** Sprite port */ 51 | val sprite = new Bundle { 52 | /** VRAM port */ 53 | val vram = new SpriteRamIO 54 | } 55 | /** Palette RAM port */ 56 | val paletteRam = new PaletteRamIO 57 | } 58 | 59 | object GPUMemIO { 60 | def apply() = new GPUMemIO 61 | } 62 | -------------------------------------------------------------------------------- /tecmo/src/gfx/LayerCtrlIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package tecmo.gfx 34 | 35 | import arcadia.UVec2 36 | import chisel3._ 37 | import tecmo._ 38 | 39 | /** A bundle that contains control signals for the layer processor. */ 40 | class LayerCtrlIO extends Bundle { 41 | /** Enable flag */ 42 | val enable = Input(Bool()) 43 | /** Graphics format */ 44 | val format = Input(Bool()) 45 | /** Scroll position */ 46 | val scroll = Input(UVec2(9.W)) 47 | /** VRAM port */ 48 | val vram = new LayerRamIO 49 | /** Tile ROM port */ 50 | val tileRom = new TileRomIO 51 | } 52 | 53 | object LayerCtrlIO { 54 | def apply() = new LayerCtrlIO 55 | } 56 | -------------------------------------------------------------------------------- /tecmo/src/gfx/PaletteEntry.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package tecmo.gfx 34 | 35 | import chisel3._ 36 | import tecmo.Config 37 | 38 | /** Represent an entry in a color palette and a priority. */ 39 | class PaletteEntry extends Bundle { 40 | /** Priority */ 41 | val priority = UInt(Config.PRIO_WIDTH.W) 42 | /** Palette index */ 43 | val palette = UInt(Config.PALETTE_WIDTH.W) 44 | /** Color index */ 45 | val color = UInt(Config.COLOR_WIDTH.W) 46 | } 47 | 48 | object PaletteEntry { 49 | /** 50 | * Constructs a new palette entry. 51 | * 52 | * @param priority The priority value. 53 | * @param palette The palette index. 54 | * @param color The color index. 55 | */ 56 | def apply(priority: Bits, palette: Bits, color: Bits): PaletteEntry = { 57 | val wire = Wire(new PaletteEntry) 58 | wire.priority := priority 59 | wire.palette := palette 60 | wire.color := color 61 | wire 62 | } 63 | 64 | /** Returns an empty palette entry. */ 65 | def zero: PaletteEntry = apply(0.U, 0.U, 0.U) 66 | } 67 | -------------------------------------------------------------------------------- /tecmo/src/gfx/SpriteCtrlIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package tecmo.gfx 34 | 35 | import chisel3._ 36 | import tecmo._ 37 | 38 | /** A bundle that contains control signals for the sprite processor. */ 39 | class SpriteCtrlIO extends Bundle { 40 | /** Graphics format */ 41 | val format = Input(Bool()) 42 | /** Enable flag */ 43 | val enable = Input(Bool()) 44 | /** VRAM port */ 45 | val vram = new SpriteRamIO 46 | /** Tile ROM port */ 47 | val tileRom = new TileRomIO 48 | } 49 | 50 | object SpriteCtrlIO { 51 | def apply() = new SpriteCtrlIO 52 | } 53 | -------------------------------------------------------------------------------- /tecmo/src/gfx/Tile.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package tecmo.gfx 34 | 35 | import arcadia.Util 36 | import chisel3._ 37 | import tecmo.Config 38 | 39 | /** Represents a tile descriptor. */ 40 | class Tile extends Bundle { 41 | /** Color code */ 42 | val colorCode = UInt(Config.PALETTE_WIDTH.W) 43 | /** Tile code */ 44 | val code = UInt(Tile.CODE_WIDTH.W) 45 | } 46 | 47 | object Tile { 48 | /** The width of the tile code */ 49 | val CODE_WIDTH = 11 50 | 51 | /** 52 | * Decodes a tile from the given data. 53 | * 54 | * {{{ 55 | * byte bits description 56 | * ------+-7654-3210-+------------- 57 | * 0 | xxxx xxxx | lo code 58 | * 1 | ---- -xxx | hi code 59 | * | xxxx ---- | color 60 | * }}} 61 | * 62 | * @param data The tile data. 63 | */ 64 | def decode(data: Bits): Tile = { 65 | val words = Util.decode(data, 2, 8) 66 | val tile = Wire(new Tile) 67 | tile.colorCode := words(1)(7, 4) 68 | tile.code := words(1)(2, 0) ## words(0)(7, 0) 69 | tile 70 | } 71 | 72 | /** 73 | * Decodes a Gemini Wing tile from the given data. 74 | * 75 | * {{{ 76 | * byte bits description 77 | * ------+-7654-3210-+------------- 78 | * 0 | xxxx xxxx | lo code 79 | * 1 | ---- xxxx | color 80 | * | -xxx ---- | hi code 81 | * }}} 82 | * 83 | * @param data The tile data. 84 | */ 85 | def decodeGemini(data: Bits): Tile = { 86 | val words = Util.decode(data, 2, 8) 87 | val tile = Wire(new Tile) 88 | tile.colorCode := words(1)(3, 0) 89 | tile.code := words(1)(6, 4) ## words(0)(7, 0) 90 | tile 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tecmo/src/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | import arcadia.mem.{AsyncReadMemIO, ReadMemIO} 34 | import tecmo.Config 35 | 36 | package object tecmo { 37 | /** Game enum */ 38 | object Game { 39 | /** Rygar */ 40 | val RYGAR = 0 41 | /** Gemini Wing */ 42 | val GEMINI = 1 43 | /** Silkworm */ 44 | val SILKWORM = 2 45 | } 46 | 47 | /** Graphics format enum */ 48 | object GraphicsFormat { 49 | /** Default graphics format */ 50 | val GFX_FORMAT_DEFAULT = 0 51 | /** Gemini Wing graphics format */ 52 | val GFX_FORMAT_GEMINI = 1 53 | } 54 | 55 | /** Program ROM IO */ 56 | class ProgRomIO extends ReadMemIO(Config.PROG_ROM_ADDR_WIDTH, Config.PROG_ROM_DATA_WIDTH) 57 | 58 | /** Bank ROM IO */ 59 | class BankRomIO extends ReadMemIO(Config.BANK_ROM_ADDR_WIDTH, Config.BANK_ROM_DATA_WIDTH) 60 | 61 | /** Sound ROM IO */ 62 | class SoundRomIO extends ReadMemIO(Config.SOUND_ROM_ADDR_WIDTH, Config.SOUND_ROM_DATA_WIDTH) 63 | 64 | /** Sample ROM IO */ 65 | class SampleRomIO extends AsyncReadMemIO(Config.PCM_ROM_ADDR_WIDTH, Config.PCM_ROM_DATA_WIDTH) 66 | 67 | /** Tile ROM IO */ 68 | class TileRomIO extends AsyncReadMemIO(Config.TILE_ROM_ADDR_WIDTH, Config.TILE_ROM_DATA_WIDTH) 69 | 70 | /** Layer RAM IO (GPU-side) */ 71 | class LayerRamIO extends ReadMemIO(Config.LAYER_RAM_GPU_ADDR_WIDTH, Config.LAYER_RAM_GPU_DATA_WIDTH) 72 | 73 | /** Sprite RAM IO (GPU-side) */ 74 | class SpriteRamIO extends ReadMemIO(Config.SPRITE_RAM_GPU_ADDR_WIDTH, Config.SPRITE_RAM_GPU_DATA_WIDTH) 75 | 76 | /** Palette RAM IO (GPU-side) */ 77 | class PaletteRamIO extends ReadMemIO(Config.PALETTE_RAM_GPU_ADDR_WIDTH, Config.PALETTE_RAM_GPU_DATA_WIDTH) 78 | } 79 | -------------------------------------------------------------------------------- /tecmo/src/snd/SoundCtrlIO.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package tecmo.snd 34 | 35 | import chisel3._ 36 | 37 | /** A bundle that contains ports to control the sound PCB. */ 38 | class SoundCtrlIO extends Bundle { 39 | /** Request flag */ 40 | val req = Input(Bool()) 41 | /** Request data */ 42 | val data = Input(Bits(8.W)) 43 | } 44 | 45 | object SoundCtrlIO { 46 | def apply(): SoundCtrlIO = new SoundCtrlIO 47 | } 48 | -------------------------------------------------------------------------------- /tecmo/test/src/gpu/ColorMixerTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package tecmo.gfx 34 | 35 | import chiseltest._ 36 | import org.scalatest._ 37 | import flatspec.AnyFlatSpec 38 | import matchers.should.Matchers 39 | 40 | class ColorMixerTest extends AnyFlatSpec with ChiselScalatestTester with Matchers { 41 | behavior of "palette RAM" 42 | 43 | it should "assert the chip select signal" in { 44 | test(new ColorMixer) { dut => 45 | dut.io.paletteRam.rd.expect(true) 46 | } 47 | } 48 | 49 | it should "set the palette RAM address" in { 50 | test(new ColorMixer) { dut => 51 | dut.io.charPen.color.poke(0x00) 52 | dut.io.paletteRam.addr.expect(0x100) 53 | 54 | dut.io.charPen.color.poke(0x01) 55 | dut.io.paletteRam.addr.expect(0x101) 56 | 57 | dut.io.charPen.palette.poke(0xf) 58 | dut.io.charPen.color.poke(0xf) 59 | dut.io.paletteRam.addr.expect(0x1ff) 60 | } 61 | } 62 | 63 | it should "set the pixel data" in { 64 | test(new ColorMixer) { dut => 65 | dut.io.paletteRam.dout.poke(0x12) 66 | dut.clock.step() 67 | dut.io.dout.expect(0x12) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tecmo/test/src/gpu/DebugLayerTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * __ __ __ __ __ __ 3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \ 4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____ 5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\ 6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/ 7 | * ______ ______ __ ______ ______ ______ 8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\ 9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/ 10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ 11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ 12 | * 13 | * https://joshbassett.info 14 | * https://twitter.com/nullobject 15 | * https://github.com/nullobject 16 | * 17 | * Copyright (c) 2022 Josh Bassett 18 | * 19 | * This program is free software: you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation, either version 3 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program. If not, see . 31 | */ 32 | 33 | package tecmo.gfx 34 | 35 | import chisel3._ 36 | import chiseltest._ 37 | import org.scalatest._ 38 | import flatspec.AnyFlatSpec 39 | import matchers.should.Matchers 40 | 41 | class DebugLayerTest extends AnyFlatSpec with ChiselScalatestTester with Matchers { 42 | behavior of "video enabled" 43 | 44 | it should "request the correct tile from the tile ROM" in { 45 | test(new DebugLayer("foo")) { dut => 46 | // tile 0 47 | dut.io.video.pos.x.poke(0) 48 | dut.clock.step() 49 | dut.io.tileRom.addr.expect(0x130) 50 | 51 | // tile 1 52 | dut.io.video.pos.x.poke(8) 53 | dut.clock.step() 54 | dut.io.tileRom.addr.expect(0x178) 55 | 56 | // tile 2 57 | dut.io.video.pos.x.poke(16) 58 | dut.clock.step() 59 | dut.io.tileRom.addr.expect(0x178) 60 | } 61 | } 62 | 63 | it should "request the correct row from the tile ROM" in { 64 | test(new DebugLayer("foo")) { dut => 65 | // row 0 66 | dut.io.video.pos.y.poke(0) 67 | dut.clock.step() 68 | dut.io.tileRom.addr.expect(0x130) 69 | 70 | // row 1 71 | dut.io.video.pos.y.poke(1) 72 | dut.clock.step() 73 | dut.io.tileRom.addr.expect(0x131) 74 | 75 | // row 7 76 | dut.io.video.pos.y.poke(7) 77 | dut.clock.step() 78 | dut.io.tileRom.addr.expect(0x137) 79 | } 80 | } 81 | 82 | it should "set the RGB value" in { 83 | test(new DebugLayer("foo")) { dut => 84 | dut.io.enable.poke(true) 85 | dut.io.color.poke(15) 86 | dut.io.tileRom.dout.poke("hffffffff".U) 87 | dut.io.video.pos.x.poke(0) 88 | dut.clock.step() 89 | dut.io.data.expect(255) 90 | } 91 | } 92 | } 93 | --------------------------------------------------------------------------------