1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-01-27 04:12:10 +00:00

Gyruss: a quick port

This commit is contained in:
Gyorgy Szombathelyi
2021-07-22 14:53:17 +02:00
parent ffa462e7da
commit 6c0a02220f
33 changed files with 10150 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
# -------------------------------------------------------------------------- #
#
# Copyright (C) 1991-2013 Altera Corporation
# Your use of Altera Corporation's design tools, logic functions
# and other software and tools, and its AMPP partner logic
# functions, and any output files from any of the foregoing
# (including device programming or simulation files), and any
# associated documentation or information are expressly subject
# to the terms and conditions of the Altera Program License
# Subscription Agreement, Altera MegaCore Function License
# Agreement, or other applicable license agreement, including,
# without limitation, that your use is for the sole purpose of
# programming logic devices manufactured by Altera and sold by
# Altera or its authorized distributors. Please refer to the
# applicable agreement for further details.
#
# -------------------------------------------------------------------------- #
#
# Quartus II 64-Bit
# Version 13.1.0 Build 162 10/23/2013 SJ Web Edition
# Date created = 00:21:03 December 03, 2019
#
# -------------------------------------------------------------------------- #
QUARTUS_VERSION = "13.1"
DATE = "00:21:03 December 03, 2019"
# Revisions
PROJECT_REVISION = "Gyruss"

View File

@@ -0,0 +1,253 @@
# -------------------------------------------------------------------------- #
#
# Copyright (C) 1991-2014 Altera Corporation
# Your use of Altera Corporation's design tools, logic functions
# and other software and tools, and its AMPP partner logic
# functions, and any output files from any of the foregoing
# (including device programming or simulation files), and any
# associated documentation or information are expressly subject
# to the terms and conditions of the Altera Program License
# Subscription Agreement, Altera MegaCore Function License
# Agreement, or other applicable license agreement, including,
# without limitation, that your use is for the sole purpose of
# programming logic devices manufactured by Altera and sold by
# Altera or its authorized distributors. Please refer to the
# applicable agreement for further details.
#
# -------------------------------------------------------------------------- #
#
# Quartus II 64-Bit
# Version 13.1.4 Build 182 03/12/2014 SJ Full Version
# Date created = 19:54:12 November 22, 2020
#
# -------------------------------------------------------------------------- #
#
# Notes:
#
# 1) The default values for assignments are stored in the file:
# Gyruss_assignment_defaults.qdf
# If this file doesn't exist, see file:
# assignment_defaults.qdf
#
# 2) Altera recommends that you do not modify this file. This
# file is updated automatically by the Quartus II software
# and any changes you make may be lost or overwritten.
#
# -------------------------------------------------------------------------- #
# Project-Wide Assignments
# ========================
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL
set_global_assignment -name LAST_QUARTUS_VERSION "13.1 SP4.26"
set_global_assignment -name PRE_FLOW_SCRIPT_FILE "quartus_sh:rtl/build_id.tcl"
# Pin & Location Assignments
# ==========================
set_location_assignment PIN_7 -to LED
set_location_assignment PIN_54 -to CLOCK_27
set_location_assignment PIN_144 -to VGA_R[5]
set_location_assignment PIN_143 -to VGA_R[4]
set_location_assignment PIN_142 -to VGA_R[3]
set_location_assignment PIN_141 -to VGA_R[2]
set_location_assignment PIN_137 -to VGA_R[1]
set_location_assignment PIN_135 -to VGA_R[0]
set_location_assignment PIN_133 -to VGA_B[5]
set_location_assignment PIN_132 -to VGA_B[4]
set_location_assignment PIN_125 -to VGA_B[3]
set_location_assignment PIN_121 -to VGA_B[2]
set_location_assignment PIN_120 -to VGA_B[1]
set_location_assignment PIN_115 -to VGA_B[0]
set_location_assignment PIN_114 -to VGA_G[5]
set_location_assignment PIN_113 -to VGA_G[4]
set_location_assignment PIN_112 -to VGA_G[3]
set_location_assignment PIN_111 -to VGA_G[2]
set_location_assignment PIN_110 -to VGA_G[1]
set_location_assignment PIN_106 -to VGA_G[0]
set_location_assignment PIN_136 -to VGA_VS
set_location_assignment PIN_119 -to VGA_HS
set_location_assignment PIN_65 -to AUDIO_L
set_location_assignment PIN_80 -to AUDIO_R
set_location_assignment PIN_105 -to SPI_DO
set_location_assignment PIN_88 -to SPI_DI
set_location_assignment PIN_126 -to SPI_SCK
set_location_assignment PIN_127 -to SPI_SS2
set_location_assignment PIN_91 -to SPI_SS3
set_location_assignment PIN_13 -to CONF_DATA0
set_location_assignment PIN_49 -to SDRAM_A[0]
set_location_assignment PIN_44 -to SDRAM_A[1]
set_location_assignment PIN_42 -to SDRAM_A[2]
set_location_assignment PIN_39 -to SDRAM_A[3]
set_location_assignment PIN_4 -to SDRAM_A[4]
set_location_assignment PIN_6 -to SDRAM_A[5]
set_location_assignment PIN_8 -to SDRAM_A[6]
set_location_assignment PIN_10 -to SDRAM_A[7]
set_location_assignment PIN_11 -to SDRAM_A[8]
set_location_assignment PIN_28 -to SDRAM_A[9]
set_location_assignment PIN_50 -to SDRAM_A[10]
set_location_assignment PIN_30 -to SDRAM_A[11]
set_location_assignment PIN_32 -to SDRAM_A[12]
set_location_assignment PIN_83 -to SDRAM_DQ[0]
set_location_assignment PIN_79 -to SDRAM_DQ[1]
set_location_assignment PIN_77 -to SDRAM_DQ[2]
set_location_assignment PIN_76 -to SDRAM_DQ[3]
set_location_assignment PIN_72 -to SDRAM_DQ[4]
set_location_assignment PIN_71 -to SDRAM_DQ[5]
set_location_assignment PIN_69 -to SDRAM_DQ[6]
set_location_assignment PIN_68 -to SDRAM_DQ[7]
set_location_assignment PIN_86 -to SDRAM_DQ[8]
set_location_assignment PIN_87 -to SDRAM_DQ[9]
set_location_assignment PIN_98 -to SDRAM_DQ[10]
set_location_assignment PIN_99 -to SDRAM_DQ[11]
set_location_assignment PIN_100 -to SDRAM_DQ[12]
set_location_assignment PIN_101 -to SDRAM_DQ[13]
set_location_assignment PIN_103 -to SDRAM_DQ[14]
set_location_assignment PIN_104 -to SDRAM_DQ[15]
set_location_assignment PIN_58 -to SDRAM_BA[0]
set_location_assignment PIN_51 -to SDRAM_BA[1]
set_location_assignment PIN_85 -to SDRAM_DQMH
set_location_assignment PIN_67 -to SDRAM_DQML
set_location_assignment PIN_60 -to SDRAM_nRAS
set_location_assignment PIN_64 -to SDRAM_nCAS
set_location_assignment PIN_66 -to SDRAM_nWE
set_location_assignment PIN_59 -to SDRAM_nCS
set_location_assignment PIN_33 -to SDRAM_CKE
set_location_assignment PIN_43 -to SDRAM_CLK
set_location_assignment PLL_1 -to "pll:pll|altpll:altpll_component"
# Classic Timing Assignments
# ==========================
set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
# Analysis & Synthesis Assignments
# ================================
set_global_assignment -name FAMILY "Cyclone III"
set_global_assignment -name TOP_LEVEL_ENTITY Gyruss_MiST
set_global_assignment -name DEVICE_FILTER_PIN_COUNT 144
set_global_assignment -name DEVICE_FILTER_SPEED_GRADE 8
set_global_assignment -name DEVICE_FILTER_PACKAGE TQFP
# Fitter Assignments
# ==================
set_global_assignment -name DEVICE EP3C25E144C8
set_global_assignment -name ENABLE_CONFIGURATION_PINS OFF
set_global_assignment -name ENABLE_NCE_PIN OFF
set_global_assignment -name ENABLE_BOOT_SEL_PIN OFF
set_global_assignment -name CYCLONEIII_CONFIGURATION_SCHEME "PASSIVE SERIAL"
set_global_assignment -name CRC_ERROR_OPEN_DRAIN OFF
set_global_assignment -name FORCE_CONFIGURATION_VCCIO ON
set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "3.3-V LVTTL"
set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name RESERVE_DATA0_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name RESERVE_DATA1_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name RESERVE_FLASH_NCE_AFTER_CONFIGURATION "USE AS REGULAR IO"
set_global_assignment -name RESERVE_DCLK_AFTER_CONFIGURATION "USE AS REGULAR IO"
# Assembler Assignments
# =====================
set_global_assignment -name GENERATE_RBF_FILE ON
set_global_assignment -name USE_CONFIGURATION_DEVICE OFF
# SignalTap II Assignments
# ========================
set_global_assignment -name ENABLE_SIGNALTAP OFF
set_global_assignment -name USE_SIGNALTAP_FILE output_files/sndcpu.stp
# Power Estimation Assignments
# ============================
set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW"
set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)"
# Advanced I/O Timing Assignments
# ===============================
set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -rise
set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -fall
set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -rise
set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -fall
# ------------------------------
# start ENTITY(Gyruss_MiST)
# Pin & Location Assignments
# ==========================
# Fitter Assignments
# ==================
# start DESIGN_PARTITION(Top)
# ---------------------------
# Incremental Compilation Assignments
# ===================================
# end DESIGN_PARTITION(Top)
# -------------------------
# end ENTITY(Gyruss_MiST)
# ----------------------------
set_global_assignment -name SYNTH_TIMING_DRIVEN_SYNTHESIS ON
set_global_assignment -name TIMEQUEST_MULTICORNER_ANALYSIS ON
set_global_assignment -name SMART_RECOMPILE ON
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_DQ[*]
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_A[*]
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_BA[0]
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_BA[1]
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_DQMH
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_DQML
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_nRAS
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_nCAS
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_nWE
set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to SDRAM_nCS
set_instance_assignment -name FAST_OUTPUT_ENABLE_REGISTER ON -to SDRAM_DQ[*]
set_instance_assignment -name FAST_INPUT_REGISTER ON -to SDRAM_DQ[*]
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_A[*]
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_DQ[*]
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_BA[*]
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_DQML
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_DQMH
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_nRAS
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_nCAS
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_nWE
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_nCS
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_CKE
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to SDRAM_CLK
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_R[*]
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_G[*]
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_B[*]
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_HS
set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_VS
set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to AUDIO_L
set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to AUDIO_R
set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SPI_DO
set_global_assignment -name SYSTEMVERILOG_FILE rtl/Gyruss_MiST.sv
set_global_assignment -name QIP_FILE rtl/pll.qip
set_global_assignment -name SYSTEMVERILOG_FILE rtl/Gyruss.sv
set_global_assignment -name SYSTEMVERILOG_FILE rtl/sdram.sv
set_global_assignment -name VERILOG_FILE rtl/jtframe_frac_cen.v
set_global_assignment -name VERILOG_FILE rtl/jt49_dcrm2.v
set_global_assignment -name VERILOG_FILE rtl/hiscore.v
set_global_assignment -name SYSTEMVERILOG_FILE rtl/Gyruss_SND.sv
set_global_assignment -name SYSTEMVERILOG_FILE rtl/Gyruss_CPU.sv
set_global_assignment -name VERILOG_FILE rtl/Filters/gyruss_lpf.v
set_global_assignment -name VERILOG_FILE rtl/Filters/gyruss_lpf_medium.v
set_global_assignment -name VERILOG_FILE rtl/Filters/gyruss_lpf_light.v
set_global_assignment -name VERILOG_FILE rtl/Filters/gyruss_lpf_heavy.v
set_global_assignment -name VERILOG_FILE rtl/Filters/audio_iir_filter.v
set_global_assignment -name QIP_FILE rtl/custom/gyruss_custom.qip
set_global_assignment -name VHDL_FILE rtl/ram_rom/spram.vhd
set_global_assignment -name SYSTEMVERILOG_FILE rtl/ram_rom/rom_loader.sv
set_global_assignment -name QIP_FILE rtl/ram_rom/gyruss_ram_rom.qip
set_global_assignment -name VHDL_FILE rtl/ram_rom/dpram_dc.vhd
set_global_assignment -name QIP_FILE ../../common/mist/mist.qip
set_global_assignment -name QIP_FILE ../../common/CPU/T80/T80.qip
set_global_assignment -name QIP_FILE ../../common/CPU/t48/i8039.qip
set_global_assignment -name QIP_FILE ../../common/Sound/JT49/jt49.qip
set_global_assignment -name SIGNALTAP_FILE output_files/sndcpu.stp
set_global_assignment -name ALLOW_POWER_UP_DONT_CARE ON
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

View File

@@ -0,0 +1,136 @@
## Generated SDC file "vectrex_MiST.out.sdc"
## Copyright (C) 1991-2013 Altera Corporation
## Your use of Altera Corporation's design tools, logic functions
## and other software and tools, and its AMPP partner logic
## functions, and any output files from any of the foregoing
## (including device programming or simulation files), and any
## associated documentation or information are expressly subject
## to the terms and conditions of the Altera Program License
## Subscription Agreement, Altera MegaCore Function License
## Agreement, or other applicable license agreement, including,
## without limitation, that your use is for the sole purpose of
## programming logic devices manufactured by Altera and sold by
## Altera or its authorized distributors. Please refer to the
## applicable agreement for further details.
## VENDOR "Altera"
## PROGRAM "Quartus II"
## VERSION "Version 13.1.0 Build 162 10/23/2013 SJ Web Edition"
## DATE "Sun Jun 24 12:53:00 2018"
##
## DEVICE "EP3C25E144C8"
##
# Clock constraints
# Automatically constrain PLL and other generated clocks
derive_pll_clocks -create_base_clocks
# Automatically calculate clock uncertainty to jitter and other effects.
derive_clock_uncertainty
# tsu/th constraints
# tco constraints
# tpd constraints
#**************************************************************
# Time Information
#**************************************************************
set_time_format -unit ns -decimal_places 3
#**************************************************************
# Create Clock
#**************************************************************
create_clock -name {SPI_SCK} -period 41.666 -waveform { 20.8 41.666 } [get_ports {SPI_SCK}]
set sys_clk "pll|altpll_component|auto_generated|pll1|clk[0]"
set sdram_clk "pll|altpll_component|auto_generated|pll1|clk[0]"
set aud_clk "pll|altpll_component|auto_generated|pll1|clk[0]"
#**************************************************************
# Create Generated Clock
#**************************************************************
#**************************************************************
# Set Clock Latency
#**************************************************************
#**************************************************************
# Set Clock Uncertainty
#**************************************************************
#**************************************************************
# Set Input Delay
#**************************************************************
set_input_delay -add_delay -clock_fall -clock [get_clocks {CLOCK_27}] 1.000 [get_ports {CLOCK_27}]
set_input_delay -add_delay -clock_fall -clock [get_clocks {SPI_SCK}] 1.000 [get_ports {CONF_DATA0}]
set_input_delay -add_delay -clock_fall -clock [get_clocks {SPI_SCK}] 1.000 [get_ports {SPI_DI}]
set_input_delay -add_delay -clock_fall -clock [get_clocks {SPI_SCK}] 1.000 [get_ports {SPI_SCK}]
set_input_delay -add_delay -clock_fall -clock [get_clocks {SPI_SCK}] 1.000 [get_ports {SPI_SS2}]
set_input_delay -add_delay -clock_fall -clock [get_clocks {SPI_SCK}] 1.000 [get_ports {SPI_SS3}]
set_input_delay -clock [get_clocks $sdram_clk] -reference_pin [get_ports {SDRAM_CLK}] -max 6.6 [get_ports SDRAM_DQ[*]]
set_input_delay -clock [get_clocks $sdram_clk] -reference_pin [get_ports {SDRAM_CLK}] -min 3.5 [get_ports SDRAM_DQ[*]]
#**************************************************************
# Set Output Delay
#**************************************************************
set_output_delay -clock [get_clocks {SPI_SCK}] 1.000 [get_ports {SPI_DO}]
set_output_delay -clock [get_clocks $aud_clk] 1.000 [get_ports {AUDIO_L}]
set_output_delay -clock [get_clocks $aud_clk] 1.000 [get_ports {AUDIO_R}]
set_output_delay -clock [get_clocks $sys_clk] 1.000 [get_ports {LED}]
set_output_delay -clock [get_clocks $sys_clk] 1.000 [get_ports {VGA_*}]
set_output_delay -clock [get_clocks $sdram_clk] -reference_pin [get_ports {SDRAM_CLK}] -max 1.5 [get_ports {SDRAM_D* SDRAM_A* SDRAM_BA* SDRAM_n* SDRAM_CKE}]
set_output_delay -clock [get_clocks $sdram_clk] -reference_pin [get_ports {SDRAM_CLK}] -min -0.8 [get_ports {SDRAM_D* SDRAM_A* SDRAM_BA* SDRAM_n* SDRAM_CKE}]
#**************************************************************
# Set Clock Groups
#**************************************************************
set_clock_groups -asynchronous -group [get_clocks {SPI_SCK}] -group [get_clocks {pll|altpll_component|auto_generated|pll1|clk[*]}]
set_clock_groups -asynchronous -group [get_clocks $sys_clk] -group [get_clocks $aud_clk]
#**************************************************************
# Set False Path
#**************************************************************
#**************************************************************
# Set Multicycle Path
#**************************************************************
set_multicycle_path -to {VGA_*[*]} -setup 2
set_multicycle_path -to {VGA_*[*]} -hold 1
#**************************************************************
# Set Maximum Delay
#**************************************************************
#**************************************************************
# Set Minimum Delay
#**************************************************************
#**************************************************************
# Set Input Transition
#**************************************************************

View File

@@ -0,0 +1,11 @@
# MiST port of Konami Gyruss by ACE
https://github.com/MiSTer-devel/Arcade-Gyruss_MiSTer
## Usage
- Create ROM and ARC files from the MRA files using the MRA utility.
Example: mra -A -z /path/to/mame/roms "Gyruss.mra"
- Copy the ROM files to the root of the SD Card
- Copy the RBF and ARC files to the same folder on the SD Card
- MRA utility: https://github.com/sebdel/mra-tools-c/

View File

@@ -0,0 +1,75 @@
<misterromdescription>
<name>Gyruss</name>
<region></region>
<homebrew>no</homebrew>
<bootleg>no</bootleg>
<version></version>
<alternative></alternative>
<platform></platform>
<series></series>
<year>1983</year>
<manufacturer>Konami</manufacturer>
<category>Shooter - Tube</category>
<setname>gyruss</setname>
<parent>gyruss</parent>
<mameversion>0218</mameversion>
<rbf>Gyruss</rbf>
<about author="MrX-8B" source="https://github.com/MrX-8B/MiSTer-Arcade-Gyruss" twitter="@MrX_8B" webpage="https://patreon.com/MrX_8B"></about>
<about author="Ace" twitter="@Ace9921Tweets"></about>
<resolution>15kHz</resolution>
<rotation>vertical (cw)</rotation>
<flip>no</flip>
<players>2 (alternating)</players>
<joystick>4-way</joystick>
<special_controls></special_controls>
<num_buttons>1</num_buttons>
<buttons default="B,Start,R,Select,L" names="Fire,Start P1,Coin,Start P2,Pause"></buttons>
<switches default="00,34,00" base="8" page_id="1" page_name="Switches">
<dip bits="0,3" name="Credits A" ids="1c/1cr,1c/2cr,1c/3cr,1c/4cr,1c/5cr,1c/7cr,1c/6cr,2c/1cr,2c/3cr,3c/1cr,2c/5cr,3c/2cr,3c/4cr,4c/3cr,4c/1cr,Free Play"/>
<dip bits="4,7" name="Credits B" ids="1c/1cr,1c/2cr,1c/3cr,1c/4cr,1c/5cr,1c/6cr,1c/7cr,2c/1cr,2c/3cr,2c/5cr,3c/1cr,3c/2cr,3c/4cr,4c/1cr,4c/3cr,Free Play"/>
<dip bits="8,9" name="Lives" ids="3,4,5,255 (Cheat)"/>
<dip bits="10" name="Cabinet type" ids="Cocktail,Upright"/>
<dip bits="11" name="Bonus" ids="30K/90K/60K+,40K/110K/70K+"/>
<dip bits="12,14" name="Difficulty" ids="1 (Easiest),2,3,4,5,6,7,8 (Hardest)"/>
<dip bits="15" name="Attract mode sound" ids="Off,On"/>
<dip bits="16" name="Attract mode music" ids="Off,On"/>
</switches>
<rom index="1"></rom>
<rom index="0" md5="none" type="merged" zip="gyruss.zip">
<part crc="c673b43d" name="gyrussk.1"></part>
<part crc="a4ec03e4" name="gyrussk.2"></part>
<part crc="27454a98" name="gyrussk.3"></part>
<part crc="822bF27e" name="gyrussk.9"></part>
<part crc="27d8329b" name="gyrussk.4"></part>
<part crc="c949db10" name="gyrussk.6"></part>
<part crc="4f22411a" name="gyrussk.5"></part>
<part crc="47cd1fbc" name="gyrussk.8"></part>
<part crc="8e8d388c" name="gyrussk.7"></part>
<part crc="f4ae1c17" name="gyrussk.1a"></part>
<part crc="ba498115" name="gyrussk.2a"></part>
<part crc="3f9b5dea" name="gyrussk.3a"></part>
<part crc="de823a81" name="gyrussk.pr2"></part>
<part crc="7ed057de" name="gyrussk.pr1"></part>
<part crc="98782db3" name="gyrussk.pr3"></part>
</rom>
<rom index="2"></rom>
<rom index="3" md5="none">
<part>
01 00 00 00 00 FF 00 02 00 02 00 01 00 FF 00 00
00 00 94 88 00 28 00 83
00 00 94 0B 00 03 00 01
</part>
</rom>
<rom index="4"></rom>
<nvram index="4" size="43"/>
<remark></remark>
<mratimestamp>20210430005030</mratimestamp>
</misterromdescription>

View File

