├── .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 | [](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 |
--------------------------------------------------------------------------------