@@ -0,0 +1,173 @@
/*MIT License
Copyright (c) 2019 Gregory Hogan (Soltan_G42)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
module iir_1st_order
#(
parameter COEFF_WIDTH = 18,
parameter COEFF_SCALE = 15,
parameter DATA_WIDTH = 16,
parameter COUNT_BITS = 10
)
(
input clk,
input reset,
input [COUNT_BITS - 1 : 0] div,
input signed [COEFF_WIDTH - 1 : 0] A2, B1, B2,
input signed [DATA_WIDTH - 1 :0] in,
output [DATA_WIDTH - 1:0] out
);
reg signed [DATA_WIDTH-1:0] x0,x1,y0;
reg signed [DATA_WIDTH + COEFF_WIDTH - 1 : 0] out32;
reg [COUNT_BITS - 1:0] count;
// Usage:
// Design your 1st order iir low/high-pass with a tool that will give you the
// filter coefficients for the difference equation. Filter coefficients can
// be generated in Octave/matlab/scipy using a command similar to
// [B, A] = butter( 1, 3500/(106528/2), 'low') for a 3500 hz 1st order low-pass
// assuming 106528Hz sample rate.
//
// The Matlab output is:
// B = [0.093863 0.093863]
// A = [1.00000 -0.81227]
//
// Then scale coefficients by multiplying by 2^COEFF_SCALE and round to nearest integer
//
// B = [3076 3076]
// A = [32768 -26616]
//
// Discard A(1) because it is assumed 1.0 before scaling
//
// This leaves you with A2 = -26616 , B1 = 3076 , B2 = 3076
// B1 + B2 - A2 should sum to 2^COEFF_SCALE = 32768
//
// Sample frequency is "clk rate/div": for Genesis this is 53.69mhz/504 = 106528hz
//
// COEFF_WIDTH must be at least COEFF_SCALE+1 and must be large enough to
// handle temporary overflow during this computation: out32 <= (B1*x0 + B2*x1) - A2*y0
assign out = y0;
always @ (*) begin
out32 <= (B1*x0 + B2*x1) - A2*y0; //Previous output is y0 not y1
end
always @ (posedge clk) begin
if(reset) begin
count <= 0;
x0 <= 0;
x1 <= 0;
y0 <= 0;
end
else begin
count <= count + 1'd1;
if (count == div - 1) begin
count <= 0;
y0 <= {out32[DATA_WIDTH + COEFF_WIDTH - 1] , out32[COEFF_SCALE + DATA_WIDTH - 2 : COEFF_SCALE]};
x1 <= x0;
x0 <= in;
end
end
end
endmodule //iir_1st_order
module iir_2nd_order
#(
parameter COEFF_WIDTH = 18,
parameter COEFF_SCALE = 14,
parameter DATA_WIDTH = 16,
parameter COUNT_BITS = 10
)
(
input clk,
input reset,
input [COUNT_BITS - 1 : 0] div,
input signed [COEFF_WIDTH - 1 : 0] A2, A3, B1, B2, B3,
input signed [DATA_WIDTH - 1 : 0] in,
output [DATA_WIDTH - 1 : 0] out
);
reg signed [DATA_WIDTH-1 : 0] x0,x1,x2;
reg signed [DATA_WIDTH-1 : 0] y0,y1;
reg signed [(DATA_WIDTH + COEFF_WIDTH - 1) : 0] out32;
reg [COUNT_BITS : 0] count;
// Usage:
// Design your 1st order iir low/high-pass with a tool that will give you the
// filter coefficients for the difference equation. Filter coefficients can
// be generated in Octave/matlab/scipy using a command similar to
// [B, A] = butter( 2, 5000/(48000/2), 'low') for a 5000 hz 2nd order low-pass
// assuming 48000Hz sample rate.
//
// Output is:
// B = [ 0.072231 0.144462 0.072231]
// A = [1.00000 -1.10923 0.39815]
//
// Then scale coefficients by multiplying by 2^COEFF_SCALE and round to nearest integer
// Make sure your coefficients can be stored as a signed number with COEFF_WIDTH bits.
//
// B = [1183 2367 1183]
// A = [16384 -18174 6523]
//
// Discard A(1) because it is assumed 1.0 before scaling
//
// This leaves you with A2 = -18174 , A3 = 6523, B1 = 1183 , B2 = 2367 , B3 = 1183
// B1 + B2 + B3 - A2 - A3 should sum to 2^COEFF_SCALE = 16384
//
// Sample frequency is "clk rate/div"
//
// COEFF_WIDTH must be at least COEFF_SCALE+1 and must be large enough to
// handle temporary overflow during this computation:
// out32 <= (B1*x0 + B2*x1 + B3*x2) - (A2*y0 + A3*y1);
assign out = y0;
always @ (*) begin
out32 <= (B1*x0 + B2*x1 + B3*x2) - (A2*y0 + A3*y1); //Previous output is y0 not y1
end
always @ (posedge clk) begin
if(reset) begin
count <= 0;
x0 <= 0;
x1 <= 0;
x2 <= 0;
y0 <= 0;
y1 <= 0;
end
else begin
count <= count + 1'd1;
if (count == div - 1) begin
count <= 0;
y1 <= y0;
y0 <= {out32[DATA_WIDTH + COEFF_WIDTH - 1] , out32[(DATA_WIDTH + COEFF_SCALE - 2) : COEFF_SCALE]};
x2 <= x1;
x1 <= x0;
x0 <= in;
end
end
end
endmodule //iir_2nd_order

View File

@@ -0,0 +1,60 @@
/*MIT License
Copyright (c) 2019 Gregory Hogan (Soltan_G42)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
//This is a variation of Gregory Hogan's MISTer Genesis core low-pass filter
//tuned to remove aliasing on Gyruss.
module gyruss_lpf(
input clk,
input reset,
input signed [15:0] in,
output signed [15:0] out);
localparam div = 10'd220; //Sample at 49.152/220 = 223418Hz
//Coefficients computed with Octave/Matlab/Online filter calculators.
//or with scipy.signal.bessel or similar tools
//0.045425748, 0.045425748
//1.0000000, -0.90914850
reg signed [17:0] A2;
reg signed [17:0] B2;
reg signed [17:0] B1;
wire signed [15:0] audio_post_lpf1;
always @ (*) begin
A2 = -18'd18211;
B1 = 18'd7278;
B2 = 18'd7278;
end
iir_1st_order lpf6db(.clk(clk),
.reset(reset),
.div(div),
.A2(A2),
.B1(B1),
.B2(B2),
.in(in),
.out(audio_post_lpf1));
assign out = audio_post_lpf1;
endmodule

View File

@@ -0,0 +1,60 @@
/*MIT License
Copyright (c) 2019 Gregory Hogan (Soltan_G42)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
//This is a variation of Gregory Hogan's MISTer Genesis core low-pass filter
//tuned to match the heaviest low-pass filter on Gyruss.
module gyruss_lpf_heavy(
input clk,
input reset,
input signed [15:0] in,
output signed [15:0] out);
localparam div = 10'd220; //Sample at 49.152/220 = 223418Hz
//Coefficients computed with Octave/Matlab/Online filter calculators.
//or with scipy.signal.bessel or similar tools
//0.0041276697, 0.0041276697
//1.0000000, -0.99174466
reg signed [17:0] A2;
reg signed [17:0] B2;
reg signed [17:0] B1;
wire signed [15:0] audio_post_lpf1;
always @ (*) begin
A2 = -18'd32498;
B1 = 18'd135;
B2 = 18'd135;
end
iir_1st_order lpf6db(.clk(clk),
.reset(reset),
.div(div),
.A2(A2),
.B1(B1),
.B2(B2),
.in(in),
.out(audio_post_lpf1));
assign out = audio_post_lpf1;
endmodule

View File

@@ -0,0 +1,60 @@
/*MIT License
Copyright (c) 2019 Gregory Hogan (Soltan_G42)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
//This is a variation of Gregory Hogan's MISTer Genesis core low-pass filter
//tuned to match the lightest low-pass filter on Gyruss.
module gyruss_lpf_light(
input clk,
input reset,
input signed [15:0] in,
output signed [15:0] out);
localparam div = 10'd220; //Sample at 49.152/220 = 223418Hz
//Coefficients computed with Octave/Matlab/Online filter calculators.
//or with scipy.signal.bessel or similar tools
//0.017174022, 0.017174022
//1.0000000, -0.96565196
reg signed [17:0] A2;
reg signed [17:0] B2;
reg signed [17:0] B1;
wire signed [15:0] audio_post_lpf1;
always @ (*) begin
A2 = -18'd31642;
B1 = 18'd563;
B2 = 18'd563;
end
iir_1st_order lpf6db(.clk(clk),
.reset(reset),
.div(div),
.A2(A2),
.B1(B1),
.B2(B2),
.in(in),
.out(audio_post_lpf1));
assign out = audio_post_lpf1;
endmodule

View File

@@ -0,0 +1,60 @@
/*MIT License
Copyright (c) 2019 Gregory Hogan (Soltan_G42)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
//This is a variation of Gregory Hogan's MISTer Genesis core low-pass filter
//tuned to match the middle low-pass filter on Gyruss.
module gyruss_lpf_medium(
input clk,
input reset,
input signed [15:0] in,
output signed [15:0] out);
localparam div = 10'd220; //Sample at 49.152/220 = 223418Hz
//Coefficients computed with Octave/Matlab/Online filter calculators.
//or with scipy.signal.bessel or similar tools
//0.0053024160, 0.0053024160
//1.0000000, -0.98939517
reg signed [17:0] A2;
reg signed [17:0] B2;
reg signed [17:0] B1;
wire signed [15:0] audio_post_lpf1;
always @ (*) begin
A2 = -18'd32420;
B1 = 18'd174;
B2 = 18'd174;
end
iir_1st_order lpf6db(.clk(clk),
.reset(reset),
.div(div),
.A2(A2),
.B1(B1),
.B2(B2),
.in(in),
.out(audio_post_lpf1));
assign out = audio_post_lpf1;
endmodule

View File

@@ -0,0 +1,192 @@
//============================================================================
//
// Gyruss top-level module
// Copyright (C) 2021 Ace
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
//Module declaration, I/O ports
module Gyruss
(
input reset,
input clk_49m, //Actual frequency: 49.152MHz
input [1:0] coin, //0 = coin 1, 1 = coin 2
input [1:0] start_buttons, //0 = Player 1, 1 = Player 2
input [3:0] p1_joystick, p2_joystick, //0 = up, 1 = down, 2 = left, 3 = right
input p1_fire,
input p2_fire,
input btn_service,
input [23:0] dip_sw,
output video_hsync, video_vsync, video_csync,
output video_hblank, video_vblank,
output ce_pix,
output [2:0] video_r, video_g,
output [1:0] video_b,
output signed [15:0] sound_l, sound_r,
//Screen centering (alters HSync, VSync and VBlank timing in the Konami 082 to reposition the video output)
input [3:0] h_center, v_center,
input [24:0] ioctl_addr,
input [7:0] ioctl_data,
input ioctl_wr,
input pause,
input [10:0] hs_address,
input [7:0] hs_data_in,
output [7:0] hs_data_out,
input hs_write,
input hs_access,
output [15:0] main_cpu_rom_addr,
input [7:0] main_cpu_rom_do,
output [12:0] sub_cpu_rom_addr,
input [7:0] sub_cpu_rom_do,
output [12:0] sp_rom_addr,
input [31:0] sp_rom_do
);
//Linking signals between PCBs
wire A5, A6, irq_trigger, cs_sounddata, cs_controls_dip1, cs_dip2, cs_dip3;
wire [7:0] controls_dip, cpubrd_D;
//ROM loader signals for MISTer (loads ROMs from SD card)
wire ep1_cs_i, ep2_cs_i, ep3_cs_i, ep4_cs_i, ep5_cs_i, ep6_cs_i, ep7_cs_i, ep8_cs_i, ep9_cs_i,
ep10_cs_i, ep11_cs_i, ep12_cs_i;
wire cp_cs_i, tl_cs_i, sl_cs_i;
//MiSTer data write selector
selector DLSEL
(
.ioctl_addr(ioctl_addr),
.ep1_cs(ep1_cs_i),
.ep2_cs(ep2_cs_i),
.ep3_cs(ep3_cs_i),
.ep4_cs(ep4_cs_i),
.ep5_cs(ep5_cs_i),
.ep6_cs(ep6_cs_i),
.ep7_cs(ep7_cs_i),
.ep8_cs(ep8_cs_i),
.ep9_cs(ep9_cs_i),
.ep10_cs(ep10_cs_i),
.ep11_cs(ep11_cs_i),
.ep12_cs(ep12_cs_i),
.cp_cs(cp_cs_i),
.tl_cs(tl_cs_i),
.sl_cs(sl_cs_i)
);
//Instantiate main PCB
Gyruss_CPU main_pcb
(
.reset(reset),
.clk_49m(clk_49m),
.red(video_r),
.green(video_g),
.blue(video_b),
.video_hsync(video_hsync),
.video_vsync(video_vsync),
.video_csync(video_csync),
.video_hblank(video_hblank),
.video_vblank(video_vblank),
.ce_pix(ce_pix),
.h_center(h_center),
.v_center(v_center),
.controls_dip(controls_dip),
.cpubrd_Dout(cpubrd_D),
.cpubrd_A5(A5),
.cpubrd_A6(A6),
.cs_sounddata(cs_sounddata),
.irq_trigger(irq_trigger),
.cs_dip2(cs_dip2),
.cs_dip3(cs_dip3),
.cs_controls_dip1(cs_controls_dip1),
.ep1_cs_i(ep1_cs_i),
.ep2_cs_i(ep2_cs_i),
.ep3_cs_i(ep3_cs_i),
.ep4_cs_i(ep4_cs_i),
.ep5_cs_i(ep5_cs_i),
.ep6_cs_i(ep6_cs_i),
.ep7_cs_i(ep7_cs_i),
.ep8_cs_i(ep8_cs_i),
.ep9_cs_i(ep9_cs_i),
.cp_cs_i(cp_cs_i),
.tl_cs_i(tl_cs_i),
.sl_cs_i(sl_cs_i),
.ioctl_addr(ioctl_addr),
.ioctl_wr(ioctl_wr),
.ioctl_data(ioctl_data),
.pause(pause),
.hs_address(hs_address),
.hs_data_in(hs_data_in),
.hs_data_out(hs_data_out),
.hs_write(hs_write),
.hs_access(hs_access),
.main_cpu_rom_addr(main_cpu_rom_addr),
.main_cpu_rom_do(main_cpu_rom_do),
.sub_cpu_rom_addr(sub_cpu_rom_addr),
.sub_cpu_rom_do(sub_cpu_rom_do),
.sp_rom_addr(sp_rom_addr),
.sp_rom_do(sp_rom_do)
);
//Instantiate sound PCB
Gyruss_SND sound_pcb
(
.reset(reset),
.clk_49m(clk_49m),
.irq_trigger(irq_trigger),
.cs_sounddata(cs_sounddata),
.dip_sw(dip_sw),
.coin(coin),
.start_buttons(start_buttons),
.p1_joystick(p1_joystick),
.p2_joystick(p2_joystick),
.p1_fire(p1_fire),
.p2_fire(p2_fire),
.btn_service(btn_service),
.cs_controls_dip1(cs_controls_dip1),
.cs_dip2(cs_dip2),
.cs_dip3(cs_dip3),
.cpubrd_A5(A5),
.cpubrd_A6(A6),
.cpubrd_Din(cpubrd_D),
.controls_dip(controls_dip),
.sound_l(sound_l),
.sound_r(sound_r),
.ep10_cs_i(ep10_cs_i),
.ep11_cs_i(ep11_cs_i),
.ep12_cs_i(ep12_cs_i),
.ioctl_addr(ioctl_addr),
.ioctl_wr(ioctl_wr),
.ioctl_data(ioctl_data)
);
endmodule

View File

@@ -0,0 +1,996 @@
//============================================================================
//
// Gyruss main PCB model
// Copyright (C) 2021 Ace
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
//Module declaration, I/O ports
module Gyruss_CPU
(
input reset,
input clk_49m, //Actual frequency: 49.152MHz
output [2:0] red, green, //8-bit RGB, 3 bits per color for red and green,
output [1:0] blue, //2 bits for blue
output video_hsync, video_vsync, video_csync, //CSync not needed for MISTer
output video_hblank, video_vblank,
output ce_pix,
input [7:0] controls_dip,
output [7:0] cpubrd_Dout,
output cpubrd_A5, cpubrd_A6,
output cs_sounddata, irq_trigger,
output cs_dip3, cs_dip2, cs_controls_dip1,
//Screen centering (alters HSync, VSync and VBlank timing in the Konami 082 to reposition the video output)
input [3:0] h_center, v_center,
input ep1_cs_i,
input ep2_cs_i,
input ep3_cs_i,
input ep4_cs_i,
input ep5_cs_i,
input ep6_cs_i,
input ep7_cs_i,
input ep8_cs_i,
input ep9_cs_i,
input cp_cs_i,
input tl_cs_i,
input sl_cs_i,
input [24:0] ioctl_addr,
input [7:0] ioctl_data,
input ioctl_wr,
input pause,
input [12:0] hs_address,
input [7:0] hs_data_in,
output [7:0] hs_data_out,
input hs_write,
input hs_access,
output [15:0] main_cpu_rom_addr,
input [7:0] main_cpu_rom_do,
output [12:0] sub_cpu_rom_addr,
input [7:0] sub_cpu_rom_do,
output [12:0] sp_rom_addr,
input [31:0] sp_rom_do
);
//------------------------------------------------------- Signal outputs -------------------------------------------------------//
//Assign active high HBlank and VBlank outputs
assign video_hblank = hblk;
assign video_vblank = vblk;
//Output pixel clock enable
assign ce_pix = cen_6m;
//Output select lines for player inputs and DIP switches to sound board
assign cs_controls_dip1 = (~n_k501_enable & z80_A[14]) & (z80_A[8:7] == 2'b01) & n_ram_write;
assign cs_dip2 = (~n_k501_enable & z80_A[14]) & (z80_A[8:7] == 2'b00) & n_ram_write;
assign cs_dip3 = (~n_k501_enable & z80_A[14]) & (z80_A[8:7] == 2'b10) & n_ram_write;
//Output primary MC6809E address lines A5 and A6 to sound board
assign cpubrd_A5 = z80_A[5];
assign cpubrd_A6 = z80_A[6];
//Assign CPU board data output to sound board
assign cpubrd_Dout = z80_Dout;
//Generate and output chip select for latching sound data to sound CPU
assign cs_sounddata = (~n_k501_enable & z80_A[14]) & (z80_A[8:7] == 2'b10) & ~n_ram_write;
//Generate sound IRQ trigger
wire cs_soundirq = (~n_k501_enable & z80_A[14]) & (z80_A[8:7] == 2'b01) & ~n_ram_write;
reg sound_irq = 1;
always_ff @(posedge clk_49m) begin
if(n_cen_3m) begin
if(cs_soundirq)
sound_irq <= 1;
else
sound_irq <= 0;
end
end
assign irq_trigger = sound_irq;
//------------------------------------------------------- Clock division -------------------------------------------------------//
//Generate 12.288MHz, 6.144MHz, 3.072MHz and 1.576MHz clock enables
reg [4:0] div = 5'd0;
always_ff @(posedge clk_49m) begin
div <= div + 5'd1;
end
reg [3:0] n_div = 4'd0;
always_ff @(negedge clk_49m) begin
n_div <= n_div + 4'd1;
end
wire cen_12m = !div[1:0];
wire cen_6m = !div[2:0];
wire cen_3m = !div[3:0];
wire n_cen_3m = !n_div;
wire cen_1m5 = !div;
//Generate E and Q clock enables for KONAMI-1 (code adapted from Sorgelig's phase generator used in the MiSTer Vectrex core)
reg k1_E, k1_Q;
always_ff @(posedge clk_49m) begin
reg [1:0] clk_phase = 0;
k1_E <= 0;
k1_Q <= 0;
if(cen_6m) begin
clk_phase <= clk_phase + 1'd1;
case(clk_phase)
2'b01: k1_Q <= 1;
2'b10: k1_E <= 1;
endcase
end
end
//------------------------------------------------------------ CPUs ------------------------------------------------------------//
//Primary CPU - Zilog Z80 (uses T80s version of the T80 soft core)
wire [15:0] z80_A;
wire [7:0] z80_Dout;
wire n_mreq, n_rd, n_rfsh;
T80s u13G
(
.RESET_n(reset),
.CLK(clk_49m),
.CEN(n_cen_3m & ~pause),
.NMI_n(z80_nmi),
.WAIT_n(n_wait),
.MREQ_n(n_mreq),
.RD_n(n_rd),
.RFSH_n(n_rfsh),
.A(z80_A),
.DI(z80_Din),
.DO(z80_Dout)
);
//Address decoding for Z80
wire cs_rom1 = ~n_mreq & n_rfsh & (z80_A[15:13] == 3'b000);
wire cs_rom2 = ~n_mreq & n_rfsh & (z80_A[15:13] == 3'b001);
wire cs_rom3 = ~n_mreq & n_rfsh & (z80_A[15:13] == 3'b010);
wire n_cs_k501 = ~(~n_mreq & n_rfsh & z80_A[15]);
wire cs_mainlatch = (~n_k501_enable & z80_A[14]) & (z80_A[8:7] == 2'b11) & ~n_ram_write;
wire cs_z80sharedram = (z80_A[14:13] == 2'b01) & ~n_k501_enable;
wire n_cs_vram_wram = ~((z80_A[14:13] == 2'b00) & ~n_k501_enable);
//Part of the RAM decoding is handled by the Konami 501 custom chip - instantiate an instance of this IC here
wire n_wait, n_ram_write, n_k501_enable;
wire [7:0] k501_D, k501_Dout;
k501 u11E
(
.CLK(clk_49m),
.CEN(cen_12m),
.H1(h_cnt[0]),
.H2(h_cnt[1]),
.RAM(n_cs_k501),
.RD(n_rd),
.WAIT(n_wait),
.WRITE(n_ram_write),
.ENABLE(n_k501_enable),
.Di(z80_Dout),
.XDi(k501_Din),
.Do(k501_Dout),
.XDo(k501_D)
);
//Multiplex data inputs to Z80
wire [7:0] z80_Din = cs_rom1 ? eprom1_D:
cs_rom2 ? eprom2_D:
cs_rom3 ? eprom3_D:
~n_cs_k501 ? k501_Dout:
8'hFF;
assign main_cpu_rom_addr = z80_A[14:0];
//Z80 ROMs
//ROM 1/3
wire [7:0] eprom1_D = main_cpu_rom_do;
/*
eprom_1 u11J
(
.ADDR(z80_A[12:0]),
.CLK(clk_49m),
.DATA(eprom1_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep1_cs_i),
.WR(ioctl_wr)
);*/
//ROM 2/3
wire [7:0] eprom2_D = main_cpu_rom_do;
/*
eprom_2 u12J
(
.ADDR(z80_A[12:0]),
.CLK(clk_49m),
.DATA(eprom2_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep2_cs_i),
.WR(ioctl_wr)
);*/
//ROM 3/3
wire [7:0] eprom3_D = main_cpu_rom_do;
/*
eprom_3 u13J
(
.ADDR(z80_A[12:0]),
.CLK(clk_49m),
.DATA(eprom3_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep3_cs_i),
.WR(ioctl_wr)
);*/
//Multiplex data input to Konami 501 data bus passthrough
wire [7:0] k501_Din = (~n_cs_vram_wram & cs_wram0 & n_vram_wram_wr) ? wram0_D:
(~n_cs_vram_wram & cs_wram1 & n_vram_wram_wr) ? wram1_D:
(~n_cs_vram_wram & ~n_cs_vram & n_vram_wram_wr) ? vram_D:
(cs_z80sharedram & n_z80_sharedram_wr) ? z80_sharedram_D:
(cs_controls_dip1 | cs_dip2 | cs_dip3) ? controls_dip:
8'hFF;
//Main latch
reg z80_nmi_mask = 0;
reg flip = 0;
always_ff @(posedge clk_49m) begin
if(!reset) begin
z80_nmi_mask <= 0;
flip <= 0;
end
else if(n_cen_3m) begin
if(cs_mainlatch)
case(z80_A[2:0])
3'b000: z80_nmi_mask <= k501_D[0];
3'b101: flip <= k501_D[0];
default:;
endcase
end
end
//Generate VBlank NMI for Z80
reg z80_nmi = 1;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(!z80_nmi_mask)
z80_nmi <= 1;
else if(vblank_irq_en)
z80_nmi <= 0;
end
end
//VRAM
wire [7:0] vram_D;
spram #(8, 11) u5J
(
.clk(clk_49m),
.we(~n_cs_vram_wram & ~n_cs_vram & ~n_vram_wram_wr),
.addr(vram_wram_A),
.data(k501_D),
.q(vram_D)
);
//Z80 work RAM
//Bank 0
// Hiscore mux
wire [10:0] u3J_addr = hs_access ? hs_address[10:0] : vram_wram_A;
wire [7:0] u3J_din = hs_access ? hs_data_in : k501_D;
wire u3J_wren = hs_access ? hs_write : (~n_cs_vram_wram & cs_wram0 & ~n_vram_wram_wr);
wire [7:0] u3J_dout;
assign wram0_D = hs_access ? 8'h00 : u3J_dout;
assign hs_data_out = hs_access ? u3J_dout : 8'h00;
wire [7:0] wram0_D;
spram #(8, 11) u3J
(
.clk(clk_49m),
.we(u3J_wren),
.addr(u3J_addr),
.data(u3J_din),
.q(u3J_dout)
);
//Bank 1
wire [7:0] wram1_D;
spram #(8, 11) u2J
(
.clk(clk_49m),
.we(~n_cs_vram_wram & cs_wram1 & ~n_vram_wram_wr),
.addr(vram_wram_A),
.data(k501_D),
.q(wram1_D)
);
//Generate select lines and write enable for work RAM and VRAM
wire n_cs_vram = z80_A[12] & ~h_cnt[1];
wire cs_wram0 = n_cs_vram & ~z80_A[11];
wire cs_wram1 = n_cs_vram & z80_A[11];
wire n_vram_wram_wr = n_cs_vram_wram | n_ram_write;
//Shared RAM
wire [7:0] z80_sharedram_D, k1_sharedram_D;
dpram_dc #(.widthad_a(11)) u17C
(
.clock_a(clk_49m),
.address_a(z80_A[10:0]),
.data_a(k501_D),
.q_a(z80_sharedram_D),
.wren_a(cs_z80sharedram & ~n_z80_sharedram_wr),
.clock_b(clk_49m),
.address_b(k1_A[10:0]),
.data_b(k1_Dout),
.q_b(k1_sharedram_D),
.wren_b(cs_k1sharedram & ~k1_rw & h_cnt[1])
);
//Generate write enable for Z80 section of shared RAM (active low)
wire n_z80_sharedram_wr = ~cs_z80sharedram | n_ram_write;
//Secondary CPU - KONAMI-1 custom encrypted MC6809E (uses synchronous version of Greg Miller's cycle-accurate MC6809E made by
//Sorgelig with a wrapper to decrypt XOR/XNOR-encrypted opcodes and a further modification to Greg's MC6809E to directly
//accept the opcodes)
wire k1_rw;
wire [15:0] k1_A;
wire [7:0] k1_Dout;
KONAMI1 u18F
(
.CLK(clk_49m),
.fallE_en(k1_E),
.fallQ_en(k1_Q),
.D(k1_Din),
.DOut(k1_Dout),
.ADDR(k1_A),
.RnW(k1_rw),
.nIRQ(k1_irq),
.nFIRQ(1),
.nNMI(1),
.nHALT(1),
.nRESET(reset)
);
//Address decoding for KONAMI-1
wire cs_beam = (k1_A[15:13] == 3'b000) & k1_rw;
wire cs_k1irqmask = (k1_A[15:13] == 3'b001) & ~k1_rw;
wire cs_spriteram = (k1_A[15:13] == 3'b010);
wire cs_k1sharedram = (k1_A[15:13] == 3'b011);
wire cs_rom4 = (k1_A[15:13] == 3'b111);
//Multiplex data inputs to KONAMI-1
wire [7:0] k1_Din = cs_beam ? v_cnt:
(cs_spriteram & cs_spriteram0 & ~spriteram0_wr) ? spriteram_D[7:0]:
(cs_spriteram & cs_spriteram1 & ~spriteram1_wr) ? spriteram_D[15:8]:
(cs_k1sharedram & k1_rw & h_cnt[1]) ? k1_sharedram_D:
cs_rom4 ? eprom4_D:
8'hFF;
assign sub_cpu_rom_addr = k1_A[12:0];
//KONAMI-1 ROM
wire [7:0] eprom4_D = sub_cpu_rom_do;
/*
eprom_4 u19E
(
.ADDR(k1_A[12:0]),
.CLK(clk_49m),
.DATA(eprom4_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep4_cs_i),
.WR(ioctl_wr)
);
*/
//Generate write enable for all KONAMI-1 RAM (active low)
wire k1_rw1 = h1d | k1_rw;
//Generate IRQ mask for KONAMI-1
reg k1_irq_mask;
always_ff @(posedge clk_49m) begin
if(!reset)
k1_irq_mask <= 0;
else if(cen_3m && cs_k1irqmask)
k1_irq_mask <= k1_Dout[0];
end
//Generate VBlank IRQ for KONAMI-1
reg k1_irq = 1;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(!k1_irq_mask)
k1_irq <= 1;
else if(vblank_irq_en)
k1_irq <= 0;
end
end
//-------------------------------------------------------- Video timing --------------------------------------------------------//
//Konami 082 custom chip - responsible for all video timings
wire vblk, vblank_irq_en;
wire [8:0] h_cnt;
wire [7:0] v_cnt;
k082 u11G
(
.reset(1),
.clk(clk_49m),
.cen(cen_6m),
.h_center(h_center),
.v_center(v_center),
.n_vsync(video_vsync),
.sync(video_csync),
.n_hsync(video_hsync),
.vblk(vblk),
.vblk_irq_en(vblank_irq_en),
.h1(h_cnt[0]),
.h2(h_cnt[1]),
.h4(h_cnt[2]),
.h8(h_cnt[3]),
.h16(h_cnt[4]),
.h32(h_cnt[5]),
.h64(h_cnt[6]),
.h128(h_cnt[7]),
.n_h256(h_cnt[8]),
.v1(v_cnt[0]),
.v2(v_cnt[1]),
.v4(v_cnt[2]),
.v8(v_cnt[3]),
.v16(v_cnt[4]),
.v32(v_cnt[5]),
.v64(v_cnt[6]),
.v128(v_cnt[7])
);
//Latch vertical counter from 082 custom chip when the horizontal counter hits 256
reg [7:0] vcnt_lat = 8'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m && h_cnt == 9'd256)
vcnt_lat <= v_cnt;
end
//Latch least significant bit of horizontal counter (to be used for sprite RAM logic)
reg h1d;
always_ff @(posedge clk_49m) begin
if(cen_6m)
h1d <= h_cnt[0];
end
//XOR horizontal counter bits [7:2] with HFLIP flag
wire [7:2] hcnt_x = h_cnt[7:2] ^ {6{flip}};
//XOR latched vertical counter bits with VFLIP flag
wire [7:0] vcnt_x = vcnt_lat ^ {8{flip}};
//--------------------------------------------------------- Tile layer ---------------------------------------------------------//
//Generate addresses for VRAM
wire [10:0] vram_wram_A = h_cnt[1] ? {h_cnt[2], vcnt_x[7:3], hcnt_x[7:3]} : z80_A[10:0];
//LDO, labelled D1 in the Time Pilot schematics, signals to the tilemap logic when to latch tilemap codes from VRAM. It pulses
//low when the lower 3 bits of the horizontal counter are all 1, then on the rising edge, tilemap codes are latched.
//Set LDO as a register and latch a 0 when the 3 least significant bits of the horizontal counter are set to 1, otherwise latch
//a 1
reg ldo = 1;
always_ff @(posedge clk_49m) begin
if(h_cnt[2:0] == 3'b111)
ldo <= 0;
else
ldo <= 1;
end
//Latch tilemap code from VRAM at every rising edge of LDO (equivalent to when LDO is low and the 3 least significant bits of
//the horizontal counter are all set to 0)
reg [7:0] tile_code = 8'd0;
always_ff @(posedge clk_49m) begin
if(!ldo && h_cnt[2:0] == 3'b000)
tile_code <= vram_D;
end
//Latch tilemap attributes from VRAM at the rising edge of bit 2 of the horizontal counter when !H256 is high
reg tile_attrib_latch = 1;
always_ff @(posedge clk_49m) begin
if(h_cnt[2:0] == 3'b011)
tile_attrib_latch <= 0;
else
tile_attrib_latch <= 1;
end
reg [7:0] tile_attrib = 8'd0;
always_ff @(posedge clk_49m) begin
if(h_cnt[8] && !tile_attrib_latch && h_cnt[2:0] == 3'b100)
tile_attrib <= vram_D;
end
//Latch tile color information, tilemap enable and flip signal for tilemap 083 custom chip every 8 pixels
wire tile_flip = tile_hflip ^ ~flip;
reg tile_083_flip = 0;
reg tilemap_en = 0;
reg [3:0] tile_color = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(h_cnt[2:0] == 3'b011) begin
tile_083_flip <= tile_flip;
tile_color <= tile_attrib[3:0];
tilemap_en <= tile_attrib[4];
end
else begin
tile_083_flip <= tile_083_flip;
tile_color <= tile_color;
tilemap_en <= tilemap_en;
end
end
end
//Generate address lines A1 - A3 of tilemap ROMs, CRA and tile flip attributes on the falling edge of horizontal
//counter bit 2
reg v1l, v2l, v4l, cra, tile_hflip, tile_vflip;
reg old_h4;
always_ff @(posedge clk_49m) begin
old_h4 <= h_cnt[2];
if(old_h4 && !h_cnt[2]) begin
v1l <= vcnt_x[0];
v2l <= vcnt_x[1];
v4l <= vcnt_x[2];
tile_hflip <= tile_attrib[6];
tile_vflip <= tile_attrib[7];
cra <= tile_attrib[5];
end
end
//Address tilemap ROMs
assign tilerom_A[12] = cra;
assign tilerom_A[11:4] = tile_code;
assign tilerom_A[3:0] = {hcnt_x[2] ^ tile_hflip, v4l ^ tile_vflip, v2l ^ tile_vflip, v1l ^ tile_vflip};
//Tilemap ROM
wire [12:0] tilerom_A;
wire [7:0] eprom5_D;
eprom_5 u2H
(
.ADDR(tilerom_A),
.CLK(clk_49m),
.DATA(eprom5_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep5_cs_i),
.WR(ioctl_wr)
);
//Konami 083 custom chip 1/2 - this one shifts the pixel data from tilemap ROMs
k083 u3G
(
.CK(clk_49m),
.CEN(cen_6m),
.LOAD(h_cnt[1:0] == 2'b11),
.FLIP(tile_083_flip),
.DB0i(eprom5_D),
.DSH0(tile_lut_A[1:0])
);
//Tilemap lookup PROM
wire [7:0] tile_lut_A;
assign tile_lut_A[7:6] = 2'b00;
assign tile_lut_A[5:2] = tile_color;
wire [3:0] tile_D;
tile_lut_prom u3E
(
.ADDR(tile_lut_A),
.CLK(clk_49m),
.DATA(tile_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(tl_cs_i),
.WR(ioctl_wr)
);
//-------------------------------------------------------- Sprite layer --------------------------------------------------------//
//Generate sprite RAM enables (both active low)
wire n_cs_spriteram = ~cs_spriteram | ~h_cnt[1];
wire cs_spriteram0 = ~n_cs_spriteram & k1_A[1];
wire cs_spriteram1 = ~n_cs_spriteram & ~k1_A[1];
//Generate write enables for sprite RAM (active low)
wire spriteram0_wr = ~n_cs_spriteram & ~k1_rw1 & k1_A[1];
wire spriteram1_wr = ~n_cs_spriteram & ~k1_rw1 & ~k1_A[1];
//Sprite RAM
wire [15:0] spriteram_D;
wire [9:0] spriteram_A;
assign spriteram_A = h_cnt[1] ? {k1_A[10:2], k1_A[0]} : {3'b000, h_cnt[7], (h_cnt[8] ^ h_cnt[7]), h_cnt[6], h_cnt[3], h_cnt[4], h_cnt[5], h_cnt[2]};
//Bank 0 (lower 4 bits)
spram #(4, 10) u13A
(
.clk(clk_49m),
.we(cs_spriteram & cs_spriteram0 & spriteram0_wr),
.addr(spriteram_A),
.data(k1_Dout[3:0]),
.q(spriteram_D[3:0])
);
//Bank 0 (upper 4 bits)
spram #(4, 10) u14A
(
.clk(clk_49m),
.we(cs_spriteram & cs_spriteram0 & spriteram0_wr),
.addr(spriteram_A),
.data(k1_Dout[7:4]),
.q(spriteram_D[7:4])
);
//Bank 1 (lower 4 bits)
spram #(4, 10) u11A
(
.clk(clk_49m),
.we(cs_spriteram & cs_spriteram1 & spriteram1_wr),
.addr(spriteram_A),
.data(k1_Dout[3:0]),
.q(spriteram_D[11:8])
);
//Bank 1 (upper 4 bits)
spram #(4, 10) u12A
(
.clk(clk_49m),
.we(cs_spriteram & cs_spriteram1 & spriteram1_wr),
.addr(spriteram_A),
.data(k1_Dout[7:4]),
.q(spriteram_D[15:12])
);
//Latch all data output from sprite RAM at 1/4 of the pixel clock
reg [15:0] spriteram_reg = 16'd0;
always_ff @(posedge clk_49m) begin
if(cen_1m5)
spriteram_reg <= spriteram_D;
end
//Konami 503 custom chip - generates sprite addresses for lower half of sprite ROMs, sprite line buffer control, enable for
//sprite write and sprite flip for 083 custom chip.
wire cs_linebuffer, sprite_flip;
wire [5:0] k503_R;
k503 u9F
(
.OB(spriteram_reg[7:0]),
.VCNT(vcnt_lat),
.H4(h_cnt[2]),
.H8(0),
.LD(h_cnt[1:0] != 2'b11),
.OCS(cs_linebuffer),
.OFLP(sprite_flip),
.R(k503_R)
);
assign spriterom_A[5] = k503_R[5];
assign spriterom_A[3:0] = k503_R[3:0];
//Latch sprite code from sprite RAM bank 1 every 8 pixels
reg [7:0] sprite_code = 8'd0;
always_ff @(posedge clk_49m) begin
if(h_cnt[2:0] == 3'b000)
sprite_code <= spriteram_reg[15:8];
end
//Assign sprite code to address the upper 7 bits and bit 4 of the sprite ROMs
assign spriterom_A[12:6] = sprite_code[7:1];
assign spriterom_A[4] = sprite_code[0];
assign sp_rom_addr = spriterom_A;
//Sprite ROMs
wire [12:0] spriterom_A;
//ROM 1/4
wire [7:0] eprom6_D = sp_rom_do[7:0];
/*
eprom_6 u9C
(
.ADDR(spriterom_A),
.CLK(clk_49m),
.DATA(eprom6_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep6_cs_i),
.WR(ioctl_wr)
);
*/
//ROM 2/4
wire [7:0] eprom7_D = sp_rom_do[15:8];
/*
eprom_7 u8C
(
.ADDR(spriterom_A),
.CLK(clk_49m),
.DATA(eprom7_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep7_cs_i),
.WR(ioctl_wr)
);
*/
//ROM 3/4
wire [7:0] eprom8_D = sp_rom_do[23:16];
/*
eprom_8 u7C
(
.ADDR(spriterom_A),
.CLK(clk_49m),
.DATA(eprom8_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep8_cs_i),
.WR(ioctl_wr)
);*/
//ROM 4/4
wire [7:0] eprom9_D = sp_rom_do[31:24];
/*
eprom_9 u6C
(
.ADDR(spriterom_A),
.CLK(clk_49m),
.DATA(eprom9_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep9_cs_i),
.WR(ioctl_wr)
);
*/
//Latch sprite attributes and horizontal position every 8 pixels
reg [4:0] sprite_attrib = 5'd0;
reg [7:0] sprite_hpos = 8'd0;
always_ff @(posedge clk_49m) begin
if(h_cnt[2:0] == 3'b100) begin
sprite_attrib <= {spriteram_reg[5], spriteram_reg[3:0]};
sprite_hpos <= spriteram_reg[15:8];
end
end
//Multiplex sprite ROM data outputs based on the state of sprite attribute bit 4
reg spriterom_sel = 0;
always_ff @(posedge clk_49m) begin
if(h_cnt[2:0] == 3'b000)
spriterom_sel <= sprite_attrib[4];
end
wire [15:0] spriterom_D = spriterom_sel ? {eprom9_D, eprom7_D} : {eprom8_D, eprom6_D};
//Konami 083 custom chip 2/2 - shifts the pixel data from sprite ROMs
k083 u7F
(
.CK(clk_49m),
.CEN(cen_6m),
.LOAD(h_cnt[1:0] == 2'b11),
.FLIP(sprite_k083_flip),
.DB0i(spriterom_D[7:0]),
.DB1i(spriterom_D[15:8]),
.DSH0(sprite_lut_A[1:0]),
.DSH1(sprite_lut_A[3:2])
);
//Latch sprite color information, enable for sprite line buffer, sprite 083 flip at every 8 pixels
reg [3:0] sprite_color = 4'd0;
reg sprite_lbuff_en, sprite_k083_flip;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(h_cnt[2:0] == 3'b011) begin
sprite_color <= sprite_attrib[3:0];
sprite_lbuff_en <= cs_linebuffer;
sprite_k083_flip <= sprite_flip;
end
end
end
//Assign sprite color information to the upper 4 bits of the sprite lookup PROM
assign sprite_lut_A[7:4] = sprite_color;
//Sprite lookup PROM
wire [7:0] sprite_lut_A;
wire [3:0] sprite_lut_D;
sprite_lut_prom u6F
(
.ADDR(sprite_lut_A),
.CLK(clk_49m),
.DATA(sprite_lut_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(sl_cs_i),
.WR(ioctl_wr)
);
//Konami 502 custom chip, responsible for generating sprites (sits between sprite ROMs and the sprite line buffer)
wire [7:0] sprite_lbuff_Do;
wire [4:0] sprite_D;
wire sprite_lbuff_sel, sprite_lbuff_dec0, sprite_lbuff_dec1;
k502 u6B
(
.RESET(1),
.CK1(clk_49m),
.CK2(clk_49m),
.CEN(cen_6m),
.LD0(h_cnt[2:0] == 3'b000),
.H2(h_cnt[1]),
.H256(h_cnt[8]),
.SPAL(sprite_lut_D),
.SPLBi({sprite_lbuff1_D, sprite_lbuff0_D}),
.SPLBo(sprite_lbuff_Do),
.OSEL(sprite_lbuff_sel),
.OLD(sprite_lbuff_dec1),
.OCLR(sprite_lbuff_dec0),
.COL(sprite_D)
);
//----------------------------------------------------- Sprite line buffer -----------------------------------------------------//
//Generate load and clear signals for counters generating addresses to sprite line buffer
reg sprite_lbuff0_ld, sprite_lbuff1_ld, sprite_lbuff0_clr, sprite_lbuff1_clr;
always_ff @(posedge clk_49m) begin
if(h_cnt[1:0] == 2'b11) begin
if(sprite_lbuff_dec0 && !sprite_lbuff_dec1) begin
sprite_lbuff0_clr <= 0;
sprite_lbuff1_clr <= 1;
end
else if(!sprite_lbuff_dec0 && sprite_lbuff_dec1) begin
sprite_lbuff0_clr <= 1;
sprite_lbuff1_clr <= 0;
end
else begin
sprite_lbuff0_clr <= 0;
sprite_lbuff1_clr <= 0;
end
end
else begin
sprite_lbuff0_clr <= 0;
sprite_lbuff1_clr <= 0;
end
if(h_cnt[2:0] == 3'b011) begin
if(!sprite_lbuff_dec1) begin
sprite_lbuff0_ld <= 1;
sprite_lbuff1_ld <= 0;
end
else begin
sprite_lbuff0_ld <= 0;
sprite_lbuff1_ld <= 1;
end
end
else begin
sprite_lbuff0_ld <= 0;
sprite_lbuff1_ld <= 0;
end
end
//Generate addresses for sprite line buffer
//Bank 0, lower 4 bits
reg [3:0] linebuffer0_l = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(sprite_lbuff0_clr)
linebuffer0_l <= 4'd0;
else
if(sprite_lbuff0_ld)
linebuffer0_l <= sprite_hpos[3:0];
else
linebuffer0_l <= linebuffer0_l + 4'd1;
end
end
//Bank 0, upper 4 bits
reg [3:0] linebuffer0_h = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(sprite_lbuff0_clr)
linebuffer0_h <= 4'd0;
else
if(sprite_lbuff0_ld)
linebuffer0_h <= sprite_hpos[7:4];
else if(linebuffer0_l == 4'hF)
linebuffer0_h <= linebuffer0_h + 4'd1;
end
end
wire [7:0] sprite_lbuff0_A = {linebuffer0_h, linebuffer0_l};
//Bank 1, lower 4 bits
reg [3:0] linebuffer1_l = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(sprite_lbuff1_clr)
linebuffer1_l <= 4'd0;
else
if(sprite_lbuff1_ld)
linebuffer1_l <= sprite_hpos[3:0];
else
linebuffer1_l <= linebuffer1_l + 4'd1;
end
end
//Bank 1, upper 4 bits
reg [3:0] linebuffer1_h = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(sprite_lbuff1_clr)
linebuffer1_h <= 4'd0;
else
if(sprite_lbuff1_ld)
linebuffer1_h <= sprite_hpos[7:4];
else if(linebuffer1_l == 4'hF)
linebuffer1_h <= linebuffer1_h + 4'd1;
end
end
wire [7:0] sprite_lbuff1_A = {linebuffer1_h, linebuffer1_l};
//Generate chip select signals for sprite line buffer
wire cs_sprite_lbuff0 = ~(sprite_lbuff_en & sprite_lbuff_sel);
wire cs_sprite_lbuff1 = ~(sprite_lbuff_en & ~sprite_lbuff_sel);
//Sprite line buffer bank 0
wire [3:0] sprite_lbuff0_D;
spram #(4, 8) u7A
(
.clk(clk_49m),
.we(cen_6m & cs_sprite_lbuff0),
.addr(sprite_lbuff0_A),
.data(sprite_lbuff_Do[3:0]),
.q(sprite_lbuff0_D)
);
//Sprite line buffer bank 1
wire [3:0] sprite_lbuff1_D;
spram #(4, 8) u7B
(
.clk(clk_49m),
.we(cen_6m & cs_sprite_lbuff1),
.addr(sprite_lbuff1_A),
.data(sprite_lbuff_Do[7:4]),
.q(sprite_lbuff1_D)
);
//----------------------------------------------------- Final video output -----------------------------------------------------//
//Generate HBlank (active high) while the horizontal counter is between 141 and 268
wire hblk = (h_cnt > 140 && h_cnt < 269);
//Multiplex tile and sprite data
wire tile_sprite_sel = (tilemap_en | sprite_D[4]);
wire [3:0] tile_sprite_D = tile_sprite_sel ? tile_D[3:0] : sprite_D[3:0];
//Latch pixel data for color PROM
reg [4:0] pixel_D;
always_ff @(posedge clk_49m) begin
if(cen_6m)
pixel_D <= {tile_sprite_sel, tile_sprite_D};
end
//Color PROM
wire [4:0] color_A = pixel_D;
wire [2:0] prom_red, prom_green;
wire [1:0] prom_blue;
color_prom u2A
(
.ADDR(color_A),
.CLK(clk_49m),
.DATA({prom_blue, prom_green, prom_red}),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(cp_cs_i),
.WR(ioctl_wr)
);
//Output video signal from color PROMs, otherwise output black if in HBlank or VBlank
assign red = (hblk | vblk) ? 3'h0 : prom_red;
assign green = (hblk | vblk) ? 3'h0 : prom_green;
assign blue = (hblk | vblk) ? 2'h0 : prom_blue;
endmodule

View File

@@ -0,0 +1,277 @@
module Gyruss_MiST (
output LED,
output [5:0] VGA_R,
output [5:0] VGA_G,
output [5:0] VGA_B,
output VGA_HS,
output VGA_VS,
output AUDIO_L,
output AUDIO_R,
input SPI_SCK,
output SPI_DO,
input SPI_DI,
input SPI_SS2,
input SPI_SS3,
input CONF_DATA0,
input CLOCK_27,
output [12:0] SDRAM_A,
inout [15:0] SDRAM_DQ,
output SDRAM_DQML,
output SDRAM_DQMH,
output SDRAM_nWE,
output SDRAM_nCAS,
output SDRAM_nRAS,
output SDRAM_nCS,
output [1:0] SDRAM_BA,
output SDRAM_CLK,
output SDRAM_CKE
);
`include "rtl\build_id.v"
localparam CONF_STR = {
"GYRUSS;;",
"O2,Rotate Controls,Off,On;",
"O34,Scanlines,Off,25%,50%,75%;",
"O5,Blend,Off,On;",
"O6,Joystick Swap,Off,On;",
"O7,Service,Off,On;",
"DIP;",
"T0,Reset;",
"V,v1.00.",`BUILD_DATE
};
wire rotate = status[2];
wire [1:0] scanlines = status[4:3];
wire blend = status[5];
wire joyswap = status[6];
wire service = status[7];
wire [1:0] orientation = 2'b11;
wire [23:0] dip_sw = ~status[31:8];
assign LED = ~ioctl_downl;
assign SDRAM_CLK = clock_49;
assign SDRAM_CKE = 1;
wire clock_49, pll_locked;
pll pll(
.inclk0(CLOCK_27),
.c0(clock_49),//49.152MHz
.locked(pll_locked)
);
wire [31:0] status;
wire [1:0] buttons;
wire [1:0] switches;
wire [7:0] joystick_0;
wire [7:0] joystick_1;
wire scandoublerD;
wire ypbpr;
wire no_csync;
wire [6:0] core_mod;
wire [15:0] audio_l, audio_r;
wire hs, vs, cs;
wire hb, vb;
wire blankn = ~(hb | vb);
wire [2:0] r, g;
wire [1:0] b;
wire key_strobe;
wire key_pressed;
wire [7:0] key_code;
wire [15:0] main_rom_addr;
wire [15:0] main_rom_do;
wire [12:0] sub_rom_addr;
wire [15:0] sub_rom_do;
wire [12:0] sp_addr;
wire [31:0] sp_do;
wire ioctl_downl;
wire [7:0] ioctl_index;
wire ioctl_wr;
wire [24:0] ioctl_addr;
wire [7:0] ioctl_dout;
data_io data_io(
.clk_sys ( clock_49 ),
.SPI_SCK ( SPI_SCK ),
.SPI_SS2 ( SPI_SS2 ),
.SPI_DI ( SPI_DI ),
.ioctl_download( ioctl_downl ),
.ioctl_index ( ioctl_index ),
.ioctl_wr ( ioctl_wr ),
.ioctl_addr ( ioctl_addr ),
.ioctl_dout ( ioctl_dout )
);
wire [24:0] bg_ioctl_addr = ioctl_addr - 16'hA000;
reg port1_req, port2_req;
sdram #(49) sdram(
.*,
.init_n ( pll_locked ),
.clk ( clock_49 ),
.port1_req ( port1_req ),
.port1_ack ( ),
.port1_a ( ioctl_addr[23:1] ),
.port1_ds ( {ioctl_addr[0], ~ioctl_addr[0]} ),
.port1_we ( ioctl_downl ),
.port1_d ( {ioctl_dout, ioctl_dout} ),
.port1_q ( ),
.cpu1_addr ( ioctl_downl ? 16'hffff : {2'b00, main_rom_addr[14:1]} ),
.cpu1_q ( main_rom_do ),
.cpu2_addr ( ioctl_downl ? 16'hffff : sub_rom_addr[12:1] + 16'h3000 ),
.cpu2_q ( sub_rom_do ),
.cpu3_addr ( ),
.cpu3_q ( ),
// port2 for sprite graphics
.port2_req ( port2_req ),
.port2_ack ( ),
.port2_a ( {bg_ioctl_addr[23:15], bg_ioctl_addr[12:0], bg_ioctl_addr[14]} ), // merge sprite roms to 32-bit wide words
.port2_ds ( {bg_ioctl_addr[13], ~bg_ioctl_addr[13]} ),
.port2_we ( ioctl_downl ),
.port2_d ( {ioctl_dout, ioctl_dout} ),
.port2_q ( ),
.sp_addr ( ioctl_downl ? 16'hffff : sp_addr ),
.sp_q ( sp_do )
);
// ROM download controller
always @(posedge clock_49) begin
reg ioctl_wr_last = 0;
ioctl_wr_last <= ioctl_wr;
if (ioctl_downl) begin
if (~ioctl_wr_last && ioctl_wr) begin
port1_req <= ~port1_req;
port2_req <= ~port2_req;
end
end
end
reg reset = 1;
reg rom_loaded = 0;
always @(posedge clock_49) begin
reg ioctl_downlD;
ioctl_downlD <= ioctl_downl;
if (ioctl_downlD & ~ioctl_downl) rom_loaded <= 1;
reset <= status[0] | buttons[1] | ~rom_loaded;
end
Gyruss Gyruss(
.reset(~reset),
.clk_49m(clock_49),
.coin({~m_coin2,~m_coin1}),
.start_buttons({~m_two_players,~m_one_player}),
.p1_joystick({~m_right, ~m_left, ~m_down, ~m_up}),
.p2_joystick({~m_right2, ~m_left2, ~m_down2, ~m_up2}),
.p1_fire(~m_fireA),
.p2_fire(~m_fire2A),
.btn_service(~service),
.dip_sw(dip_sw),
.video_hsync(hs),
.video_vsync(vs),
.video_csync(cs),
.video_hblank(hb),
.video_vblank(vb),
.video_r(r),
.video_g(g),
.video_b(b),
.sound_l(audio_l),
.sound_r(audio_r),
.ioctl_addr(ioctl_addr),
.ioctl_data(ioctl_dout),
.ioctl_wr(ioctl_wr),
.main_cpu_rom_addr(main_rom_addr),
.main_cpu_rom_do(main_rom_addr[0] ? main_rom_do[15:8] : main_rom_do[7:0]),
.sub_cpu_rom_addr(sub_rom_addr),
.sub_cpu_rom_do(sub_rom_addr[0] ? sub_rom_do[15:8] : sub_rom_do[7:0]),
.sp_rom_addr(sp_addr),
.sp_rom_do(sp_do)
);
mist_video #(.COLOR_DEPTH(3), .SD_HCNT_WIDTH(10)) mist_video(
.clk_sys ( clock_49 ),
.SPI_SCK ( SPI_SCK ),
.SPI_SS3 ( SPI_SS3 ),
.SPI_DI ( SPI_DI ),
.R ( blankn ? r : 0 ),
.G ( blankn ? g : 0 ),
.B ( blankn ? {b, b[1]} : 0 ),
.HSync ( hs ),
.VSync ( vs ),
.VGA_R ( VGA_R ),
.VGA_G ( VGA_G ),
.VGA_B ( VGA_B ),
.VGA_VS ( VGA_VS ),
.VGA_HS ( VGA_HS ),
.ce_divider ( 0 ),
.rotate ( { orientation[1], rotate } ),
.blend ( blend ),
.scandoubler_disable( scandoublerD ),
.scanlines ( scanlines ),
.ypbpr ( ypbpr ),
.no_csync ( no_csync )
);
user_io #(.STRLEN(($size(CONF_STR)>>3)))user_io(
.clk_sys (clock_49 ),
.conf_str (CONF_STR ),
.SPI_CLK (SPI_SCK ),
.SPI_SS_IO (CONF_DATA0 ),
.SPI_MISO (SPI_DO ),
.SPI_MOSI (SPI_DI ),
.buttons (buttons ),
.switches (switches ),
.scandoubler_disable (scandoublerD),
.ypbpr (ypbpr ),
.no_csync (no_csync ),
.core_mod (core_mod ),
.key_strobe (key_strobe ),
.key_pressed (key_pressed ),
.key_code (key_code ),
.joystick_0 (joystick_0 ),
.joystick_1 (joystick_1 ),
.status (status )
);
dac #(.C_bits(16))dac_l(
.clk_i(clock_49),
.res_n_i(1'b1),
.dac_i({~audio_l[15], audio_l[14:0]}),
.dac_o(AUDIO_L)
);
dac #(.C_bits(16))dac_r(
.clk_i(clock_49),
.res_n_i(1'b1),
.dac_i({~audio_r[15], audio_r[14:0]}),
.dac_o(AUDIO_R)
);
wire m_up, m_down, m_left, m_right, m_fireA, m_fireB, m_fireC, m_fireD, m_fireE, m_fireF;
wire m_up2, m_down2, m_left2, m_right2, m_fire2A, m_fire2B, m_fire2C, m_fire2D, m_fire2E, m_fire2F;
wire m_tilt, m_coin1, m_coin2, m_coin3, m_coin4, m_one_player, m_two_players, m_three_players, m_four_players;
arcade_inputs inputs (
.clk ( clock_49 ),
.key_strobe ( key_strobe ),
.key_pressed ( key_pressed ),
.key_code ( key_code ),
.joystick_0 ( joystick_0 ),
.joystick_1 ( joystick_1 ),
.rotate ( rotate ),
.orientation ( orientation ),
.joyswap ( joyswap ),
.oneplayer ( 1'b0 ),
.controls ( {m_tilt, m_coin4, m_coin3, m_coin2, m_coin1, m_four_players, m_three_players, m_two_players, m_one_player} ),
.player1 ( {m_fireF, m_fireE, m_fireD, m_fireC, m_fireB, m_fireA, m_up, m_down, m_left, m_right} ),
.player2 ( {m_fire2F, m_fire2E, m_fire2D, m_fire2C, m_fire2B, m_fire2A, m_up2, m_down2, m_left2, m_right2} )
);
endmodule

View File

@@ -0,0 +1,762 @@
//============================================================================
//
// Gyruss sound PCB model
// Copyright (C) 2021 Ace
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
module Gyruss_SND
(
input reset,
input clk_49m, //Actual frequency: 49.152MHz
input [23:0] dip_sw,
input [1:0] coin, //0 = coin 1, 1 = coin 2
input [1:0] start_buttons, //0 = Player 1, 1 = Player 2
input [3:0] p1_joystick, p2_joystick, //0 = up, 1 = down, 2 = left, 3 = right
input p1_fire,
input p2_fire,
input btn_service,
input cpubrd_A5, cpubrd_A6,
input cs_controls_dip1, cs_dip2, cs_dip3,
input irq_trigger, cs_sounddata,
input [7:0] cpubrd_Din,
output [7:0] controls_dip,
output signed [15:0] sound_l, sound_r,
input ep10_cs_i,
input ep11_cs_i,
input ep12_cs_i,
input [24:0] ioctl_addr,
input [7:0] ioctl_data,
input ioctl_wr
//The sound board contains a video passthrough but video will instead be tapped
//straight from the CPU board implementation (this passthrough is redundant for
//an FPGA implementation)
);
//------------------------------------------------------- Signal outputs -------------------------------------------------------//
//Multiplex controls and DIP switches to be output to CPU board
assign controls_dip = cs_controls_dip1 ? controls_dip1:
cs_dip2 ? dip_sw[15:8]:
cs_dip3 ? dip_sw[23:16]:
8'hFF;
//------------------------------------------------------- Clock division -------------------------------------------------------//
//Generate clock enables for sound data and IRQ logic, and DC offset removal
reg [8:0] div = 9'd0;
always_ff @(posedge clk_49m) begin
div <= div + 9'd1;
end
reg [3:0] n_div = 4'd0;
always_ff @(negedge clk_49m) begin
n_div <= n_div + 4'd1;
end
wire n_cen_3m = !n_div;
wire cen_dcrm = !div;
//Generate 3.579545MHz clock enable for Z80, 1.789772MHz clock enable for AY-3-8910s, clock enable for AY-3-8910 timer
//(uses Jotego's fractional clock divider from JTFRAME)
wire cen_3m58, cen_1m79, cen_timer;
jtframe_frac_cen #(11) sound_cen
(
.clk(clk_49m),
.n(10'd60),
.m(10'd824),
.cen({cen_timer, 8'bZZZZZZZZ, cen_1m79, cen_3m58})
);
//Also use Jotego's fractional clock divider to generate an 8MHz clock enable for the i8039 MCU
wire cen_8m;
jtframe_frac_cen #(2) i8039_cen
(
.clk(clk_49m),
.n(10'd42),
.m(10'd258),
.cen({1'bZ, cen_8m})
);
//------------------------------------------------------------ CPU -------------------------------------------------------------//
//Sound CPU - Zilog Z80 (uses T80s version of the T80 soft core)
wire [15:0] sound_A;
wire [7:0] sound_Dout;
wire n_m1, n_mreq, n_iorq, n_rd, n_wr, n_rfsh;
T80s u6B
(
.RESET_n(reset),
.CLK(clk_49m),
.CEN(cen_3m58),
.INT_n(n_irq),
.M1_n(n_m1),
.MREQ_n(n_mreq),
.IORQ_n(n_iorq),
.RD_n(n_rd),
.WR_n(n_wr),
.RFSH_n(n_rfsh),
.A(sound_A),
.DI(sound_Din),
.DO(sound_Dout)
);
//Address decoding for Z80
wire n_rw = n_rd & n_wr;
wire cs_soundrom0 = (~n_rw & ~n_mreq & n_rfsh & (sound_A[15:13] == 3'b000));
wire cs_soundrom1 = (~n_rw & ~n_mreq & n_rfsh & (sound_A[15:13] == 3'b001));
wire cs_soundram = (~n_rw & ~n_mreq & n_rfsh & (sound_A[15:13] == 3'b011));
wire cs_sound = (~n_rw & ~n_mreq & n_rfsh & (sound_A[15:13] == 3'b100));
wire cs_ay1 = (~n_iorq & n_m1 & (sound_A[4:2] == 3'b000));
wire cs_ay2 = (~n_iorq & n_m1 & (sound_A[4:2] == 3'b001));
wire cs_ay3 = (~n_iorq & n_m1 & (sound_A[4:2] == 3'b010));
wire cs_ay4 = (~n_iorq & n_m1 & (sound_A[4:2] == 3'b011));
wire cs_ay5 = (~n_iorq & n_m1 & (sound_A[4:2] == 3'b100));
wire cs_i8039_irq = (~n_iorq & n_m1 & (sound_A[4:2] == 3'b101));
wire cs_i8039_latch = (~n_iorq & n_m1 & (sound_A[4:2] == 3'b110));
//Multiplex data input to Z80
wire [7:0] sound_Din = cs_soundrom0 ? eprom10_D:
cs_soundrom1 ? eprom11_D:
(cs_soundram & n_wr) ? sndram_D:
cs_sound ? sound_D:
(~ay1_bdir & ay1_bc1) ? ay1_D:
(~ay2_bdir & ay2_bc1) ? ay2_D:
(~ay3_bdir & ay3_bc1) ? ay3_D:
(~ay4_bdir & ay4_bc1) ? ay4_D:
(~ay5_bdir & ay5_bc1) ? ay5_D:
8'hFF;
//Sound ROMs
//ROM 1/2
wire [7:0] eprom10_D;
eprom_10 u6A
(
.ADDR(sound_A[12:0]),
.CLK(clk_49m),
.DATA(eprom10_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep10_cs_i),
.WR(ioctl_wr)
);
//ROM 2/2
wire [7:0] eprom11_D;
eprom_11 u8A
(
.ADDR(sound_A[12:0]),
.CLK(clk_49m),
.DATA(eprom11_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep11_cs_i),
.WR(ioctl_wr)
);
//Sound RAM (lower 4 bits)
wire [7:0] sndram_D;
spram #(4, 10) u4A
(
.clk(clk_49m),
.we(cs_soundram & ~n_wr),
.addr(sound_A[9:0]),
.data(sound_Dout[3:0]),
.q(sndram_D[3:0])
);
//Sound RAM (upper 4 bits)
spram #(4, 10) u5A
(
.clk(clk_49m),
.we(cs_soundram & ~n_wr),
.addr(sound_A[9:0]),
.data(sound_Dout[7:4]),
.q(sndram_D[7:4])
);
//Latch sound data coming in from CPU board
reg [7:0] sound_D = 8'd0;
always_ff @(posedge clk_49m) begin
if(n_cen_3m && cs_sounddata)
sound_D <= cpubrd_Din;
end
//Generate Z80 interrupts
wire irq_clr = (~reset | ~(n_iorq | n_m1));
reg n_irq = 1;
always_ff @(posedge clk_49m or posedge irq_clr) begin
if(irq_clr)
n_irq <= 1;
else if(n_cen_3m && irq_trigger)
n_irq <= 0;
end
//--------------------------------------------------- Controls & DIP switches --------------------------------------------------//
//Multiplex player inputs and DIP switch bank 1
wire [7:0] controls_dip1 = ({cpubrd_A6, cpubrd_A5} == 2'b00) ? {3'b111, start_buttons, btn_service, coin}:
({cpubrd_A6, cpubrd_A5} == 2'b01) ? {3'b111, p1_fire, p1_joystick[1:0], p1_joystick[3:2]}:
({cpubrd_A6, cpubrd_A5} == 2'b10) ? {3'b111, p2_fire, p2_joystick[1:0], p2_joystick[3:2]}:
({cpubrd_A6, cpubrd_A5} == 2'b11) ? dip_sw[7:0]:
8'hFF;
//--------------------------------------------------------- Sound chips --------------------------------------------------------//
//Generate BC1 and BDIR signals for the five AY-3-8910s
wire ay1_bdir = ~(~cs_ay1 | sound_A[0]);
wire ay1_bc1 = ~(~cs_ay1 | sound_A[1]);
wire ay2_bdir = ~(~cs_ay2 | sound_A[0]);
wire ay2_bc1 = ~(~cs_ay2 | sound_A[1]);
wire ay3_bdir = ~(~cs_ay3 | sound_A[0]);
wire ay3_bc1 = ~(~cs_ay3 | sound_A[1]);
wire ay4_bdir = ~(~cs_ay4 | sound_A[0]);
wire ay4_bc1 = ~(~cs_ay4 | sound_A[1]);
wire ay5_bdir = ~(~cs_ay5 | sound_A[0]);
wire ay5_bc1 = ~(~cs_ay5 | sound_A[1]);
//AY-3-8910 timer (code adapted from MiSTer-X's Gyruss core)
reg [3:0] timer_sel;
wire [3:0] timer_val;
always_comb begin
case(timer_sel)
0: timer_val = 4'h0;
1: timer_val = 4'h1;
2: timer_val = 4'h2;
3: timer_val = 4'h3;
4: timer_val = 4'h4;
5: timer_val = 4'h9;
6: timer_val = 4'hA;
7: timer_val = 4'hB;
8: timer_val = 4'hA;
9: timer_val = 4'hD;
default: timer_val = 0;
endcase
end
reg [3:0] timer = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_timer) begin
timer <= timer_val;
timer_sel <= (timer_sel == 4'd9) ? 4'd0 : (timer_sel + 4'd1);
end
end
//Sound chip 1 (AY-3-8910 - uses JT49 by Jotego)
wire [7:0] ay1_D;
wire [7:0] ay1A_raw, ay1B_raw, ay1C_raw;
wire [5:0] ay1_filter;
jt49_bus #(.COMP(3'b100)) u11D
(
.rst_n(reset),
.clk(clk_49m),
.clk_en(cen_1m79),
.bdir(ay1_bdir),
.bc1(ay1_bc1),
.din(sound_Dout),
.sel(1),
.dout(ay1_D),
.A(ay1A_raw),
.B(ay1B_raw),
.C(ay1C_raw),
.IOB_out({2'bZZ, ay1_filter})
);
//Sound chip 2 (AY-3-8910 - uses JT49 by Jotego)
wire [7:0] ay2_D;
wire [7:0] ay2A_raw, ay2B_raw, ay2C_raw;
wire [5:0] ay2_filter;
jt49_bus #(.COMP(3'b100)) u12D
(
.rst_n(reset),
.clk(clk_49m),
.clk_en(cen_1m79),
.bdir(ay2_bdir),
.bc1(ay2_bc1),
.din(sound_Dout),
.sel(1),
.dout(ay2_D),
.A(ay2A_raw),
.B(ay2B_raw),
.C(ay2C_raw),
.IOB_out({2'bZZ, ay2_filter})
);
//Sound chip 3 (AY-3-8910 - uses JT49 by Jotego)
wire [7:0] ay3_D;
wire [7:0] ay3A_raw, ay3B_raw, ay3C_raw;
jt49_bus #(.COMP(3'b100)) u10B
(
.rst_n(reset),
.clk(clk_49m),
.clk_en(cen_1m79),
.bdir(ay3_bdir),
.bc1(ay3_bc1),
.din(sound_Dout),
.sel(1),
.dout(ay3_D),
.A(ay3A_raw),
.B(ay3B_raw),
.C(ay3C_raw),
.IOA_in({4'b0000, timer})
);
//Sound chip 4 (AY-3-8910 - uses JT49 by Jotego)
wire [7:0] ay4_D;
wire [7:0] ay4A_raw, ay4B_raw, ay4C_raw;
jt49_bus #(.COMP(3'b100)) u9B
(
.rst_n(reset),
.clk(clk_49m),
.clk_en(cen_1m79),
.bdir(ay4_bdir),
.bc1(ay4_bc1),
.din(sound_Dout),
.sel(1),
.dout(ay4_D),
.A(ay4A_raw),
.B(ay4B_raw),
.C(ay4C_raw)
);
//Sound chip 5 (AY-3-8910 - uses JT49 by Jotego)
wire [7:0] ay5_D;
wire [7:0] ay5A_raw, ay5B_raw, ay5C_raw;
jt49_bus #(.COMP(3'b100)) u8B
(
.rst_n(reset),
.clk(clk_49m),
.clk_en(cen_1m79),
.bdir(ay5_bdir),
.bc1(ay5_bc1),
.din(sound_Dout),
.sel(1),
.dout(ay5_D),
.A(ay5A_raw),
.B(ay5B_raw),
.C(ay5C_raw)
);
//Sound chip 6 (Intel 8039 MCU - uses t8039_notri variant of T48)
wire [7:0] i8039_raw;
wire [7:0] i8039_Dout;
wire i8039_ale, n_i8039_psen, n_i8039_rd, n_i8039_irq_clr;
t8039_notri u7H
(
.xtal_i(clk_49m),
.xtal_en_i(cen_8m),
.reset_n_i(reset),
.int_n_i(n_i8039_irq),
.ea_i(1),
.rd_n_o(n_i8039_rd),
.psen_n_o(n_i8039_psen),
.ale_o(i8039_ale),
.db_i(i8039_Din),
.db_o(i8039_Dout),
.p2_o({n_i8039_irq_clr, 3'bZZZ, eprom12_A[11:8]}),
.p1_o(i8039_raw)
);
//Multiplex data into i8039
wire [7:0] i8039_Din = ~n_i8039_psen ? eprom12_D:
~n_i8039_rd ? i8039_latch:
8'hFF;
//i8039 ROM
wire [11:0] eprom12_A;
wire [7:0] eprom12_D;
eprom_12 u11H
(
.ADDR(eprom12_A),
.CLK(clk_49m),
.DATA(eprom12_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep12_cs_i),
.WR(ioctl_wr)
);
//Latch data from Z80 to i8039
reg [7:0] i8039_latch = 8'd0;
always_ff @(posedge clk_49m) begin
if(cen_3m58 && cs_i8039_latch)
i8039_latch <= sound_Dout;
end
//Latch address lines A[7:0] for MCU ROM
reg [7:0] i8039_rom_lat = 8'd0;
always_ff @(negedge i8039_ale) begin
i8039_rom_lat <= i8039_Dout;
end
assign eprom12_A[7:0] = i8039_rom_lat;
//Generate i8039 IRQ
reg n_i8039_irq = 1;
always_ff @(posedge clk_49m) begin
if(!n_i8039_irq_clr)
n_i8039_irq <= 1;
else if(cen_3m58 && cs_i8039_irq)
n_i8039_irq <= 0;
end
//----------------------------------------------------- Final audio output -----------------------------------------------------//
//Apply gain and remove DC offset from AY-3-8910s and i8039 (uses jt49_dcrm2 from JT49 by Jotego for DC offset removal)
wire signed [15:0] ay1A_dcrm, ay1B_dcrm, ay1C_dcrm, ay2A_dcrm, ay2B_dcrm, ay2C_dcrm;
wire signed [15:0] ay3A_sound, ay3B_sound, ay3C_sound, ay4A_sound, ay4B_sound, ay4C_sound, ay5A_sound, ay5B_sound, ay5C_sound;
wire signed [15:0] i8039_sound;
jt49_dcrm2 #(16) dcrm_ay1A
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay1A_raw, 4'd0}),
.dout(ay1A_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay1B
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay1B_raw, 4'd0}),
.dout(ay1B_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay1C
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay1C_raw, 4'd0}),
.dout(ay1C_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay2A
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay2A_raw, 4'd0}),
.dout(ay2A_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay2B
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay2B_raw, 4'd0}),
.dout(ay2B_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay2C
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay2C_raw, 4'd0}),
.dout(ay2C_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay3A
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay3A_raw, 4'd0}),
.dout(ay3A_sound)
);
jt49_dcrm2 #(16) dcrm_ay3B
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay3B_raw, 4'd0}),
.dout(ay3B_sound)
);
jt49_dcrm2 #(16) dcrm_ay3C
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay3C_raw, 4'd0}),
.dout(ay3C_sound)
);
jt49_dcrm2 #(16) dcrm_ay4A
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay4A_raw, 4'd0}),
.dout(ay4A_sound)
);
jt49_dcrm2 #(16) dcrm_ay4B
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay4B_raw, 4'd0}),
.dout(ay4B_sound)
);
jt49_dcrm2 #(16) dcrm_ay4C
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay4C_raw, 4'd0}),
.dout(ay4C_sound)
);
jt49_dcrm2 #(16) dcrm_ay5A
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay5A_raw, 4'd0}),
.dout(ay5A_sound)
);
jt49_dcrm2 #(16) dcrm_ay5B
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay5B_raw, 4'd0}),
.dout(ay5B_sound)
);
jt49_dcrm2 #(16) dcrm_ay5C
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({4'd0, ay5C_raw, 4'd0}),
.dout(ay5C_sound)
);
jt49_dcrm2 #(16) dcrm_i8039
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({2'd0, i8039_raw, 6'd0}),
.dout(i8039_sound)
);
//Two of Gyruss's AY-3-8910s contain selectable low-pass filters with the following cutoff frequencies:
//3386.28Hz, 723.43Hz, 596.09Hz
//Model this here (the PCB handles this via 3 74HC4066 switching ICs located at 9E, 9F and 10F)
wire signed [15:0] ay1A_light, ay1A_med, ay1A_heavy, ay1B_light, ay1B_med, ay1B_heavy, ay1C_light, ay1C_med, ay1C_heavy;
wire signed [15:0] ay2A_light, ay2A_med, ay2A_heavy, ay2B_light, ay2B_med, ay2B_heavy, ay2C_light, ay2C_med, ay2C_heavy;
wire signed [15:0] ay1A_sound, ay1B_sound, ay1C_sound, ay2A_sound, ay2B_sound, ay2C_sound;
gyruss_lpf_light ay1A_lpf_light
(
.clk(clk_49m),
.reset(~reset),
.in(ay1A_dcrm),
.out(ay1A_light)
);
gyruss_lpf_medium ay1A_lpf_medium
(
.clk(clk_49m),
.reset(~reset),
.in(ay1A_dcrm),
.out(ay1A_med)
);
gyruss_lpf_heavy ay1A_lpf_heavy
(
.clk(clk_49m),
.reset(~reset),
.in(ay1A_dcrm),
.out(ay1A_heavy)
);
gyruss_lpf_light ay1B_lpf_light
(
.clk(clk_49m),
.reset(~reset),
.in(ay1B_dcrm),
.out(ay1B_light)
);
gyruss_lpf_medium ay1B_lpf_medium
(
.clk(clk_49m),
.reset(~reset),
.in(ay1B_dcrm),
.out(ay1B_med)
);
gyruss_lpf_heavy ay1B_lpf_heavy
(
.clk(clk_49m),
.reset(~reset),
.in(ay1B_dcrm),
.out(ay1B_heavy)
);
gyruss_lpf_light ay1C_lpf_light
(
.clk(clk_49m),
.reset(~reset),
.in(ay1C_dcrm),
.out(ay1C_light)
);
gyruss_lpf_medium ay1C_lpf_medium
(
.clk(clk_49m),
.reset(~reset),
.in(ay1C_dcrm),
.out(ay1C_med)
);
gyruss_lpf_heavy ay1C_lpf_heavy
(
.clk(clk_49m),
.reset(~reset),
.in(ay1C_dcrm),
.out(ay1C_heavy)
);
gyruss_lpf_light ay2A_lpf_light
(
.clk(clk_49m),
.reset(~reset),
.in(ay2A_dcrm),
.out(ay2A_light)
);
gyruss_lpf_medium ay2A_lpf_medium
(
.clk(clk_49m),
.reset(~reset),
.in(ay2A_dcrm),
.out(ay2A_med)
);
gyruss_lpf_heavy ay2A_lpf_heavy
(
.clk(clk_49m),
.reset(~reset),
.in(ay2A_dcrm),
.out(ay2A_heavy)
);
gyruss_lpf_light ay2B_lpf_light
(
.clk(clk_49m),
.reset(~reset),
.in(ay2B_dcrm),
.out(ay2B_light)
);
gyruss_lpf_medium ay2B_lpf_medium
(
.clk(clk_49m),
.reset(~reset),
.in(ay2B_dcrm),
.out(ay2B_med)
);
gyruss_lpf_heavy ay2B_lpf_heavy
(
.clk(clk_49m),
.reset(~reset),
.in(ay2B_dcrm),
.out(ay2B_heavy)
);
gyruss_lpf_light ay2C_lpf_light
(
.clk(clk_49m),
.reset(~reset),
.in(ay2C_dcrm),
.out(ay2C_light)
);
gyruss_lpf_medium ay2C_lpf_medium
(
.clk(clk_49m),
.reset(~reset),
.in(ay2C_dcrm),
.out(ay2C_med)
);
gyruss_lpf_heavy ay2C_lpf_heavy
(
.clk(clk_49m),
.reset(~reset),
.in(ay2C_dcrm),
.out(ay2C_heavy)
);
//Apply audio filtering based on the state of the low-pass filter controls
always_comb begin
case(ay1_filter[1:0])
2'b00: ay1A_sound = ay1A_dcrm;
2'b01: ay1A_sound = ay1A_light <<< 1;
2'b10: ay1A_sound = ay1A_med <<< 1;
2'b11: ay1A_sound = ay1A_heavy <<< 1;
endcase
case(ay1_filter[3:2])
2'b00: ay1B_sound = ay1B_dcrm;
2'b01: ay1B_sound = ay1B_light <<< 1;
2'b10: ay1B_sound = ay1B_med <<< 1;
2'b11: ay1B_sound = ay1B_heavy <<< 1;
endcase
case(ay1_filter[5:4])
2'b00: ay1C_sound = ay1C_dcrm;
2'b01: ay1C_sound = ay1C_light <<< 1;
2'b10: ay1C_sound = ay1C_med <<< 1;
2'b11: ay1C_sound = ay1C_heavy <<< 1;
endcase
case(ay2_filter[1:0])
2'b00: ay2A_sound = ay2A_dcrm;
2'b01: ay2A_sound = ay2A_light <<< 1;
2'b10: ay2A_sound = ay2A_med <<< 1;
2'b11: ay2A_sound = ay2A_heavy <<< 1;
endcase
case(ay2_filter[3:2])
2'b00: ay2B_sound = ay2B_dcrm;
2'b01: ay2B_sound = ay2B_light <<< 1;
2'b10: ay2B_sound = ay2B_med <<< 1;
2'b11: ay2B_sound = ay2B_heavy <<< 1;
endcase
case(ay2_filter[5:4])
2'b00: ay2C_sound = ay2C_dcrm;
2'b01: ay2C_sound = ay2C_light <<< 1;
2'b10: ay2C_sound = ay2C_med <<< 1;
2'b11: ay2C_sound = ay2C_heavy <<< 1;
endcase
end
//Mix the left and right outputs, then apply an antialiasing low-pass filter to eliminate ringing noise from the
//AY-3-8910s and output the final result (this game has variable low-pass filtering based on how loud the PCB's volume dials
//are set and will be modeled externally)
wire signed [15:0] left_mix = (ay2A_sound + ay2B_sound + ay2C_sound + ay5A_sound + ay5B_sound + ay5C_sound + i8039_sound);
wire signed [15:0] right_mix = (ay1A_sound + ay1B_sound + ay1C_sound + ay3A_sound + ay3B_sound + ay3C_sound + ay4A_sound +
ay4B_sound + ay4C_sound);
wire signed [15:0] left_lpf, right_lpf;
gyruss_lpf aalpf_l
(
.clk(clk_49m),
.reset(~reset),
.in(left_mix),
.out(left_lpf)
);
gyruss_lpf aalpf_r
(
.clk(clk_49m),
.reset(~reset),
.in(right_mix),
.out(right_lpf)
);
assign sound_l = (ay2A_sound + ay2B_sound + ay2C_sound + ay5A_sound + ay5B_sound + ay5C_sound + i8039_sound);
assign sound_r = (ay1A_sound + ay1B_sound + ay1C_sound + ay3A_sound + ay3B_sound + ay3C_sound + ay4A_sound +
ay4B_sound + ay4C_sound);
//assign sound_l = left_lpf;
//assign sound_r = right_lpf;
endmodule

View File

@@ -0,0 +1,35 @@
# ================================================================================
#
# Build ID Verilog Module Script
# Jeff Wiencrot - 8/1/2011
#
# Generates a Verilog module that contains a timestamp,
# from the current build. These values are available from the build_date, build_time,
# physical_address, and host_name output ports of the build_id module in the build_id.v
# Verilog source file.
#
# ================================================================================
proc generateBuildID_Verilog {} {
# Get the timestamp (see: http://www.altera.com/support/examples/tcl/tcl-date-time-stamp.html)
set buildDate [ clock format [ clock seconds ] -format %y%m%d ]
set buildTime [ clock format [ clock seconds ] -format %H%M%S ]
# Create a Verilog file for output
set outputFileName "rtl/build_id.v"
set outputFile [open $outputFileName "w"]
# Output the Verilog source
puts $outputFile "`define BUILD_DATE \"$buildDate\""
puts $outputFile "`define BUILD_TIME \"$buildTime\""
close $outputFile
# Send confirmation message to the Messages window
post_message "Generated build identification Verilog module: [pwd]/$outputFileName"
post_message "Date: $buildDate"
post_message "Time: $buildTime"
}
# Comment out this line to prevent the process from automatically executing when the file is sourced:
generateBuildID_Verilog

View File

@@ -0,0 +1,79 @@
//============================================================================
//
// SystemVerilog implementation of the KONAMI-1 custom chip, a custom MC6809E
// variant with XOR/XNOR encryption
// Implements MC6809E core by Greg Miller (synchronous version modified by
// Sorgelig with further modifications to allow direct injection of opcodes)
// Copyright (C) 2021 Ace
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
module KONAMI1
(
input CLK,
input fallE_en,
input fallQ_en,
input [7:0] D,
output [7:0] DOut,
output [15:0] ADDR,
output RnW,
output BS,
output BA,
input nIRQ,
input nFIRQ,
input nNMI,
output AVMA,
output BUSY,
output LIC,
input nHALT,
input nRESET
);
//Decrypt XOR/XNOR encrypted opcode
wire [7:0] opcode = D ^ {ADDR[1], 1'b0, ~ADDR[1], 1'b0, ADDR[3], 1'b0, ~ADDR[3], 1'b0};
//Passthrough to modified MC6809is core with direct opcode injection and IS_KONAMI1 parameter set
//to TRUE
mc6809is #(.IS_KONAMI1("TRUE")) cpucore
(
.CLK(CLK),
.fallE_en(fallE_en),
.fallQ_en(fallQ_en),
.OP(opcode),
.nHALT(nHALT),
.nRESET(nRESET),
.D(D),
.DOut(DOut),
.ADDR(ADDR),
.RnW(RnW),
.BS(BS),
.BA(BA),
.nIRQ(nIRQ),
.nFIRQ(nFIRQ),
.nNMI(nNMI),
.AVMA(AVMA),
.BUSY(BUSY),
.LIC(LIC),
.nDMABREQ(1)
);
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
set_global_assignment -name SYSTEMVERILOG_FILE rtl/custom/k082.sv
set_global_assignment -name SYSTEMVERILOG_FILE rtl/custom/k083.sv
set_global_assignment -name SYSTEMVERILOG_FILE rtl/custom/k501.sv
set_global_assignment -name SYSTEMVERILOG_FILE rtl/custom/k502.sv
set_global_assignment -name SYSTEMVERILOG_FILE rtl/custom/k503.sv
set_global_assignment -name SYSTEMVERILOG_FILE rtl/custom/KONAMI-1/KONAMI1.sv
set_global_assignment -name VERILOG_FILE rtl/custom/KONAMI-1/mc6809isk.v

View File

@@ -0,0 +1,168 @@
//============================================================================
//
// SystemVerilog implementation of the Konami 082 custom chip, used by
// several Konami arcade PCBs to generate video timings
// Copyright (C) 2020, 2021 Ace
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
//Chip pinout:
/* _____________
_| |_
reset |_|1 28|_| VCC
_| |_
h1 |_|2 27|_| GND
_| |_
h2 |_|3 26|_| v1
_| |_
h4 |_|4 25|_| v2
_| |_
h8 |_|5 24|_| v4
_| |_
h16 |_|6 23|_| v8
_| |_
h32 |_|7 22|_| v16
_| |_
h64 |_|8 21|_| v32
_| |_
h128 |_|9 20|_| v64
_| |_
n_h256 |_|10 19|_| v128
_| |_
h256 |_|11 18|_| n_vsync
_| |_
VCC |_|12 17|_| sync
_| |_
clk |_|13 16|_| vblk
_| |_
GND |_|14 15|_| n_vblk
|_____________|
Note: Pins 12 and 27 may control other features of the 082 - these, if any, have not
been modelled yet.
*/
module k082
(
input reset, //Active low
input clk,
input cen, //Set to 1 if using this code to replace a real 082
input [3:0] h_center, v_center, //These inputs are additions for screen centering and don't exist on the actual 082
output n_vsync, sync,
output n_hsync, //Not exposed on the original chip
output reg vblk = 1,
output n_vblk,
output reg vblk_irq_en = 0, //This is an extra output not present on the real 082 to signal when to
//trigger a VBlank IRQ (signal is active high)
output h1, h2, h4, h8, h16, h32, h64, h128, h256, n_h256,
output v1, v2, v4, v8, v16, v32, v64, v128
);
//The horizontal and vertical counters are 9 bits wide - delcare them here
reg [8:0] h_cnt = 9'd0;
reg [8:0] v_cnt = 9'd0;
//Define the range of values the vertical counter will count between based on the additional vertical center signal
//Shift the screen up by 1 line when horizontal centering shifts the screen left
wire [8:0] vcnt_start = 9'd248 - v_center;
wire [8:0] vcnt_end = 9'd511 - v_center;
//The horizontal and vertical counters behave as follows at every rising edge of the pixel clock:
//-Start at 0, then count to 511 (both counters increment by 1 when the horizontal counter is set to 48)
//-Horizontal counter resets to 128 for a total of 383 horizontal lines
//-Vertical counter resets to 248 for a total of 263 vertical lines (adjustable with added vertical center signal)
//-Vertical counter increments when the horizontal counter equals 176
//-VBlank is active when the horizontal counter is between 495 - 511 and 248 - 270
//Model this behavior here
always_ff @(posedge clk or negedge reset) begin
if(!reset) begin
h_cnt <= 9'd0;
v_cnt <= 9'd0;
end
else if(cen) begin
case(h_cnt)
48: begin
v_cnt <= v_cnt + 9'd1;
h_cnt <= h_cnt + 9'd1;
end
176: begin
h_cnt <= h_cnt + 9'd1;
case(v_cnt)
16: begin
vblk <= 0;
v_cnt <= v_cnt + 9'd1;
end
271: begin
vblk <= 0;
v_cnt <= v_cnt + 9'd1;
end
495: begin
vblk <= 1;
vblk_irq_en <= 1;
v_cnt <= v_cnt + 9'd1;
end
vcnt_end: v_cnt <= vcnt_start;
default: v_cnt <= v_cnt + 9'd1;
endcase
end
177: begin
vblk_irq_en <= 0;
h_cnt <= h_cnt + 9'd1;
end
511: h_cnt <= 9'd128;
default: h_cnt <= h_cnt + 9'd1;
endcase
end
end
//The Konami 082 has both an active low VBlank and an active high VBlank - generate the active low VBlank by inverting
//the active high VBlank generated in the previous sequential block
assign n_vblk = ~vblk;
//Generate active low HSync, VSync and composite sync
assign n_hsync = h_center[3] ? ~(h_cnt > (182 - h_center[2:0]) && h_cnt < (215 - h_center[2:0])):
~(h_cnt > (175 - h_center[2:0]) && h_cnt < (208 - h_center[2:0]));
assign n_vsync = h_center[3] ? ~(v_cnt >= vcnt_start + 9'd1 && v_cnt <= vcnt_start + 9'd9) : ~(v_cnt >= vcnt_start && v_cnt <= vcnt_start + 9'd8);
assign sync = n_hsync ^ n_vsync;
//Assign the individual horizontal counter bits to their respective outputs (also invert bit 8 of the horizontal counter for H256)
assign h1 = h_cnt[0];
assign h2 = h_cnt[1];
assign h4 = h_cnt[2];
assign h8 = h_cnt[3];
assign h16 = h_cnt[4];
assign h32 = h_cnt[5];
assign h64 = h_cnt[6];
assign h128 = h_cnt[7];
assign h256 = ~h_cnt[8];
assign n_h256 = h_cnt[8];
//Assign the individual vertical counter bits to their respective outputs
assign v1 = v_cnt[0];
assign v2 = v_cnt[1];
assign v4 = v_cnt[2];
assign v8 = v_cnt[3];
assign v16 = v_cnt[4];
assign v32 = v_cnt[5];
assign v64 = v_cnt[6];
assign v128 = v_cnt[7];
endmodule

View File

@@ -0,0 +1,101 @@
//============================================================================
//
// SystemVerilog implementation of the Konami 083 custom chip, a custom
// shift register used on many early Konami arcade PCBs for handling graphics
// ROMs
// Copyright (C) 2020, 2021 Ace & ElectronAsh
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
//Chip pinout:
/* _____________
_| |_
CK |_|1 28|_| VCC
_| |_
FLIP |_|2 27|_| LOAD
_| |_
DB1i(4) |_|3 26|_| DB1i(6)
_| |_
DB1i(0) |_|4 25|_| DB1i(2)
_| |_
DB0i(4) |_|5 24|_| DB0i(6)
_| |_
DB0i(6) |_|6 23|_| DB0i(2)
_| |_
DB1i(5) |_|7 22|_| DB1i(7)
_| |_
DB1i(1) |_|8 21|_| DB1i(3)
_| |_
DB0i(5) |_|9 20|_| DB0i(7)
_| |_
DB0i(1) |_|10 19|_| DB0i(3)
_| |_
NC |_|11 18|_| NC
_| |_
DSH1(1) |_|12 17|_| DSH1(0)
_| |_
DSH0(1) |_|13 16|_| DSH0(0)
_| |_
GND |_|14 15|_| NC
|_____________|
*/
module k083
(
input CK,
input CEN, //Set to 1 if using this code to replace a real 083
input FLIP,
input LOAD,
input [7:0] DB0i, DB1i,
output [1:0] DSH0, DSH1
);
//Internal registers (shift right and shift left)
reg [7:0] pixel_D0_l, pixel_D0_r;
reg [7:0] pixel_D1_l, pixel_D1_r;
//Latch and shift pixel data
always_ff @(posedge CK) begin
if(CEN) begin
if(LOAD) begin
pixel_D0_l <= DB0i;
pixel_D1_l <= DB1i;
pixel_D0_r <= DB0i;
pixel_D1_r <= DB1i;
end
else begin
pixel_D0_l[3:0] <= {pixel_D0_l[2:0], 1'b0};
pixel_D0_l[7:4] <= {pixel_D0_l[6:4], 1'b0};
pixel_D1_l[3:0] <= {pixel_D1_l[2:0], 1'b0};
pixel_D1_l[7:4] <= {pixel_D1_l[6:4], 1'b0};
pixel_D0_r[3:0] <= {1'b0, pixel_D0_r[3:1]};
pixel_D0_r[7:4] <= {1'b0, pixel_D0_r[7:5]};
pixel_D1_r[3:0] <= {1'b0, pixel_D1_r[3:1]};
pixel_D1_r[7:4] <= {1'b0, pixel_D1_r[7:5]};
end
end
end
//Output shifted pixel data (reverse the bits if FLIP is low)
assign DSH0 = FLIP ? {pixel_D0_l[3], pixel_D0_l[7]} : {pixel_D0_r[0], pixel_D0_r[4]};
assign DSH1 = FLIP ? {pixel_D1_l[3], pixel_D1_l[7]} : {pixel_D1_r[0], pixel_D1_r[4]};
endmodule

View File

@@ -0,0 +1,101 @@
//============================================================================
//
// SystemVerilog implementation of the Konami 501 custom chip, used by some
// Konami arcade PCBs for partial address decoding and obfuscation of data
// I/O
// Copyright (C) 2021 Ace
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
//Chip pinout:
/* _____________
_| |_
D[7] |_|1 28|_| VCC
_| |_
D[6] |_|2 27|_| XD[7]
_| |_
D[5] |_|3 26|_| XD[6]
_| |_
D[4] |_|4 25|_| XD[5]
_| |_
D[3] |_|5 24|_| XD[4]
_| |_
D[2] |_|6 23|_| XD[3]
_| |_
D[1] |_|7 22|_| XD[2]
_| |_
D[0] |_|8 21|_| XD[1]
_| |_
H2 |_|9 20|_| XD[0]
_| |_
H1 |_|10 19|_| WAIT
_| |_
CLK |_|11 18|_| ENABLE
_| |_
RAM |_|12 17|_| WRITE
_| |_
RD |_|13 16|_| NC
_| |_
GND |_|14 15|_| NC
|_____________|
Note: The data bus is bidirectional - this model splits these pins into separate data I/O
*/
module k501
(
input CLK, //Clock input (add a PLL to multiply the 6.144MHz pixel clock if replacing a real 501)
input CEN, //Clock enable at 12.288MHz
input H1, H2, //Bits 0 and 1 of the horizontal counter
input RAM, //Chip select (active low)
input RD, //Z80 read input
output WAIT, //Z80 wait output
output WRITE, //Write output
output ENABLE, //Enable output
input [7:0] Di, XDi, //Inputs from data busses
output [7:0] Do, XDo //Outputs to data busses
);
//Data bus passthrough
assign XDo = Di;
assign Do = XDi;
//Latch bit 0 of the horizontal counter on each edge of the clock and preset to 1 if bit 1 of the horizontal counter
//is high
reg [1:0] h1_reg = 2'd0;
always_ff @(posedge CLK) begin
if(H2)
h1_reg[0] <= 1;
else if(CEN)
h1_reg <= {h1_reg[0], H1};
end
//AND both latched instances of H1
wire h1_lat = &h1_reg;
//Generate WAIT output
assign WAIT = ~H2 | RAM;
//Generate WRITE and ENABLE outputs
assign WRITE = ~RD | ENABLE;
assign ENABLE = h1_lat | RAM;
endmodule

View File

@@ -0,0 +1,162 @@
//============================================================================
//
// SystemVerilog implementation of the Konami 502 custom chip, used for
// generating sprites on a number of '80s Konami arcade PCBs
// Copyright (C) 2020, 2021 Ace
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
//Chip pinout:
/* _____________
_| |_
SPLB(0) |_|1 28|_| VCC
_| |_
SPLB(1) |_|2 27|_| RESET
_| |_
SPLB(2) |_|3 26|_| SPLB(4)
_| |_
SPLB(3) |_|4 25|_| SPLB(5)
_| |_
CK1 |_|5 24|_| SPLB(6)
_| |_
CK2 |_|6 23|_| SPLB(7)
_| |_
H2 |_|7 22|_| OLD
_| |_
LD0 |_|8 21|_| OCLR
_| |_
H256 |_|9 20|_| OSEL
_| |_
SPAL(3) |_|10 19|_| COL(4)
_| |_
SPAL(2) |_|11 18|_| COL(3)
_| |_
SPAL(1) |_|12 17|_| COL(2)
_| |_
SPAL(0) |_|13 16|_| COL(1)
_| |_
GND |_|14 15|_| COL(0)
|_____________|
Note: The SPLB pins are bidirectional - this model splits these pins into separate
data I/O
*/
module k502
(
input RESET,
input CK1,
input CK2,
input CEN, //Set to 1 if using this code to replace a real 502
input LD0,
input H2,
input H256,
input [3:0] SPAL,
input [7:0] SPLBi,
output OSEL,
output OLD,
output OCLR,
output [7:0] SPLBo,
output [4:0] COL
);
//As the Konami 502 doesn't have a dedicated input for bit 2 of the horizontal counter (H4), generate
//this signal internally by dividing H2 by 2
reg h2_div = 0;
always_ff @(posedge H2) begin
h2_div <= ~h2_div;
end
wire h4 = h2_div;
//Latch H256 on rising edge of LD0 and delay by one cycle (set to 0 if 502 is held in reset)
reg h256_lat = 0;
always_ff @(posedge LD0 or negedge RESET) begin
if(!RESET)
h256_lat <= 0;
else
h256_lat <= H256;
end
reg h256_dly = 0;
always_ff @(posedge h256_lat or negedge RESET) begin
if(!RESET)
h256_dly <= 0;
else
h256_dly <= ~h256_dly;
end
//Generate OSEL, OLD and OCLR
reg [1:0] osel_reg;
always_ff @(negedge H2 or negedge RESET) begin
if(!RESET)
osel_reg <= 2'b00;
else begin
if(!h4)
osel_reg[1] <= h256_dly;
else
osel_reg[0] <= osel_reg[1];
end
end
assign OLD = ~osel_reg[1];
assign OSEL = osel_reg[0];
assign OCLR = ~osel_reg[0];
//Multiplex incoming line buffer RAM data
wire [3:0] lbuff_Dmux = OCLR ? SPLBi[3:0] : SPLBi[7:4];
//Latch incoming line buffer RAM data on the falling edge of CK1
reg [7:0] lbuff_lat;
always_ff @(negedge CK1) begin
if(!RESET)
lbuff_lat <= 8'd0;
else if(CEN)
lbuff_lat <= SPLBi;
end
//Latch multiplexed line buffer RAM data on the falling edge of CK2
reg [3:0] lbuff_mux_lat;
always_ff @(negedge CK2) begin
if(!RESET)
lbuff_mux_lat <= 4'd0;
else if(CEN)
lbuff_mux_lat <= lbuff_Dmux;
end
//Assign sprite data output
assign COL[4] = ~(|lbuff_mux_lat[3:0]);
assign COL[3:0] = lbuff_mux_lat[3:0];
//Select sprite or palette data based on a 4-way AND of the inverted latched line buffer data
//(upper 4 bits and lower 4 bits produce separate select signals)
wire sprite_pal_sel2 = (~lbuff_lat[7] & ~lbuff_lat[6] & ~lbuff_lat[5] & ~lbuff_lat[4]);
wire sprite_pal_sel1 = (~lbuff_lat[3] & ~lbuff_lat[2] & ~lbuff_lat[1] & ~lbuff_lat[0]);
//Multiplex sprite data from line buffer with palette data (lower 4 bits)
wire [7:0] sprite_pal_mux;
assign sprite_pal_mux[3:0] = osel_reg[0] ? (sprite_pal_sel1 ? SPAL : SPLBi[3:0]) : 4'h0;
//Multiplex sprite data from line buffer with palette data (upper 4 bits)
assign sprite_pal_mux[7:4] = ~osel_reg[0] ? (sprite_pal_sel2 ? SPAL : SPLBi[7:4]) : 4'h0;
//Output data to sprite line buffer
assign SPLBo = sprite_pal_mux;
endmodule

View File

@@ -0,0 +1,119 @@
//============================================================================
//
// SystemVerilog implementation of the Konami 503 custom chip, used by
// several Konami arcade PCBs for handling sprite data
// Copyright (C) 2020, 2021 Ace
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
//Chip pinout:
/* _____________
_| |_
OB(7) |_|1 40|_| VCC
_| |_
OB(6) |_|2 39|_| VCNT(7)
_| |_
OB(5) |_|3 38|_| VCNT(6)
_| |_
OB(4) |_|4 37|_| VCNT(5)
_| |_
OB(3) |_|5 36|_| VCNT(4)
_| |_
OB(2) |_|6 35|_| VCNT(3)
_| |_
OB(1) |_|7 34|_| VCNT(2)
_| |_
OB(0) |_|8 33|_| VCNT(1)
_| |_
R(5) |_|9 32|_| VCNT(0)
_| |_
R(4) |_|10 31|_| NC
_| |_
R(3) |_|11 30|_| OFLP
_| |_
R(2) |_|12 29|_| OCS
_| |_
R(1) |_|13 28|_| NC
_| |_
R(0) |_|14 27|_| NC
_| |_
LD |_|15 26|_| NC
_| |_
H4 |_|16 25|_| NC
_| |_
H8 |_|17 24|_| NC
_| |_
OCOL |_|18 23|_| NC
_| |_
ODAT |_|19 22|_| NC
_| |_
GND |_|20 21|_| NC
|_____________|
*/
module k503
(
input [7:0] OB, //Sprite data input
input [7:0] VCNT, //Vertical counter input
input H4, H8, //Horizontal counter bits 2 (H4) and 3 (H8)
input LD, //LD input (pulses low when bits 0 and 1 of the horizontal counter are both 1)
output OCS, //Sprite line buffer chip select output
output OFLP, //Sprite flip output
output ODAT, //Signal to latch upper bits of sprite address
output OCOL, //Signal to load addresses for sprite line buffer
output [5:0] R //Lower 6 bits of sprite address
);
//Sum sprite bits with vertical counter
wire [7:0] sprite_sum = OB + VCNT;
//Sprite select signal
wire sprite_sel = ~(&sprite_sum[7:4]);
//Sprite flip control
reg hflip, vflip;
always_ff @(posedge H4) begin
hflip <= OB[6];
vflip <= OB[7];
end
//Latch sprite information
reg [6:0] sprite;
always_ff @(negedge H4) begin
sprite <= {sprite_sel, hflip, vflip, sprite_sum[3:0]};
end
wire sprite_vflip = sprite[4];
assign OFLP = sprite[5];
assign OCS = sprite[6];
//Assign OCOL (sprite color) and ODAT (sprite data) outputs
assign OCOL = ({H8, H4, LD} != 3'b100);
assign ODAT = ({H8, H4, LD} != 3'b010);
//XOR final output for R
assign R[5] = (sprite[3] ^ sprite_vflip);
assign R[4] = (OFLP ^ H8);
assign R[3] = (OFLP ^ ~H4);
assign R[2] = (sprite[2] ^ sprite_vflip);
assign R[1] = (sprite[1] ^ sprite_vflip);
assign R[0] = (sprite[0] ^ sprite_vflip);
endmodule

View File

@@ -0,0 +1,602 @@
//============================================================================
// MAME hiscore.dat support for MiSTer arcade cores.
//
// https://github.com/JimmyStones/Hiscores_MiSTer
//
// Copyright (c) 2021 Alan Steremberg
// Copyright (c) 2021 Jim Gregory
//
// 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.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//============================================================================
/*
Version history:
0001 - 2021-03-06 - First marked release
0002 - 2021-03-06 - Added HS_DUMPFORMAT localparam to identify dump version (for future use)
Add HS_CONFIGINDEX and HS_DUMPINDEX parameters to configure ioctl_indexes
0003 - 2021-03-10 - Added WRITE_REPEATCOUNT and WRITE_REPEATWAIT to handle tricky write situations
0004 - 2021-03-15 - Fix ram_access assignment
0005 - 2021-03-18 - Add configurable score table width, clean up some stupid mistakes
0006 - 2021-03-27 - Move 'tweakable' parameters into MRA data header
0007 - 2021-04-15 - Improve state machine maintainability, add new 'pause padding' states
0008 - 2021-05-12 - Feed back core-level pause to halt startup timer
============================================================================
*/
module hiscore
#(
parameter HS_ADDRESSWIDTH=10, // Max size of game RAM address for highscores
parameter HS_SCOREWIDTH=8, // Max size of capture RAM For highscore data (default 8 = 256 bytes max)
parameter HS_CONFIGINDEX=3, // ioctl_index for config transfer
parameter HS_DUMPINDEX=4, // ioctl_index for dump transfer
parameter CFG_ADDRESSWIDTH=4, // Max size of RAM address for highscore.dat entries (default 4 = 16 entries max)
parameter CFG_LENGTHWIDTH=1 // Max size of length for each highscore.dat entries (default 1 = 256 bytes max)
)
(
input clk,
input paused, // Signal from core confirming CPU is paused
input reset,
input ioctl_upload,
input ioctl_download,
input ioctl_wr,
input [24:0] ioctl_addr,
input [7:0] ioctl_dout,
input [7:0] ioctl_din,
input [7:0] ioctl_index,
output [HS_ADDRESSWIDTH-1:0] ram_address, // Address in game RAM to read/write score data
output [7:0] data_to_ram, // Data to write to game RAM
output reg ram_write, // Write to game RAM (active high)
output ram_access, // RAM read or write required (active high)
output reg pause_cpu // Pause core CPU to prepare for/relax after RAM access
);
// Parameters read from config header
reg [31:0] START_WAIT =32'd0; // Delay before beginning check process
reg [15:0] CHECK_WAIT =16'hFF; // Delay between start/end check attempts
reg [15:0] CHECK_HOLD =16'd2; // Hold time for start/end check reads
reg [15:0] WRITE_HOLD =16'd2; // Hold time for game RAM writes
reg [15:0] WRITE_REPEATCOUNT =16'b1; // Number of times to write score to game RAM
reg [15:0] WRITE_REPEATWAIT =16'b1111; // Delay between subsequent write attempts to game RAM
reg [7:0] ACCESS_PAUSEPAD =8'd4; // Cycles to wait with paused CPU before and after RAM access
// State machine constants
localparam SM_STATEWIDTH = 4; // Width of state machine net
localparam SM_INIT = 0;
localparam SM_TIMER = 1;
localparam SM_CHECKPREP = 2;
localparam SM_CHECKBEGIN = 3;
localparam SM_CHECKSTARTVAL = 4;
localparam SM_CHECKENDVAL = 5;
localparam SM_CHECKCANCEL = 6;
localparam SM_WRITEPREP = 7;
localparam SM_WRITEBEGIN = 8;
localparam SM_WRITEREADY = 9;
localparam SM_WRITEDONE = 10;
localparam SM_WRITECOMPLETE = 11;
localparam SM_WRITERETRY = 12;
localparam SM_STOPPED = 13;
/*
Hiscore config data structure (version 1)
-----------------------------------------
[16 byte header]
[8 byte * no. of entries]
- Header format
00 00 FF FF 00 FF 00 02 00 02 00 01 11 11 00 00
[ SW ] [ CW] [ CH] [ WH] [WRC] [WRW] [PAD]
4 byte START_WAIT
2 byte CHECK_WAIT
2 byte CHECK_HOLD
2 byte WRITE_HOLD
2 byte WRITE_REPEATCOUNT
2 byte WRITE_REPEATWAIT
2 byte (padding/future use)
- Entry format (when CFG_LENGTHWIDTH=1)
00 00 43 0b 0f 10 01 00
00 00 40 23 02 04 12 00
[ ADDR ] LEN START END PAD
4 bytes Address of ram entry (in core memory map)
1 byte Length of ram entry in bytes
1 byte Start value to check for at start of address range before proceeding
1 byte End value to check for at end of address range before proceeding
1 byte (padding)
- Entry format (when CFG_LENGTHWIDTH=2)
00 00 43 0b 00 0f 10 01
00 00 40 23 00 02 04 12
[ ADDR ] [LEN ] START END
4 bytes Address of ram entry (in core memory map)
2 bytes Length of ram entry in bytes
1 byte Start value to check for at start of address range before proceeding
1 byte End value to check for at end of address range before proceeding
*/
localparam HS_VERSION =8; // Version identifier for module
localparam HS_DUMPFORMAT =1; // Version identifier for dump format
localparam HS_HEADERLENGTH =16; // Size of header chunk (default=16 bytes)
// HS_DUMPFORMAT = 1 --> No header, just the extracted hiscore data
// Hiscore config and dump status
wire downloading_config;
wire parsing_header;
wire downloading_dump;
wire uploading_dump;
reg downloaded_config = 1'b0;
reg downloaded_dump = 1'b0;
reg uploaded_dump = 1'b0;
reg [3:0] initialised;
reg writing_scores = 1'b0;
reg checking_scores = 1'b0;
assign downloading_config = ioctl_download && (ioctl_index==HS_CONFIGINDEX);
assign parsing_header = downloading_config && (ioctl_addr<=HS_HEADERLENGTH);
assign downloading_dump = ioctl_download && (ioctl_index==HS_DUMPINDEX);
assign uploading_dump = ioctl_upload && (ioctl_index==HS_DUMPINDEX);
assign ram_access = uploading_dump | writing_scores | checking_scores;
assign ram_address = ram_addr[HS_ADDRESSWIDTH-1:0];
reg [(SM_STATEWIDTH-1):0] state = SM_INIT; // Current state machine index
reg [(SM_STATEWIDTH-1):0] next_state = SM_INIT; // Next state machine index to move to after wait timer expires
reg [31:0] wait_timer; // Wait timer for inital/read/write delays
reg [CFG_ADDRESSWIDTH-1:0] counter = 1'b0; // Index for current config table entry
reg [CFG_ADDRESSWIDTH-1:0] total_entries = 1'b0; // Total count of config table entries
reg reset_last = 1'b0; // Last cycle reset
reg [7:0] write_counter = 1'b0; // Index of current game RAM write attempt
reg [7:0] last_ioctl_index; // Last cycle HPS IO index
reg last_ioctl_download = 0;// Last cycle HPS IO download
reg last_ioctl_upload = 0; // Last cycle HPS IO upload
reg [7:0] last_ioctl_dout; // Last cycle HPS IO data out
reg [7:0] last_ioctl_dout2; // Last cycle +1 HPS IO data out
reg [7:0] last_ioctl_dout3; // Last cycle +2 HPS IO data out
reg [24:0] ram_addr; // Target RAM address for hiscore read/write
reg [24:0] old_io_addr;
reg [24:0] base_io_addr;
wire [23:0] addr_base;
wire [(CFG_LENGTHWIDTH*8)-1:0] length;
wire [24:0] end_addr = (addr_base + length - 1'b1);
reg [HS_SCOREWIDTH-1:0] local_addr;
wire [7:0] start_val;
wire [7:0] end_val;
wire [23:0] address_data_in;
wire [(CFG_LENGTHWIDTH*8)-1:0] length_data_in;
assign address_data_in = {last_ioctl_dout2, last_ioctl_dout, ioctl_dout};
assign length_data_in = (CFG_LENGTHWIDTH == 1'b1) ? ioctl_dout : {last_ioctl_dout, ioctl_dout};
wire address_we = downloading_config & ~parsing_header & (ioctl_addr[2:0] == 3'd3);
wire length_we = downloading_config & ~parsing_header & (ioctl_addr[2:0] == 3'd3 + CFG_LENGTHWIDTH);
wire startdata_we = downloading_config & ~parsing_header & (ioctl_addr[2:0] == 3'd4 + CFG_LENGTHWIDTH);
wire enddata_we = downloading_config & ~parsing_header & (ioctl_addr[2:0] == 3'd5 + CFG_LENGTHWIDTH);
// RAM chunks used to store configuration data
// - address_table
// - length_table
// - startdata_table
// - enddata_table
dpram_hs #(.aWidth(CFG_ADDRESSWIDTH),.dWidth(24))
address_table(
.clk(clk),
.addr_a(ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2),
.we_a(address_we & ioctl_wr),
.d_a(address_data_in),
.addr_b(counter),
.q_b(addr_base)
);
// Length table - variable width depending on CFG_LENGTHWIDTH
dpram_hs #(.aWidth(CFG_ADDRESSWIDTH),.dWidth(CFG_LENGTHWIDTH*8))
length_table(
.clk(clk),
.addr_a(ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2),
.we_a(length_we & ioctl_wr),
.d_a(length_data_in),
.addr_b(counter),
.q_b(length)
);
dpram_hs #(.aWidth(CFG_ADDRESSWIDTH),.dWidth(8))
startdata_table(
.clk(clk),
.addr_a(ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2),
.we_a(startdata_we & ioctl_wr),
.d_a(ioctl_dout),
.addr_b(counter),
.q_b(start_val)
);
dpram_hs #(.aWidth(CFG_ADDRESSWIDTH),.dWidth(8))
enddata_table(
.clk(clk),
.addr_a(ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2),
.we_a(enddata_we & ioctl_wr),
.d_a(ioctl_dout),
.addr_b(counter),
.q_b(end_val)
);
// RAM chunk used to store hiscore data
dpram_hs #(.aWidth(HS_SCOREWIDTH),.dWidth(8))
hiscoredata (
.clk(clk),
.addr_a(ioctl_addr[(HS_SCOREWIDTH-1):0]),
.we_a(downloading_dump),
.d_a(ioctl_dout),
.addr_b(local_addr),
.we_b(ioctl_upload),
.d_b(ioctl_din),
.q_b(data_to_ram)
);
wire [3:0] header_chunk = ioctl_addr[3:0];
always @(posedge clk)
begin
if (downloading_config)
begin
// Get header chunk data
if(parsing_header)
begin
if(ioctl_wr)
begin
if(header_chunk == 4'd3) START_WAIT <= { last_ioctl_dout3, last_ioctl_dout2, last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd5) CHECK_WAIT <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd7) CHECK_HOLD <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd9) WRITE_HOLD <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd11) WRITE_REPEATCOUNT <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd13) WRITE_REPEATWAIT <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd14) ACCESS_PAUSEPAD <= ioctl_dout;
end
end
else
begin
// Keep track of the largest entry during config download
total_entries <= ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2;
end
end
// Track completion of configuration and dump download
if ((last_ioctl_download != ioctl_download) && (ioctl_download == 1'b0))
begin
if (last_ioctl_index==HS_CONFIGINDEX) downloaded_config <= 1'b1;
if (last_ioctl_index==HS_DUMPINDEX) downloaded_dump <= 1'b1;
end
// Track completion of dump upload
if ((last_ioctl_upload != ioctl_upload) && (ioctl_upload == 1'b0))
begin
if (last_ioctl_index==HS_DUMPINDEX)
begin
uploaded_dump <= 1'b1;
// Mark uploaded dump as readable in case of reset
downloaded_dump <= 1'b1;
end
end
// Track last ioctl values
last_ioctl_download <= ioctl_download;
last_ioctl_upload <= ioctl_upload;
last_ioctl_index <= ioctl_index;
if(ioctl_download && ioctl_wr)
begin
last_ioctl_dout3 = last_ioctl_dout2;
last_ioctl_dout2 = last_ioctl_dout;
last_ioctl_dout = ioctl_dout;
end
if(downloaded_config)
begin
// Check for end of state machine reset to initialise state machine
reset_last <= reset;
if (reset_last == 1'b1 && reset == 1'b0)
begin
wait_timer <= START_WAIT;
next_state <= SM_INIT;
state <= SM_TIMER;
counter <= 1'b0;
initialised <= initialised + 1'b1;
end
else
begin
// Upload scores to HPS
if (uploading_dump == 1'b1)
begin
// generate addresses to read high score from game memory. Base addresses off ioctl_address
if (ioctl_addr == 25'b0) begin
local_addr <= 0;
base_io_addr <= 25'b0;
counter <= 1'b0000;
end
// Move to next entry when last address is reached
if (old_io_addr!=ioctl_addr && ram_addr==end_addr[24:0])
begin
counter <= counter + 1'b1;
base_io_addr <= ioctl_addr;
end
// Set game ram address for reading back to HPS
ram_addr <= addr_base + (ioctl_addr - base_io_addr);
// Set local addresses to update cached dump in case of reset
local_addr <= ioctl_addr[HS_SCOREWIDTH-1:0];
end
if (ioctl_upload == 1'b0 && downloaded_dump == 1'b1 && reset == 1'b0)
begin
// State machine to write data to game RAM
case (state)
SM_INIT: // Start state machine
begin
// Setup base addresses
local_addr <= 0;
base_io_addr <= 25'b0;
// Reset entry counter and states
counter <= 0;
writing_scores <= 1'b0;
checking_scores <= 1'b0;
pause_cpu <= 1'b0;
state <= SM_CHECKPREP;
end
// Start/end check states
// ----------------------
SM_CHECKPREP: // Prepare start/end check run - pause CPU in readiness for RAM access
begin
state <= SM_TIMER;
next_state <= SM_CHECKBEGIN;
pause_cpu <= 1'b1;
wait_timer <= ACCESS_PAUSEPAD;
end
SM_CHECKBEGIN: // Begin start/end check run - enable RAM access
begin
checking_scores <= 1'b1;
ram_addr <= {1'b0, addr_base};
state <= SM_CHECKSTARTVAL;
wait_timer <= CHECK_HOLD;
end
SM_CHECKSTARTVAL: // Start check
begin
// Check for matching start value
if(wait_timer != CHECK_HOLD & ioctl_din == start_val)
begin
// Prepare end check
ram_addr <= end_addr;
state <= SM_CHECKENDVAL;
wait_timer <= CHECK_HOLD;
end
else
begin
ram_addr <= {1'b0, addr_base};
if (wait_timer > 1'b0)
begin
wait_timer <= wait_timer - 1'b1;
end
else
begin
// - If no match after read wait then stop check run and schedule restart of state machine
next_state <= SM_CHECKCANCEL;
state <= SM_TIMER;
checking_scores <= 1'b0;
wait_timer <= ACCESS_PAUSEPAD;
end
end
end
SM_CHECKENDVAL: // End check
begin
// Check for matching end value
if (wait_timer != CHECK_HOLD & ioctl_din == end_val)
begin
if (counter == total_entries)
begin
// If this was the last entry then move on to writing scores to game ram
checking_scores <= 1'b0;
state <= SM_WRITEBEGIN; // Bypass SM_WRITEPREP as we are already paused
counter <= 1'b0;
write_counter <= 1'b0;
ram_write <= 1'b0;
ram_addr <= {1'b0, addr_base};
end
else
begin
// Increment counter and restart state machine to check next entry
counter <= counter + 1'b1;
state <= SM_CHECKBEGIN;
end
end
else
begin
ram_addr <= end_addr;
if (wait_timer > 1'b0)
begin
wait_timer <= wait_timer - 1'b1;
end
else
begin
// - If no match after read wait then stop check run and schedule restart of state machine
next_state <= SM_CHECKCANCEL;
state <= SM_TIMER;
checking_scores <= 1'b0;
wait_timer <= ACCESS_PAUSEPAD;
end
end
end
SM_CHECKCANCEL: // Cancel start/end check run - disable RAM access and keep CPU paused
begin
pause_cpu <= 1'b0;
next_state <= SM_INIT;
state <= SM_TIMER;
wait_timer <= CHECK_WAIT;
end
// Write to game RAM states
// ----------------------
SM_WRITEPREP: // Prepare to write scores - pause CPU in readiness for RAM access (only used on subsequent write attempts)
begin
state <= SM_TIMER;
next_state <= SM_WRITEBEGIN;
pause_cpu <= 1'b1;
wait_timer <= ACCESS_PAUSEPAD;
end
SM_WRITEBEGIN: // Writing scores to game RAM begins
begin
writing_scores <= 1'b1; // Enable muxes if necessary
write_counter <= write_counter + 1'b1;
state <= SM_WRITEREADY;
end
SM_WRITEREADY: // local ram should be correct, start write to game RAM
begin
ram_addr <= addr_base + (local_addr - base_io_addr);
state <= SM_TIMER;
next_state <= SM_WRITEDONE;
wait_timer <= WRITE_HOLD;
ram_write <= 1'b1;
end
SM_WRITEDONE:
begin
local_addr <= local_addr + 1'b1; // Increment to next byte of entry
if (ram_addr == end_addr)
begin
// End of entry reached
if (counter == total_entries)
begin
state <= SM_WRITECOMPLETE;
end
else
begin
// Move to next entry
counter <= counter + 1'b1;
write_counter <= 1'b0;
base_io_addr <= local_addr + 1'b1;
state <= SM_WRITEBEGIN;
end
end
else
begin
state <= SM_WRITEREADY;
end
ram_write <= 1'b0;
end
SM_WRITECOMPLETE: // Hiscore write to RAM completed
begin
ram_write <= 1'b0;
writing_scores <= 1'b0;
state <= SM_TIMER;
if(write_counter < WRITE_REPEATCOUNT)
begin
// Schedule next write
next_state <= SM_WRITERETRY;
local_addr <= 0;
wait_timer <= WRITE_REPEATWAIT;
end
else
begin
next_state <= SM_STOPPED;
wait_timer <= ACCESS_PAUSEPAD;
end
end
SM_WRITERETRY: // Stop pause and schedule next write
begin
pause_cpu <= 1'b0;
state <= SM_TIMER;
next_state <= SM_WRITEPREP;
wait_timer <= WRITE_REPEATWAIT;
end
SM_STOPPED:
begin
pause_cpu <= 1'b0;
end
SM_TIMER: // timer wait state
begin
// Do not progress timer if CPU is paused by source other than this module
// - Stops initial hiscore load delay being foiled by user pausing/entering OSD
if (paused == 1'b0 || pause_cpu == 1'b1)
begin
if (wait_timer > 1'b0)
wait_timer <= wait_timer - 1'b1;
else
state <= next_state;
end
end
endcase
end
end
end
old_io_addr<=ioctl_addr;
end
endmodule
module dpram_hs #(
parameter dWidth=8,
parameter aWidth=8
)(
input clk,
input [aWidth-1:0] addr_a,
input [dWidth-1:0] d_a,
input we_a,
output reg [dWidth-1:0] q_a,
input [aWidth-1:0] addr_b,
input [dWidth-1:0] d_b,
input we_b,
output reg [dWidth-1:0] q_b
);
reg [dWidth-1:0] ram [2**aWidth-1:0];
always @(posedge clk) begin
if (we_a) begin
ram[addr_a] <= d_a;
q_a <= d_a;
end
else
begin
q_a <= ram[addr_a];
end
if (we_b) begin
ram[addr_b] <= d_b;
q_b <= d_b;
end
else
begin
q_b <= ram[addr_b];
end
end
endmodule

View File

@@ -0,0 +1,64 @@
/* This file is part of JT49.
JT49 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.
JT49 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with JT49. If not, see <http://www.gnu.org/licenses/>.
Author: Jose Tejada Gomez. Twitter: @topapate
Version: 1.0
Date: 15-Jan-2019
*/
// DC removal filter
// input is unsigned
// output is signed
module jt49_dcrm2 #(parameter sw=8) (
input clk,
input cen,
input rst,
input [sw-1:0] din,
output signed [sw-1:0] dout
);
localparam dw=10; // width of the decimal portion
reg signed [sw+dw:0] integ, exact, error;
//reg signed [2*(9+dw)-1:0] mult;
// wire signed [sw+dw:0] plus1 = { {sw+dw{1'b0}},1'b1};
reg signed [sw:0] pre_dout;
// reg signed [sw+dw:0] dout_ext;
reg signed [sw:0] q;
always @(*) begin
exact = integ+error;
q = exact[sw+dw:dw];
pre_dout = { 1'b0, din } - q;
//dout_ext = { pre_dout, {dw{1'b0}} };
//mult = dout_ext;
end
assign dout = pre_dout[sw-1:0];
always @(posedge clk)
if( rst ) begin
integ <= {sw+dw+1{1'b0}};
error <= {sw+dw+1{1'b0}};
end else if( cen ) begin
/* verilator lint_off WIDTH */
integ <= integ + pre_dout; //mult[sw+dw*2:dw];
/* verilator lint_on WIDTH */
error <= exact-{q, {dw{1'b0}}};
end
endmodule

View File

@@ -0,0 +1,58 @@
///////////////////////////////////////////////////////////////////////////
// Fractional clock enable signal
// W refers to the number of divided down cen signals available
// each one is divided by 2
module jtframe_frac_cen #(parameter W=2)(
input clk,
input [9:0] n, // numerator
input [9:0] m, // denominator
output reg [W-1:0] cen,
output reg [W-1:0] cenb // 180 shifted
);
wire [10:0] step={1'b0,n};
wire [10:0] lim ={1'b0,m};
wire [10:0] absmax = lim+step;
reg [10:0] cencnt=11'd0;
reg [10:0] next;
reg [10:0] next2;
always @(*) begin
next = cencnt+step;
next2 = next-lim;
end
reg half = 1'b0;
wire over = next>=lim;
wire halfway = next >= (lim>>1) && !half;
reg [W-1:0] edgecnt = {W{1'b0}};
wire [W-1:0] next_edgecnt = edgecnt + 1'b1;
wire [W-1:0] toggle = next_edgecnt & ~edgecnt;
always @(posedge clk) begin
cen <= {W{1'b0}};
cenb <= {W{1'b0}};
if( cencnt >= absmax ) begin
// something went wrong: restart
cencnt <= 11'd0;
end else
if( halfway ) begin
half <= 1'b1;
cenb[0] <= 1'b1;
end
if( over ) begin
cencnt <= next2;
half <= 1'b0;
edgecnt <= next_edgecnt;
cen <= { toggle[W-2:0], 1'b1 };
end else begin
cencnt <= next;
end
end
endmodule

View File

@@ -0,0 +1,4 @@
set_global_assignment -name IP_TOOL_NAME "ALTPLL"
set_global_assignment -name IP_TOOL_VERSION "13.1"
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "pll.v"]
set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "pll.ppf"]

View File

@@ -0,0 +1,348 @@
// megafunction wizard: %ALTPLL%
// GENERATION: STANDARD
// VERSION: WM1.0
// MODULE: altpll
// ============================================================
// File Name: pll.v
// Megafunction Name(s):
// altpll
//
// Simulation Library Files(s):
// altera_mf
// ============================================================
// ************************************************************
// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
//
// 13.1.4 Build 182 03/12/2014 SJ Full Version
// ************************************************************
//Copyright (C) 1991-2014 Altera Corporation
//Your use of Altera Corporation's design tools, logic functions
//and other software and tools, and its AMPP partner logic
//functions, and any output files from any of the foregoing
//(including device programming or simulation files), and any
//associated documentation or information are expressly subject
//to the terms and conditions of the Altera Program License
//Subscription Agreement, Altera MegaCore Function License
//Agreement, or other applicable license agreement, including,
//without limitation, that your use is for the sole purpose of
//programming logic devices manufactured by Altera and sold by
//Altera or its authorized distributors. Please refer to the
//applicable agreement for further details.
// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module pll (
areset,
inclk0,
c0,
c1,
locked);
input areset;
input inclk0;
output c0;
output c1;
output locked;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
tri0 areset;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif
wire [4:0] sub_wire0;
wire sub_wire2;
wire [0:0] sub_wire6 = 1'h0;
wire [0:0] sub_wire3 = sub_wire0[0:0];
wire [1:1] sub_wire1 = sub_wire0[1:1];
wire c1 = sub_wire1;
wire locked = sub_wire2;
wire c0 = sub_wire3;
wire sub_wire4 = inclk0;
wire [1:0] sub_wire5 = {sub_wire6, sub_wire4};
altpll altpll_component (
.areset (areset),
.inclk (sub_wire5),
.clk (sub_wire0),
.locked (sub_wire2),
.activeclock (),
.clkbad (),
.clkena ({6{1'b1}}),
.clkloss (),
.clkswitch (1'b0),
.configupdate (1'b0),
.enable0 (),
.enable1 (),
.extclk (),
.extclkena ({4{1'b1}}),
.fbin (1'b1),
.fbmimicbidir (),
.fbout (),
.fref (),
.icdrclk (),
.pfdena (1'b1),
.phasecounterselect ({4{1'b1}}),
.phasedone (),
.phasestep (1'b1),
.phaseupdown (1'b1),
.pllena (1'b1),
.scanaclr (1'b0),
.scanclk (1'b0),
.scanclkena (1'b1),
.scandata (1'b0),
.scandataout (),
.scandone (),
.scanread (1'b0),
.scanwrite (1'b0),
.sclkout0 (),
.sclkout1 (),
.vcooverrange (),
.vcounderrange ());
defparam
altpll_component.bandwidth_type = "AUTO",
altpll_component.clk0_divide_by = 105,
altpll_component.clk0_duty_cycle = 50,
altpll_component.clk0_multiply_by = 191,
altpll_component.clk0_phase_shift = "0",
altpll_component.clk1_divide_by = 360,
altpll_component.clk1_duty_cycle = 50,
altpll_component.clk1_multiply_by = 191,
altpll_component.clk1_phase_shift = "0",
altpll_component.compensate_clock = "CLK0",
altpll_component.inclk0_input_frequency = 37037,
altpll_component.intended_device_family = "Cyclone III",
altpll_component.lpm_hint = "CBX_MODULE_PREFIX=pll",
altpll_component.lpm_type = "altpll",
altpll_component.operation_mode = "NORMAL",
altpll_component.pll_type = "AUTO",
altpll_component.port_activeclock = "PORT_UNUSED",
altpll_component.port_areset = "PORT_USED",
altpll_component.port_clkbad0 = "PORT_UNUSED",
altpll_component.port_clkbad1 = "PORT_UNUSED",
altpll_component.port_clkloss = "PORT_UNUSED",
altpll_component.port_clkswitch = "PORT_UNUSED",
altpll_component.port_configupdate = "PORT_UNUSED",
altpll_component.port_fbin = "PORT_UNUSED",
altpll_component.port_inclk0 = "PORT_USED",
altpll_component.port_inclk1 = "PORT_UNUSED",
altpll_component.port_locked = "PORT_USED",
altpll_component.port_pfdena = "PORT_UNUSED",
altpll_component.port_phasecounterselect = "PORT_UNUSED",
altpll_component.port_phasedone = "PORT_UNUSED",
altpll_component.port_phasestep = "PORT_UNUSED",
altpll_component.port_phaseupdown = "PORT_UNUSED",
altpll_component.port_pllena = "PORT_UNUSED",
altpll_component.port_scanaclr = "PORT_UNUSED",
altpll_component.port_scanclk = "PORT_UNUSED",
altpll_component.port_scanclkena = "PORT_UNUSED",
altpll_component.port_scandata = "PORT_UNUSED",
altpll_component.port_scandataout = "PORT_UNUSED",
altpll_component.port_scandone = "PORT_UNUSED",
altpll_component.port_scanread = "PORT_UNUSED",
altpll_component.port_scanwrite = "PORT_UNUSED",
altpll_component.port_clk0 = "PORT_USED",
altpll_component.port_clk1 = "PORT_USED",
altpll_component.port_clk2 = "PORT_UNUSED",
altpll_component.port_clk3 = "PORT_UNUSED",
altpll_component.port_clk4 = "PORT_UNUSED",
altpll_component.port_clk5 = "PORT_UNUSED",
altpll_component.port_clkena0 = "PORT_UNUSED",
altpll_component.port_clkena1 = "PORT_UNUSED",
altpll_component.port_clkena2 = "PORT_UNUSED",
altpll_component.port_clkena3 = "PORT_UNUSED",
altpll_component.port_clkena4 = "PORT_UNUSED",
altpll_component.port_clkena5 = "PORT_UNUSED",
altpll_component.port_extclk0 = "PORT_UNUSED",
altpll_component.port_extclk1 = "PORT_UNUSED",
altpll_component.port_extclk2 = "PORT_UNUSED",
altpll_component.port_extclk3 = "PORT_UNUSED",
altpll_component.self_reset_on_loss_lock = "OFF",
altpll_component.width_clock = 5;
endmodule
// ============================================================
// CNX file retrieval info
// ============================================================
// Retrieval info: PRIVATE: ACTIVECLK_CHECK STRING "0"
// Retrieval info: PRIVATE: BANDWIDTH STRING "1.000"
// Retrieval info: PRIVATE: BANDWIDTH_FEATURE_ENABLED STRING "1"
// Retrieval info: PRIVATE: BANDWIDTH_FREQ_UNIT STRING "MHz"
// Retrieval info: PRIVATE: BANDWIDTH_PRESET STRING "Low"
// Retrieval info: PRIVATE: BANDWIDTH_USE_AUTO STRING "1"
// Retrieval info: PRIVATE: BANDWIDTH_USE_PRESET STRING "0"
// Retrieval info: PRIVATE: CLKBAD_SWITCHOVER_CHECK STRING "0"
// Retrieval info: PRIVATE: CLKLOSS_CHECK STRING "0"
// Retrieval info: PRIVATE: CLKSWITCH_CHECK STRING "0"
// Retrieval info: PRIVATE: CNX_NO_COMPENSATE_RADIO STRING "0"
// Retrieval info: PRIVATE: CREATE_CLKBAD_CHECK STRING "0"
// Retrieval info: PRIVATE: CREATE_INCLK1_CHECK STRING "0"
// Retrieval info: PRIVATE: CUR_DEDICATED_CLK STRING "c0"
// Retrieval info: PRIVATE: CUR_FBIN_CLK STRING "c0"
// Retrieval info: PRIVATE: DEVICE_SPEED_GRADE STRING "8"
// Retrieval info: PRIVATE: DIV_FACTOR0 NUMERIC "105"
// Retrieval info: PRIVATE: DIV_FACTOR1 NUMERIC "360"
// Retrieval info: PRIVATE: DUTY_CYCLE0 STRING "50.00000000"
// Retrieval info: PRIVATE: DUTY_CYCLE1 STRING "50.00000000"
// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE0 STRING "49.114285"
// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE1 STRING "14.325000"
// Retrieval info: PRIVATE: EXPLICIT_SWITCHOVER_COUNTER STRING "0"
// Retrieval info: PRIVATE: EXT_FEEDBACK_RADIO STRING "0"
// Retrieval info: PRIVATE: GLOCKED_COUNTER_EDIT_CHANGED STRING "1"
// Retrieval info: PRIVATE: GLOCKED_FEATURE_ENABLED STRING "0"
// Retrieval info: PRIVATE: GLOCKED_MODE_CHECK STRING "0"
// Retrieval info: PRIVATE: GLOCK_COUNTER_EDIT NUMERIC "1048575"
// Retrieval info: PRIVATE: HAS_MANUAL_SWITCHOVER STRING "1"
// Retrieval info: PRIVATE: INCLK0_FREQ_EDIT STRING "27.000"
// Retrieval info: PRIVATE: INCLK0_FREQ_UNIT_COMBO STRING "MHz"
// Retrieval info: PRIVATE: INCLK1_FREQ_EDIT STRING "100.000"
// Retrieval info: PRIVATE: INCLK1_FREQ_EDIT_CHANGED STRING "1"
// Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_CHANGED STRING "1"
// Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_COMBO STRING "MHz"
// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: PRIVATE: INT_FEEDBACK__MODE_RADIO STRING "1"
// Retrieval info: PRIVATE: LOCKED_OUTPUT_CHECK STRING "1"
// Retrieval info: PRIVATE: LONG_SCAN_RADIO STRING "1"
// Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE STRING "Not Available"
// Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE_DIRTY NUMERIC "0"
// Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT0 STRING "deg"
// Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT1 STRING "ps"
// Retrieval info: PRIVATE: MIG_DEVICE_SPEED_GRADE STRING "Any"
// Retrieval info: PRIVATE: MIRROR_CLK0 STRING "0"
// Retrieval info: PRIVATE: MIRROR_CLK1 STRING "0"
// Retrieval info: PRIVATE: MULT_FACTOR0 NUMERIC "191"
// Retrieval info: PRIVATE: MULT_FACTOR1 NUMERIC "191"
// Retrieval info: PRIVATE: NORMAL_MODE_RADIO STRING "1"
// Retrieval info: PRIVATE: OUTPUT_FREQ0 STRING "49.15200000"
// Retrieval info: PRIVATE: OUTPUT_FREQ1 STRING "14.31818000"
// Retrieval info: PRIVATE: OUTPUT_FREQ_MODE0 STRING "0"
// Retrieval info: PRIVATE: OUTPUT_FREQ_MODE1 STRING "0"
// Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT0 STRING "MHz"
// Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT1 STRING "MHz"
// Retrieval info: PRIVATE: PHASE_RECONFIG_FEATURE_ENABLED STRING "1"
// Retrieval info: PRIVATE: PHASE_RECONFIG_INPUTS_CHECK STRING "0"
// Retrieval info: PRIVATE: PHASE_SHIFT0 STRING "0.00000000"
// Retrieval info: PRIVATE: PHASE_SHIFT1 STRING "0.00000000"
// Retrieval info: PRIVATE: PHASE_SHIFT_STEP_ENABLED_CHECK STRING "0"
// Retrieval info: PRIVATE: PHASE_SHIFT_UNIT0 STRING "deg"
// Retrieval info: PRIVATE: PHASE_SHIFT_UNIT1 STRING "deg"
// Retrieval info: PRIVATE: PLL_ADVANCED_PARAM_CHECK STRING "0"
// Retrieval info: PRIVATE: PLL_ARESET_CHECK STRING "1"
// Retrieval info: PRIVATE: PLL_AUTOPLL_CHECK NUMERIC "1"
// Retrieval info: PRIVATE: PLL_ENHPLL_CHECK NUMERIC "0"
// Retrieval info: PRIVATE: PLL_FASTPLL_CHECK NUMERIC "0"
// Retrieval info: PRIVATE: PLL_FBMIMIC_CHECK STRING "0"
// Retrieval info: PRIVATE: PLL_LVDS_PLL_CHECK NUMERIC "0"
// Retrieval info: PRIVATE: PLL_PFDENA_CHECK STRING "0"
// Retrieval info: PRIVATE: PLL_TARGET_HARCOPY_CHECK NUMERIC "0"
// Retrieval info: PRIVATE: PRIMARY_CLK_COMBO STRING "inclk0"
// Retrieval info: PRIVATE: RECONFIG_FILE STRING "pll.mif"
// Retrieval info: PRIVATE: SACN_INPUTS_CHECK STRING "0"
// Retrieval info: PRIVATE: SCAN_FEATURE_ENABLED STRING "1"
// Retrieval info: PRIVATE: SELF_RESET_LOCK_LOSS STRING "0"
// Retrieval info: PRIVATE: SHORT_SCAN_RADIO STRING "0"
// Retrieval info: PRIVATE: SPREAD_FEATURE_ENABLED STRING "0"
// Retrieval info: PRIVATE: SPREAD_FREQ STRING "50.000"
// Retrieval info: PRIVATE: SPREAD_FREQ_UNIT STRING "KHz"
// Retrieval info: PRIVATE: SPREAD_PERCENT STRING "0.500"
// Retrieval info: PRIVATE: SPREAD_USE STRING "0"
// Retrieval info: PRIVATE: SRC_SYNCH_COMP_RADIO STRING "0"
// Retrieval info: PRIVATE: STICKY_CLK0 STRING "1"
// Retrieval info: PRIVATE: STICKY_CLK1 STRING "1"
// Retrieval info: PRIVATE: SWITCHOVER_COUNT_EDIT NUMERIC "1"
// Retrieval info: PRIVATE: SWITCHOVER_FEATURE_ENABLED STRING "1"
// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
// Retrieval info: PRIVATE: USE_CLK0 STRING "1"
// Retrieval info: PRIVATE: USE_CLK1 STRING "1"
// Retrieval info: PRIVATE: USE_CLKENA0 STRING "0"
// Retrieval info: PRIVATE: USE_CLKENA1 STRING "0"
// Retrieval info: PRIVATE: USE_MIL_SPEED_GRADE NUMERIC "0"
// Retrieval info: PRIVATE: ZERO_DELAY_RADIO STRING "0"
// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
// Retrieval info: CONSTANT: BANDWIDTH_TYPE STRING "AUTO"
// Retrieval info: CONSTANT: CLK0_DIVIDE_BY NUMERIC "105"
// Retrieval info: CONSTANT: CLK0_DUTY_CYCLE NUMERIC "50"
// Retrieval info: CONSTANT: CLK0_MULTIPLY_BY NUMERIC "191"
// Retrieval info: CONSTANT: CLK0_PHASE_SHIFT STRING "0"
// Retrieval info: CONSTANT: CLK1_DIVIDE_BY NUMERIC "360"
// Retrieval info: CONSTANT: CLK1_DUTY_CYCLE NUMERIC "50"
// Retrieval info: CONSTANT: CLK1_MULTIPLY_BY NUMERIC "191"
// Retrieval info: CONSTANT: CLK1_PHASE_SHIFT STRING "0"
// Retrieval info: CONSTANT: COMPENSATE_CLOCK STRING "CLK0"
// Retrieval info: CONSTANT: INCLK0_INPUT_FREQUENCY NUMERIC "37037"
// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: CONSTANT: LPM_TYPE STRING "altpll"
// Retrieval info: CONSTANT: OPERATION_MODE STRING "NORMAL"
// Retrieval info: CONSTANT: PLL_TYPE STRING "AUTO"
// Retrieval info: CONSTANT: PORT_ACTIVECLOCK STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_ARESET STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_CLKBAD0 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_CLKBAD1 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_CLKLOSS STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_CLKSWITCH STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_CONFIGUPDATE STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_FBIN STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_INCLK0 STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_INCLK1 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_LOCKED STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_PFDENA STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PHASECOUNTERSELECT STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PHASEDONE STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PHASESTEP STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PHASEUPDOWN STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_PLLENA STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANACLR STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANCLK STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANCLKENA STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANDATA STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANDATAOUT STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANDONE STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANREAD STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_SCANWRITE STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clk0 STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_clk1 STRING "PORT_USED"
// Retrieval info: CONSTANT: PORT_clk2 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clk3 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clk4 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clk5 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena0 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena1 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena2 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena3 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena4 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_clkena5 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_extclk0 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_extclk1 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_extclk2 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: PORT_extclk3 STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: SELF_RESET_ON_LOSS_LOCK STRING "OFF"
// Retrieval info: CONSTANT: WIDTH_CLOCK NUMERIC "5"
// Retrieval info: USED_PORT: @clk 0 0 5 0 OUTPUT_CLK_EXT VCC "@clk[4..0]"
// Retrieval info: USED_PORT: areset 0 0 0 0 INPUT GND "areset"
// Retrieval info: USED_PORT: c0 0 0 0 0 OUTPUT_CLK_EXT VCC "c0"
// Retrieval info: USED_PORT: c1 0 0 0 0 OUTPUT_CLK_EXT VCC "c1"
// Retrieval info: USED_PORT: inclk0 0 0 0 0 INPUT_CLK_EXT GND "inclk0"
// Retrieval info: USED_PORT: locked 0 0 0 0 OUTPUT GND "locked"
// Retrieval info: CONNECT: @areset 0 0 0 0 areset 0 0 0 0
// Retrieval info: CONNECT: @inclk 0 0 1 1 GND 0 0 0 0
// Retrieval info: CONNECT: @inclk 0 0 1 0 inclk0 0 0 0 0
// Retrieval info: CONNECT: c0 0 0 0 0 @clk 0 0 1 0
// Retrieval info: CONNECT: c1 0 0 0 0 @clk 0 0 1 1
// Retrieval info: CONNECT: locked 0 0 0 0 @locked 0 0 0 0
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.v TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.ppf TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.inc FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.cmp FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll.bsf FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll_inst.v FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL pll_bb.v FALSE
// Retrieval info: LIB_FILE: altera_mf
// Retrieval info: CBX_MODULE_PREFIX: ON

View File

@@ -0,0 +1,136 @@
LIBRARY ieee;
USE ieee.std_logic_1164.all;
LIBRARY altera_mf;
USE altera_mf.all;
ENTITY dpram_dc IS
GENERIC
(
init_file : string := " ";
widthad_a : natural;
width_a : natural := 8;
outdata_reg_a : string := "UNREGISTERED";
outdata_reg_b : string := "UNREGISTERED"
);
PORT
(
address_a : IN STD_LOGIC_VECTOR (widthad_a-1 DOWNTO 0);
address_b : IN STD_LOGIC_VECTOR (widthad_a-1 DOWNTO 0) := (others => '0');
clock_a : IN STD_LOGIC ;
clock_b : IN STD_LOGIC ;
data_a : IN STD_LOGIC_VECTOR (width_a-1 DOWNTO 0) := (others => '0');
data_b : IN STD_LOGIC_VECTOR (width_a-1 DOWNTO 0) := (others => '0');
wren_a : IN STD_LOGIC := '0';
wren_b : IN STD_LOGIC := '0';
byteena_a : IN STD_LOGIC_VECTOR (width_a/8-1 DOWNTO 0) := (others => '1');
byteena_b : IN STD_LOGIC_VECTOR (width_a/8-1 DOWNTO 0) := (others => '1');
q_a : OUT STD_LOGIC_VECTOR (width_a-1 DOWNTO 0);
q_b : OUT STD_LOGIC_VECTOR (width_a-1 DOWNTO 0)
);
END dpram_dc;
ARCHITECTURE SYN OF dpram_dc IS
SIGNAL sub_wire0 : STD_LOGIC_VECTOR (width_a-1 DOWNTO 0);
SIGNAL sub_wire1 : STD_LOGIC_VECTOR (width_a-1 DOWNTO 0);
COMPONENT altsyncram
GENERIC (
address_reg_b : STRING;
clock_enable_input_a : STRING;
clock_enable_input_b : STRING;
clock_enable_output_a : STRING;
clock_enable_output_b : STRING;
indata_reg_b : STRING;
init_file : STRING;
intended_device_family : STRING;
lpm_type : STRING;
numwords_a : NATURAL;
numwords_b : NATURAL;
operation_mode : STRING;
outdata_aclr_a : STRING;
outdata_aclr_b : STRING;
outdata_reg_a : STRING;
outdata_reg_b : STRING;
power_up_uninitialized : STRING;
read_during_write_mode_port_a : STRING;
read_during_write_mode_port_b : STRING;
widthad_a : NATURAL;
widthad_b : NATURAL;
width_a : NATURAL;
width_b : NATURAL;
width_byteena_a : NATURAL;
width_byteena_b : NATURAL;
wrcontrol_wraddress_reg_b : STRING
);
PORT (
wren_a : IN STD_LOGIC ;
clock0 : IN STD_LOGIC ;
wren_b : IN STD_LOGIC ;
clock1 : IN STD_LOGIC ;
address_a : IN STD_LOGIC_VECTOR (widthad_a-1 DOWNTO 0);
address_b : IN STD_LOGIC_VECTOR (widthad_a-1 DOWNTO 0);
q_a : OUT STD_LOGIC_VECTOR (width_a-1 DOWNTO 0);
q_b : OUT STD_LOGIC_VECTOR (width_a-1 DOWNTO 0);
byteena_a : IN STD_LOGIC_VECTOR (width_a/8-1 DOWNTO 0) ;
byteena_b : IN STD_LOGIC_VECTOR (width_a/8-1 DOWNTO 0) ;
data_a : IN STD_LOGIC_VECTOR (width_a-1 DOWNTO 0);
data_b : IN STD_LOGIC_VECTOR (width_a-1 DOWNTO 0)
);
END COMPONENT;
BEGIN
q_a <= sub_wire0(width_a-1 DOWNTO 0);
q_b <= sub_wire1(width_a-1 DOWNTO 0);
altsyncram_component : altsyncram
GENERIC MAP (
address_reg_b => "CLOCK1",
clock_enable_input_a => "BYPASS",
clock_enable_input_b => "BYPASS",
clock_enable_output_a => "BYPASS",
clock_enable_output_b => "BYPASS",
indata_reg_b => "CLOCK1",
init_file => init_file,
intended_device_family => "Cyclone III",
lpm_type => "altsyncram",
numwords_a => 2**widthad_a,
numwords_b => 2**widthad_a,
operation_mode => "BIDIR_DUAL_PORT",
outdata_aclr_a => "NONE",
outdata_aclr_b => "NONE",
outdata_reg_a => outdata_reg_a,
outdata_reg_b => outdata_reg_a,
power_up_uninitialized => "FALSE",
read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ",
read_during_write_mode_port_b => "NEW_DATA_NO_NBE_READ",
widthad_a => widthad_a,
widthad_b => widthad_a,
width_a => width_a,
width_b => width_a,
width_byteena_a => width_a/8,
width_byteena_b => width_a/8,
wrcontrol_wraddress_reg_b => "CLOCK1"
)
PORT MAP (
wren_a => wren_a,
clock0 => clock_a,
wren_b => wren_b,
clock1 => clock_b,
address_a => address_a,
address_b => address_b,
data_a => data_a,
data_b => data_b,
q_a => sub_wire0,
q_b => sub_wire1,
byteena_a => byteena_a,
byteena_b => byteena_b
);
END SYN;

View File

@@ -0,0 +1,3 @@
set_global_assignment -name SYSTEMVERILOG_FILE rtl/ram_rom/rom_loader.sv
set_global_assignment -name VHDL_FILE rtl/ram_rom/spram.vhd
set_global_assignment -name VHDL_FILE rtl/ram_rom/dpram_dc.vhd

View File

@@ -0,0 +1,452 @@
//============================================================================
//
// SD card ROM loader and ROM selector for MISTer.
// Copyright (C) 2019, 2020 Kitrinx (aka Rysha)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
// Rom layout for Gyruss:
// 0x0000 - 0x1FFF = eprom_1
// 0x2000 - 0x3FFF = eprom_2
// 0x4000 - 0x5FFF = eprom_3
// 0x6000 - 0x7FFF = eprom_4
// 0x8000 - 0x9FFF = eprom_5
// 0xA000 - 0xBFFF = eprom_6
// 0xC000 - 0xDFFF = eprom_7
// 0xE000 - 0xFFFF = eprom_8
// 0x10000 - 0x11FFF = eprom_9
// 0x12000 - 0x13FFF = eprom_10
// 0x14000 - 0x15FFF = eprom_11
// 0x16000 - 0x16FFF = eprom_12
// 0x17000 - 0x170FF = tile_lut_prom
// 0x17100 - 0x171FF = sprite_lut_prom
// 0x17200 - 0x1721F = color_prom
module selector
(
input logic [24:0] ioctl_addr,
output logic ep1_cs, ep2_cs, ep3_cs, ep4_cs, ep5_cs, ep6_cs, ep7_cs, ep8_cs,
ep9_cs, ep10_cs, ep11_cs, ep12_cs, cp_cs, tl_cs, sl_cs
);
always_comb begin
{ep1_cs, ep2_cs, ep3_cs, ep4_cs, ep5_cs, ep6_cs, ep7_cs, ep8_cs, ep9_cs,
ep10_cs, ep11_cs, ep12_cs, cp_cs, tl_cs, sl_cs} = 0;
if(ioctl_addr < 'h2000)
ep1_cs = 1; // 0x2000 13
else if(ioctl_addr < 'h4000)
ep2_cs = 1; // 0x2000 13
else if(ioctl_addr < 'h6000)
ep3_cs = 1; // 0x2000 13
else if(ioctl_addr < 'h8000)
ep4_cs = 1; // 0x2000 13
else if(ioctl_addr < 'hA000)
ep5_cs = 1; // 0x2000 13
else if(ioctl_addr < 'hC000)
ep6_cs = 1; // 0x2000 13
else if(ioctl_addr < 'hE000)
ep7_cs = 1; // 0x2000 13
else if(ioctl_addr < 'h10000)
ep8_cs = 1; // 0x2000 13
else if(ioctl_addr < 'h12000)
ep9_cs = 1; // 0x2000 13
else if(ioctl_addr < 'h14000)
ep10_cs = 1; // 0x2000 13
else if(ioctl_addr < 'h16000)
ep11_cs = 1; // 0x2000 13
else if(ioctl_addr < 'h17000)
ep12_cs = 1; // 0x1000 12
else if(ioctl_addr < 'h17100)
tl_cs = 1; // 0x100 8
else if(ioctl_addr < 'h17200)
sl_cs = 1; // 0x100 8
else
cp_cs = 1; // 0x20 5
end
endmodule
////////////
// EPROMS //
////////////
module eprom_1
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_1
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_2
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_2
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_3
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_3
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_4
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_4
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_5
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_5
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_6
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_6
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_7
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_7
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_8
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_8
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_9
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_9
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_10
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_10
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_11
(
input logic CLK,
input logic CLK_DL,
input logic [12:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(13)) eprom_11
(
.clock_a(CLK),
.address_a(ADDR[12:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[12:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module eprom_12
(
input logic CLK,
input logic CLK_DL,
input logic [11:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(12)) eprom_12
(
.clock_a(CLK),
.address_a(ADDR[11:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[11:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
///////////
// PROMS //
///////////
module color_prom
(
input logic CLK,
input logic CLK_DL,
input logic [4:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [7:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [7:0] DATA
);
dpram_dc #(.widthad_a(5)) color_prom_1
(
.clock_a(CLK),
.address_a(ADDR[4:0]),
.q_a(DATA[7:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[4:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module tile_lut_prom
(
input logic CLK,
input logic CLK_DL,
input logic [7:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [3:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [3:0] DATA
);
dpram_dc #(.widthad_a(8)) tile_lut_prom
(
.clock_a(CLK),
.address_a(ADDR[7:0]),
.q_a(DATA[3:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[7:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule
module sprite_lut_prom
(
input logic CLK,
input logic CLK_DL,
input logic [7:0] ADDR,
input logic [24:0] ADDR_DL,
input logic [3:0] DATA_IN,
input logic CS_DL,
input logic WR,
output logic [3:0] DATA
);
dpram_dc #(.widthad_a(8)) sprite_lut_prom
(
.clock_a(CLK),
.address_a(ADDR[7:0]),
.q_a(DATA[3:0]),
.clock_b(CLK_DL),
.address_b(ADDR_DL[7:0]),
.data_b(DATA_IN),
.wren_b(WR & CS_DL)
);
endmodule

View File

@@ -0,0 +1,46 @@
library ieee;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.ALL;
use IEEE.numeric_std.all;
entity spram is
generic
(
DATA_WIDTH : natural := 8;
ADDR_WIDTH : natural := 10
);
port
(
clk : in std_logic;
addr : in std_logic_vector((ADDR_WIDTH - 1) downto 0);
data : in std_logic_vector((DATA_WIDTH - 1) downto 0);
q : out std_logic_vector((DATA_WIDTH - 1) downto 0);
we : in std_logic := '0'
);
end spram;
architecture rtl of spram is
subtype word_t is std_logic_vector((DATA_WIDTH-1) downto 0);
type memory_t is array(2**ADDR_WIDTH-1 downto 0) of word_t;
shared variable ram : memory_t;
begin
process(clk)
begin
if(rising_edge(clk)) then
if(we = '1') then
ram(to_integer(unsigned(addr))) := data;
q <= data;
else
q <= ram(to_integer(unsigned(addr)));
end if;
end if;
end process;
end rtl;

View File

@@ -0,0 +1,357 @@
//
// sdram.v
//
// sdram controller implementation for the MiST board
// https://github.com/mist-devel/mist-board
//
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
// Copyright (c) 2019 Gyorgy Szombathelyi
//
// This source file 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.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
module sdram (
// interface to the MT48LC16M16 chip
inout reg [15:0] SDRAM_DQ, // 16 bit bidirectional data bus
output reg [12:0] SDRAM_A, // 13 bit multiplexed address bus
output reg SDRAM_DQML, // two byte masks
output reg SDRAM_DQMH, // two byte masks
output reg [1:0] SDRAM_BA, // two banks
output SDRAM_nCS, // a single chip select
output SDRAM_nWE, // write enable
output SDRAM_nRAS, // row address select
output SDRAM_nCAS, // columns address select
// cpu/chipset interface
input init_n, // init signal after FPGA config to initialize RAM
input clk, // sdram clock
input port1_req,
output reg port1_ack,
input port1_we,
input [23:1] port1_a,
input [1:0] port1_ds,
input [15:0] port1_d,
output reg [15:0] port1_q,
input [16:1] cpu1_addr,
output reg [15:0] cpu1_q,
input [16:1] cpu2_addr,
output reg [15:0] cpu2_q,
input [16:1] cpu3_addr,
output reg [15:0] cpu3_q,
input port2_req,
output reg port2_ack,
input port2_we,
input [23:1] port2_a,
input [1:0] port2_ds,
input [15:0] port2_d,
output reg [31:0] port2_q,
input [16:2] sp_addr,
output reg [31:0] sp_q
);
parameter MHZ = 16'd80; // 80 MHz default clock, set it to proper value to calculate refresh rate
localparam RASCAS_DELAY = 3'd2; // tRCD=20ns -> 2 cycles@<100MHz
localparam BURST_LENGTH = 3'b001; // 000=1, 001=2, 010=4, 011=8
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
localparam CAS_LATENCY = 3'd2; // 2/3 allowed
localparam OP_MODE = 2'b00; // only 00 (standard operation) allowed
localparam NO_WRITE_BURST = 1'b1; // 0= write burst enabled, 1=only single access write
localparam MODE = { 3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH};
// 64ms/8192 rows = 7.8us
localparam RFRSH_CYCLES = 16'd78*MHZ/4'd10;
// ---------------------------------------------------------------------
// ------------------------ cycle state machine ------------------------
// ---------------------------------------------------------------------
/*
SDRAM state machine for 2 bank interleaved access
2 words burst, CL2
cmd issued registered
0 RAS0 cas1 - data0 read burst terminated
1 ras0
2 data1 returned
3 CAS0 data1 returned
4 RAS1 cas0
5 ras1
6 CAS1 data0 returned
*/
localparam STATE_RAS0 = 3'd0; // first state in cycle
localparam STATE_RAS1 = 3'd4; // Second ACTIVE command after RAS0 + tRRD (15ns)
localparam STATE_CAS0 = STATE_RAS0 + RASCAS_DELAY + 1'd1; // CAS phase - 3
localparam STATE_CAS1 = STATE_RAS1 + RASCAS_DELAY; // CAS phase - 6
localparam STATE_READ0 = 3'd0;// STATE_CAS0 + CAS_LATENCY + 2'd2; // 7
localparam STATE_READ1 = 3'd3;
localparam STATE_DS1b = 3'd0;
localparam STATE_READ1b = 3'd4;
localparam STATE_LAST = 3'd6;
reg [2:0] t;
always @(posedge clk) begin
t <= t + 1'd1;
if (t == STATE_LAST) t <= STATE_RAS0;
end
// ---------------------------------------------------------------------
// --------------------------- startup/reset ---------------------------
// ---------------------------------------------------------------------
// wait 1ms (32 8Mhz cycles) after FPGA config is done before going
// into normal operation. Initialize the ram in the last 16 reset cycles (cycles 15-0)
reg [4:0] reset;
reg init = 1'b1;
always @(posedge clk, negedge init_n) begin
if(!init_n) begin
reset <= 5'h1f;
init <= 1'b1;
end else begin
if((t == STATE_LAST) && (reset != 0)) reset <= reset - 5'd1;
init <= !(reset == 0);
end
end
// ---------------------------------------------------------------------
// ------------------ generate ram control signals ---------------------
// ---------------------------------------------------------------------
// all possible commands
localparam CMD_INHIBIT = 4'b1111;
localparam CMD_NOP = 4'b0111;
localparam CMD_ACTIVE = 4'b0011;
localparam CMD_READ = 4'b0101;
localparam CMD_WRITE = 4'b0100;
localparam CMD_BURST_TERMINATE = 4'b0110;
localparam CMD_PRECHARGE = 4'b0010;
localparam CMD_AUTO_REFRESH = 4'b0001;
localparam CMD_LOAD_MODE = 4'b0000;
reg [3:0] sd_cmd; // current command sent to sd ram
reg [15:0] sd_din;
// drive control signals according to current command
assign SDRAM_nCS = sd_cmd[3];
assign SDRAM_nRAS = sd_cmd[2];
assign SDRAM_nCAS = sd_cmd[1];
assign SDRAM_nWE = sd_cmd[0];
reg [24:1] addr_latch[3];
reg [24:1] addr_latch_next[2];
reg [16:1] addr_last[4];
reg [16:2] addr_last2[2];
reg [15:0] din_latch[2];
reg [1:0] oe_latch;
reg [1:0] we_latch;
reg [1:0] ds[2];
reg port1_state;
reg port2_state;
localparam PORT_NONE = 3'd0;
localparam PORT_CPU1 = 3'd1;
localparam PORT_CPU2 = 3'd2;
localparam PORT_CPU3 = 3'd3;
localparam PORT_SP = 3'd1;
localparam PORT_REQ = 3'd4;
reg [2:0] next_port[2];
reg [2:0] port[2];
reg refresh;
reg [10:0] refresh_cnt;
wire need_refresh = (refresh_cnt >= RFRSH_CYCLES);
// PORT1: bank 0,1
always @(*) begin
if (refresh) begin
next_port[0] = PORT_NONE;
addr_latch_next[0] = addr_latch[0];
end else if (port1_req ^ port1_state) begin
next_port[0] = PORT_REQ;
addr_latch_next[0] = { 1'b0, port1_a };
end else if (cpu1_addr != addr_last[PORT_CPU1]) begin
next_port[0] = PORT_CPU1;
addr_latch_next[0] = { 8'd0, cpu1_addr };
end else if (cpu2_addr != addr_last[PORT_CPU2]) begin
next_port[0] = PORT_CPU2;
addr_latch_next[0] = { 8'd0, cpu2_addr };
end else if (cpu3_addr != addr_last[PORT_CPU3]) begin
next_port[0] = PORT_CPU3;
addr_latch_next[0] = { 8'd0, cpu3_addr };
end else begin
next_port[0] = PORT_NONE;
addr_latch_next[0] = addr_latch[0];
end
end
// PORT1: bank 2,3
always @(*) begin
if (port2_req ^ port2_state) begin
next_port[1] = PORT_REQ;
addr_latch_next[1] = { 1'b1, port2_a };
end else if (sp_addr != addr_last2[PORT_SP]) begin
next_port[1] = PORT_SP;
addr_latch_next[1] = { 1'b1, 7'd0, sp_addr, 1'b0 };
end else begin
next_port[1] = PORT_NONE;
addr_latch_next[1] = addr_latch[1];
end
end
always @(posedge clk) begin
// permanently latch ram data to reduce delays
sd_din <= SDRAM_DQ;
SDRAM_DQ <= 16'bZZZZZZZZZZZZZZZZ;
{ SDRAM_DQMH, SDRAM_DQML } <= 2'b11;
sd_cmd <= CMD_NOP; // default: idle
refresh_cnt <= refresh_cnt + 1'd1;
if(init) begin
// initialization takes place at the end of the reset phase
if(t == STATE_RAS0) begin
if(reset == 15) begin
sd_cmd <= CMD_PRECHARGE;
SDRAM_A[10] <= 1'b1; // precharge all banks
end
if(reset == 10 || reset == 8) begin
sd_cmd <= CMD_AUTO_REFRESH;
end
if(reset == 2) begin
sd_cmd <= CMD_LOAD_MODE;
SDRAM_A <= MODE;
SDRAM_BA <= 2'b00;
end
end
end else begin
// RAS phase
// bank 0,1
if(t == STATE_RAS0) begin
addr_latch[0] <= addr_latch_next[0];
port[0] <= next_port[0];
{ oe_latch[0], we_latch[0] } <= 2'b00;
if (next_port[0] != PORT_NONE) begin
sd_cmd <= CMD_ACTIVE;
SDRAM_A <= addr_latch_next[0][22:10];
SDRAM_BA <= addr_latch_next[0][24:23];
addr_last[next_port[0]] <= addr_latch_next[0][16:1];
if (next_port[0] == PORT_REQ) begin
{ oe_latch[0], we_latch[0] } <= { ~port1_we, port1_we };
ds[0] <= port1_ds;
din_latch[0] <= port1_d;
port1_state <= port1_req;
end else begin
{ oe_latch[0], we_latch[0] } <= 2'b10;
ds[0] <= 2'b11;
end
end
end
// bank 2,3
if(t == STATE_RAS1) begin
refresh <= 1'b0;
addr_latch[1] <= addr_latch_next[1];
{ oe_latch[1], we_latch[1] } <= 2'b00;
port[1] <= next_port[1];
if (next_port[1] != PORT_NONE) begin
sd_cmd <= CMD_ACTIVE;
SDRAM_A <= addr_latch_next[1][22:10];
SDRAM_BA <= addr_latch_next[1][24:23];
addr_last2[next_port[1]] <= addr_latch_next[1][16:2];
if (next_port[1] == PORT_REQ) begin
{ oe_latch[1], we_latch[1] } <= { ~port1_we, port1_we };
ds[1] <= port2_ds;
din_latch[1] <= port2_d;
port2_state <= port2_req;
end else begin
{ oe_latch[1], we_latch[1] } <= 2'b10;
ds[1] <= 2'b11;
end
end
if (next_port[1] == PORT_NONE && need_refresh && !we_latch[0] && !oe_latch[0]) begin
refresh <= 1'b1;
refresh_cnt <= 0;
sd_cmd <= CMD_AUTO_REFRESH;
end
end
// CAS phase
if(t == STATE_CAS0 && (we_latch[0] || oe_latch[0])) begin
sd_cmd <= we_latch[0]?CMD_WRITE:CMD_READ;
{ SDRAM_DQMH, SDRAM_DQML } <= ~ds[0];
if (we_latch[0]) begin
SDRAM_DQ <= din_latch[0];
port1_ack <= port1_req;
end
SDRAM_A <= { 4'b0010, addr_latch[0][9:1] }; // auto precharge
SDRAM_BA <= addr_latch[0][24:23];
end
if(t == STATE_CAS1 && (we_latch[1] || oe_latch[1])) begin
sd_cmd <= we_latch[1]?CMD_WRITE:CMD_READ;
{ SDRAM_DQMH, SDRAM_DQML } <= ~ds[1];
if (we_latch[1]) begin
SDRAM_DQ <= din_latch[1];
port2_ack <= port2_req;
end
SDRAM_A <= { 4'b0010, addr_latch[1][9:1] }; // auto precharge
SDRAM_BA <= addr_latch[1][24:23];
end
// Data returned
if(t == STATE_READ0 && oe_latch[0]) begin
case(port[0])
PORT_REQ: begin port1_q <= sd_din; port1_ack <= port1_req; end
PORT_CPU1: begin cpu1_q <= sd_din; end
PORT_CPU2: begin cpu2_q <= sd_din; end
PORT_CPU3: begin cpu3_q <= sd_din; end
default: ;
endcase;
end
if(t == STATE_READ1 && oe_latch[1]) begin
case(port[1])
PORT_REQ: port2_q[15:0] <= sd_din;
PORT_SP : sp_q[15:0] <= sd_din;
default: ;
endcase;
end
if(t == STATE_DS1b && oe_latch[1]) { SDRAM_DQMH, SDRAM_DQML } <= ~ds[1];
if(t == STATE_READ1b && oe_latch[1]) begin
case(port[1])
PORT_REQ: begin port2_q[31:16] <= sd_din; port2_ack <= port2_req; end
PORT_SP : begin sp_q[31:16] <= sd_din; end
default: ;
endcase;
end
end
end
endmodule