From a4d66e762422efd38e648e3c6f614c1e9864658d Mon Sep 17 00:00:00 2001 From: Luan Freitas Date: Thu, 20 Aug 2026 02:40:08 -0300 Subject: [PATCH 1/4] FPGA build: optional guard against XST absorbing RAM address registers --- verilog/common.mk | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/verilog/common.mk b/verilog/common.mk index d4cbac14f..1a4008de5 100644 --- a/verilog/common.mk +++ b/verilog/common.mk @@ -126,6 +126,23 @@ main.ngc: main.xst main.prj $(UCF) rm -f $@ mkdir -p $(XILINX_XST_TMPDIR) $(XILINX_ENV) $(XILINX_BIN)/xst -ifn main.xst -ofn main.syr +# Opt-in guard (set XST_ABSORB_WHITELIST in the core Makefile, `|`-separated): +# when XST implements a RAM as BLOCK RAM it may silently absorb the read +# address register (INFO:Xst:3226 "... absorbing ... "), turning an +# asynchronous-read array into a synchronous one -- the simulated RTL no +# longer matches the netlist, and on Spartan-3 such a RAM has been measured +# serving entry 0 forever. Fail the build on any absorption not whitelisted. +ifdef XST_ABSORB_WHITELIST + @bad=$$(grep -o 'absorbing the following register(s):.*' main.syr \ + | sed 's/^[^:]*: *//' | tr ' ' '\n' | tr -d '<>' \ + | sed '/^$$/d' | grep -v -x -E '$(XST_ABSORB_WHITELIST)' || true); \ + if [ -n "$$bad" ]; then \ + echo "ERROR: XST absorbed non-whitelisted register(s) into BLOCK RAM: $$bad"; \ + echo " (async-read array turned sync -- see XST_ABSORB_WHITELIST)"; \ + rm -f main.ngc; \ + exit 1; \ + fi +endif main.ngd: main.ngc $(XIL_IP) $(call T,[mk2] fpga_$(CORE) - Translate) From 130edb53ce693887de7c3853829f7abaad495cc8 Mon Sep 17 00:00:00 2001 From: Luan Freitas Date: Thu, 20 Aug 2026 02:40:08 -0300 Subject: [PATCH 2/4] SPC7110: add FPGA core, derived from the ares reference implementation (ISC) --- Makefile | 4 +- verilog/sd2snes_spc7110/DCM.v | 72 + verilog/sd2snes_spc7110/DCM_Scope.v | 84 + verilog/sd2snes_spc7110/Makefile | 23 + verilog/sd2snes_spc7110/address.v | 107 + verilog/sd2snes_spc7110/cheat.v | 388 +++ verilog/sd2snes_spc7110/clk_test.v | 52 + verilog/sd2snes_spc7110/dac.v | 286 ++ verilog/sd2snes_spc7110/ip/mk2/DCM_Scope.xaw | 3 + verilog/sd2snes_spc7110/ip/mk2/coregen.cgc | 2376 +++++++++++++++++ verilog/sd2snes_spc7110/ip/mk2/dac_buf.v | 184 ++ verilog/sd2snes_spc7110/ip/mk2/dac_buf.xco | 108 + verilog/sd2snes_spc7110/ip/mk2/dac_buf.xise | 388 +++ verilog/sd2snes_spc7110/ip/mk2/msu_databuf.v | 184 ++ .../sd2snes_spc7110/ip/mk2/msu_databuf.xco | 108 + .../sd2snes_spc7110/ip/mk2/msu_databuf.xise | 388 +++ verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.v | 190 ++ .../sd2snes_spc7110/ip/mk2/snescmd_buf.xco | 108 + .../sd2snes_spc7110/ip/mk2/snescmd_buf.xise | 388 +++ verilog/sd2snes_spc7110/ip/mk3/dac_buf.qip | 6 + verilog/sd2snes_spc7110/ip/mk3/dac_buf.v | 214 ++ .../sd2snes_spc7110/ip/mk3/msu_databuf.qip | 6 + verilog/sd2snes_spc7110/ip/mk3/msu_databuf.v | 214 ++ verilog/sd2snes_spc7110/ip/mk3/pll.qip | 7 + verilog/sd2snes_spc7110/ip/mk3/pll.v | 321 +++ .../sd2snes_spc7110/ip/mk3/snescmd_buf.qip | 6 + verilog/sd2snes_spc7110/ip/mk3/snescmd_buf.v | 243 ++ verilog/sd2snes_spc7110/main.qsf | 527 ++++ verilog/sd2snes_spc7110/main.sdc | 179 ++ verilog/sd2snes_spc7110/main.ucf | 699 +++++ verilog/sd2snes_spc7110/main.v | 1135 ++++++++ verilog/sd2snes_spc7110/mcu_cmd.v | 597 +++++ verilog/sd2snes_spc7110/msu.v | 215 ++ verilog/sd2snes_spc7110/pll.ppf | 11 + verilog/sd2snes_spc7110/rtc.v | 472 ++++ verilog/sd2snes_spc7110/sd2snes_spc7110.qpf | 30 + verilog/sd2snes_spc7110/sd2snes_spc7110.xise | 451 ++++ verilog/sd2snes_spc7110/sd_dma.v | 155 ++ verilog/sd2snes_spc7110/spc7110_alu.v | 258 ++ verilog/sd2snes_spc7110/spc7110_data.v | 314 +++ verilog/sd2snes_spc7110/spc7110_dcu.v | 1146 ++++++++ verilog/sd2snes_spc7110/spc7110_map.v | 145 + verilog/sd2snes_spc7110/spc7110_regs.v | 504 ++++ verilog/sd2snes_spc7110/spc7110_rtcif.v | 604 +++++ verilog/sd2snes_spc7110/spi.v | 127 + 45 files changed, 14025 insertions(+), 2 deletions(-) create mode 100644 verilog/sd2snes_spc7110/DCM.v create mode 100644 verilog/sd2snes_spc7110/DCM_Scope.v create mode 100644 verilog/sd2snes_spc7110/Makefile create mode 100644 verilog/sd2snes_spc7110/address.v create mode 100644 verilog/sd2snes_spc7110/cheat.v create mode 100644 verilog/sd2snes_spc7110/clk_test.v create mode 100644 verilog/sd2snes_spc7110/dac.v create mode 100644 verilog/sd2snes_spc7110/ip/mk2/DCM_Scope.xaw create mode 100644 verilog/sd2snes_spc7110/ip/mk2/coregen.cgc create mode 100644 verilog/sd2snes_spc7110/ip/mk2/dac_buf.v create mode 100644 verilog/sd2snes_spc7110/ip/mk2/dac_buf.xco create mode 100644 verilog/sd2snes_spc7110/ip/mk2/dac_buf.xise create mode 100644 verilog/sd2snes_spc7110/ip/mk2/msu_databuf.v create mode 100644 verilog/sd2snes_spc7110/ip/mk2/msu_databuf.xco create mode 100644 verilog/sd2snes_spc7110/ip/mk2/msu_databuf.xise create mode 100644 verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.v create mode 100644 verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.xco create mode 100644 verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.xise create mode 100644 verilog/sd2snes_spc7110/ip/mk3/dac_buf.qip create mode 100644 verilog/sd2snes_spc7110/ip/mk3/dac_buf.v create mode 100644 verilog/sd2snes_spc7110/ip/mk3/msu_databuf.qip create mode 100644 verilog/sd2snes_spc7110/ip/mk3/msu_databuf.v create mode 100644 verilog/sd2snes_spc7110/ip/mk3/pll.qip create mode 100644 verilog/sd2snes_spc7110/ip/mk3/pll.v create mode 100644 verilog/sd2snes_spc7110/ip/mk3/snescmd_buf.qip create mode 100644 verilog/sd2snes_spc7110/ip/mk3/snescmd_buf.v create mode 100644 verilog/sd2snes_spc7110/main.qsf create mode 100644 verilog/sd2snes_spc7110/main.sdc create mode 100644 verilog/sd2snes_spc7110/main.ucf create mode 100644 verilog/sd2snes_spc7110/main.v create mode 100644 verilog/sd2snes_spc7110/mcu_cmd.v create mode 100644 verilog/sd2snes_spc7110/msu.v create mode 100644 verilog/sd2snes_spc7110/pll.ppf create mode 100644 verilog/sd2snes_spc7110/rtc.v create mode 100644 verilog/sd2snes_spc7110/sd2snes_spc7110.qpf create mode 100644 verilog/sd2snes_spc7110/sd2snes_spc7110.xise create mode 100644 verilog/sd2snes_spc7110/sd_dma.v create mode 100644 verilog/sd2snes_spc7110/spc7110_alu.v create mode 100644 verilog/sd2snes_spc7110/spc7110_data.v create mode 100644 verilog/sd2snes_spc7110/spc7110_dcu.v create mode 100644 verilog/sd2snes_spc7110/spc7110_map.v create mode 100644 verilog/sd2snes_spc7110/spc7110_regs.v create mode 100644 verilog/sd2snes_spc7110/spc7110_rtcif.v create mode 100644 verilog/sd2snes_spc7110/spi.v diff --git a/Makefile b/Makefile index e39bfca4c..874669862 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,8 @@ MK3MENU := m3nu.bin FPGAPATH := verilog MK2EXT := bit MK3EXT := bi3 -MK2CORES := base cx4 gsu obc1 sdd1 sa1 dsp sgb sgb_msu -MK3CORES := base cx4 gsu obc1 sdd1 sa1 dsp sgb +MK2CORES := base cx4 gsu obc1 sdd1 sa1 dsp sgb sgb_msu spc7110 +MK3CORES := base cx4 gsu obc1 sdd1 sa1 dsp sgb spc7110 MK2FPGA := $(foreach C,$(MK2CORES),$(FPGAPATH)/sd2snes_$C/fpga_$C.$(MK2EXT)) MK3FPGA := $(foreach C,$(MK3CORES),$(FPGAPATH)/sd2snes_$C/fpga_$C.$(MK3EXT)) diff --git a/verilog/sd2snes_spc7110/DCM.v b/verilog/sd2snes_spc7110/DCM.v new file mode 100644 index 000000000..24dcc88eb --- /dev/null +++ b/verilog/sd2snes_spc7110/DCM.v @@ -0,0 +1,72 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 13:06:52 06/28/2009 +// Design Name: +// Module Name: dcm +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module my_dcm ( + input CLKIN, + output CLKFX, + output LOCKED, + input RST, + output[7:0] STATUS +); + +// DCM: Digital Clock Manager Circuit +// Spartan-3 +// Xilinx HDL Language Template, version 11.1 + + DCM #( + .SIM_MODE("SAFE"), // Simulation: "SAFE" vs. "FAST", see "Synthesis and Simulation Design Guide" for details + .CLKDV_DIVIDE(2.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5 + // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0 + .CLKFX_DIVIDE(1), // Can be any integer from 1 to 32 + .CLKFX_MULTIPLY(4), // Can be any integer from 2 to 32 + .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature + .CLKIN_PERIOD(41.667), // Specify period of input clock + .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE + .CLK_FEEDBACK("NONE"), // Specify clock feedback of NONE, 1X or 2X + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or + // an integer from 0 to 15 + .DFS_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for frequency synthesis + .DLL_FREQUENCY_MODE("LOW"), // HIGH or LOW frequency mode for DLL + .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE + .FACTORY_JF(16'hFFFF), // FACTORY JF values +// .LOC("DCM_X0Y0"), + .PHASE_SHIFT(0), // Amount of fixed phase shift from -255 to 255 + .STARTUP_WAIT("TRUE") // Delay configuration DONE until DCM LOCK, TRUE/FALSE + ) DCM_inst ( + .CLK0(CLK0), // 0 degree DCM CLK output + .CLK180(CLK180), // 180 degree DCM CLK output + .CLK270(CLK270), // 270 degree DCM CLK output + .CLK2X(CLK2X), // 2X DCM CLK output + .CLK2X180(CLK2X180), // 2X, 180 degree DCM CLK out + .CLK90(CLK90), // 90 degree DCM CLK output + .CLKDV(CLKDV), // Divided DCM CLK out (CLKDV_DIVIDE) + .CLKFX(CLKFX), // DCM CLK synthesis out (M/D) + .CLKFX180(CLKFX180), // 180 degree CLK synthesis out + .LOCKED(LOCKED), // DCM LOCK status output + .PSDONE(PSDONE), // Dynamic phase adjust done output + .STATUS(STATUS), // 8-bit DCM status bits output + .CLKFB(CLKFB), // DCM clock feedback + .CLKIN(CLKIN), // Clock input (from IBUFG, BUFG or DCM) + .PSCLK(PSCLK), // Dynamic phase adjust clock input + .PSEN(PSEN), // Dynamic phase adjust enable input + .PSINCDEC(PSINCDEC), // Dynamic phase adjust increment/decrement + .RST(RST) // DCM asynchronous reset input + ); +endmodule diff --git a/verilog/sd2snes_spc7110/DCM_Scope.v b/verilog/sd2snes_spc7110/DCM_Scope.v new file mode 100644 index 000000000..c806b13e1 --- /dev/null +++ b/verilog/sd2snes_spc7110/DCM_Scope.v @@ -0,0 +1,84 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 1995-2013 Xilinx, Inc. All rights reserved. +//////////////////////////////////////////////////////////////////////////////// +// ____ ____ +// / /\/ / +// /___/ \ / Vendor: Xilinx +// \ \ \/ Version : 14.7 +// \ \ Application : xaw2verilog +// / / Filename : DCM_Scope.v +// /___/ /\ Timestamp : 10/02/2020 08:51:22 +// \ \ / \ +// \___\/\___\ +// +//Command: xaw2verilog -intstyle D:/prj/sd2snes/verilog/sd2snes_sdd1/ip/mk2/DCM_Scope.xaw -st DCM_Scope.v +//Design Name: DCM_Scope +//Device: xc3s400-4pq208 +// +// Module DCM_Scope +// Generated by Xilinx Architecture Wizard +// Written for synthesis tool: XST +// Period Jitter (unit interval) for block DCM_INST = 0.07 UI +// Period Jitter (Peak-to-Peak) for block DCM_INST = 0.75 ns +`timescale 1ns / 1ps + +module DCM_Scope(CLKIN_IN, + RST_IN, + CLKDV_OUT, + CLKFX_OUT, + CLKIN_IBUFG_OUT, + CLK0_OUT, + LOCKED_OUT); + + input CLKIN_IN; + input RST_IN; + output CLKDV_OUT; + output CLKFX_OUT; + output CLKIN_IBUFG_OUT; + output CLK0_OUT; + output LOCKED_OUT; + + wire CLKDV_BUF; + wire CLKFB_IN; + wire CLKFX_BUF; + wire CLKIN_IBUFG; + wire CLK0_BUF; + wire GND_BIT; + + assign GND_BIT = 0; + assign CLKIN_IBUFG_OUT = CLKIN_IBUFG; + assign CLK0_OUT = CLKFB_IN; + BUFG CLKDV_BUFG_INST (.I(CLKDV_BUF), + .O(CLKDV_OUT)); + BUFG CLKFX_BUFG_INST (.I(CLKFX_BUF), + .O(CLKFX_OUT)); + IBUFG CLKIN_IBUFG_INST (.I(CLKIN_IN), + .O(CLKIN_IBUFG)); + BUFG CLK0_BUFG_INST (.I(CLK0_BUF), + .O(CLKFB_IN)); + DCM #( .CLK_FEEDBACK("1X"), .CLKDV_DIVIDE(4.0), .CLKFX_DIVIDE(1), + .CLKFX_MULTIPLY(4), .CLKIN_DIVIDE_BY_2("FALSE"), + .CLKIN_PERIOD(41.667), .CLKOUT_PHASE_SHIFT("NONE"), + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), .DFS_FREQUENCY_MODE("LOW"), + .DLL_FREQUENCY_MODE("LOW"), .DUTY_CYCLE_CORRECTION("TRUE"), + .FACTORY_JF(16'h8080), .PHASE_SHIFT(0), .STARTUP_WAIT("TRUE") ) + DCM_INST (.CLKFB(CLKFB_IN), + .CLKIN(CLKIN_IBUFG), + .DSSEN(GND_BIT), + .PSCLK(GND_BIT), + .PSEN(GND_BIT), + .PSINCDEC(GND_BIT), + .RST(RST_IN), + .CLKDV(CLKDV_BUF), + .CLKFX(CLKFX_BUF), + .CLKFX180(), + .CLK0(CLK0_BUF), + .CLK2X(), + .CLK2X180(), + .CLK90(), + .CLK180(), + .CLK270(), + .LOCKED(LOCKED_OUT), + .PSDONE(), + .STATUS()); +endmodule diff --git a/verilog/sd2snes_spc7110/Makefile b/verilog/sd2snes_spc7110/Makefile new file mode 100644 index 000000000..c70273a81 --- /dev/null +++ b/verilog/sd2snes_spc7110/Makefile @@ -0,0 +1,23 @@ +CORE = spc7110 + +VSRC = address.v cheat.v clk_test.v dac.v DCM_Scope.v main.v mcu_cmd.v msu.v \ + rtc.v sd_dma.v spc7110_alu.v spc7110_data.v spc7110_dcu.v spc7110_map.v \ + spc7110_regs.v spc7110_rtcif.v spi.v +VHSRC = + +COMMON_IP = dac_buf msu_databuf snescmd_buf + +XIL_IP = +XIL_IPCORE_DIR = ip/mk2 + +INT_IP = pll + +# Registers XST is ALLOWED to absorb into a BLOCK RAM address port (Xst:3226). +# ctx_q is synchronous-read by construction; everything else in this core must +# stay asynchronous-read (the tile buffers shipped broken once when XST turned +# three of them into BRAMs that never advanced their address on real silicon). +# Extend with | when adding a legitimately synchronous-read array. +XST_ABSORB_WHITELIST = ctx_q + +include ../settings.mk +include ../common.mk diff --git a/verilog/sd2snes_spc7110/address.v b/verilog/sd2snes_spc7110/address.v new file mode 100644 index 000000000..2b139309e --- /dev/null +++ b/verilog/sd2snes_spc7110/address.v @@ -0,0 +1,107 @@ +`timescale 1 ns / 1 ns +////////////////////////////////////////////////////////////////////////////////// +// Company: Rehkopf +// Engineer: Rehkopf +// +// Create Date: 01:13:46 05/09/2009 +// Design Name: +// Module Name: address +// Project Name: +// Target Devices: +// Tool versions: +// Description: Address logic w/ SaveRAM masking +// +// Dependencies: +// +// Revision: +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module address( + input [15:0] featurebits, // peripheral enable/disable + input [23:0] SNES_ADDR, // requested address from SNES + input [7:0] SNES_PA, // peripheral address from SNES + input SNES_ROMSEL, // ROMSEL from SNES + output [23:0] ROM_ADDR, // Address to request from SRAM0 + output ROM_HIT, // enable SRAM0 + output IS_SAVERAM, // address/CS mapped as SRAM? + output IS_ROM, // address mapped as ROM? + output IS_WRITABLE, // address somehow mapped as writable area? + input [23:0] SAVERAM_MASK, + input [23:0] ROM_MASK, + /* cartridge mapping from spc7110_map: the four $4830-$4834 selected 1 MB + windows over program ROM / data ROM, and the $6000-$7FFF SRAM gate */ + input SPC7110_ROM_HIT, + input [23:0] SPC7110_PSRAM_ADDR, + input SPC7110_IS_SRAM, + output msu_enable, + output r213f_enable, + output r2100_hit, + output snescmd_enable, + output nmicmd_enable, + output return_vector_enable, + output branch1_enable, + output branch2_enable, + output branch3_enable +); +// SPC7110 behaviour referenced here follows the ares implementation (ISC); +// the full ISC notice is carried in spc7110_map.v / spc7110_dcu.v. + +/* feature bits. see src/fpga_spi.c for mapping */ +parameter [2:0] + FEAT_MSU1 = 3, + FEAT_213F = 4 +; + +wire [23:0] SRAM_SNES_ADDR; + +/* + SPC7110 memory mapper. + + The cartridge mapping itself -- program ROM window, the four $4830-$4834 + selected data ROM windows and the $6000-$7FFF SRAM gate -- is decoded by + spc7110_map.v, which sees the register file directly. This file only turns + its verdict into the final PSRAM address: the mapped ROM offset comes in ready + to use, and the save RAM offset is built here because the SPC7110 packs it + differently from the stock LoROM mirroring. +*/ + +// active high to select ROM in banks 00-3f,80-bf:8000-ffff and 40-7d,c0-ff:0000-ffff +// (decoded by SNES). This one stays the plain ROMSEL decode: it drives the data +// bus level shifter, not the PSRAM address. +assign IS_ROM = ~SNES_ROMSEL; + +/* Save RAM mapping: $00-3f,80-bf:6000-7fff, enabled by $4830 bit 7 (which is + already part of SPC7110_IS_SRAM). The reference folds the address as + ((bank & 0x7f) << 13) | (addr & 0x1fff), i.e. every enabled bank contributes + its own 8 KB page instead of the four-banks-per-page mirroring of a LoROM + cartridge. The window can only be reached with address bit 22 clear, so the + seven bank bits are exactly SNES_ADDR[22:16]. */ +wire [19:0] SPC7110_SRAM_OFFSET = {SNES_ADDR[22:16], SNES_ADDR[12:0]}; + +assign IS_SAVERAM = SPC7110_IS_SRAM; + +// '1' to signal access to cartrigde writable range (Backup RAM or BS-X RAM) +assign IS_WRITABLE = IS_SAVERAM; + +assign SRAM_SNES_ADDR = IS_SAVERAM ? 24'hE00000 + ({4'h0, SPC7110_SRAM_OFFSET} & SAVERAM_MASK) + : SPC7110_PSRAM_ADDR; + +assign ROM_ADDR = SRAM_SNES_ADDR; + +// '1' when accesing PSRAM for ROM, Backup RAM, BS-X RAM +assign ROM_HIT = SPC7110_ROM_HIT | IS_WRITABLE; + +// '1' when accessing to MSU register map $2000:$2007 +assign msu_enable = featurebits[FEAT_MSU1] & (!SNES_ADDR[22] && ((SNES_ADDR[15:0] & 16'hfff8) == 16'h2000)); + +assign r213f_enable = featurebits[FEAT_213F] & (SNES_PA == 8'h3f); +assign r2100_hit = (SNES_PA == 8'h00); + +assign snescmd_enable = ({SNES_ADDR[22], SNES_ADDR[15:9]} == 8'b0_0010101); +assign nmicmd_enable = (SNES_ADDR == 24'h002BF2); +assign return_vector_enable = (SNES_ADDR == 24'h002A6C); +assign branch1_enable = (SNES_ADDR == 24'h002A1F); +assign branch2_enable = (SNES_ADDR == 24'h002A59); +assign branch3_enable = (SNES_ADDR == 24'h002A5E); +endmodule diff --git a/verilog/sd2snes_spc7110/cheat.v b/verilog/sd2snes_spc7110/cheat.v new file mode 100644 index 000000000..3048045fb --- /dev/null +++ b/verilog/sd2snes_spc7110/cheat.v @@ -0,0 +1,388 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 16:53:07 07/01/2014 +// Design Name: +// Module Name: cheat +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module cheat( + input clk, + input [7:0] SNES_PA, + input [23:0] SNES_ADDR, + input [7:0] SNES_DATA, + input SNES_wr_strobe, + input SNES_rd_strobe, + input SNES_reset_strobe, + input snescmd_enable, + input nmicmd_enable, + input return_vector_enable, + input branch1_enable, + input branch2_enable, + input branch3_enable, + input pad_latch, + input snes_ajr, + input SNES_cycle_start, + input [2:0] pgm_idx, + input pgm_we, + input [31:0] pgm_in, + output [7:0] data_out, + output cheat_hit, + output snescmd_unlock +); + +wire snescmd_wr_strobe = snescmd_enable & SNES_wr_strobe; + +reg cheat_enable = 0; +reg nmi_enable = 0; +reg irq_enable = 0; +reg holdoff_enable = 0; // temp disable hooks after reset +reg buttons_enable = 0; +reg wram_present = 0; +wire branch_wram = cheat_enable & wram_present; + +reg auto_nmi_enable = 1; +reg auto_irq_enable = 0; +reg auto_nmi_enable_sync = 0; +reg auto_irq_enable_sync = 0; +reg hook_enable_sync = 0; + +reg [1:0] sync_delay = 2'b10; + +reg [4:0] nmi_usage = 5'h00; +reg [4:0] irq_usage = 5'h00; +reg [20:0] usage_count = 21'h1fffff; + +reg [29:0] hook_enable_count = 0; +reg hook_disable = 0; + +reg [1:0] vector_unlock_r = 0; +wire vector_unlock = |vector_unlock_r; + +reg [1:0] reset_unlock_r = 2'b10; +wire reset_unlock = |reset_unlock_r; + +reg [23:0] cheat_addr[5:0]; +reg [7:0] cheat_data[5:0]; +reg [5:0] cheat_enable_mask; + +reg snescmd_unlock_r = 0; +assign snescmd_unlock = snescmd_unlock_r; + +reg [7:0] nmicmd = 0; +reg [7:0] return_vector = 8'hea; + +reg [7:0] branch1_offset = 8'h00; +reg [7:0] branch2_offset = 8'h00; +reg [7:0] branch3_offset = 8'h04; + +reg [15:0] pad_data = 0; + +wire [5:0] cheat_match_bits ={(cheat_enable_mask[5] & (SNES_ADDR == cheat_addr[5])), + (cheat_enable_mask[4] & (SNES_ADDR == cheat_addr[4])), + (cheat_enable_mask[3] & (SNES_ADDR == cheat_addr[3])), + (cheat_enable_mask[2] & (SNES_ADDR == cheat_addr[2])), + (cheat_enable_mask[1] & (SNES_ADDR == cheat_addr[1])), + (cheat_enable_mask[0] & (SNES_ADDR == cheat_addr[0]))}; +wire cheat_addr_match = |cheat_match_bits; + +wire [1:0] nmi_match_bits = {SNES_ADDR == 24'h00FFEA, SNES_ADDR == 24'h00FFEB}; +wire [1:0] irq_match_bits = {SNES_ADDR == 24'h00FFEE, SNES_ADDR == 24'h00FFEF}; +wire [1:0] rst_match_bits = {SNES_ADDR == 24'h00FFFC, SNES_ADDR == 24'h00FFFD}; + +wire nmi_addr_match = |nmi_match_bits; +wire irq_addr_match = |irq_match_bits; +wire rst_addr_match = |rst_match_bits; + +wire hook_enable = ~|hook_enable_count; + +assign data_out = cheat_match_bits[0] ? cheat_data[0] + : cheat_match_bits[1] ? cheat_data[1] + : cheat_match_bits[2] ? cheat_data[2] + : cheat_match_bits[3] ? cheat_data[3] + : cheat_match_bits[4] ? cheat_data[4] + : cheat_match_bits[5] ? cheat_data[5] + : nmi_match_bits[1] ? 8'h10 + : irq_match_bits[1] ? 8'h10 + : rst_match_bits[1] ? 8'h7D + : nmicmd_enable ? nmicmd + : return_vector_enable ? return_vector + : branch1_enable ? branch1_offset + : branch2_enable ? branch2_offset + : branch3_enable ? branch3_offset + : 8'h2a; + +assign cheat_hit = (snescmd_unlock & hook_enable_sync & (nmicmd_enable | return_vector_enable | branch1_enable | branch2_enable | branch3_enable)) + | (reset_unlock & rst_addr_match) + | (cheat_enable & cheat_addr_match) + | (hook_enable_sync & (((auto_nmi_enable_sync & nmi_enable) & nmi_addr_match & vector_unlock) + |((auto_irq_enable_sync & irq_enable) & irq_addr_match & vector_unlock))); + +// irq/nmi detect based on CPU access pattern +// 4 writes (mirrored to B bus) signify that the CPU pushes PB, PC and +// SR to the stack and is going to read the vector address in the next +// two cycles. +// B bus mirror is used (combined with A BUS /WR!) so the write pattern +// cannot be confused with backwards DMA transfers. + +reg [7:0] next_pa_addr = 0; +reg [2:0] cpu_push_cnt = 0; + +always @(posedge clk) begin + if(SNES_reset_strobe) begin + cpu_push_cnt <= 0; + end else if(SNES_wr_strobe) begin + cpu_push_cnt <= cpu_push_cnt + 1; + if(cpu_push_cnt == 3'b0) begin + next_pa_addr <= SNES_PA - 1; + end else begin + if(SNES_PA == next_pa_addr) begin + next_pa_addr <= next_pa_addr - 1; + end else begin + cpu_push_cnt <= 3'b0; + end + end + end else if(SNES_rd_strobe) begin + cpu_push_cnt <= 3'b0; + end +end + +// make patched vectors visible for last cycles of NMI/IRQ handling only +always @(posedge clk) begin + if(SNES_reset_strobe) begin + vector_unlock_r <= 2'b00; + end else if(SNES_rd_strobe) begin + if(hook_enable_sync + & ((auto_nmi_enable_sync & nmi_enable & nmi_match_bits[1]) + |(auto_irq_enable_sync & irq_enable & irq_match_bits[1])) + & cpu_push_cnt == 4) begin + vector_unlock_r <= 2'b11; + end else if(|vector_unlock_r) begin + vector_unlock_r <= vector_unlock_r - 1; + end + end +end + +// make patched reset vector visible for first fetch only +// (including masked read by Ultra16) +always @(posedge clk) begin + if(SNES_reset_strobe) begin + reset_unlock_r <= 2'b11; + end else if(SNES_cycle_start) begin + if(rst_addr_match & |reset_unlock_r) begin + reset_unlock_r <= reset_unlock_r - 1; + end + end +end + +reg snescmd_unlock_disable_strobe = 1'b0; +reg [6:0] snescmd_unlock_disable_countdown = 0; +reg snescmd_unlock_disable = 0; + +always @(posedge clk) begin + if(SNES_reset_strobe) begin + snescmd_unlock_r <= 0; + snescmd_unlock_disable <= 0; + end else begin + if(SNES_rd_strobe) begin + // *** GAME -> INGAME HOOK *** + if(hook_enable_sync + & ((auto_nmi_enable_sync & nmi_enable & nmi_match_bits[1]) + |(auto_irq_enable_sync & irq_enable & irq_match_bits[1])) + & cpu_push_cnt == 4) begin + // remember where we came from (IRQ/NMI) for hook exit + return_vector <= SNES_ADDR[7:0]; + snescmd_unlock_r <= 1; + end + if(rst_match_bits[1] & |reset_unlock_r) begin + snescmd_unlock_r <= 1; + end + end + // give some time to exit snescmd memory and jump to original vector + // sta @NMI_VECT_DISABLE 1-2 (after effective write) + // jmp ($ffxx) 3 (excluding address fetch) + // *** (INGAME HOOK -> GAME) *** + if(SNES_cycle_start) begin + if(snescmd_unlock_disable) begin + if(|snescmd_unlock_disable_countdown) begin + snescmd_unlock_disable_countdown <= snescmd_unlock_disable_countdown - 1; + end else if(snescmd_unlock_disable_countdown == 0) begin + snescmd_unlock_r <= 0; + snescmd_unlock_disable <= 0; + end + end + end + if(snescmd_unlock_disable_strobe) begin + snescmd_unlock_disable_countdown <= 7'd6; + snescmd_unlock_disable <= 1; + end + end +end + + +always @(posedge clk) usage_count <= usage_count - 1; + +// Try and autoselect NMI or IRQ hook +always @(posedge clk) begin + if(usage_count == 21'b0) begin + nmi_usage <= SNES_cycle_start & nmi_match_bits[1]; + irq_usage <= SNES_cycle_start & irq_match_bits[1]; + if(|nmi_usage & |irq_usage) begin + auto_nmi_enable <= 1'b1; + auto_irq_enable <= 1'b0; + end else if(irq_usage == 5'b0) begin + auto_nmi_enable <= 1'b1; + auto_irq_enable <= 1'b0; + end else if(nmi_usage == 5'b0) begin + auto_nmi_enable <= 1'b0; + auto_irq_enable <= 1'b1; + end + end else begin + if(SNES_cycle_start & nmi_match_bits[0]) nmi_usage <= nmi_usage + 1; + if(SNES_cycle_start & irq_match_bits[0]) irq_usage <= irq_usage + 1; + end +end + +// Do not change vectors while they are being read +always @(posedge clk) begin + if(SNES_cycle_start) begin + if(nmi_addr_match | irq_addr_match) sync_delay <= 2'b10; + else begin + if (|sync_delay) sync_delay <= sync_delay - 1; + if (sync_delay == 2'b00) begin + auto_nmi_enable_sync <= auto_nmi_enable; + auto_irq_enable_sync <= auto_irq_enable; + hook_enable_sync <= hook_enable; + end + end + end +end + +// CMD 0x85: disable hooks for 10 seconds +always @(posedge clk) begin + if((snescmd_unlock & snescmd_wr_strobe & ~|SNES_ADDR[8:0] & (SNES_DATA == 8'h85)) + | (holdoff_enable & SNES_reset_strobe)) begin + hook_enable_count <= 30'd960000000; + end else if (|hook_enable_count) begin + hook_enable_count <= hook_enable_count - 1; + end +end + +always @(posedge clk) begin + if(SNES_reset_strobe) begin + snescmd_unlock_disable_strobe <= 1'b0; + end else begin + snescmd_unlock_disable_strobe <= 1'b0; + if(snescmd_unlock & snescmd_wr_strobe) begin + if(~|SNES_ADDR[8:0]) begin + case(SNES_DATA) + 8'h82: cheat_enable <= 1; + 8'h83: cheat_enable <= 0; + 8'h84: {nmi_enable, irq_enable} <= 2'b00; + endcase + end else if(SNES_ADDR[8:0] == 9'h1fd) begin + snescmd_unlock_disable_strobe <= 1'b1; + end + end else if(pgm_we) begin + if(pgm_idx < 6) begin + cheat_addr[pgm_idx] <= pgm_in[31:8]; + cheat_data[pgm_idx] <= pgm_in[7:0]; + end else if(pgm_idx == 6) begin // set rom patch enable + cheat_enable_mask <= pgm_in[5:0]; + end else if(pgm_idx == 7) begin // set/reset global enable / hooks + // pgm_in[13:8] are reset bit flags + // pgm_in[5:0] are set bit flags + {wram_present, buttons_enable, holdoff_enable, irq_enable, nmi_enable, cheat_enable} + <= ({wram_present, buttons_enable, holdoff_enable, irq_enable, nmi_enable, cheat_enable} + & ~pgm_in[13:8]) + | pgm_in[5:0]; + end + end + end +end + +// map controller input to cmd output +// check button combinations +// L+R+Start+Select : $3030 +// L+R+Select+X : $2070 +// L+R+Start+A : $10b0 +// L+R+Start+B : $9030 +// L+R+Start+Y : $5030 +// L+R+Start+X : $1070 +always @(posedge clk) begin + if(snescmd_wr_strobe) begin + if(SNES_ADDR[8:0] == 9'h1f0) begin + pad_data[7:0] <= SNES_DATA; + end else if(SNES_ADDR[8:0] == 9'h1f1) begin + pad_data[15:8] <= SNES_DATA; + end + end +end + +always @* begin + case(pad_data) + 16'h3030: nmicmd = 8'h80; + 16'h2070: nmicmd = 8'h81; + 16'h10b0: nmicmd = 8'h82; + 16'h9030: nmicmd = 8'h83; + 16'h5030: nmicmd = 8'h84; + 16'h1070: nmicmd = 8'h85; + default: nmicmd = 8'h00; + endcase +end + +always @* begin + if(buttons_enable) begin + if(snes_ajr) begin + if(nmicmd) begin + branch1_offset = 8'h30; // nmi_echocmd + end else begin + if(branch_wram) begin + branch1_offset = 8'h3a; // nmi_patches + end else begin + branch1_offset = 8'h43; // nmi_exit + end + end + end else begin + if(pad_latch) begin + if(branch_wram) begin + branch1_offset = 8'h3a; // nmi_patches + end else begin + branch1_offset = 8'h43; // nmi_exit + end + end else begin + branch1_offset = 8'h00; // continue with MJR + end + end + end else begin + if(branch_wram) begin + branch1_offset = 8'h3a; // nmi_patches + end else begin + branch1_offset = 8'h43; // nmi_exit + end + end +end + +always @* begin + if(nmicmd == 8'h81) begin + branch2_offset = 8'h14; // nmi_stop + end else if(branch_wram) begin + branch2_offset = 8'h00; // nmi_patches + end else begin + branch2_offset = 8'h09; // nmi_exit + end +end + +endmodule diff --git a/verilog/sd2snes_spc7110/clk_test.v b/verilog/sd2snes_spc7110/clk_test.v new file mode 100644 index 000000000..a9825d05c --- /dev/null +++ b/verilog/sd2snes_spc7110/clk_test.v @@ -0,0 +1,52 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 22:40:46 12/20/2010 +// Design Name: +// Module Name: clk_test +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module clk_test( + input clk, + input sysclk, + output [31:0] snes_sysclk_freq +); + +reg [31:0] snes_sysclk_freq_r; +assign snes_sysclk_freq = snes_sysclk_freq_r; + +reg [31:0] sysclk_counter; +reg [31:0] sysclk_value; + +initial snes_sysclk_freq_r = 32'hFFFFFFFF; +initial sysclk_counter = 0; +initial sysclk_value = 0; + +reg [1:0] sysclk_sreg; +always @(posedge clk) sysclk_sreg <= {sysclk_sreg[0], sysclk}; +wire sysclk_rising = (sysclk_sreg == 2'b01); + +always @(posedge clk) begin + if(sysclk_counter < 96000000) begin + sysclk_counter <= sysclk_counter + 1; + if(sysclk_rising) sysclk_value <= sysclk_value + 1; + end else begin + snes_sysclk_freq_r <= sysclk_value; + sysclk_counter <= 0; + sysclk_value <= 0; + end +end + +endmodule diff --git a/verilog/sd2snes_spc7110/dac.v b/verilog/sd2snes_spc7110/dac.v new file mode 100644 index 000000000..15bcb452a --- /dev/null +++ b/verilog/sd2snes_spc7110/dac.v @@ -0,0 +1,286 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 19:26:11 07/23/2010 +// Design Name: +// Module Name: dac_test +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module dac( + input clkin, + input sysclk, + input we, + input[10:0] pgm_address, + input[7:0] pgm_data, + input[7:0] volume, + input vol_latch, + input [2:0] vol_select, + input [8:0] dac_address_ext, + input play, + input reset, + input palmode, + output sdout, + output mclk_out, + output lrck_out, + output sclk_out, + output DAC_STATUS +); + +reg[8:0] dac_address_r; +reg[8:0] dac_address_r_sync; +wire[8:0] dac_address = dac_address_r_sync; + +wire[31:0] dac_data; +assign DAC_STATUS = dac_address_r[8]; +reg[10:0] vol_reg; +reg[10:0] vol_target_reg; +reg[1:0] vol_latch_reg; +reg vol_valid; +reg[2:0] sysclk_sreg; +wire sysclk_rising = (sysclk_sreg[2:1] == 2'b01); + +always @(posedge clkin) begin + sysclk_sreg <= {sysclk_sreg[1:0], sysclk}; +end + +`ifdef MK2 +dac_buf snes_dac_buf ( + .clka(clkin), + .wea(~we), // Bus [0 : 0] + .addra(pgm_address), // Bus [10 : 0] + .dina(pgm_data), // Bus [7 : 0] + .clkb(clkin), + .addrb(dac_address), // Bus [8 : 0] + .doutb(dac_data)); // Bus [31 : 0] +`endif +`ifdef MK3 +dac_buf snes_dac_buf ( + .clock(clkin), + .wren(~we), // Bus [0 : 0] + .wraddress(pgm_address), // Bus [10 : 0] + .data(pgm_data), // Bus [7 : 0] + .rdaddress(dac_address), // Bus [8 : 0] + .q(dac_data)); // Bus [31 : 0] +`endif + +reg [10:0] cnt; +reg [15:0] smpcnt; +reg [1:0] samples; +reg [15:0] smpshift; + +wire mclk = cnt[2]; // mclk = clk/8 +wire lrck = cnt[8]; // lrck = mclk/64 +wire sclk = cnt[3]; // sclk = lrck*32 + +reg [2:0] mclk_sreg; +reg [2:0] lrck_sreg; +reg [1:0] sclk_sreg; + +assign mclk_out = ~mclk_sreg[2]; +assign lrck_out = lrck_sreg[2]; +assign sclk_out = sclk_sreg[1]; + +wire lrck_rising = ({lrck_sreg[0],lrck} == 2'b01); +wire lrck_falling = ({lrck_sreg[0],lrck} == 2'b10); + +wire sclk_rising = ({sclk_sreg[0],sclk} == 2'b01); +wire sclk_falling = ({sclk_sreg[0],sclk} == 2'b10); + +wire vol_latch_rising = (vol_latch_reg[1:0] == 2'b01); +reg sdout_reg; +assign sdout = sdout_reg; + +reg play_r; + +initial begin + cnt = 9'h100; + smpcnt = 16'b0; + lrck_sreg = 2'b00; + sclk_sreg = 2'b00; + mclk_sreg = 2'b00; + dac_address_r = 9'b0; + vol_valid = 1'b0; + vol_latch_reg = 1'b0; + vol_reg = 9'h000; + vol_target_reg = 9'h000; + samples <= 2'b00; +end + +/* + 21477272.727272... / 37500 * 1232 = 44100 * 16 + 21281370 / 709379 * 23520 = 44100 * 16 +*/ +reg [19:0] phaseacc = 0; +wire [14:0] phasemul = (palmode ? 23520 : 1232); +wire [19:0] phasediv = (palmode ? 709379 : 37500); +reg [3:0] subcount = 0; + +reg int_strobe = 0, comb_strobe = 0; + +always @(posedge clkin) begin + int_strobe <= 0; + comb_strobe <= 0; + if(reset) begin + dac_address_r <= dac_address_ext; + phaseacc <= 0; + subcount <= 0; + end else if(sysclk_rising) begin + if(phaseacc >= phasediv) begin + phaseacc <= phaseacc - phasediv + phasemul; + subcount <= subcount + 1; + int_strobe <= 1; + if (subcount == 0) begin + comb_strobe <= 1; + dac_address_r <= dac_address_r + play_r; + end + end else begin + phaseacc <= phaseacc + phasemul; + end + end +end + +parameter ST0_IDLE = 10'b1000000000; +parameter ST1_COMB1 = 10'b0000000001; +parameter ST2_COMB2 = 10'b0000000010; +parameter ST3_COMB3 = 10'b0000000100; +parameter ST4_INT1 = 10'b0000010000; +parameter ST5_INT2 = 10'b0000100000; +parameter ST6_INT3 = 10'b0001000000; + +reg [63:0] ci[2:0], co[2:0], io[2:0]; +reg [9:0] cicstate = 10'h200; +wire [63:0] bufi = {{16{dac_data[31]}}, dac_data[31:16], {16{dac_data[15]}}, dac_data[15:0]}; + +always @(posedge clkin) begin + if(reset) begin + cicstate <= ST0_IDLE; + {ci[2], ci[1], ci[0]} <= 192'h0; + {co[2], co[1], co[0]} <= 192'h0; + {io[2], io[1], io[0]} <= 192'h0; + end else if(int_strobe) begin + if(comb_strobe) cicstate <= ST1_COMB1; + else cicstate <= ST4_INT1; + end else begin + case(cicstate) +/****** COMB STAGES ******/ + ST1_COMB1: begin + cicstate <= ST2_COMB2; + ci[0] <= bufi; + co[0][63:32] <= bufi[63:32] - ci[0][63:32]; + co[0][31:0] <= bufi[31:0] - ci[0][31:0]; + end + ST2_COMB2: begin + cicstate <= ST3_COMB3; + ci[1] <= co[0]; + co[1][63:32] <= co[0][63:32] - ci[1][63:32]; + co[1][31:0] <= co[0][31:0] - ci[1][31:0]; + end + ST3_COMB3: begin + cicstate <= ST4_INT1; + ci[2] <= co[1]; + co[2][63:32] <= co[1][63:32] - ci[2][63:32]; + co[2][31:0] <= co[1][31:0] - ci[2][31:0]; + end +/****** INTEGRATOR STAGES ******/ + ST4_INT1: begin + io[0][63:32] <= co[2][63:32] + io[0][63:32]; + io[0][31:0] <= co[2][31:0] + io[0][31:0]; + cicstate <= ST5_INT2; + end + ST5_INT2: begin + io[1][63:32] <= io[0][63:32] + io[1][63:32]; + io[1][31:0] <= io[0][31:0] + io[1][31:0]; + cicstate <= ST6_INT3; + end + ST6_INT3: begin + io[2][63:32] <= io[1][63:32] + io[2][63:32]; + io[2][31:0] <= io[1][31:0] + io[2][31:0]; + cicstate <= ST0_IDLE; + end + default: begin + cicstate <= ST0_IDLE; + end + endcase + end +end + +always @(posedge clkin) begin + cnt <= cnt + 1; + mclk_sreg <= {mclk_sreg[1:0], mclk}; + lrck_sreg <= {lrck_sreg[1:0], lrck}; + sclk_sreg <= {sclk_sreg[0], sclk}; + vol_latch_reg <= {vol_latch_reg[0], vol_latch}; + play_r <= play; +end + +wire [9:0] vol_orig = volume + volume[7]; +wire [9:0] vol_3db = volume + volume[7:1] + volume[7]; +wire [9:0] vol_6db = {1'b0, volume, volume[7]} + volume[7]; +wire [9:0] vol_9db = {1'b0, volume, 1'b0} + volume + volume[7:6]; +wire [9:0] vol_12db = {volume, volume[7:6]}; + +reg [9:0] vol_scaled; +always @* begin + case(vol_select) + 3'b000: vol_scaled = vol_orig; + 3'b001: vol_scaled = vol_3db; + 3'b010: vol_scaled = vol_6db; + 3'b011: vol_scaled = vol_9db; + 3'b100: vol_scaled = vol_12db; + default: vol_scaled = vol_orig; + endcase +end + +always @(posedge clkin) begin + vol_target_reg <= vol_scaled; +end + +always @(posedge clkin) begin + if (lrck_rising) begin + dac_address_r_sync <= dac_address_r; + end +end + +// ramp volume only on sample boundaries +always @(posedge clkin) begin + if (lrck_rising) begin + if(vol_reg > vol_target_reg) + vol_reg <= vol_reg - 1; + else if(vol_reg < vol_target_reg) + vol_reg <= vol_reg + 1; + end +end + +wire signed [15:0] dac_data_ch = lrck ? io[2][59:44] : io[2][27:12]; +wire signed [25:0] vol_sample; +wire signed [15:0] vol_sample_sat; +assign vol_sample = dac_data_ch * $signed({1'b0, vol_reg}); +assign vol_sample_sat = ((vol_sample[25:23] == 3'b000 || vol_sample[25:23] == 3'b111) ? vol_sample[23:8] + : vol_sample[25] ? 16'sh8000 + : 16'sh7fff); + +always @(posedge clkin) begin + if (sclk_falling) begin + smpcnt <= smpcnt + 1; + sdout_reg <= smpshift[15]; + if (lrck_rising | lrck_falling) begin + smpshift <= vol_sample_sat; + end else begin + smpshift <= {smpshift[14:0], 1'b0}; + end + end +end + +endmodule diff --git a/verilog/sd2snes_spc7110/ip/mk2/DCM_Scope.xaw b/verilog/sd2snes_spc7110/ip/mk2/DCM_Scope.xaw new file mode 100644 index 000000000..199e842a8 --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/DCM_Scope.xaw @@ -0,0 +1,3 @@ +XILINX-XDB 0.1 STUB 0.1 ASCII +XILINX-XDM V1.6e +$9`x4>7<8=1;"=9;;1,414=6<2;?":6417%k<5>7:2;S>6<;1:65?17)>?=<7L\LHVKY52=FZ^PTCCBV_BCPGDBNFNUH@F?7;@PT^ZIIDPUH@FGA_DZWAWHFD8?0M_YU_NLO]ZEKC@DTZLBZE0`8EWQ]WFDGURJLM^QTMQEOAGMTOAE>0:CQS_YHFESTHI\PC133?DTPRVEE@TQKDS]@5475:CQS_YHFEST^H]JT^NLCLE602KY[WQ@NM[\RDJNLVNM_RC@DDc8EVUHKV]BXEh4AVX\GIME]O^R\H?>6:CT^ZEKCK_MXT^J1^LLAAOS9:1J[WQLLJ]LQQVR\V^R\Hm4AVX\BIIP\PZN46OXZ^RBVQb:>7NBD2Y:8GIM5P82;96MCK826?FJLL_30OAEKV^@VBc=DDBN]SO[IG^KMWQeEKCM\TCXZ:;BNHBG>@FK]EYURDI5:DBHVC>3OHT_B[]CDa8B@CCJHI@SO[I9:DJJZSIA]Yj7KAZT^WMMQUf3OE^XRXNLTG2?L3]72>W?7l|xz29gghd<~lxxeb`/1/27?sncdl1so>};01/7tt470tJK|984@Az71?@=03;p_9:5878;>7<77b3xj=4=92d3?784$9393c=z[=914;478;307``2:383459l1:tW3a<7280:6nu\458;2?>?2898ik;=:3:4g4=e?00;6=4::by'f?>03-;o6574$0g910<,j09n6*k:678 c<5:2.:<7<=;%32>70<,881>?5+12853>"6<3=>7)?::59'53<1=2.:;7;>;%3;>76<,=81;6*=e;;8 60=<2.8;774$2c914=#;o0j7):?:5d8 17=><1/85488:&7a?2<,=31:=5+4c854>"3l3=j7);?:79'17<212.>878>;%76>37<,<<1<6*:a;41?!04201/:54>;%55>2g<,>h1;n5+e;:8 4e=

6=n;00;6)8m:968 3b=?h10e>>50;&5f?>33-5<#>k0386*9d;5b?>o083:1(;l5859'2d<0i21b;<4?:%4a>=2<,?k1;l54i7g94?"1j32?7)8n:6c8?l2d290/:o474:&5e?1f32e9>7>5$7`9<1=#>m0a;02?!7e2<907b;4?:%4a>=2<3f8<6=4+6c8;0>=h:k0;6)8m:968 3b=?h1/=l4=1:9l6=<72-5<#>k03865`3383>!0e21>0(;j57`9'5d<5921d?>4?:%4a>=2<3f=<6=4+6c8;1>"113=j76a:e;29 3d=0=1/:i48a:9l2c<72-5$7`9<1=#>m0k07pl=d;296?6=8r.=o7<>;h74>5<#>k0386*9d;5b?>i1>3:1(;l5859'2a<0i21vn>;50;094?6|,?i1><5f5683>!0e21>0(;j57`98k30=83.=n76;;%4g>2g<3ty8n7>52z?73?5>349h6;84$0;97a=z{:;1<79;06?xu0;3:1=v3;7;51?!132<=0q~<<:1818202;801?:5569~w7e=838p19952c9>6a<2?2wx?94?:3y>02<4:27897;8;|q6b?6=9r7?;7;j;%57>30{<1`>01<,>>19:5rs3d94?7|5;n1:;5+75852>{t;10;6"0<3<=7p}4}zf;l1<7?t}o13>5<6std8=7>51zm77<728qvb>=50;3xyk53290:wp`<5;295~{i;?0;6{|l0=?6=9rwvqpNOCz65>=31 + + xilinx.com + project + coregen + 1.0 + + + FIFO_In + + + FIFO_In + Common_Clock_Distributed_RAM + 2 + 2 + Native + First_Word_Fall_Through + 8 + 16 + 8 + 16 + false + false + true + true + Asynchronous_Reset + 1 + true + 0 + false + false + true + Active_High + false + Active_High + false + Active_High + false + Active_High + false + false + true + false + 5 + false + 5 + false + 5 + false + 1 + 1 + Single_Programmable_Full_Threshold_Constant + 8 + 7 + No_Programmable_Empty_Threshold + 4 + 5 + AXI4_Stream + Common_Clock + false + Slave_Interface_Clock_Enable + false + false + 4 + 32 + 64 + false + 1 + false + 1 + false + 1 + false + 1 + false + 1 + false + 64 + false + 8 + false + 4 + false + 4 + true + false + false + 4 + false + 4 + FIFO + Common_Clock_Block_RAM + Data_FIFO + false + false + false + 16 + false + No_Programmable_Full_Threshold + 1023 + No_Programmable_Empty_Threshold + 1022 + FIFO + Common_Clock_Block_RAM + Data_FIFO + false + false + false + 1024 + false + No_Programmable_Full_Threshold + 1023 + No_Programmable_Empty_Threshold + 1022 + FIFO + Common_Clock_Block_RAM + Data_FIFO + false + false + false + 16 + false + No_Programmable_Full_Threshold + 1023 + No_Programmable_Empty_Threshold + 1022 + FIFO + Common_Clock_Block_RAM + Data_FIFO + false + false + false + 16 + false + No_Programmable_Full_Threshold + 1023 + No_Programmable_Empty_Threshold + 1022 + FIFO + Common_Clock_Block_RAM + Data_FIFO + false + false + false + 1024 + false + No_Programmable_Full_Threshold + 1023 + No_Programmable_Empty_Threshold + 1022 + FIFO + Common_Clock_Block_RAM + Data_FIFO + false + false + false + 1024 + false + No_Programmable_Full_Threshold + 1023 + No_Programmable_Empty_Threshold + 1022 + Fully_Registered + Fully_Registered + Fully_Registered + Fully_Registered + Fully_Registered + Fully_Registered + false + Active_High + false + Active_High + false + false + false + false + false + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg/ + + + xc3s400 + spartan3 + pq208 + -4 + + + BusFormatAngleBracketNotRipped + Verilog + true + Other + false + false + false + Ngc + false + + + Behavioral + Verilog + false + + + 2012-11-19+12:39 + + + + + + dac_buf + Block Memory Generator + + + dac_buf + Native + AXI4_Full + Memory_Slave + false + 4 + Simple_Dual_Port_RAM + false + No_ECC + false + false + false + Single_Bit_Error_Injection + false + 9 + Minimum_Area + 8kx2 + true + 8 + 2048 + 8 + WRITE_FIRST + Always_Enabled + 32 + 32 + WRITE_FIRST + Always_Enabled + false + false + false + false + false + false + false + false + 0 + false + no_coe_file_loaded + true + 0 + false + false + CE + 0 + false + false + CE + 0 + SYNC + false + 100 + 50 + 100 + 0 + 100 + 100 + ALL + false + false + Stand_Alone + no_Mem_file_loaded + spartan3 + spartan3 + /home/ikari/prj/sd2snes/verilog/sd2snes_sdd1/ip/mk2/tmp/_cg/ + 0 + 1 + 0 + 0 + 4 + 1 + 9 + 1 + 1 + 0 + no_coe_file_loaded + 1 + 0 + SYNC + 0 + CE + 0 + 0 + 0 + 0 + 0 + 1 + WRITE_FIRST + 8 + 8 + 2048 + 2048 + 11 + 0 + CE + 0 + 0 + 0 + 0 + 0 + 1 + WRITE_FIRST + 32 + 32 + 512 + 512 + 9 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + ALL + 1 + 0 + 0 + 0 + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg/ + + + xc3s400 + spartan3 + pq208 + -4 + + + BusFormatAngleBracketNotRipped + VHDL + true + Other + false + false + false + Ngc + false + + + Behavioral + VHDL + false + + + 2012-11-19+16:22 + + + + + apply_current_project_options_generator + + + model_parameter_resolution_generator + + ./summary.log + unknown + Mon May 18 10:45:31 GMT 2026 + 0xCF133F2B + generationID_1879581046 + + + + ip_xco_generator + + ./dac_buf.xco + xco + Mon May 18 10:45:31 GMT 2026 + 0x17B7E67A + generationID_1879581046 + + + + associated_files_generator + + ./dac_buf/blk_mem_gen_v7_3_readme.txt + ignore + txt + Sun Oct 13 18:11:47 GMT 2013 + 0xF95F2A26 + generationID_1879581046 + + + ./dac_buf/doc/blk_mem_gen_v7_3_vinfo.html + ignore + unknown + Sun Oct 13 18:11:47 GMT 2013 + 0x702C7FA8 + generationID_1879581046 + + + ./dac_buf/doc/pg058-blk-mem-gen.pdf + ignore + pdf + Sun Oct 13 18:11:47 GMT 2013 + 0x39526EEE + generationID_1879581046 + + + + ejava_generator + + ./dac_buf/example_design/dac_buf_exdes.ucf + ignore + ucf + Mon May 18 10:45:32 GMT 2026 + 0x8915DFA1 + generationID_1879581046 + + + ./dac_buf/example_design/dac_buf_exdes.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0x634B6490 + generationID_1879581046 + + + ./dac_buf/example_design/dac_buf_exdes.xdc + ignore + xdc + Mon May 18 10:45:32 GMT 2026 + 0x78E2D49A + generationID_1879581046 + + + ./dac_buf/example_design/dac_buf_prod.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0x94EE105B + generationID_1879581046 + + + ./dac_buf/implement/implement.bat + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x9A364397 + generationID_1879581046 + + + ./dac_buf/implement/implement.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x6DF08493 + generationID_1879581046 + + + ./dac_buf/implement/planAhead_ise.bat + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x1BFCAC13 + generationID_1879581046 + + + ./dac_buf/implement/planAhead_ise.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x85B28792 + generationID_1879581046 + + + ./dac_buf/implement/planAhead_ise.tcl + ignore + tcl + Mon May 18 10:45:32 GMT 2026 + 0x3154D8A3 + generationID_1879581046 + + + ./dac_buf/implement/xst.prj + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x4EE99FD4 + generationID_1879581046 + + + ./dac_buf/implement/xst.scr + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x6A3A3A29 + generationID_1879581046 + + + ./dac_buf/simulation/addr_gen.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0x0500E48D + generationID_1879581046 + + + ./dac_buf/simulation/bmg_stim_gen.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0x0C07C136 + generationID_1879581046 + + + ./dac_buf/simulation/bmg_tb_pkg.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0xE0D93E5B + generationID_1879581046 + + + ./dac_buf/simulation/checker.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0x3EC35020 + generationID_1879581046 + + + ./dac_buf/simulation/dac_buf_synth.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0x372A98C8 + generationID_1879581046 + + + ./dac_buf/simulation/dac_buf_tb.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0xBA5ACD4D + generationID_1879581046 + + + ./dac_buf/simulation/data_gen.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0xD9F7C4A0 + generationID_1879581046 + + + ./dac_buf/simulation/functional/simcmds.tcl + ignore + tcl + Mon May 18 10:45:32 GMT 2026 + 0xC126ACCF + generationID_1879581046 + + + ./dac_buf/simulation/functional/simulate_isim.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x2F9B2C6F + generationID_1879581046 + + + ./dac_buf/simulation/functional/simulate_mti.bat + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./dac_buf/simulation/functional/simulate_mti.do + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x3CF753C2 + generationID_1879581046 + + + ./dac_buf/simulation/functional/simulate_mti.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./dac_buf/simulation/functional/simulate_ncsim.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x2A0BC536 + generationID_1879581046 + + + ./dac_buf/simulation/functional/simulate_vcs.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x7049964D + generationID_1879581046 + + + ./dac_buf/simulation/functional/ucli_commands.key + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x1276711D + generationID_1879581046 + + + ./dac_buf/simulation/functional/vcs_session.tcl + ignore + tcl + Mon May 18 10:45:32 GMT 2026 + 0x86FE79B8 + generationID_1879581046 + + + ./dac_buf/simulation/functional/wave_mti.do + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x219DF720 + generationID_1879581046 + + + ./dac_buf/simulation/functional/wave_ncsim.sv + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x43567DB9 + generationID_1879581046 + + + ./dac_buf/simulation/random.vhd + ignore + vhdl + Mon May 18 10:45:32 GMT 2026 + 0x2939160A + generationID_1879581046 + + + ./dac_buf/simulation/timing/simcmds.tcl + ignore + tcl + Mon May 18 10:45:32 GMT 2026 + 0xC126ACCF + generationID_1879581046 + + + ./dac_buf/simulation/timing/simulate_isim.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0xB8BF353B + generationID_1879581046 + + + ./dac_buf/simulation/timing/simulate_mti.bat + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./dac_buf/simulation/timing/simulate_mti.do + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x79DB1C8B + generationID_1879581046 + + + ./dac_buf/simulation/timing/simulate_mti.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./dac_buf/simulation/timing/simulate_ncsim.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x2CEFCCB3 + generationID_1879581046 + + + ./dac_buf/simulation/timing/simulate_vcs.sh + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x7A8FEE7E + generationID_1879581046 + + + ./dac_buf/simulation/timing/ucli_commands.key + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x1276711D + generationID_1879581046 + + + ./dac_buf/simulation/timing/vcs_session.tcl + ignore + tcl + Mon May 18 10:45:32 GMT 2026 + 0x6F508954 + generationID_1879581046 + + + ./dac_buf/simulation/timing/wave_mti.do + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x67B3B092 + generationID_1879581046 + + + ./dac_buf/simulation/timing/wave_ncsim.sv + ignore + unknown + Mon May 18 10:45:32 GMT 2026 + 0x73C5E622 + generationID_1879581046 + + + + ngc_netlist_generator + + ./dac_buf.ngc + ngc + Mon May 18 10:45:51 GMT 2026 + 0x203DE71D + generationID_1879581046 + + + + obfuscate_netlist_generator + + + padded_implementation_netlist_generator + + + instantiation_template_generator + + ./dac_buf.vho + vho + Mon May 18 10:45:52 GMT 2026 + 0x81A4C438 + generationID_1879581046 + + + + structural_simulation_model_generator + + ./dac_buf.vhd + vhdl + Mon May 18 10:45:52 GMT 2026 + 0x2D0D1CCE + generationID_1879581046 + + + + all_documents_generator + + ./dac_buf/doc/blk_mem_gen_v7_3_vinfo.html + ignore + unknown + Mon May 18 10:45:52 GMT 2026 + 0x702C7FA8 + generationID_1879581046 + + + + asy_generator + + ./dac_buf.asy + asy + Mon May 18 10:45:53 GMT 2026 + 0xBAE97F25 + generationID_1879581046 + + + ./summary.log + unknown + Mon May 18 10:45:53 GMT 2026 + 0xCF133F2B + generationID_1879581046 + + + + xmdf_generator + + ./dac_buf_xmdf.tcl + tclXmdf + tcl + Mon May 18 10:45:53 GMT 2026 + 0xA4F557BC + generationID_1879581046 + + + + synthesis_ise_generator + + ./dac_buf.gise + ignore + gise + Mon May 18 10:45:55 GMT 2026 + 0x35F53496 + generationID_1879581046 + + + ./dac_buf.xise + ignore + xise + Mon May 18 10:45:55 GMT 2026 + 0xE31ECD74 + generationID_1879581046 + + + + ise_generator + + ./_xmsgs/pn_parser.xmsgs + ignore + unknown + Mon May 18 10:45:57 GMT 2026 + 0x4224C199 + generationID_1879581046 + + + ./dac_buf.gise + ignore + gise + Mon May 18 10:45:57 GMT 2026 + 0x2E8AFA38 + generationID_1879581046 + + + ./dac_buf.xise + ignore + xise + Mon May 18 10:45:57 GMT 2026 + 0x8EC734D2 + generationID_1879581046 + + + + deliver_readme_generator + + + flist_generator + + ./dac_buf_flist.txt + ignore + txtFlist + txt + Mon May 18 10:45:57 GMT 2026 + 0x0F09C828 + generationID_1879581046 + + + + + + + msu_databuf + Block Memory Generator + + + msu_databuf + Native + AXI4_Full + Memory_Slave + false + 4 + Simple_Dual_Port_RAM + false + No_ECC + false + false + false + Single_Bit_Error_Injection + false + 9 + Minimum_Area + 8kx2 + true + 8 + 16384 + 8 + WRITE_FIRST + Always_Enabled + 8 + 8 + WRITE_FIRST + Always_Enabled + false + false + false + false + false + false + false + false + 0 + false + no_coe_file_loaded + false + 0 + false + false + CE + 0 + false + false + CE + 0 + SYNC + false + 100 + 50 + 100 + 0 + 100 + 100 + ALL + false + false + Stand_Alone + no_Mem_file_loaded + spartan3 + spartan3 + /home/ikari/prj/sd2snes/verilog/sd2snes_sdd1/ip/mk2/tmp/_cg/ + 0 + 1 + 0 + 0 + 4 + 1 + 9 + 1 + 1 + 0 + no_coe_file_loaded + 0 + 0 + SYNC + 0 + CE + 0 + 0 + 0 + 0 + 0 + 1 + WRITE_FIRST + 8 + 8 + 16384 + 16384 + 14 + 0 + CE + 0 + 0 + 0 + 0 + 0 + 1 + WRITE_FIRST + 8 + 8 + 16384 + 16384 + 14 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + ALL + 1 + 0 + 0 + 0 + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg/ + + + xc3s400 + spartan3 + pq208 + -4 + + + BusFormatAngleBracketNotRipped + VHDL + true + Other + false + false + false + Ngc + false + + + Behavioral + VHDL + false + + + 2012-11-19+16:22 + + + + + apply_current_project_options_generator + + + model_parameter_resolution_generator + + ./summary.log + unknown + Mon May 18 10:46:00 GMT 2026 + 0x2CA2F431 + generationID_1879581046 + + + + ip_xco_generator + + ./msu_databuf.xco + xco + Mon May 18 10:46:00 GMT 2026 + 0x465B3C9A + generationID_1879581046 + + + + associated_files_generator + + ./msu_databuf/blk_mem_gen_v7_3_readme.txt + ignore + txt + Sun Oct 13 18:11:47 GMT 2013 + 0xF95F2A26 + generationID_1879581046 + + + ./msu_databuf/doc/blk_mem_gen_v7_3_vinfo.html + ignore + unknown + Sun Oct 13 18:11:47 GMT 2013 + 0x702C7FA8 + generationID_1879581046 + + + ./msu_databuf/doc/pg058-blk-mem-gen.pdf + ignore + pdf + Sun Oct 13 18:11:47 GMT 2013 + 0x39526EEE + generationID_1879581046 + + + + ejava_generator + + ./msu_databuf/example_design/msu_databuf_exdes.ucf + ignore + ucf + Mon May 18 10:46:00 GMT 2026 + 0x8915DFA1 + generationID_1879581046 + + + ./msu_databuf/example_design/msu_databuf_exdes.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0x11982DED + generationID_1879581046 + + + ./msu_databuf/example_design/msu_databuf_exdes.xdc + ignore + xdc + Mon May 18 10:46:00 GMT 2026 + 0x78E2D49A + generationID_1879581046 + + + ./msu_databuf/example_design/msu_databuf_prod.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0x9A0DF795 + generationID_1879581046 + + + ./msu_databuf/implement/implement.bat + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x7F0DDA61 + generationID_1879581046 + + + ./msu_databuf/implement/implement.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x9B702420 + generationID_1879581046 + + + ./msu_databuf/implement/planAhead_ise.bat + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x9E6071ED + generationID_1879581046 + + + ./msu_databuf/implement/planAhead_ise.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x19C41152 + generationID_1879581046 + + + ./msu_databuf/implement/planAhead_ise.tcl + ignore + tcl + Mon May 18 10:46:00 GMT 2026 + 0xAD591405 + generationID_1879581046 + + + ./msu_databuf/implement/xst.prj + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xD6A13291 + generationID_1879581046 + + + ./msu_databuf/implement/xst.scr + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x6C988C64 + generationID_1879581046 + + + ./msu_databuf/simulation/addr_gen.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0x0500E48D + generationID_1879581046 + + + ./msu_databuf/simulation/bmg_stim_gen.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0x334DD429 + generationID_1879581046 + + + ./msu_databuf/simulation/bmg_tb_pkg.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0xE0D93E5B + generationID_1879581046 + + + ./msu_databuf/simulation/checker.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0x3EC35020 + generationID_1879581046 + + + ./msu_databuf/simulation/data_gen.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0xD9F7C4A0 + generationID_1879581046 + + + ./msu_databuf/simulation/functional/simcmds.tcl + ignore + tcl + Mon May 18 10:46:00 GMT 2026 + 0x9E7C751E + generationID_1879581046 + + + ./msu_databuf/simulation/functional/simulate_isim.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x524FA34C + generationID_1879581046 + + + ./msu_databuf/simulation/functional/simulate_mti.bat + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./msu_databuf/simulation/functional/simulate_mti.do + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xDA353621 + generationID_1879581046 + + + ./msu_databuf/simulation/functional/simulate_mti.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./msu_databuf/simulation/functional/simulate_ncsim.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xBE29582E + generationID_1879581046 + + + ./msu_databuf/simulation/functional/simulate_vcs.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xFBA02DD3 + generationID_1879581046 + + + ./msu_databuf/simulation/functional/ucli_commands.key + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xF0017B03 + generationID_1879581046 + + + ./msu_databuf/simulation/functional/vcs_session.tcl + ignore + tcl + Mon May 18 10:46:00 GMT 2026 + 0x19A2DC33 + generationID_1879581046 + + + ./msu_databuf/simulation/functional/wave_mti.do + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x0C6EEF80 + generationID_1879581046 + + + ./msu_databuf/simulation/functional/wave_ncsim.sv + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xE19DCC37 + generationID_1879581046 + + + ./msu_databuf/simulation/msu_databuf_synth.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0x349F0FD2 + generationID_1879581046 + + + ./msu_databuf/simulation/msu_databuf_tb.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0x5D43B40D + generationID_1879581046 + + + ./msu_databuf/simulation/random.vhd + ignore + vhdl + Mon May 18 10:46:00 GMT 2026 + 0x2939160A + generationID_1879581046 + + + ./msu_databuf/simulation/timing/simcmds.tcl + ignore + tcl + Mon May 18 10:46:00 GMT 2026 + 0x9E7C751E + generationID_1879581046 + + + ./msu_databuf/simulation/timing/simulate_isim.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xA45A06C3 + generationID_1879581046 + + + ./msu_databuf/simulation/timing/simulate_mti.bat + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./msu_databuf/simulation/timing/simulate_mti.do + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xDB808132 + generationID_1879581046 + + + ./msu_databuf/simulation/timing/simulate_mti.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./msu_databuf/simulation/timing/simulate_ncsim.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xCA9755BD + generationID_1879581046 + + + ./msu_databuf/simulation/timing/simulate_vcs.sh + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x9CACC83C + generationID_1879581046 + + + ./msu_databuf/simulation/timing/ucli_commands.key + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0xF0017B03 + generationID_1879581046 + + + ./msu_databuf/simulation/timing/vcs_session.tcl + ignore + tcl + Mon May 18 10:46:00 GMT 2026 + 0x7FC3A169 + generationID_1879581046 + + + ./msu_databuf/simulation/timing/wave_mti.do + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x17E72D2E + generationID_1879581046 + + + ./msu_databuf/simulation/timing/wave_ncsim.sv + ignore + unknown + Mon May 18 10:46:00 GMT 2026 + 0x734950A4 + generationID_1879581046 + + + + ngc_netlist_generator + + ./msu_databuf/blk_mem_gen_v7_3_readme.sync-conflict-20260518-124613-BWX2WZH.txt + ignore + txt + Sun Oct 13 18:11:47 GMT 2013 + 0xF95F2A26 + generationID_1879581046 + + + ./msu_databuf/doc/blk_mem_gen_v7_3_vinfo.sync-conflict-20260518-124613-BWX2WZH.html + ignore + unknown + Sun Oct 13 18:11:47 GMT 2013 + 0x702C7FA8 + generationID_1879581046 + + + ./msu_databuf/doc/pg058-blk-mem-gen.sync-conflict-20260518-124613-BWX2WZH.pdf + ignore + pdf + Sun Oct 13 18:11:47 GMT 2013 + 0x39526EEE + generationID_1879581046 + + + ./msu_databuf.ngc + ngc + Mon May 18 10:46:27 GMT 2026 + 0x9F7199A9 + generationID_1879581046 + + + + obfuscate_netlist_generator + + + padded_implementation_netlist_generator + + + instantiation_template_generator + + ./msu_databuf.vho + vho + Mon May 18 10:46:27 GMT 2026 + 0x74EC080A + generationID_1879581046 + + + + structural_simulation_model_generator + + ./msu_databuf.vhd + vhdl + Mon May 18 10:46:27 GMT 2026 + 0xF89C086B + generationID_1879581046 + + + + all_documents_generator + + ./msu_databuf/doc/blk_mem_gen_v7_3_vinfo.html + ignore + unknown + Mon May 18 10:46:27 GMT 2026 + 0x702C7FA8 + generationID_1879581046 + + + + asy_generator + + ./msu_databuf.asy + asy + Mon May 18 10:46:29 GMT 2026 + 0x13FF58F0 + generationID_1879581046 + + + ./summary.log + unknown + Mon May 18 10:46:29 GMT 2026 + 0x2CA2F431 + generationID_1879581046 + + + + xmdf_generator + + ./msu_databuf_xmdf.tcl + tclXmdf + tcl + Mon May 18 10:46:29 GMT 2026 + 0xE9D794F2 + generationID_1879581046 + + + + synthesis_ise_generator + + ./msu_databuf.gise + ignore + gise + Mon May 18 10:46:30 GMT 2026 + 0xBEF74AC2 + generationID_1879581046 + + + ./msu_databuf.xise + ignore + xise + Mon May 18 10:46:30 GMT 2026 + 0x66BFA3F9 + generationID_1879581046 + + + + ise_generator + + ./_xmsgs/pn_parser.xmsgs + ignore + unknown + Mon May 18 10:46:32 GMT 2026 + 0x22875F5D + generationID_1879581046 + + + ./msu_databuf.gise + ignore + gise + Mon May 18 10:46:32 GMT 2026 + 0x1A674224 + generationID_1879581046 + + + ./msu_databuf.xise + ignore + xise + Mon May 18 10:46:32 GMT 2026 + 0xC9A729A8 + generationID_1879581046 + + + + deliver_readme_generator + + + flist_generator + + ./msu_databuf_flist.txt + ignore + txtFlist + txt + Mon May 18 10:46:32 GMT 2026 + 0xC7C30484 + generationID_1879581046 + + + + + + + snescmd_buf + Block Memory Generator + + + snescmd_buf + Native + AXI4_Full + Memory_Slave + false + 4 + True_Dual_Port_RAM + false + No_ECC + false + false + false + Single_Bit_Error_Injection + false + 9 + Minimum_Area + 8kx2 + true + 8 + 512 + 8 + WRITE_FIRST + Always_Enabled + 8 + 8 + WRITE_FIRST + Always_Enabled + false + false + false + false + false + false + false + false + 0 + false + no_coe_file_loaded + false + 0 + false + false + CE + 0 + false + false + CE + 0 + SYNC + false + 100 + 50 + 100 + 50 + 100 + 100 + ALL + false + false + Stand_Alone + no_Mem_file_loaded + spartan3 + spartan3 + /home/ikari/prj/sd2snes/verilog/sd2snes_sdd1/ip/mk2/tmp/_cg/ + 0 + 1 + 0 + 0 + 4 + 2 + 9 + 1 + 1 + 0 + no_coe_file_loaded + 0 + 0 + SYNC + 0 + CE + 0 + 0 + 0 + 0 + 0 + 1 + WRITE_FIRST + 8 + 8 + 512 + 512 + 9 + 0 + CE + 0 + 0 + 0 + 0 + 0 + 1 + WRITE_FIRST + 8 + 8 + 512 + 512 + 9 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + ALL + 1 + 0 + 0 + 0 + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg/ + + + xc3s400 + spartan3 + pq208 + -4 + + + BusFormatAngleBracketNotRipped + VHDL + true + Other + false + false + false + Ngc + false + + + Behavioral + VHDL + false + + + 2012-11-19+16:22 + + + + + apply_current_project_options_generator + + + model_parameter_resolution_generator + + ./summary.log + unknown + Mon May 18 10:46:36 GMT 2026 + 0xE5D1B7A5 + generationID_1879581046 + + + + ip_xco_generator + + ./snescmd_buf.xco + xco + Mon May 18 10:46:36 GMT 2026 + 0x474CCD0F + generationID_1879581046 + + + + associated_files_generator + + ./snescmd_buf/blk_mem_gen_v7_3_readme.txt + ignore + txt + Sun Oct 13 18:11:47 GMT 2013 + 0xF95F2A26 + generationID_1879581046 + + + ./snescmd_buf/doc/blk_mem_gen_v7_3_vinfo.html + ignore + unknown + Sun Oct 13 18:11:47 GMT 2013 + 0x702C7FA8 + generationID_1879581046 + + + ./snescmd_buf/doc/pg058-blk-mem-gen.pdf + ignore + pdf + Sun Oct 13 18:11:47 GMT 2013 + 0x39526EEE + generationID_1879581046 + + + + ejava_generator + + ./snescmd_buf/example_design/snescmd_buf_exdes.ucf + ignore + ucf + Mon May 18 10:46:36 GMT 2026 + 0x8915DFA1 + generationID_1879581046 + + + ./snescmd_buf/example_design/snescmd_buf_exdes.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0x76F182AD + generationID_1879581046 + + + ./snescmd_buf/example_design/snescmd_buf_exdes.xdc + ignore + xdc + Mon May 18 10:46:36 GMT 2026 + 0x78E2D49A + generationID_1879581046 + + + ./snescmd_buf/example_design/snescmd_buf_prod.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0xEB2A54B4 + generationID_1879581046 + + + ./snescmd_buf/implement/implement.bat + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0xA3531749 + generationID_1879581046 + + + ./snescmd_buf/implement/implement.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0xFB7945FF + generationID_1879581046 + + + ./snescmd_buf/implement/planAhead_ise.bat + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0xD4D84CCF + generationID_1879581046 + + + ./snescmd_buf/implement/planAhead_ise.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x537C2C70 + generationID_1879581046 + + + ./snescmd_buf/implement/planAhead_ise.tcl + ignore + tcl + Mon May 18 10:46:36 GMT 2026 + 0x3EB85A78 + generationID_1879581046 + + + ./snescmd_buf/implement/xst.prj + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x73BFB978 + generationID_1879581046 + + + ./snescmd_buf/implement/xst.scr + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0xD34ADE85 + generationID_1879581046 + + + ./snescmd_buf/simulation/addr_gen.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0x0500E48D + generationID_1879581046 + + + ./snescmd_buf/simulation/bmg_stim_gen.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0xD4F75450 + generationID_1879581046 + + + ./snescmd_buf/simulation/bmg_tb_pkg.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0xE0D93E5B + generationID_1879581046 + + + ./snescmd_buf/simulation/checker.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0x3EC35020 + generationID_1879581046 + + + ./snescmd_buf/simulation/data_gen.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0xD9F7C4A0 + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/simcmds.tcl + ignore + tcl + Mon May 18 10:46:36 GMT 2026 + 0x280275EF + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/simulate_isim.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0xD8F09016 + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/simulate_mti.bat + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/simulate_mti.do + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0xF0B96FB6 + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/simulate_mti.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/simulate_ncsim.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x8298E7AC + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/simulate_vcs.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x5DF4A220 + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/ucli_commands.key + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x744AEC6A + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/vcs_session.tcl + ignore + tcl + Mon May 18 10:46:36 GMT 2026 + 0xD372EC95 + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/wave_mti.do + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x67B2B83D + generationID_1879581046 + + + ./snescmd_buf/simulation/functional/wave_ncsim.sv + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x1DCD63C5 + generationID_1879581046 + + + ./snescmd_buf/simulation/random.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0x2939160A + generationID_1879581046 + + + ./snescmd_buf/simulation/snescmd_buf_synth.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0xED369999 + generationID_1879581046 + + + ./snescmd_buf/simulation/snescmd_buf_tb.vhd + ignore + vhdl + Mon May 18 10:46:36 GMT 2026 + 0xD4DEE771 + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/simcmds.tcl + ignore + tcl + Mon May 18 10:46:36 GMT 2026 + 0x280275EF + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/simulate_isim.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x8A76C2DD + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/simulate_mti.bat + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/simulate_mti.do + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x5D9D425C + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/simulate_mti.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x86EA5D67 + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/simulate_ncsim.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x05BC4018 + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/simulate_vcs.sh + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x031853E4 + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/ucli_commands.key + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x744AEC6A + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/vcs_session.tcl + ignore + tcl + Mon May 18 10:46:36 GMT 2026 + 0x499801E0 + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/wave_mti.do + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0x3BBD6A88 + generationID_1879581046 + + + ./snescmd_buf/simulation/timing/wave_ncsim.sv + ignore + unknown + Mon May 18 10:46:36 GMT 2026 + 0xA221C309 + generationID_1879581046 + + + + ngc_netlist_generator + + ./snescmd_buf.ngc + ngc + Mon May 18 10:46:55 GMT 2026 + 0x12FFFC0D + generationID_1879581046 + + + + obfuscate_netlist_generator + + + padded_implementation_netlist_generator + + + instantiation_template_generator + + ./snescmd_buf.vho + vho + Mon May 18 10:46:56 GMT 2026 + 0xC47A38B4 + generationID_1879581046 + + + + structural_simulation_model_generator + + ./snescmd_buf.vhd + vhdl + Mon May 18 10:46:56 GMT 2026 + 0xE75ABAAB + generationID_1879581046 + + + + all_documents_generator + + ./snescmd_buf/doc/blk_mem_gen_v7_3_vinfo.html + ignore + unknown + Mon May 18 10:46:56 GMT 2026 + 0x702C7FA8 + generationID_1879581046 + + + + asy_generator + + ./snescmd_buf.asy + asy + Mon May 18 10:46:57 GMT 2026 + 0x1A9B7AAA + generationID_1879581046 + + + ./summary.log + unknown + Mon May 18 10:46:57 GMT 2026 + 0xE5D1B7A5 + generationID_1879581046 + + + + xmdf_generator + + ./snescmd_buf_xmdf.tcl + tclXmdf + tcl + Mon May 18 10:46:57 GMT 2026 + 0x8349E5F1 + generationID_1879581046 + + + + synthesis_ise_generator + + ./snescmd_buf.gise + ignore + gise + Mon May 18 10:46:59 GMT 2026 + 0x3F90DBA7 + generationID_1879581046 + + + ./snescmd_buf.xise + ignore + xise + Mon May 18 10:46:59 GMT 2026 + 0xF52456FE + generationID_1879581046 + + + + ise_generator + + ./_xmsgs/pn_parser.xmsgs + ignore + unknown + Mon May 18 10:47:01 GMT 2026 + 0x67AEF7C6 + generationID_1879581046 + + + ./snescmd_buf.gise + ignore + gise + Mon May 18 10:47:01 GMT 2026 + 0x2421D401 + generationID_1879581046 + + + ./snescmd_buf.xise + ignore + xise + Mon May 18 10:47:01 GMT 2026 + 0xD9BB6510 + generationID_1879581046 + + + + deliver_readme_generator + + + flist_generator + + ./snescmd_buf_flist.txt + ignore + txtFlist + txt + Mon May 18 10:47:01 GMT 2026 + 0xE16AB9F8 + generationID_1879581046 + + + + + + + DCM_Scope + + + DCM_Scope + D:\prj\sd2snes\verilog\sd2snes_sdd1\ip\mk2\DCM_Scope.xaw + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg/ + + + xc3s400 + spartan3 + pq208 + -4 + + + BusFormatAngleBracketNotRipped + VHDL + true + Other + false + false + false + Ngc + false + + + Behavioral + VHDL + false + + + 2013-10-13+16:06 + + + + + customization_generator + + + model_parameter_resolution_generator + + + asy_generator + + + flist_generator + + + + + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg/ + + + xc3s400 + spartan3 + pq208 + -4 + + + BusFormatAngleBracketNotRipped + VHDL + true + Other + false + false + false + Ngc + false + + + Behavioral + VHDL + false + + + + + diff --git a/verilog/sd2snes_spc7110/ip/mk2/dac_buf.v b/verilog/sd2snes_spc7110/ip/mk2/dac_buf.v new file mode 100644 index 000000000..8ae642573 --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/dac_buf.v @@ -0,0 +1,184 @@ +/******************************************************************************* +* This file is owned and controlled by Xilinx and must be used solely * +* for design, simulation, implementation and creation of design files * +* limited to Xilinx devices or technologies. Use with non-Xilinx * +* devices or technologies is expressly prohibited and immediately * +* terminates your license. * +* * +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" SOLELY * +* FOR USE IN DEVELOPING PROGRAMS AND SOLUTIONS FOR XILINX DEVICES. BY * +* PROVIDING THIS DESIGN, CODE, OR INFORMATION AS ONE POSSIBLE * +* IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, XILINX IS * +* MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE FROM ANY * +* CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING ANY * +* RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY * +* DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE * +* IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR * +* REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF * +* INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE. * +* * +* Xilinx products are not intended for use in life support appliances, * +* devices, or systems. Use in such applications are expressly * +* prohibited. * +* * +* (c) Copyright 1995-2018 Xilinx, Inc. * +* All rights reserved. * +*******************************************************************************/ +// You must compile the wrapper file dac_buf.v when simulating +// the core, dac_buf. When compiling the wrapper file, be sure to +// reference the XilinxCoreLib Verilog simulation library. For detailed +// instructions, please refer to the "CORE Generator Help". + +// The synthesis directives "translate_off/translate_on" specified below are +// supported by Xilinx, Mentor Graphics and Synplicity synthesis +// tools. Ensure they are correct for your synthesis tool(s). + +`timescale 1ns/1ps + +module dac_buf( + clka, + wea, + addra, + dina, + clkb, + addrb, + doutb +); + +input clka; +input [0 : 0] wea; +input [10 : 0] addra; +input [7 : 0] dina; +input clkb; +input [8 : 0] addrb; +output [31 : 0] doutb; + +// synthesis translate_off + + BLK_MEM_GEN_V7_3 #( + .C_ADDRA_WIDTH(11), + .C_ADDRB_WIDTH(9), + .C_ALGORITHM(1), + .C_AXI_ID_WIDTH(4), + .C_AXI_SLAVE_TYPE(0), + .C_AXI_TYPE(1), + .C_BYTE_SIZE(9), + .C_COMMON_CLK(1), + .C_DEFAULT_DATA("0"), + .C_DISABLE_WARN_BHV_COLL(0), + .C_DISABLE_WARN_BHV_RANGE(0), + .C_ENABLE_32BIT_ADDRESS(0), + .C_FAMILY("spartan3"), + .C_HAS_AXI_ID(0), + .C_HAS_ENA(0), + .C_HAS_ENB(0), + .C_HAS_INJECTERR(0), + .C_HAS_MEM_OUTPUT_REGS_A(0), + .C_HAS_MEM_OUTPUT_REGS_B(0), + .C_HAS_MUX_OUTPUT_REGS_A(0), + .C_HAS_MUX_OUTPUT_REGS_B(0), + .C_HAS_REGCEA(0), + .C_HAS_REGCEB(0), + .C_HAS_RSTA(0), + .C_HAS_RSTB(0), + .C_HAS_SOFTECC_INPUT_REGS_A(0), + .C_HAS_SOFTECC_OUTPUT_REGS_B(0), + .C_INIT_FILE("BlankString"), + .C_INIT_FILE_NAME("no_coe_file_loaded"), + .C_INITA_VAL("0"), + .C_INITB_VAL("0"), + .C_INTERFACE_TYPE(0), + .C_LOAD_INIT_FILE(0), + .C_MEM_TYPE(1), + .C_MUX_PIPELINE_STAGES(0), + .C_PRIM_TYPE(1), + .C_READ_DEPTH_A(2048), + .C_READ_DEPTH_B(512), + .C_READ_WIDTH_A(8), + .C_READ_WIDTH_B(32), + .C_RST_PRIORITY_A("CE"), + .C_RST_PRIORITY_B("CE"), + .C_RST_TYPE("SYNC"), + .C_RSTRAM_A(0), + .C_RSTRAM_B(0), + .C_SIM_COLLISION_CHECK("ALL"), + .C_USE_BRAM_BLOCK(0), + .C_USE_BYTE_WEA(0), + .C_USE_BYTE_WEB(0), + .C_USE_DEFAULT_DATA(1), + .C_USE_ECC(0), + .C_USE_SOFTECC(0), + .C_WEA_WIDTH(1), + .C_WEB_WIDTH(1), + .C_WRITE_DEPTH_A(2048), + .C_WRITE_DEPTH_B(512), + .C_WRITE_MODE_A("WRITE_FIRST"), + .C_WRITE_MODE_B("WRITE_FIRST"), + .C_WRITE_WIDTH_A(8), + .C_WRITE_WIDTH_B(32), + .C_XDEVICEFAMILY("spartan3") + ) + inst ( + .CLKA(clka), + .WEA(wea), + .ADDRA(addra), + .DINA(dina), + .CLKB(clkb), + .ADDRB(addrb), + .DOUTB(doutb), + .RSTA(), + .ENA(), + .REGCEA(), + .DOUTA(), + .RSTB(), + .ENB(), + .REGCEB(), + .WEB(), + .DINB(), + .INJECTSBITERR(), + .INJECTDBITERR(), + .SBITERR(), + .DBITERR(), + .RDADDRECC(), + .S_ACLK(), + .S_ARESETN(), + .S_AXI_AWID(), + .S_AXI_AWADDR(), + .S_AXI_AWLEN(), + .S_AXI_AWSIZE(), + .S_AXI_AWBURST(), + .S_AXI_AWVALID(), + .S_AXI_AWREADY(), + .S_AXI_WDATA(), + .S_AXI_WSTRB(), + .S_AXI_WLAST(), + .S_AXI_WVALID(), + .S_AXI_WREADY(), + .S_AXI_BID(), + .S_AXI_BRESP(), + .S_AXI_BVALID(), + .S_AXI_BREADY(), + .S_AXI_ARID(), + .S_AXI_ARADDR(), + .S_AXI_ARLEN(), + .S_AXI_ARSIZE(), + .S_AXI_ARBURST(), + .S_AXI_ARVALID(), + .S_AXI_ARREADY(), + .S_AXI_RID(), + .S_AXI_RDATA(), + .S_AXI_RRESP(), + .S_AXI_RLAST(), + .S_AXI_RVALID(), + .S_AXI_RREADY(), + .S_AXI_INJECTSBITERR(), + .S_AXI_INJECTDBITERR(), + .S_AXI_SBITERR(), + .S_AXI_DBITERR(), + .S_AXI_RDADDRECC() + ); + +// synthesis translate_on + +endmodule diff --git a/verilog/sd2snes_spc7110/ip/mk2/dac_buf.xco b/verilog/sd2snes_spc7110/ip/mk2/dac_buf.xco new file mode 100644 index 000000000..4638a012d --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/dac_buf.xco @@ -0,0 +1,108 @@ +############################################################## +# +# Xilinx Core Generator version 14.7 +# Date: Mon May 18 10:45:31 2026 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# Generated from component: xilinx.com:ip:blk_mem_gen:7.3 +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = VHDL +SET device = xc3s400 +SET devicefamily = spartan3 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = pq208 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -4 +SET verilogsim = false +SET vhdlsim = true +# END Project Options +# BEGIN Select +SELECT Block_Memory_Generator xilinx.com:ip:blk_mem_gen:7.3 +# END Select +# BEGIN Parameters +CSET additional_inputs_for_power_estimation=false +CSET algorithm=Minimum_Area +CSET assume_synchronous_clk=true +CSET axi_id_width=4 +CSET axi_slave_type=Memory_Slave +CSET axi_type=AXI4_Full +CSET byte_size=9 +CSET coe_file=no_coe_file_loaded +CSET collision_warnings=ALL +CSET component_name=dac_buf +CSET disable_collision_warnings=false +CSET disable_out_of_range_warnings=false +CSET ecc=false +CSET ecctype=No_ECC +CSET enable_32bit_address=false +CSET enable_a=Always_Enabled +CSET enable_b=Always_Enabled +CSET error_injection_type=Single_Bit_Error_Injection +CSET fill_remaining_memory_locations=true +CSET interface_type=Native +CSET load_init_file=false +CSET mem_file=no_Mem_file_loaded +CSET memory_type=Simple_Dual_Port_RAM +CSET operating_mode_a=WRITE_FIRST +CSET operating_mode_b=WRITE_FIRST +CSET output_reset_value_a=0 +CSET output_reset_value_b=0 +CSET pipeline_stages=0 +CSET port_a_clock=100 +CSET port_a_enable_rate=100 +CSET port_a_write_rate=50 +CSET port_b_clock=100 +CSET port_b_enable_rate=100 +CSET port_b_write_rate=0 +CSET primitive=8kx2 +CSET read_width_a=8 +CSET read_width_b=32 +CSET register_porta_input_of_softecc=false +CSET register_porta_output_of_memory_core=false +CSET register_porta_output_of_memory_primitives=false +CSET register_portb_output_of_memory_core=false +CSET register_portb_output_of_memory_primitives=false +CSET register_portb_output_of_softecc=false +CSET remaining_memory_locations=0 +CSET reset_memory_latch_a=false +CSET reset_memory_latch_b=false +CSET reset_priority_a=CE +CSET reset_priority_b=CE +CSET reset_type=SYNC +CSET softecc=false +CSET use_axi_id=false +CSET use_bram_block=Stand_Alone +CSET use_byte_write_enable=false +CSET use_error_injection_pins=false +CSET use_regcea_pin=false +CSET use_regceb_pin=false +CSET use_rsta_pin=false +CSET use_rstb_pin=false +CSET write_depth_a=2048 +CSET write_width_a=8 +CSET write_width_b=32 +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2012-11-19T16:22:25Z +# END Extra information +GENERATE +# CRC: 485eb869 diff --git a/verilog/sd2snes_spc7110/ip/mk2/dac_buf.xise b/verilog/sd2snes_spc7110/ip/mk2/dac_buf.xise new file mode 100644 index 000000000..d86cfb25f --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/dac_buf.xise @@ -0,0 +1,388 @@ + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/verilog/sd2snes_spc7110/ip/mk2/msu_databuf.v b/verilog/sd2snes_spc7110/ip/mk2/msu_databuf.v new file mode 100644 index 000000000..d9921c07f --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/msu_databuf.v @@ -0,0 +1,184 @@ +/******************************************************************************* +* This file is owned and controlled by Xilinx and must be used solely * +* for design, simulation, implementation and creation of design files * +* limited to Xilinx devices or technologies. Use with non-Xilinx * +* devices or technologies is expressly prohibited and immediately * +* terminates your license. * +* * +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" SOLELY * +* FOR USE IN DEVELOPING PROGRAMS AND SOLUTIONS FOR XILINX DEVICES. BY * +* PROVIDING THIS DESIGN, CODE, OR INFORMATION AS ONE POSSIBLE * +* IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, XILINX IS * +* MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE FROM ANY * +* CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING ANY * +* RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY * +* DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE * +* IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR * +* REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF * +* INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE. * +* * +* Xilinx products are not intended for use in life support appliances, * +* devices, or systems. Use in such applications are expressly * +* prohibited. * +* * +* (c) Copyright 1995-2018 Xilinx, Inc. * +* All rights reserved. * +*******************************************************************************/ +// You must compile the wrapper file msu_databuf.v when simulating +// the core, msu_databuf. When compiling the wrapper file, be sure to +// reference the XilinxCoreLib Verilog simulation library. For detailed +// instructions, please refer to the "CORE Generator Help". + +// The synthesis directives "translate_off/translate_on" specified below are +// supported by Xilinx, Mentor Graphics and Synplicity synthesis +// tools. Ensure they are correct for your synthesis tool(s). + +`timescale 1ns/1ps + +module msu_databuf( + clka, + wea, + addra, + dina, + clkb, + addrb, + doutb +); + +input clka; +input [0 : 0] wea; +input [13 : 0] addra; +input [7 : 0] dina; +input clkb; +input [13 : 0] addrb; +output [7 : 0] doutb; + +// synthesis translate_off + + BLK_MEM_GEN_V7_3 #( + .C_ADDRA_WIDTH(14), + .C_ADDRB_WIDTH(14), + .C_ALGORITHM(1), + .C_AXI_ID_WIDTH(4), + .C_AXI_SLAVE_TYPE(0), + .C_AXI_TYPE(1), + .C_BYTE_SIZE(9), + .C_COMMON_CLK(1), + .C_DEFAULT_DATA("0"), + .C_DISABLE_WARN_BHV_COLL(0), + .C_DISABLE_WARN_BHV_RANGE(0), + .C_ENABLE_32BIT_ADDRESS(0), + .C_FAMILY("spartan3"), + .C_HAS_AXI_ID(0), + .C_HAS_ENA(0), + .C_HAS_ENB(0), + .C_HAS_INJECTERR(0), + .C_HAS_MEM_OUTPUT_REGS_A(0), + .C_HAS_MEM_OUTPUT_REGS_B(0), + .C_HAS_MUX_OUTPUT_REGS_A(0), + .C_HAS_MUX_OUTPUT_REGS_B(0), + .C_HAS_REGCEA(0), + .C_HAS_REGCEB(0), + .C_HAS_RSTA(0), + .C_HAS_RSTB(0), + .C_HAS_SOFTECC_INPUT_REGS_A(0), + .C_HAS_SOFTECC_OUTPUT_REGS_B(0), + .C_INIT_FILE("BlankString"), + .C_INIT_FILE_NAME("no_coe_file_loaded"), + .C_INITA_VAL("0"), + .C_INITB_VAL("0"), + .C_INTERFACE_TYPE(0), + .C_LOAD_INIT_FILE(0), + .C_MEM_TYPE(1), + .C_MUX_PIPELINE_STAGES(0), + .C_PRIM_TYPE(1), + .C_READ_DEPTH_A(16384), + .C_READ_DEPTH_B(16384), + .C_READ_WIDTH_A(8), + .C_READ_WIDTH_B(8), + .C_RST_PRIORITY_A("CE"), + .C_RST_PRIORITY_B("CE"), + .C_RST_TYPE("SYNC"), + .C_RSTRAM_A(0), + .C_RSTRAM_B(0), + .C_SIM_COLLISION_CHECK("ALL"), + .C_USE_BRAM_BLOCK(0), + .C_USE_BYTE_WEA(0), + .C_USE_BYTE_WEB(0), + .C_USE_DEFAULT_DATA(0), + .C_USE_ECC(0), + .C_USE_SOFTECC(0), + .C_WEA_WIDTH(1), + .C_WEB_WIDTH(1), + .C_WRITE_DEPTH_A(16384), + .C_WRITE_DEPTH_B(16384), + .C_WRITE_MODE_A("WRITE_FIRST"), + .C_WRITE_MODE_B("WRITE_FIRST"), + .C_WRITE_WIDTH_A(8), + .C_WRITE_WIDTH_B(8), + .C_XDEVICEFAMILY("spartan3") + ) + inst ( + .CLKA(clka), + .WEA(wea), + .ADDRA(addra), + .DINA(dina), + .CLKB(clkb), + .ADDRB(addrb), + .DOUTB(doutb), + .RSTA(), + .ENA(), + .REGCEA(), + .DOUTA(), + .RSTB(), + .ENB(), + .REGCEB(), + .WEB(), + .DINB(), + .INJECTSBITERR(), + .INJECTDBITERR(), + .SBITERR(), + .DBITERR(), + .RDADDRECC(), + .S_ACLK(), + .S_ARESETN(), + .S_AXI_AWID(), + .S_AXI_AWADDR(), + .S_AXI_AWLEN(), + .S_AXI_AWSIZE(), + .S_AXI_AWBURST(), + .S_AXI_AWVALID(), + .S_AXI_AWREADY(), + .S_AXI_WDATA(), + .S_AXI_WSTRB(), + .S_AXI_WLAST(), + .S_AXI_WVALID(), + .S_AXI_WREADY(), + .S_AXI_BID(), + .S_AXI_BRESP(), + .S_AXI_BVALID(), + .S_AXI_BREADY(), + .S_AXI_ARID(), + .S_AXI_ARADDR(), + .S_AXI_ARLEN(), + .S_AXI_ARSIZE(), + .S_AXI_ARBURST(), + .S_AXI_ARVALID(), + .S_AXI_ARREADY(), + .S_AXI_RID(), + .S_AXI_RDATA(), + .S_AXI_RRESP(), + .S_AXI_RLAST(), + .S_AXI_RVALID(), + .S_AXI_RREADY(), + .S_AXI_INJECTSBITERR(), + .S_AXI_INJECTDBITERR(), + .S_AXI_SBITERR(), + .S_AXI_DBITERR(), + .S_AXI_RDADDRECC() + ); + +// synthesis translate_on + +endmodule diff --git a/verilog/sd2snes_spc7110/ip/mk2/msu_databuf.xco b/verilog/sd2snes_spc7110/ip/mk2/msu_databuf.xco new file mode 100644 index 000000000..5d4bb7347 --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/msu_databuf.xco @@ -0,0 +1,108 @@ +############################################################## +# +# Xilinx Core Generator version 14.7 +# Date: Mon May 18 10:46:00 2026 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# Generated from component: xilinx.com:ip:blk_mem_gen:7.3 +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = VHDL +SET device = xc3s400 +SET devicefamily = spartan3 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = pq208 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -4 +SET verilogsim = false +SET vhdlsim = true +# END Project Options +# BEGIN Select +SELECT Block_Memory_Generator xilinx.com:ip:blk_mem_gen:7.3 +# END Select +# BEGIN Parameters +CSET additional_inputs_for_power_estimation=false +CSET algorithm=Minimum_Area +CSET assume_synchronous_clk=true +CSET axi_id_width=4 +CSET axi_slave_type=Memory_Slave +CSET axi_type=AXI4_Full +CSET byte_size=9 +CSET coe_file=no_coe_file_loaded +CSET collision_warnings=ALL +CSET component_name=msu_databuf +CSET disable_collision_warnings=false +CSET disable_out_of_range_warnings=false +CSET ecc=false +CSET ecctype=No_ECC +CSET enable_32bit_address=false +CSET enable_a=Always_Enabled +CSET enable_b=Always_Enabled +CSET error_injection_type=Single_Bit_Error_Injection +CSET fill_remaining_memory_locations=false +CSET interface_type=Native +CSET load_init_file=false +CSET mem_file=no_Mem_file_loaded +CSET memory_type=Simple_Dual_Port_RAM +CSET operating_mode_a=WRITE_FIRST +CSET operating_mode_b=WRITE_FIRST +CSET output_reset_value_a=0 +CSET output_reset_value_b=0 +CSET pipeline_stages=0 +CSET port_a_clock=100 +CSET port_a_enable_rate=100 +CSET port_a_write_rate=50 +CSET port_b_clock=100 +CSET port_b_enable_rate=100 +CSET port_b_write_rate=0 +CSET primitive=8kx2 +CSET read_width_a=8 +CSET read_width_b=8 +CSET register_porta_input_of_softecc=false +CSET register_porta_output_of_memory_core=false +CSET register_porta_output_of_memory_primitives=false +CSET register_portb_output_of_memory_core=false +CSET register_portb_output_of_memory_primitives=false +CSET register_portb_output_of_softecc=false +CSET remaining_memory_locations=0 +CSET reset_memory_latch_a=false +CSET reset_memory_latch_b=false +CSET reset_priority_a=CE +CSET reset_priority_b=CE +CSET reset_type=SYNC +CSET softecc=false +CSET use_axi_id=false +CSET use_bram_block=Stand_Alone +CSET use_byte_write_enable=false +CSET use_error_injection_pins=false +CSET use_regcea_pin=false +CSET use_regceb_pin=false +CSET use_rsta_pin=false +CSET use_rstb_pin=false +CSET write_depth_a=16384 +CSET write_width_a=8 +CSET write_width_b=8 +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2012-11-19T16:22:25Z +# END Extra information +GENERATE +# CRC: a548ec2 diff --git a/verilog/sd2snes_spc7110/ip/mk2/msu_databuf.xise b/verilog/sd2snes_spc7110/ip/mk2/msu_databuf.xise new file mode 100644 index 000000000..cbf55139a --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/msu_databuf.xise @@ -0,0 +1,388 @@ + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.v b/verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.v new file mode 100644 index 000000000..a9cb78d7a --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.v @@ -0,0 +1,190 @@ +/******************************************************************************* +* This file is owned and controlled by Xilinx and must be used solely * +* for design, simulation, implementation and creation of design files * +* limited to Xilinx devices or technologies. Use with non-Xilinx * +* devices or technologies is expressly prohibited and immediately * +* terminates your license. * +* * +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" SOLELY * +* FOR USE IN DEVELOPING PROGRAMS AND SOLUTIONS FOR XILINX DEVICES. BY * +* PROVIDING THIS DESIGN, CODE, OR INFORMATION AS ONE POSSIBLE * +* IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, XILINX IS * +* MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE FROM ANY * +* CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING ANY * +* RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY * +* DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE * +* IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR * +* REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF * +* INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE. * +* * +* Xilinx products are not intended for use in life support appliances, * +* devices, or systems. Use in such applications are expressly * +* prohibited. * +* * +* (c) Copyright 1995-2018 Xilinx, Inc. * +* All rights reserved. * +*******************************************************************************/ +// You must compile the wrapper file snescmd_buf.v when simulating +// the core, snescmd_buf. When compiling the wrapper file, be sure to +// reference the XilinxCoreLib Verilog simulation library. For detailed +// instructions, please refer to the "CORE Generator Help". + +// The synthesis directives "translate_off/translate_on" specified below are +// supported by Xilinx, Mentor Graphics and Synplicity synthesis +// tools. Ensure they are correct for your synthesis tool(s). + +`timescale 1ns/1ps + +module snescmd_buf( + clka, + wea, + addra, + dina, + douta, + clkb, + web, + addrb, + dinb, + doutb +); + +input clka; +input [0 : 0] wea; +input [8 : 0] addra; +input [7 : 0] dina; +output [7 : 0] douta; +input clkb; +input [0 : 0] web; +input [8 : 0] addrb; +input [7 : 0] dinb; +output [7 : 0] doutb; + +// synthesis translate_off + + BLK_MEM_GEN_V7_3 #( + .C_ADDRA_WIDTH(9), + .C_ADDRB_WIDTH(9), + .C_ALGORITHM(1), + .C_AXI_ID_WIDTH(4), + .C_AXI_SLAVE_TYPE(0), + .C_AXI_TYPE(1), + .C_BYTE_SIZE(9), + .C_COMMON_CLK(1), + .C_DEFAULT_DATA("0"), + .C_DISABLE_WARN_BHV_COLL(0), + .C_DISABLE_WARN_BHV_RANGE(0), + .C_ENABLE_32BIT_ADDRESS(0), + .C_FAMILY("spartan3"), + .C_HAS_AXI_ID(0), + .C_HAS_ENA(0), + .C_HAS_ENB(0), + .C_HAS_INJECTERR(0), + .C_HAS_MEM_OUTPUT_REGS_A(0), + .C_HAS_MEM_OUTPUT_REGS_B(0), + .C_HAS_MUX_OUTPUT_REGS_A(0), + .C_HAS_MUX_OUTPUT_REGS_B(0), + .C_HAS_REGCEA(0), + .C_HAS_REGCEB(0), + .C_HAS_RSTA(0), + .C_HAS_RSTB(0), + .C_HAS_SOFTECC_INPUT_REGS_A(0), + .C_HAS_SOFTECC_OUTPUT_REGS_B(0), + .C_INIT_FILE("BlankString"), + .C_INIT_FILE_NAME("no_coe_file_loaded"), + .C_INITA_VAL("0"), + .C_INITB_VAL("0"), + .C_INTERFACE_TYPE(0), + .C_LOAD_INIT_FILE(0), + .C_MEM_TYPE(2), + .C_MUX_PIPELINE_STAGES(0), + .C_PRIM_TYPE(1), + .C_READ_DEPTH_A(512), + .C_READ_DEPTH_B(512), + .C_READ_WIDTH_A(8), + .C_READ_WIDTH_B(8), + .C_RST_PRIORITY_A("CE"), + .C_RST_PRIORITY_B("CE"), + .C_RST_TYPE("SYNC"), + .C_RSTRAM_A(0), + .C_RSTRAM_B(0), + .C_SIM_COLLISION_CHECK("ALL"), + .C_USE_BRAM_BLOCK(0), + .C_USE_BYTE_WEA(0), + .C_USE_BYTE_WEB(0), + .C_USE_DEFAULT_DATA(0), + .C_USE_ECC(0), + .C_USE_SOFTECC(0), + .C_WEA_WIDTH(1), + .C_WEB_WIDTH(1), + .C_WRITE_DEPTH_A(512), + .C_WRITE_DEPTH_B(512), + .C_WRITE_MODE_A("WRITE_FIRST"), + .C_WRITE_MODE_B("WRITE_FIRST"), + .C_WRITE_WIDTH_A(8), + .C_WRITE_WIDTH_B(8), + .C_XDEVICEFAMILY("spartan3") + ) + inst ( + .CLKA(clka), + .WEA(wea), + .ADDRA(addra), + .DINA(dina), + .DOUTA(douta), + .CLKB(clkb), + .WEB(web), + .ADDRB(addrb), + .DINB(dinb), + .DOUTB(doutb), + .RSTA(), + .ENA(), + .REGCEA(), + .RSTB(), + .ENB(), + .REGCEB(), + .INJECTSBITERR(), + .INJECTDBITERR(), + .SBITERR(), + .DBITERR(), + .RDADDRECC(), + .S_ACLK(), + .S_ARESETN(), + .S_AXI_AWID(), + .S_AXI_AWADDR(), + .S_AXI_AWLEN(), + .S_AXI_AWSIZE(), + .S_AXI_AWBURST(), + .S_AXI_AWVALID(), + .S_AXI_AWREADY(), + .S_AXI_WDATA(), + .S_AXI_WSTRB(), + .S_AXI_WLAST(), + .S_AXI_WVALID(), + .S_AXI_WREADY(), + .S_AXI_BID(), + .S_AXI_BRESP(), + .S_AXI_BVALID(), + .S_AXI_BREADY(), + .S_AXI_ARID(), + .S_AXI_ARADDR(), + .S_AXI_ARLEN(), + .S_AXI_ARSIZE(), + .S_AXI_ARBURST(), + .S_AXI_ARVALID(), + .S_AXI_ARREADY(), + .S_AXI_RID(), + .S_AXI_RDATA(), + .S_AXI_RRESP(), + .S_AXI_RLAST(), + .S_AXI_RVALID(), + .S_AXI_RREADY(), + .S_AXI_INJECTSBITERR(), + .S_AXI_INJECTDBITERR(), + .S_AXI_SBITERR(), + .S_AXI_DBITERR(), + .S_AXI_RDADDRECC() + ); + +// synthesis translate_on + +endmodule diff --git a/verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.xco b/verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.xco new file mode 100644 index 000000000..a6b8a5b82 --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.xco @@ -0,0 +1,108 @@ +############################################################## +# +# Xilinx Core Generator version 14.7 +# Date: Mon May 18 10:46:36 2026 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# Generated from component: xilinx.com:ip:blk_mem_gen:7.3 +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = VHDL +SET device = xc3s400 +SET devicefamily = spartan3 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = pq208 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -4 +SET verilogsim = false +SET vhdlsim = true +# END Project Options +# BEGIN Select +SELECT Block_Memory_Generator xilinx.com:ip:blk_mem_gen:7.3 +# END Select +# BEGIN Parameters +CSET additional_inputs_for_power_estimation=false +CSET algorithm=Minimum_Area +CSET assume_synchronous_clk=true +CSET axi_id_width=4 +CSET axi_slave_type=Memory_Slave +CSET axi_type=AXI4_Full +CSET byte_size=9 +CSET coe_file=no_coe_file_loaded +CSET collision_warnings=ALL +CSET component_name=snescmd_buf +CSET disable_collision_warnings=false +CSET disable_out_of_range_warnings=false +CSET ecc=false +CSET ecctype=No_ECC +CSET enable_32bit_address=false +CSET enable_a=Always_Enabled +CSET enable_b=Always_Enabled +CSET error_injection_type=Single_Bit_Error_Injection +CSET fill_remaining_memory_locations=false +CSET interface_type=Native +CSET load_init_file=false +CSET mem_file=no_Mem_file_loaded +CSET memory_type=True_Dual_Port_RAM +CSET operating_mode_a=WRITE_FIRST +CSET operating_mode_b=WRITE_FIRST +CSET output_reset_value_a=0 +CSET output_reset_value_b=0 +CSET pipeline_stages=0 +CSET port_a_clock=100 +CSET port_a_enable_rate=100 +CSET port_a_write_rate=50 +CSET port_b_clock=100 +CSET port_b_enable_rate=100 +CSET port_b_write_rate=50 +CSET primitive=8kx2 +CSET read_width_a=8 +CSET read_width_b=8 +CSET register_porta_input_of_softecc=false +CSET register_porta_output_of_memory_core=false +CSET register_porta_output_of_memory_primitives=false +CSET register_portb_output_of_memory_core=false +CSET register_portb_output_of_memory_primitives=false +CSET register_portb_output_of_softecc=false +CSET remaining_memory_locations=0 +CSET reset_memory_latch_a=false +CSET reset_memory_latch_b=false +CSET reset_priority_a=CE +CSET reset_priority_b=CE +CSET reset_type=SYNC +CSET softecc=false +CSET use_axi_id=false +CSET use_bram_block=Stand_Alone +CSET use_byte_write_enable=false +CSET use_error_injection_pins=false +CSET use_regcea_pin=false +CSET use_regceb_pin=false +CSET use_rsta_pin=false +CSET use_rstb_pin=false +CSET write_depth_a=512 +CSET write_width_a=8 +CSET write_width_b=8 +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2012-11-19T16:22:25Z +# END Extra information +GENERATE +# CRC: bd26e67e diff --git a/verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.xise b/verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.xise new file mode 100644 index 000000000..74fbd4db7 --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk2/snescmd_buf.xise @@ -0,0 +1,388 @@ + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/verilog/sd2snes_spc7110/ip/mk3/dac_buf.qip b/verilog/sd2snes_spc7110/ip/mk3/dac_buf.qip new file mode 100644 index 000000000..93ecff86d --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk3/dac_buf.qip @@ -0,0 +1,6 @@ +set_global_assignment -name IP_TOOL_NAME "RAM: 2-PORT" +set_global_assignment -name IP_TOOL_VERSION "20.1" +set_global_assignment -name IP_GENERATED_DEVICE_FAMILY "{Cyclone IV E}" +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "dac_buf.v"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "dac_buf_inst.v"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "dac_buf_bb.v"] diff --git a/verilog/sd2snes_spc7110/ip/mk3/dac_buf.v b/verilog/sd2snes_spc7110/ip/mk3/dac_buf.v new file mode 100644 index 000000000..74b0819cd --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk3/dac_buf.v @@ -0,0 +1,214 @@ +// megafunction wizard: %RAM: 2-PORT% +// GENERATION: STANDARD +// VERSION: WM1.0 +// MODULE: altsyncram + +// ============================================================ +// File Name: dac_buf.v +// Megafunction Name(s): +// altsyncram +// +// Simulation Library Files(s): +// altera_mf +// ============================================================ +// ************************************************************ +// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! +// +// 20.1.1 Build 720 11/11/2020 SJ Lite Edition +// ************************************************************ + + +//Copyright (C) 2020 Intel Corporation. All rights reserved. +//Your use of Intel Corporation's design tools, logic functions +//and other software and tools, and any 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 Intel Program License +//Subscription Agreement, the Intel Quartus Prime License Agreement, +//the Intel FPGA IP License Agreement, or other applicable license +//agreement, including, without limitation, that your use is for +//the sole purpose of programming logic devices manufactured by +//Intel and sold by Intel or its authorized distributors. Please +//refer to the applicable agreement for further details, at +//https://fpgasoftware.intel.com/eula. + + +// synopsys translate_off +`timescale 1 ps / 1 ps +// synopsys translate_on +module dac_buf ( + clock, + data, + rdaddress, + wraddress, + wren, + q); + + input clock; + input [7:0] data; + input [8:0] rdaddress; + input [10:0] wraddress; + input wren; + output [31:0] q; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_off +`endif + tri1 clock; + tri0 wren; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_on +`endif + + wire [31:0] sub_wire0; + wire [31:0] q = sub_wire0[31:0]; + + altsyncram altsyncram_component ( + .address_a (wraddress), + .address_b (rdaddress), + .clock0 (clock), + .data_a (data), + .wren_a (wren), + .q_b (sub_wire0), + .aclr0 (1'b0), + .aclr1 (1'b0), + .addressstall_a (1'b0), + .addressstall_b (1'b0), + .byteena_a (1'b1), + .byteena_b (1'b1), + .clock1 (1'b1), + .clocken0 (1'b1), + .clocken1 (1'b1), + .clocken2 (1'b1), + .clocken3 (1'b1), + .data_b ({32{1'b1}}), + .eccstatus (), + .q_a (), + .rden_a (1'b1), + .rden_b (1'b1), + .wren_b (1'b0)); + defparam + altsyncram_component.address_aclr_b = "NONE", + altsyncram_component.address_reg_b = "CLOCK0", + altsyncram_component.clock_enable_input_a = "BYPASS", + altsyncram_component.clock_enable_input_b = "BYPASS", + altsyncram_component.clock_enable_output_b = "BYPASS", + altsyncram_component.intended_device_family = "Cyclone IV E", + altsyncram_component.lpm_type = "altsyncram", + altsyncram_component.numwords_a = 2048, + altsyncram_component.numwords_b = 512, + altsyncram_component.operation_mode = "DUAL_PORT", + altsyncram_component.outdata_aclr_b = "NONE", + altsyncram_component.outdata_reg_b = "CLOCK0", + altsyncram_component.power_up_uninitialized = "FALSE", + altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE", + altsyncram_component.widthad_a = 11, + altsyncram_component.widthad_b = 9, + altsyncram_component.width_a = 8, + altsyncram_component.width_b = 32, + altsyncram_component.width_byteena_a = 1; + + +endmodule + +// ============================================================ +// CNX file retrieval info +// ============================================================ +// Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0" +// Retrieval info: PRIVATE: ADDRESSSTALL_B NUMERIC "0" +// Retrieval info: PRIVATE: BYTEENA_ACLR_A NUMERIC "0" +// Retrieval info: PRIVATE: BYTEENA_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: BYTE_ENABLE_A NUMERIC "0" +// Retrieval info: PRIVATE: BYTE_ENABLE_B NUMERIC "0" +// Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8" +// Retrieval info: PRIVATE: BlankMemory NUMERIC "1" +// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0" +// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_B NUMERIC "0" +// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0" +// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_B NUMERIC "0" +// Retrieval info: PRIVATE: CLRdata NUMERIC "0" +// Retrieval info: PRIVATE: CLRq NUMERIC "0" +// Retrieval info: PRIVATE: CLRrdaddress NUMERIC "0" +// Retrieval info: PRIVATE: CLRrren NUMERIC "0" +// Retrieval info: PRIVATE: CLRwraddress NUMERIC "0" +// Retrieval info: PRIVATE: CLRwren NUMERIC "0" +// Retrieval info: PRIVATE: Clock NUMERIC "0" +// Retrieval info: PRIVATE: Clock_A NUMERIC "0" +// Retrieval info: PRIVATE: Clock_B NUMERIC "0" +// Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0" +// Retrieval info: PRIVATE: INDATA_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: INDATA_REG_B NUMERIC "0" +// Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_B" +// Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0" +// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E" +// Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0" +// Retrieval info: PRIVATE: JTAG_ID STRING "NONE" +// Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0" +// Retrieval info: PRIVATE: MEMSIZE NUMERIC "16384" +// Retrieval info: PRIVATE: MEM_IN_BITS NUMERIC "0" +// Retrieval info: PRIVATE: MIFfilename STRING "" +// Retrieval info: PRIVATE: OPERATION_MODE NUMERIC "2" +// Retrieval info: PRIVATE: OUTDATA_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: OUTDATA_REG_B NUMERIC "1" +// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0" +// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_MIXED_PORTS NUMERIC "2" +// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_A NUMERIC "3" +// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_B NUMERIC "3" +// Retrieval info: PRIVATE: REGdata NUMERIC "1" +// Retrieval info: PRIVATE: REGq NUMERIC "1" +// Retrieval info: PRIVATE: REGrdaddress NUMERIC "1" +// Retrieval info: PRIVATE: REGrren NUMERIC "1" +// Retrieval info: PRIVATE: REGwraddress NUMERIC "1" +// Retrieval info: PRIVATE: REGwren NUMERIC "1" +// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0" +// Retrieval info: PRIVATE: USE_DIFF_CLKEN NUMERIC "0" +// Retrieval info: PRIVATE: UseDPRAM NUMERIC "1" +// Retrieval info: PRIVATE: VarWidth NUMERIC "1" +// Retrieval info: PRIVATE: WIDTH_READ_A NUMERIC "8" +// Retrieval info: PRIVATE: WIDTH_READ_B NUMERIC "32" +// Retrieval info: PRIVATE: WIDTH_WRITE_A NUMERIC "8" +// Retrieval info: PRIVATE: WIDTH_WRITE_B NUMERIC "32" +// Retrieval info: PRIVATE: WRADDR_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: WRADDR_REG_B NUMERIC "0" +// Retrieval info: PRIVATE: WRCTRL_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: enable NUMERIC "0" +// Retrieval info: PRIVATE: rden NUMERIC "0" +// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all +// Retrieval info: CONSTANT: ADDRESS_ACLR_B STRING "NONE" +// Retrieval info: CONSTANT: ADDRESS_REG_B STRING "CLOCK0" +// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS" +// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_B STRING "BYPASS" +// Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_B STRING "BYPASS" +// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E" +// Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram" +// Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "2048" +// Retrieval info: CONSTANT: NUMWORDS_B NUMERIC "512" +// Retrieval info: CONSTANT: OPERATION_MODE STRING "DUAL_PORT" +// Retrieval info: CONSTANT: OUTDATA_ACLR_B STRING "NONE" +// Retrieval info: CONSTANT: OUTDATA_REG_B STRING "CLOCK0" +// Retrieval info: CONSTANT: POWER_UP_UNINITIALIZED STRING "FALSE" +// Retrieval info: CONSTANT: READ_DURING_WRITE_MODE_MIXED_PORTS STRING "DONT_CARE" +// Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "11" +// Retrieval info: CONSTANT: WIDTHAD_B NUMERIC "9" +// Retrieval info: CONSTANT: WIDTH_A NUMERIC "8" +// Retrieval info: CONSTANT: WIDTH_B NUMERIC "32" +// Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1" +// Retrieval info: USED_PORT: clock 0 0 0 0 INPUT VCC "clock" +// Retrieval info: USED_PORT: data 0 0 8 0 INPUT NODEFVAL "data[7..0]" +// Retrieval info: USED_PORT: q 0 0 32 0 OUTPUT NODEFVAL "q[31..0]" +// Retrieval info: USED_PORT: rdaddress 0 0 9 0 INPUT NODEFVAL "rdaddress[8..0]" +// Retrieval info: USED_PORT: wraddress 0 0 11 0 INPUT NODEFVAL "wraddress[10..0]" +// Retrieval info: USED_PORT: wren 0 0 0 0 INPUT GND "wren" +// Retrieval info: CONNECT: @address_a 0 0 11 0 wraddress 0 0 11 0 +// Retrieval info: CONNECT: @address_b 0 0 9 0 rdaddress 0 0 9 0 +// Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0 +// Retrieval info: CONNECT: @data_a 0 0 8 0 data 0 0 8 0 +// Retrieval info: CONNECT: @wren_a 0 0 0 0 wren 0 0 0 0 +// Retrieval info: CONNECT: q 0 0 32 0 @q_b 0 0 32 0 +// Retrieval info: GEN_FILE: TYPE_NORMAL dac_buf.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL dac_buf.inc FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL dac_buf.cmp FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL dac_buf.bsf FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL dac_buf_inst.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL dac_buf_bb.v TRUE +// Retrieval info: LIB_FILE: altera_mf diff --git a/verilog/sd2snes_spc7110/ip/mk3/msu_databuf.qip b/verilog/sd2snes_spc7110/ip/mk3/msu_databuf.qip new file mode 100644 index 000000000..b5eaecfda --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk3/msu_databuf.qip @@ -0,0 +1,6 @@ +set_global_assignment -name IP_TOOL_NAME "RAM: 2-PORT" +set_global_assignment -name IP_TOOL_VERSION "20.1" +set_global_assignment -name IP_GENERATED_DEVICE_FAMILY "{Cyclone IV E}" +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "msu_databuf.v"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "msu_databuf_inst.v"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "msu_databuf_bb.v"] diff --git a/verilog/sd2snes_spc7110/ip/mk3/msu_databuf.v b/verilog/sd2snes_spc7110/ip/mk3/msu_databuf.v new file mode 100644 index 000000000..4b7217f4a --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk3/msu_databuf.v @@ -0,0 +1,214 @@ +// megafunction wizard: %RAM: 2-PORT% +// GENERATION: STANDARD +// VERSION: WM1.0 +// MODULE: altsyncram + +// ============================================================ +// File Name: msu_databuf.v +// Megafunction Name(s): +// altsyncram +// +// Simulation Library Files(s): +// altera_mf +// ============================================================ +// ************************************************************ +// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! +// +// 20.1.1 Build 720 11/11/2020 SJ Lite Edition +// ************************************************************ + + +//Copyright (C) 2020 Intel Corporation. All rights reserved. +//Your use of Intel Corporation's design tools, logic functions +//and other software and tools, and any 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 Intel Program License +//Subscription Agreement, the Intel Quartus Prime License Agreement, +//the Intel FPGA IP License Agreement, or other applicable license +//agreement, including, without limitation, that your use is for +//the sole purpose of programming logic devices manufactured by +//Intel and sold by Intel or its authorized distributors. Please +//refer to the applicable agreement for further details, at +//https://fpgasoftware.intel.com/eula. + + +// synopsys translate_off +`timescale 1 ps / 1 ps +// synopsys translate_on +module msu_databuf ( + clock, + data, + rdaddress, + wraddress, + wren, + q); + + input clock; + input [7:0] data; + input [13:0] rdaddress; + input [13:0] wraddress; + input wren; + output [7:0] q; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_off +`endif + tri1 clock; + tri0 wren; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_on +`endif + + wire [7:0] sub_wire0; + wire [7:0] q = sub_wire0[7:0]; + + altsyncram altsyncram_component ( + .address_a (wraddress), + .address_b (rdaddress), + .clock0 (clock), + .data_a (data), + .wren_a (wren), + .q_b (sub_wire0), + .aclr0 (1'b0), + .aclr1 (1'b0), + .addressstall_a (1'b0), + .addressstall_b (1'b0), + .byteena_a (1'b1), + .byteena_b (1'b1), + .clock1 (1'b1), + .clocken0 (1'b1), + .clocken1 (1'b1), + .clocken2 (1'b1), + .clocken3 (1'b1), + .data_b ({8{1'b1}}), + .eccstatus (), + .q_a (), + .rden_a (1'b1), + .rden_b (1'b1), + .wren_b (1'b0)); + defparam + altsyncram_component.address_aclr_b = "NONE", + altsyncram_component.address_reg_b = "CLOCK0", + altsyncram_component.clock_enable_input_a = "BYPASS", + altsyncram_component.clock_enable_input_b = "BYPASS", + altsyncram_component.clock_enable_output_b = "BYPASS", + altsyncram_component.intended_device_family = "Cyclone IV E", + altsyncram_component.lpm_type = "altsyncram", + altsyncram_component.numwords_a = 16384, + altsyncram_component.numwords_b = 16384, + altsyncram_component.operation_mode = "DUAL_PORT", + altsyncram_component.outdata_aclr_b = "NONE", + altsyncram_component.outdata_reg_b = "CLOCK0", + altsyncram_component.power_up_uninitialized = "FALSE", + altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE", + altsyncram_component.widthad_a = 14, + altsyncram_component.widthad_b = 14, + altsyncram_component.width_a = 8, + altsyncram_component.width_b = 8, + altsyncram_component.width_byteena_a = 1; + + +endmodule + +// ============================================================ +// CNX file retrieval info +// ============================================================ +// Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0" +// Retrieval info: PRIVATE: ADDRESSSTALL_B NUMERIC "0" +// Retrieval info: PRIVATE: BYTEENA_ACLR_A NUMERIC "0" +// Retrieval info: PRIVATE: BYTEENA_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: BYTE_ENABLE_A NUMERIC "0" +// Retrieval info: PRIVATE: BYTE_ENABLE_B NUMERIC "0" +// Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8" +// Retrieval info: PRIVATE: BlankMemory NUMERIC "1" +// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0" +// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_B NUMERIC "0" +// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0" +// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_B NUMERIC "0" +// Retrieval info: PRIVATE: CLRdata NUMERIC "0" +// Retrieval info: PRIVATE: CLRq NUMERIC "0" +// Retrieval info: PRIVATE: CLRrdaddress NUMERIC "0" +// Retrieval info: PRIVATE: CLRrren NUMERIC "0" +// Retrieval info: PRIVATE: CLRwraddress NUMERIC "0" +// Retrieval info: PRIVATE: CLRwren NUMERIC "0" +// Retrieval info: PRIVATE: Clock NUMERIC "0" +// Retrieval info: PRIVATE: Clock_A NUMERIC "0" +// Retrieval info: PRIVATE: Clock_B NUMERIC "0" +// Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0" +// Retrieval info: PRIVATE: INDATA_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: INDATA_REG_B NUMERIC "0" +// Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_B" +// Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0" +// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E" +// Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0" +// Retrieval info: PRIVATE: JTAG_ID STRING "NONE" +// Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0" +// Retrieval info: PRIVATE: MEMSIZE NUMERIC "131072" +// Retrieval info: PRIVATE: MEM_IN_BITS NUMERIC "0" +// Retrieval info: PRIVATE: MIFfilename STRING "" +// Retrieval info: PRIVATE: OPERATION_MODE NUMERIC "2" +// Retrieval info: PRIVATE: OUTDATA_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: OUTDATA_REG_B NUMERIC "1" +// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0" +// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_MIXED_PORTS NUMERIC "2" +// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_A NUMERIC "3" +// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_B NUMERIC "3" +// Retrieval info: PRIVATE: REGdata NUMERIC "1" +// Retrieval info: PRIVATE: REGq NUMERIC "1" +// Retrieval info: PRIVATE: REGrdaddress NUMERIC "1" +// Retrieval info: PRIVATE: REGrren NUMERIC "1" +// Retrieval info: PRIVATE: REGwraddress NUMERIC "1" +// Retrieval info: PRIVATE: REGwren NUMERIC "1" +// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0" +// Retrieval info: PRIVATE: USE_DIFF_CLKEN NUMERIC "0" +// Retrieval info: PRIVATE: UseDPRAM NUMERIC "1" +// Retrieval info: PRIVATE: VarWidth NUMERIC "0" +// Retrieval info: PRIVATE: WIDTH_READ_A NUMERIC "8" +// Retrieval info: PRIVATE: WIDTH_READ_B NUMERIC "8" +// Retrieval info: PRIVATE: WIDTH_WRITE_A NUMERIC "8" +// Retrieval info: PRIVATE: WIDTH_WRITE_B NUMERIC "8" +// Retrieval info: PRIVATE: WRADDR_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: WRADDR_REG_B NUMERIC "0" +// Retrieval info: PRIVATE: WRCTRL_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: enable NUMERIC "0" +// Retrieval info: PRIVATE: rden NUMERIC "0" +// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all +// Retrieval info: CONSTANT: ADDRESS_ACLR_B STRING "NONE" +// Retrieval info: CONSTANT: ADDRESS_REG_B STRING "CLOCK0" +// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS" +// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_B STRING "BYPASS" +// Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_B STRING "BYPASS" +// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E" +// Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram" +// Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "16384" +// Retrieval info: CONSTANT: NUMWORDS_B NUMERIC "16384" +// Retrieval info: CONSTANT: OPERATION_MODE STRING "DUAL_PORT" +// Retrieval info: CONSTANT: OUTDATA_ACLR_B STRING "NONE" +// Retrieval info: CONSTANT: OUTDATA_REG_B STRING "CLOCK0" +// Retrieval info: CONSTANT: POWER_UP_UNINITIALIZED STRING "FALSE" +// Retrieval info: CONSTANT: READ_DURING_WRITE_MODE_MIXED_PORTS STRING "DONT_CARE" +// Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "14" +// Retrieval info: CONSTANT: WIDTHAD_B NUMERIC "14" +// Retrieval info: CONSTANT: WIDTH_A NUMERIC "8" +// Retrieval info: CONSTANT: WIDTH_B NUMERIC "8" +// Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1" +// Retrieval info: USED_PORT: clock 0 0 0 0 INPUT VCC "clock" +// Retrieval info: USED_PORT: data 0 0 8 0 INPUT NODEFVAL "data[7..0]" +// Retrieval info: USED_PORT: q 0 0 8 0 OUTPUT NODEFVAL "q[7..0]" +// Retrieval info: USED_PORT: rdaddress 0 0 14 0 INPUT NODEFVAL "rdaddress[13..0]" +// Retrieval info: USED_PORT: wraddress 0 0 14 0 INPUT NODEFVAL "wraddress[13..0]" +// Retrieval info: USED_PORT: wren 0 0 0 0 INPUT GND "wren" +// Retrieval info: CONNECT: @address_a 0 0 14 0 wraddress 0 0 14 0 +// Retrieval info: CONNECT: @address_b 0 0 14 0 rdaddress 0 0 14 0 +// Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0 +// Retrieval info: CONNECT: @data_a 0 0 8 0 data 0 0 8 0 +// Retrieval info: CONNECT: @wren_a 0 0 0 0 wren 0 0 0 0 +// Retrieval info: CONNECT: q 0 0 8 0 @q_b 0 0 8 0 +// Retrieval info: GEN_FILE: TYPE_NORMAL msu_databuf.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL msu_databuf.inc FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL msu_databuf.cmp FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL msu_databuf.bsf FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL msu_databuf_inst.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL msu_databuf_bb.v TRUE +// Retrieval info: LIB_FILE: altera_mf diff --git a/verilog/sd2snes_spc7110/ip/mk3/pll.qip b/verilog/sd2snes_spc7110/ip/mk3/pll.qip new file mode 100644 index 000000000..56eb2bdb4 --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk3/pll.qip @@ -0,0 +1,7 @@ +set_global_assignment -name IP_TOOL_NAME "ALTPLL" +set_global_assignment -name IP_TOOL_VERSION "21.1" +set_global_assignment -name IP_GENERATED_DEVICE_FAMILY "{Cyclone IV E}" +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_inst.v"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "pll_bb.v"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "pll.ppf"] diff --git a/verilog/sd2snes_spc7110/ip/mk3/pll.v b/verilog/sd2snes_spc7110/ip/mk3/pll.v new file mode 100644 index 000000000..d17dadcea --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk3/pll.v @@ -0,0 +1,321 @@ +// 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! +// +// 21.1.1 Build 850 06/23/2022 SJ Lite Edition +// ************************************************************ + + +//Copyright (C) 2022 Intel Corporation. All rights reserved. +//Your use of Intel Corporation's design tools, logic functions +//and other software and tools, and any 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 Intel Program License +//Subscription Agreement, the Intel Quartus Prime License Agreement, +//the Intel FPGA IP License Agreement, or other applicable license +//agreement, including, without limitation, that your use is for +//the sole purpose of programming logic devices manufactured by +//Intel and sold by Intel or its authorized distributors. Please +//refer to the applicable agreement for further details, at +//https://fpgasoftware.intel.com/eula. + + +// synopsys translate_off +`timescale 1 ps / 1 ps +// synopsys translate_on +module pll ( + areset, + inclk0, + c0, + locked); + + input areset; + input inclk0; + output c0; + output locked; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_off +`endif + tri0 areset; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_on +`endif + + wire [0:0] sub_wire2 = 1'h0; + wire [4:0] sub_wire3; + wire sub_wire5; + wire sub_wire0 = inclk0; + wire [1:0] sub_wire1 = {sub_wire2, sub_wire0}; + wire [0:0] sub_wire4 = sub_wire3[0:0]; + wire c0 = sub_wire4; + wire locked = sub_wire5; + + altpll altpll_component ( + .areset (areset), + .inclk (sub_wire1), + .clk (sub_wire3), + .locked (sub_wire5), + .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 = 1, + altpll_component.clk0_duty_cycle = 50, + altpll_component.clk0_multiply_by = 12, + altpll_component.clk0_phase_shift = "0", + altpll_component.compensate_clock = "CLK0", + altpll_component.inclk0_input_frequency = 125000, + altpll_component.intended_device_family = "Cyclone IV E", + 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_UNUSED", + 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 "Any" +// Retrieval info: PRIVATE: DIV_FACTOR0 NUMERIC "1" +// Retrieval info: PRIVATE: DUTY_CYCLE0 STRING "50.00000000" +// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE0 STRING "96.000000" +// 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 "8.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 IV E" +// 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: MIG_DEVICE_SPEED_GRADE STRING "Any" +// Retrieval info: PRIVATE: MIRROR_CLK0 STRING "0" +// Retrieval info: PRIVATE: MULT_FACTOR0 NUMERIC "4" +// Retrieval info: PRIVATE: NORMAL_MODE_RADIO STRING "1" +// Retrieval info: PRIVATE: OUTPUT_FREQ0 STRING "96.00000000" +// Retrieval info: PRIVATE: OUTPUT_FREQ_MODE0 STRING "1" +// Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT0 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_SHIFT_STEP_ENABLED_CHECK STRING "0" +// Retrieval info: PRIVATE: PHASE_SHIFT_UNIT0 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: 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_CLKENA0 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 "1" +// Retrieval info: CONSTANT: CLK0_DUTY_CYCLE NUMERIC "50" +// Retrieval info: CONSTANT: CLK0_MULTIPLY_BY NUMERIC "12" +// Retrieval info: CONSTANT: CLK0_PHASE_SHIFT STRING "0" +// Retrieval info: CONSTANT: COMPENSATE_CLOCK STRING "CLK0" +// Retrieval info: CONSTANT: INCLK0_INPUT_FREQUENCY NUMERIC "125000" +// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E" +// 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_UNUSED" +// 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: 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: 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 TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL pll_bb.v TRUE +// Retrieval info: LIB_FILE: altera_mf +// Retrieval info: CBX_MODULE_PREFIX: ON diff --git a/verilog/sd2snes_spc7110/ip/mk3/snescmd_buf.qip b/verilog/sd2snes_spc7110/ip/mk3/snescmd_buf.qip new file mode 100644 index 000000000..dc4347419 --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk3/snescmd_buf.qip @@ -0,0 +1,6 @@ +set_global_assignment -name IP_TOOL_NAME "RAM: 2-PORT" +set_global_assignment -name IP_TOOL_VERSION "21.1" +set_global_assignment -name IP_GENERATED_DEVICE_FAMILY "{Cyclone IV E}" +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "snescmd_buf.v"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "snescmd_buf_inst.v"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "snescmd_buf_bb.v"] diff --git a/verilog/sd2snes_spc7110/ip/mk3/snescmd_buf.v b/verilog/sd2snes_spc7110/ip/mk3/snescmd_buf.v new file mode 100644 index 000000000..eb3b8d1ed --- /dev/null +++ b/verilog/sd2snes_spc7110/ip/mk3/snescmd_buf.v @@ -0,0 +1,243 @@ +// megafunction wizard: %RAM: 2-PORT% +// GENERATION: STANDARD +// VERSION: WM1.0 +// MODULE: altsyncram + +// ============================================================ +// File Name: snescmd_buf.v +// Megafunction Name(s): +// altsyncram +// +// Simulation Library Files(s): +// altera_mf +// ============================================================ +// ************************************************************ +// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! +// +// 21.1.1 Build 850 06/23/2022 SJ Lite Edition +// ************************************************************ + + +//Copyright (C) 2022 Intel Corporation. All rights reserved. +//Your use of Intel Corporation's design tools, logic functions +//and other software and tools, and any 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 Intel Program License +//Subscription Agreement, the Intel Quartus Prime License Agreement, +//the Intel FPGA IP License Agreement, or other applicable license +//agreement, including, without limitation, that your use is for +//the sole purpose of programming logic devices manufactured by +//Intel and sold by Intel or its authorized distributors. Please +//refer to the applicable agreement for further details, at +//https://fpgasoftware.intel.com/eula. + + +// synopsys translate_off +`timescale 1 ps / 1 ps +// synopsys translate_on +module snescmd_buf ( + address_a, + address_b, + clock, + data_a, + data_b, + wren_a, + wren_b, + q_a, + q_b); + + input [8:0] address_a; + input [8:0] address_b; + input clock; + input [7:0] data_a; + input [7:0] data_b; + input wren_a; + input wren_b; + output [7:0] q_a; + output [7:0] q_b; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_off +`endif + tri1 clock; + tri0 wren_a; + tri0 wren_b; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_on +`endif + + wire [7:0] sub_wire0; + wire [7:0] sub_wire1; + wire [7:0] q_a = sub_wire0[7:0]; + wire [7:0] q_b = sub_wire1[7:0]; + + altsyncram altsyncram_component ( + .address_a (address_a), + .address_b (address_b), + .clock0 (clock), + .data_a (data_a), + .data_b (data_b), + .wren_a (wren_a), + .wren_b (wren_b), + .q_a (sub_wire0), + .q_b (sub_wire1), + .aclr0 (1'b0), + .aclr1 (1'b0), + .addressstall_a (1'b0), + .addressstall_b (1'b0), + .byteena_a (1'b1), + .byteena_b (1'b1), + .clock1 (1'b1), + .clocken0 (1'b1), + .clocken1 (1'b1), + .clocken2 (1'b1), + .clocken3 (1'b1), + .eccstatus (), + .rden_a (1'b1), + .rden_b (1'b1)); + defparam + altsyncram_component.address_reg_b = "CLOCK0", + altsyncram_component.clock_enable_input_a = "BYPASS", + altsyncram_component.clock_enable_input_b = "BYPASS", + altsyncram_component.clock_enable_output_a = "BYPASS", + altsyncram_component.clock_enable_output_b = "BYPASS", + altsyncram_component.indata_reg_b = "CLOCK0", + altsyncram_component.intended_device_family = "Cyclone IV E", + altsyncram_component.lpm_type = "altsyncram", + altsyncram_component.numwords_a = 512, + altsyncram_component.numwords_b = 512, + altsyncram_component.operation_mode = "BIDIR_DUAL_PORT", + altsyncram_component.outdata_aclr_a = "NONE", + altsyncram_component.outdata_aclr_b = "NONE", + altsyncram_component.outdata_reg_a = "CLOCK0", + altsyncram_component.outdata_reg_b = "CLOCK0", + altsyncram_component.power_up_uninitialized = "FALSE", + altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE", + altsyncram_component.read_during_write_mode_port_a = "NEW_DATA_WITH_NBE_READ", + altsyncram_component.read_during_write_mode_port_b = "NEW_DATA_WITH_NBE_READ", + altsyncram_component.widthad_a = 9, + altsyncram_component.widthad_b = 9, + altsyncram_component.width_a = 8, + altsyncram_component.width_b = 8, + altsyncram_component.width_byteena_a = 1, + altsyncram_component.width_byteena_b = 1, + altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK0"; + + +endmodule + +// ============================================================ +// CNX file retrieval info +// ============================================================ +// Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0" +// Retrieval info: PRIVATE: ADDRESSSTALL_B NUMERIC "0" +// Retrieval info: PRIVATE: BYTEENA_ACLR_A NUMERIC "0" +// Retrieval info: PRIVATE: BYTEENA_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: BYTE_ENABLE_A NUMERIC "0" +// Retrieval info: PRIVATE: BYTE_ENABLE_B NUMERIC "0" +// Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8" +// Retrieval info: PRIVATE: BlankMemory NUMERIC "1" +// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0" +// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_B NUMERIC "0" +// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0" +// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_B NUMERIC "0" +// Retrieval info: PRIVATE: CLRdata NUMERIC "0" +// Retrieval info: PRIVATE: CLRq NUMERIC "0" +// Retrieval info: PRIVATE: CLRrdaddress NUMERIC "0" +// Retrieval info: PRIVATE: CLRrren NUMERIC "0" +// Retrieval info: PRIVATE: CLRwraddress NUMERIC "0" +// Retrieval info: PRIVATE: CLRwren NUMERIC "0" +// Retrieval info: PRIVATE: Clock NUMERIC "0" +// Retrieval info: PRIVATE: Clock_A NUMERIC "0" +// Retrieval info: PRIVATE: Clock_B NUMERIC "0" +// Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0" +// Retrieval info: PRIVATE: INDATA_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: INDATA_REG_B NUMERIC "1" +// Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_A" +// Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0" +// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E" +// Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0" +// Retrieval info: PRIVATE: JTAG_ID STRING "NONE" +// Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0" +// Retrieval info: PRIVATE: MEMSIZE NUMERIC "4096" +// Retrieval info: PRIVATE: MEM_IN_BITS NUMERIC "0" +// Retrieval info: PRIVATE: MIFfilename STRING "" +// Retrieval info: PRIVATE: OPERATION_MODE NUMERIC "3" +// Retrieval info: PRIVATE: OUTDATA_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: OUTDATA_REG_B NUMERIC "1" +// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0" +// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_MIXED_PORTS NUMERIC "2" +// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_A NUMERIC "4" +// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_B NUMERIC "4" +// Retrieval info: PRIVATE: REGdata NUMERIC "1" +// Retrieval info: PRIVATE: REGq NUMERIC "1" +// Retrieval info: PRIVATE: REGrdaddress NUMERIC "0" +// Retrieval info: PRIVATE: REGrren NUMERIC "0" +// Retrieval info: PRIVATE: REGwraddress NUMERIC "1" +// Retrieval info: PRIVATE: REGwren NUMERIC "1" +// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0" +// Retrieval info: PRIVATE: USE_DIFF_CLKEN NUMERIC "0" +// Retrieval info: PRIVATE: UseDPRAM NUMERIC "1" +// Retrieval info: PRIVATE: VarWidth NUMERIC "0" +// Retrieval info: PRIVATE: WIDTH_READ_A NUMERIC "8" +// Retrieval info: PRIVATE: WIDTH_READ_B NUMERIC "8" +// Retrieval info: PRIVATE: WIDTH_WRITE_A NUMERIC "8" +// Retrieval info: PRIVATE: WIDTH_WRITE_B NUMERIC "8" +// Retrieval info: PRIVATE: WRADDR_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: WRADDR_REG_B NUMERIC "1" +// Retrieval info: PRIVATE: WRCTRL_ACLR_B NUMERIC "0" +// Retrieval info: PRIVATE: enable NUMERIC "0" +// Retrieval info: PRIVATE: rden NUMERIC "0" +// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all +// Retrieval info: CONSTANT: ADDRESS_REG_B STRING "CLOCK0" +// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS" +// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_B STRING "BYPASS" +// Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_A STRING "BYPASS" +// Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_B STRING "BYPASS" +// Retrieval info: CONSTANT: INDATA_REG_B STRING "CLOCK0" +// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E" +// Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram" +// Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "512" +// Retrieval info: CONSTANT: NUMWORDS_B NUMERIC "512" +// Retrieval info: CONSTANT: OPERATION_MODE STRING "BIDIR_DUAL_PORT" +// Retrieval info: CONSTANT: OUTDATA_ACLR_A STRING "NONE" +// Retrieval info: CONSTANT: OUTDATA_ACLR_B STRING "NONE" +// Retrieval info: CONSTANT: OUTDATA_REG_A STRING "CLOCK0" +// Retrieval info: CONSTANT: OUTDATA_REG_B STRING "CLOCK0" +// Retrieval info: CONSTANT: POWER_UP_UNINITIALIZED STRING "FALSE" +// Retrieval info: CONSTANT: READ_DURING_WRITE_MODE_MIXED_PORTS STRING "DONT_CARE" +// Retrieval info: CONSTANT: READ_DURING_WRITE_MODE_PORT_A STRING "NEW_DATA_WITH_NBE_READ" +// Retrieval info: CONSTANT: READ_DURING_WRITE_MODE_PORT_B STRING "NEW_DATA_WITH_NBE_READ" +// Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "9" +// Retrieval info: CONSTANT: WIDTHAD_B NUMERIC "9" +// Retrieval info: CONSTANT: WIDTH_A NUMERIC "8" +// Retrieval info: CONSTANT: WIDTH_B NUMERIC "8" +// Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1" +// Retrieval info: CONSTANT: WIDTH_BYTEENA_B NUMERIC "1" +// Retrieval info: CONSTANT: WRCONTROL_WRADDRESS_REG_B STRING "CLOCK0" +// Retrieval info: USED_PORT: address_a 0 0 9 0 INPUT NODEFVAL "address_a[8..0]" +// Retrieval info: USED_PORT: address_b 0 0 9 0 INPUT NODEFVAL "address_b[8..0]" +// Retrieval info: USED_PORT: clock 0 0 0 0 INPUT VCC "clock" +// Retrieval info: USED_PORT: data_a 0 0 8 0 INPUT NODEFVAL "data_a[7..0]" +// Retrieval info: USED_PORT: data_b 0 0 8 0 INPUT NODEFVAL "data_b[7..0]" +// Retrieval info: USED_PORT: q_a 0 0 8 0 OUTPUT NODEFVAL "q_a[7..0]" +// Retrieval info: USED_PORT: q_b 0 0 8 0 OUTPUT NODEFVAL "q_b[7..0]" +// Retrieval info: USED_PORT: wren_a 0 0 0 0 INPUT GND "wren_a" +// Retrieval info: USED_PORT: wren_b 0 0 0 0 INPUT GND "wren_b" +// Retrieval info: CONNECT: @address_a 0 0 9 0 address_a 0 0 9 0 +// Retrieval info: CONNECT: @address_b 0 0 9 0 address_b 0 0 9 0 +// Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0 +// Retrieval info: CONNECT: @data_a 0 0 8 0 data_a 0 0 8 0 +// Retrieval info: CONNECT: @data_b 0 0 8 0 data_b 0 0 8 0 +// Retrieval info: CONNECT: @wren_a 0 0 0 0 wren_a 0 0 0 0 +// Retrieval info: CONNECT: @wren_b 0 0 0 0 wren_b 0 0 0 0 +// Retrieval info: CONNECT: q_a 0 0 8 0 @q_a 0 0 8 0 +// Retrieval info: CONNECT: q_b 0 0 8 0 @q_b 0 0 8 0 +// Retrieval info: GEN_FILE: TYPE_NORMAL snescmd_buf.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL snescmd_buf.inc FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL snescmd_buf.cmp FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL snescmd_buf.bsf FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL snescmd_buf_inst.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL snescmd_buf_bb.v TRUE +// Retrieval info: LIB_FILE: altera_mf diff --git a/verilog/sd2snes_spc7110/main.qsf b/verilog/sd2snes_spc7110/main.qsf new file mode 100644 index 000000000..d25ce1f38 --- /dev/null +++ b/verilog/sd2snes_spc7110/main.qsf @@ -0,0 +1,527 @@ +# -------------------------------------------------------------------------- # +# +# Copyright (C) 2018 Intel Corporation. All rights reserved. +# Your use of Intel 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 Intel Program License +# Subscription Agreement, the Intel Quartus Prime License Agreement, +# the Intel FPGA IP License Agreement, or other applicable license +# agreement, including, without limitation, that your use is for +# the sole purpose of programming logic devices manufactured by +# Intel and sold by Intel or its authorized distributors. Please +# refer to the applicable agreement for further details. +# +# -------------------------------------------------------------------------- # +# +# Quartus Prime +# Version 18.1.0 Build 625 09/12/2018 SJ Lite Edition +# Date created = 16:18:15 February 25, 2019 +# +# -------------------------------------------------------------------------- # +# +# Notes: +# +# 1) The default values for assignments are stored in the file: +# main_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 Prime software +# and any changes you make may be lost or overwritten. +# +# -------------------------------------------------------------------------- # + + +set_global_assignment -name FAMILY "Cyclone IV E" +set_global_assignment -name DEVICE EP4CE15F17C8 +set_global_assignment -name TOP_LEVEL_ENTITY main +set_global_assignment -name ORIGINAL_QUARTUS_VERSION 18.1.0 +set_global_assignment -name PROJECT_CREATION_TIME_DATE "16:18:15 FEBRUARY 25, 2019" +set_global_assignment -name LAST_QUARTUS_VERSION "21.1.1 Lite Edition" +set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files +set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 +set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 +set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1 +set_global_assignment -name NOMINAL_CORE_SUPPLY_VOLTAGE 1.2V +set_global_assignment -name EDA_SIMULATION_TOOL "Questa Intel FPGA (Verilog)" +set_global_assignment -name EDA_TIME_SCALE "1 ps" -section_id eda_simulation +set_global_assignment -name EDA_OUTPUT_DATA_FORMAT "VERILOG HDL" -section_id eda_simulation +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 IO_STANDARD "3.3-V LVTTL" -to SNES_IRQ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to CLKIN +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DAC_LRCK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DAC_MCLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DAC_SDOUT +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to MCU_RDY +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[18] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[17] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[16] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[15] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[14] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[13] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[12] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[11] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[10] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[9] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[8] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_ADDR[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_DATA[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_DATA[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_DATA[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_DATA[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_DATA[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_DATA[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_DATA[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_DATA[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_OE +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to RAM_WE +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[21] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[20] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[19] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[18] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[17] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[16] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[15] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[14] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[13] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[12] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[11] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[10] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[9] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[8] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ADDR[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_BHE +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_BLE +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[15] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[14] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[13] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[12] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[11] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[10] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[9] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[8] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_DATA[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_OE +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_WE +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_CLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_CMD +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[23] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[22] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[21] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[20] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[19] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[18] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[17] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[16] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[15] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[14] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[13] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[12] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[11] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[10] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[9] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[8] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ADDR_IN[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_CPU_CLK_IN +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATA[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATA[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATA[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATA[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATA[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATA[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATA[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATA[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATABUS_DIR +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_DATABUS_OE +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PARD_IN +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PAWR_IN +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PA_IN[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PA_IN[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PA_IN[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PA_IN[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PA_IN[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PA_IN[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PA_IN[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_PA_IN[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_READ_IN +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_REFRESH +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_ROMSEL_IN +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_SYSCLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_WRITE_IN +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SPI_MISO +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SPI_MOSI +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SPI_SCK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SPI_SS +set_location_assignment PIN_M2 -to CLKIN +set_location_assignment PIN_P2 -to SPI_SS +set_location_assignment PIN_M1 -to SPI_SCK +set_location_assignment PIN_N5 -to DAC_LRCK +set_location_assignment PIN_N3 -to DAC_MCLK +set_location_assignment PIN_P1 -to DAC_SDOUT +set_location_assignment PIN_H2 -to MCU_RDY +set_location_assignment PIN_N11 -to RAM_ADDR[18] +set_location_assignment PIN_R12 -to RAM_ADDR[17] +set_location_assignment PIN_N12 -to RAM_ADDR[16] +set_location_assignment PIN_T12 -to RAM_ADDR[15] +set_location_assignment PIN_T13 -to RAM_ADDR[14] +set_location_assignment PIN_R11 -to RAM_ADDR[13] +set_location_assignment PIN_R13 -to RAM_ADDR[12] +set_location_assignment PIN_R10 -to RAM_ADDR[11] +set_location_assignment PIN_N9 -to RAM_ADDR[10] +set_location_assignment PIN_M10 -to RAM_ADDR[9] +set_location_assignment PIN_T11 -to RAM_ADDR[8] +set_location_assignment PIN_T14 -to RAM_ADDR[7] +set_location_assignment PIN_R14 -to RAM_ADDR[6] +set_location_assignment PIN_P14 -to RAM_ADDR[5] +set_location_assignment PIN_T15 -to RAM_ADDR[4] +set_location_assignment PIN_R7 -to RAM_ADDR[3] +set_location_assignment PIN_M7 -to RAM_ADDR[2] +set_location_assignment PIN_L7 -to RAM_ADDR[1] +set_location_assignment PIN_T6 -to RAM_ADDR[0] +set_location_assignment PIN_P9 -to RAM_DATA[7] +set_location_assignment PIN_P8 -to RAM_DATA[6] +set_location_assignment PIN_N8 -to RAM_DATA[5] +set_location_assignment PIN_M8 -to RAM_DATA[4] +set_location_assignment PIN_L8 -to RAM_DATA[3] +set_location_assignment PIN_T7 -to RAM_DATA[2] +set_location_assignment PIN_P6 -to RAM_DATA[1] +set_location_assignment PIN_R6 -to RAM_DATA[0] +set_location_assignment PIN_T10 -to RAM_OE +set_location_assignment PIN_P11 -to RAM_WE +set_location_assignment PIN_G16 -to ROM_1CE +set_location_assignment PIN_J16 -to ROM_2CE +set_location_assignment PIN_B11 -to ROM_ADDR[21] +set_location_assignment PIN_C14 -to ROM_ADDR[20] +set_location_assignment PIN_D11 -to ROM_ADDR[19] +set_location_assignment PIN_C11 -to ROM_ADDR[18] +set_location_assignment PIN_P15 -to ROM_ADDR[17] +set_location_assignment PIN_C16 -to ROM_ADDR[16] +set_location_assignment PIN_D14 -to ROM_ADDR[15] +set_location_assignment PIN_A12 -to ROM_ADDR[14] +set_location_assignment PIN_B13 -to ROM_ADDR[13] +set_location_assignment PIN_D12 -to ROM_ADDR[12] +set_location_assignment PIN_A14 -to ROM_ADDR[11] +set_location_assignment PIN_A13 -to ROM_ADDR[10] +set_location_assignment PIN_B12 -to ROM_ADDR[9] +set_location_assignment PIN_E11 -to ROM_ADDR[8] +set_location_assignment PIN_F16 -to ROM_ADDR[7] +set_location_assignment PIN_J14 -to ROM_ADDR[6] +set_location_assignment PIN_N16 -to ROM_ADDR[5] +set_location_assignment PIN_K16 -to ROM_ADDR[4] +set_location_assignment PIN_L16 -to ROM_ADDR[3] +set_location_assignment PIN_J15 -to ROM_ADDR[2] +set_location_assignment PIN_J13 -to ROM_ADDR[1] +set_location_assignment PIN_K15 -to ROM_ADDR[0] +set_location_assignment PIN_L14 -to ROM_BHE +set_location_assignment PIN_L13 -to ROM_BLE +set_location_assignment PIN_A11 -to ROM_DATA[15] +set_location_assignment PIN_B10 -to ROM_DATA[14] +set_location_assignment PIN_E10 -to ROM_DATA[13] +set_location_assignment PIN_A10 -to ROM_DATA[12] +set_location_assignment PIN_R16 -to ROM_DATA[11] +set_location_assignment PIN_N14 -to ROM_DATA[10] +set_location_assignment PIN_P16 -to ROM_DATA[9] +set_location_assignment PIN_N15 -to ROM_DATA[8] +set_location_assignment PIN_A15 -to ROM_DATA[7] +set_location_assignment PIN_C15 -to ROM_DATA[6] +set_location_assignment PIN_B16 -to ROM_DATA[5] +set_location_assignment PIN_D15 -to ROM_DATA[4] +set_location_assignment PIN_D16 -to ROM_DATA[3] +set_location_assignment PIN_F13 -to ROM_DATA[2] +set_location_assignment PIN_F14 -to ROM_DATA[1] +set_location_assignment PIN_F15 -to ROM_DATA[0] +set_location_assignment PIN_L15 -to ROM_OE +set_location_assignment PIN_B14 -to ROM_WE +set_location_assignment PIN_T4 -to SD_CLK +set_location_assignment PIN_R5 -to SD_CMD +set_location_assignment PIN_T2 -to SD_DAT[3] +set_location_assignment PIN_R3 -to SD_DAT[2] +set_location_assignment PIN_T3 -to SD_DAT[1] +set_location_assignment PIN_R4 -to SD_DAT[0] +set_location_assignment PIN_J1 -to SNES_DATA[7] +set_location_assignment PIN_G2 -to SNES_DATA[6] +set_location_assignment PIN_F1 -to SNES_DATA[5] +set_location_assignment PIN_F3 -to SNES_DATA[4] +set_location_assignment PIN_J2 -to SNES_DATA[3] +set_location_assignment PIN_G1 -to SNES_DATA[2] +set_location_assignment PIN_G5 -to SNES_DATA[1] +set_location_assignment PIN_F2 -to SNES_DATA[0] +set_location_assignment PIN_D2 -to SNES_DATABUS_DIR +set_location_assignment PIN_D1 -to SNES_DATABUS_OE +set_location_assignment PIN_B1 -to SNES_IRQ +set_location_assignment PIN_F9 -to SNES_REFRESH +set_location_assignment PIN_A9 -to SNES_SYSCLK +set_location_assignment PIN_P3 -to SPI_MISO +set_location_assignment PIN_R1 -to SPI_MOSI +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to CLKIN +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to DAC_LRCK +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to DAC_MCLK +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to DAC_SDOUT +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to MCU_RDY +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[18] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[17] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[16] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[15] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[14] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[13] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[12] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[11] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[10] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[9] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[8] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[7] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[6] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[5] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[4] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[3] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[2] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[1] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_ADDR[0] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_DATA[7] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_DATA[6] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_DATA[5] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_DATA[4] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_DATA[3] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_DATA[2] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_DATA[1] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_DATA[0] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_OE +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to RAM_WE +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_1CE +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_2CE +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[21] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[20] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[19] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[18] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[17] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[16] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[15] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[14] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[13] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[12] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[11] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[10] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[9] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[8] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[7] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[6] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[5] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[4] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[3] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[2] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[1] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ADDR[0] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_BHE +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_BLE +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[15] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[14] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[13] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[12] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[11] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[10] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[9] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[8] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[7] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[6] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[5] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[4] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[3] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[2] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[1] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_DATA[0] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_OE +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_WE +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SD_CLK +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SD_CMD +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SD_DAT[3] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SD_DAT[2] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SD_DAT[1] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SD_DAT[0] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATA[7] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATA[6] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATA[5] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATA[4] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATA[3] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATA[2] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATA[1] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATA[0] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATABUS_DIR +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_DATABUS_OE +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_IRQ +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_REFRESH +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_SYSCLK +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SPI_MISO +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SPI_MOSI +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SPI_SCK +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SPI_SS +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_1CE +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_2CE +set_location_assignment PIN_A2 -to SNES_ADDR_IN[23] +set_location_assignment PIN_C3 -to SNES_ADDR_IN[22] +set_location_assignment PIN_A3 -to SNES_ADDR_IN[21] +set_location_assignment PIN_A4 -to SNES_ADDR_IN[20] +set_location_assignment PIN_B5 -to SNES_ADDR_IN[19] +set_location_assignment PIN_E6 -to SNES_ADDR_IN[18] +set_location_assignment PIN_C6 -to SNES_ADDR_IN[17] +set_location_assignment PIN_A6 -to SNES_ADDR_IN[16] +set_location_assignment PIN_B7 -to SNES_ADDR_IN[15] +set_location_assignment PIN_F8 -to SNES_ADDR_IN[14] +set_location_assignment PIN_D8 -to SNES_ADDR_IN[13] +set_location_assignment PIN_B8 -to SNES_ADDR_IN[12] +set_location_assignment PIN_A8 -to SNES_ADDR_IN[11] +set_location_assignment PIN_C8 -to SNES_ADDR_IN[10] +set_location_assignment PIN_E8 -to SNES_ADDR_IN[9] +set_location_assignment PIN_A7 -to SNES_ADDR_IN[8] +set_location_assignment PIN_E7 -to SNES_ADDR_IN[7] +set_location_assignment PIN_B6 -to SNES_ADDR_IN[6] +set_location_assignment PIN_D6 -to SNES_ADDR_IN[5] +set_location_assignment PIN_A5 -to SNES_ADDR_IN[4] +set_location_assignment PIN_D5 -to SNES_ADDR_IN[3] +set_location_assignment PIN_B4 -to SNES_ADDR_IN[2] +set_location_assignment PIN_B3 -to SNES_ADDR_IN[1] +set_location_assignment PIN_D3 -to SNES_ADDR_IN[0] +set_location_assignment PIN_K1 -to SNES_CPU_CLK_IN +set_location_assignment PIN_C2 -to SNES_ROMSEL_IN +set_location_assignment PIN_E9 -to SNES_PA_IN[7] +set_location_assignment PIN_D9 -to SNES_PA_IN[6] +set_location_assignment PIN_L4 -to SNES_PA_IN[5] +set_location_assignment PIN_N1 -to SNES_PA_IN[4] +set_location_assignment PIN_L1 -to SNES_PA_IN[3] +set_location_assignment PIN_L3 -to SNES_PA_IN[2] +set_location_assignment PIN_K5 -to SNES_PA_IN[1] +set_location_assignment PIN_L2 -to SNES_PA_IN[0] +set_location_assignment PIN_B9 -to SNES_PARD_IN +set_location_assignment PIN_C9 -to SNES_PAWR_IN +set_location_assignment PIN_C1 -to SNES_READ_IN +set_location_assignment PIN_K2 -to SNES_WRITE_IN +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[23] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[22] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[21] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[20] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[19] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[18] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[17] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[16] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[15] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[14] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[13] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[12] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[11] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[10] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[9] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[8] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[7] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[6] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[5] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[4] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[3] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[2] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[1] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ADDR_IN[0] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_CPU_CLK_IN +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_ROMSEL_IN +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PA_IN[7] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PA_IN[6] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PA_IN[5] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PA_IN[4] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PA_IN[3] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PA_IN[2] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PA_IN[1] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PA_IN[0] +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PARD_IN +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_PAWR_IN +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_READ_IN +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_WRITE_IN +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ROM_ZZ +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to ROM_ZZ +set_location_assignment PIN_G15 -to ROM_ZZ +set_location_assignment PIN_M6 -to PM6_out +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to PM6_out +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to PM6_out +set_location_assignment PIN_N6 -to PN6_out +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to PN6_out +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to PN6_out +set_location_assignment PIN_T5 -to PT5_in +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to PT5_in +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to PT5_in +set_location_assignment PIN_N2 -to SNES_CIC_CLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SNES_CIC_CLK +set_instance_assignment -name CURRENT_STRENGTH_NEW 4MA -to SNES_CIC_CLK +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)" +set_global_assignment -name ENABLE_OCT_DONE OFF +set_global_assignment -name CYCLONEIII_CONFIGURATION_SCHEME "PASSIVE SERIAL" +set_global_assignment -name USE_CONFIGURATION_DEVICE OFF +set_global_assignment -name GENERATE_RBF_FILE ON +set_global_assignment -name CRC_ERROR_OPEN_DRAIN OFF +set_global_assignment -name CONFIGURATION_VCCIO_LEVEL 3.3V +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 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 +set_global_assignment -name VERILOG_MACRO "MK3=" + +set_global_assignment -name VERILOG_FILE spi.v +set_global_assignment -name VERILOG_FILE sd_dma.v +set_global_assignment -name VERILOG_FILE msu.v +set_global_assignment -name VERILOG_FILE mcu_cmd.v +set_global_assignment -name VERILOG_FILE main.v +set_global_assignment -name VERILOG_FILE dac.v +set_global_assignment -name VERILOG_FILE clk_test.v +set_global_assignment -name VERILOG_FILE cheat.v +set_global_assignment -name VERILOG_FILE address.v +set_global_assignment -name VERILOG_FILE rtc.v +set_global_assignment -name VERILOG_FILE spc7110_regs.v +set_global_assignment -name VERILOG_FILE spc7110_alu.v +set_global_assignment -name VERILOG_FILE spc7110_data.v +set_global_assignment -name VERILOG_FILE spc7110_dcu.v +set_global_assignment -name VERILOG_FILE spc7110_map.v +set_global_assignment -name VERILOG_FILE spc7110_rtcif.v +set_global_assignment -name SDC_FILE main.sdc +set_global_assignment -name QIP_FILE ip/mk3/pll.qip +set_global_assignment -name QIP_FILE ip/mk3/dac_buf.qip +set_global_assignment -name QIP_FILE ip/mk3/msu_databuf.qip +set_global_assignment -name QIP_FILE ip/mk3/snescmd_buf.qip +set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/verilog/sd2snes_spc7110/main.sdc b/verilog/sd2snes_spc7110/main.sdc new file mode 100644 index 000000000..e31c21e18 --- /dev/null +++ b/verilog/sd2snes_spc7110/main.sdc @@ -0,0 +1,179 @@ +## Generated SDC file "main.sdc" + +## Copyright (C) 2018 Intel Corporation. All rights reserved. +## Your use of Intel 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 Intel Program License +## Subscription Agreement, the Intel Quartus Prime License Agreement, +## the Intel FPGA IP License Agreement, or other applicable license +## agreement, including, without limitation, that your use is for +## the sole purpose of programming logic devices manufactured by +## Intel and sold by Intel or its authorized distributors. Please +## refer to the applicable agreement for further details. + + +## VENDOR "Altera" +## PROGRAM "Quartus Prime" +## VERSION "Version 18.0.0 Build 614 04/24/2018 SJ Lite Edition" + +## DATE "Fri Jul 27 00:34:51 2018" + +## +## DEVICE "EP4CE15F17C8" +## + + +#************************************************************** +# Time Information +#************************************************************** + +set_time_format -unit ns -decimal_places 3 + + + +#************************************************************** +# Create Clock +#************************************************************** + +create_clock -name {CLKIN} -period 125 -waveform { 0.000 62.5 } [get_ports {CLKIN}] +create_clock -name {SPI_SCK} -period 20.833 -waveform { 0.000 10.417 } [get_ports { SPI_SCK }] + + +#************************************************************** +# Create Generated Clock +#************************************************************** + +create_generated_clock -name {snes_pll|altpll_component|auto_generated|pll1|clk[0]} -source [get_pins {snes_pll|altpll_component|auto_generated|pll1|inclk[0]}] -duty_cycle 50/1 -multiply_by 12 -master_clock {CLKIN} [get_pins {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] + + +#************************************************************** +# Set Clock Latency +#************************************************************** + + + +#************************************************************** +# Set Clock Uncertainty +#************************************************************** + +set_clock_uncertainty -rise_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] 0.020 +set_clock_uncertainty -rise_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] 0.020 +set_clock_uncertainty -rise_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -rise_to [get_clocks {CLKIN}] -setup 0.100 +set_clock_uncertainty -rise_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -rise_to [get_clocks {CLKIN}] -hold 0.070 +set_clock_uncertainty -rise_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -fall_to [get_clocks {CLKIN}] -setup 0.100 +set_clock_uncertainty -rise_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -fall_to [get_clocks {CLKIN}] -hold 0.070 +set_clock_uncertainty -fall_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] 0.020 +set_clock_uncertainty -fall_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] 0.020 +set_clock_uncertainty -fall_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -rise_to [get_clocks {CLKIN}] -setup 0.100 +set_clock_uncertainty -fall_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -rise_to [get_clocks {CLKIN}] -hold 0.070 +set_clock_uncertainty -fall_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -fall_to [get_clocks {CLKIN}] -setup 0.100 +set_clock_uncertainty -fall_from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -fall_to [get_clocks {CLKIN}] -hold 0.070 +set_clock_uncertainty -rise_from [get_clocks {SPI_SCK}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -setup 0.080 +set_clock_uncertainty -rise_from [get_clocks {SPI_SCK}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -hold 0.110 +set_clock_uncertainty -rise_from [get_clocks {SPI_SCK}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -setup 0.080 +set_clock_uncertainty -rise_from [get_clocks {SPI_SCK}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -hold 0.110 +set_clock_uncertainty -rise_from [get_clocks {SPI_SCK}] -rise_to [get_clocks {SPI_SCK}] 0.020 +set_clock_uncertainty -rise_from [get_clocks {SPI_SCK}] -fall_to [get_clocks {SPI_SCK}] 0.020 +set_clock_uncertainty -fall_from [get_clocks {SPI_SCK}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -setup 0.080 +set_clock_uncertainty -fall_from [get_clocks {SPI_SCK}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -hold 0.110 +set_clock_uncertainty -fall_from [get_clocks {SPI_SCK}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -setup 0.080 +set_clock_uncertainty -fall_from [get_clocks {SPI_SCK}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -hold 0.110 +set_clock_uncertainty -fall_from [get_clocks {SPI_SCK}] -rise_to [get_clocks {SPI_SCK}] 0.020 +set_clock_uncertainty -fall_from [get_clocks {SPI_SCK}] -fall_to [get_clocks {SPI_SCK}] 0.020 +set_clock_uncertainty -rise_from [get_clocks {CLKIN}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -setup 0.070 +set_clock_uncertainty -rise_from [get_clocks {CLKIN}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -hold 0.100 +set_clock_uncertainty -rise_from [get_clocks {CLKIN}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -setup 0.070 +set_clock_uncertainty -rise_from [get_clocks {CLKIN}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -hold 0.100 +set_clock_uncertainty -rise_from [get_clocks {CLKIN}] -rise_to [get_clocks {CLKIN}] 0.020 +set_clock_uncertainty -rise_from [get_clocks {CLKIN}] -fall_to [get_clocks {CLKIN}] 0.020 +set_clock_uncertainty -fall_from [get_clocks {CLKIN}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -setup 0.070 +set_clock_uncertainty -fall_from [get_clocks {CLKIN}] -rise_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -hold 0.100 +set_clock_uncertainty -fall_from [get_clocks {CLKIN}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -setup 0.070 +set_clock_uncertainty -fall_from [get_clocks {CLKIN}] -fall_to [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -hold 0.100 +set_clock_uncertainty -fall_from [get_clocks {CLKIN}] -rise_to [get_clocks {CLKIN}] 0.020 +set_clock_uncertainty -fall_from [get_clocks {CLKIN}] -fall_to [get_clocks {CLKIN}] 0.020 + + +#************************************************************** +# Set Input Delay +#************************************************************** + + + +#************************************************************** +# Set Output Delay +#************************************************************** + + + +#************************************************************** +# Set Clock Groups +#************************************************************** + + + +#************************************************************** +# Set False Path +#************************************************************** + +# RTC clock-domain crossing, WRITE direction (same constraint the base core +# carries). rtc:snes_rtc runs on CLKIN (8 MHz); its counter register rtc_data_r +# is loaded across the domain boundary from the PLL clk[0] domain (96 MHz) by +# both writers -- the MCU (SPI command $e5, via mcu_cmd) and the cartridge +# ($4840-$4842, via spc7110_rtcif). That transfer is quasi-static: the value +# only changes when the clock is actually set. Without the cut, the fitter +# reports a hold "violation" on the crossing that is not physically meaningful +# and whose closure depends on placement (the base core used to hand-pick a +# fitter seed for it, and any logic change reopened it). Intra-CLKIN paths into +# rtc_data_r stay constrained. +set_false_path -from [get_clocks {snes_pll|altpll_component|auto_generated|pll1|clk[0]}] -to [get_registers {rtc:snes_rtc|rtc_data_r[*]}] + + + +#************************************************************** +# Set Multicycle Path +#************************************************************** + + + +#************************************************************** +# Set Maximum Delay +#************************************************************** + +# RTC clock-domain crossing, READ direction. rtc.v publishes rtc_data_out_r in +# the CLKIN domain once per second; spc7110_rtcif captures all 60 bits at once +# on the SNES chip-select edge (snap) and keeps a copy to detect that a new +# second was published (rtc_prev), both in the CLK2 domain. The two clocks are +# related (CLK2 is CLKIN x12 out of the PLL), so unlike the write direction this +# path IS analysed by default and the fitter closes it; it is named here so it +# is visible as what it is -- a multi-bit crossing of a quasi-static value -- +# and bounded to one CLK2 period, which is the requirement the default analysis +# already derives from the edge relationship. Note that a two-flop synchroniser +# would NOT be an improvement here: on a 60 bit bus it fixes metastability per +# bit but not the tear, so if the capture ever has to be made exact the right +# answer is a "latch done" strobe out of rtc.v, not more flops. +set_max_delay -from [get_registers {rtc:snes_rtc|rtc_data_out_r[*]}] -to [get_registers {*spc7110_rtcif*|snap[*]}] 10.417 +set_max_delay -from [get_registers {rtc:snes_rtc|rtc_data_out_r[*]}] -to [get_registers {*spc7110_rtcif*|rtc_prev[*]}] 10.417 +# The virtual-battery registers sample the same quasi-static bus under the same +# session/snapshot discipline (they are further destinations of the crossing +# constrained above, added later); same rationale, same bound. +set_max_delay -from [get_registers {rtc:snes_rtc|rtc_data_out_r[*]}] -to [get_registers {*spc7110_rtcif*|date_ref[*]}] 10.417 +set_max_delay -from [get_registers {rtc:snes_rtc|rtc_data_out_r[*]}] -to [get_registers {*spc7110_rtcif*|dow_r[*]}] 10.417 +set_max_delay -from [get_registers {rtc:snes_rtc|rtc_data_out_r[*]}] -to [get_registers {*spc7110_rtcif*|dow_ss[*]}] 10.417 +set_max_delay -from [get_registers {rtc:snes_rtc|rtc_data_out_r[*]}] -to [get_registers {*spc7110_rtcif*|frz[*]}] 10.417 + + + +#************************************************************** +# Set Minimum Delay +#************************************************************** + + + +#************************************************************** +# Set Input Transition +#************************************************************** + diff --git a/verilog/sd2snes_spc7110/main.ucf b/verilog/sd2snes_spc7110/main.ucf new file mode 100644 index 000000000..945bd5e86 --- /dev/null +++ b/verilog/sd2snes_spc7110/main.ucf @@ -0,0 +1,699 @@ +NET "CLKIN" TNM_NET = "CLKIN"; +TIMESPEC TS_CLKIN = PERIOD "CLKIN" 24.01 MHz HIGH 50 %; +# RTC clock-domain crossings (mirror of the mk3 .sdc cuts). The only logic in +# the CLKIN domain is the rtc time-keeping core; both directions are safe by +# protocol, not by timing: the write side holds its data around a strobe kept +# up for 64 CLK2 cycles (>= 16 CLKIN periods at 24 MHz) that CLKIN-side +# synchronizer chains edge-detect (2 FFs for pgm_we, 3 for we1 -- data-hold +# handshake), and the read side snapshots a quasi-static 60-bit bus that +# changes once per second. Without these cuts PAR reports hold violations on +# paths that the handshake already guards. +# Putting CLK2 in a user TNM_NET suppresses the automatic DCM-derived PERIOD, +# so CLK2_GRP must carry its own (same TS_CLKIN * 4 the tools would derive) -- +# without it the whole 96 MHz domain goes unanalyzed. +NET "CLK2" TNM_NET = "CLK2_GRP"; +TIMESPEC TS_CLK2 = PERIOD "CLK2_GRP" TS_CLKIN * 4 HIGH 50 %; +# GUARD BAND on the 96 MHz domain -- do not drop this line. +# Spartan-3 is one of the architectures whose timing engine computes NO clock +# uncertainty at all: trce says so itself (INFO Timing:3389/3390, "does not +# support Discrete Jitter and Phase Error ... add SYSTEM_JITTER") and signs +# every path off with "Clock Uncertainty: 0.000ns". CLK2 is the CLKFX output of +# a DCM (24.01 MHz x 4), and the Architecture Wizard measured the period jitter +# of THIS DCM in THIS device and stamped it in the header of DCM_Scope.v: +# 0.07 UI = 0.75 ns peak to peak, i.e. up to 0.375 ns of one clock period. None +# of that reaches the default analysis, so a fit that reports "met" can still be +# a third of a nanosecond short on real silicon -- and place-and-route stops +# improving the moment it reaches the (too loose) requirement. +# +# trce turns SYSTEM_JITTER into a per-path uncertainty of jitter/sqrt(2) (source +# and destination jitter added in quadrature), so the 0.48 ns below buys a +# 0.339 ns guard band on every path of the domain: 90% of the 0.375 ns the +# datasheet asks for, and enough to keep the tools working the whole way down. +# The value is the LARGEST this design closes on: at 0.53 (full jitter) and +# above the constraint becomes unreachable, place-and-route gives up early and +# the PHYSICAL result gets dramatically worse -- at 0.75 the worst case falls +# from -0.05 ns to -1.6 ns. The PERIOD above is untouched, so TS_CLK2 still +# reports against the real 10.412 ns requirement; Timing Score 0 here therefore +# means 10.412 - (data path) >= 0.36 ns, not zero. +SYSTEM_JITTER = 0.48 ns; +# PLACER COST TABLE, and why it is pinned. +# This core fills over 90% of the device and its worst paths are the +# decompressor datapath, so closure sits within a nanosecond of the requirement +# and the placement MAP produces decides which side of it the fit lands on. +# Swept over cost tables 1-23 with the current netlist: only 1 and 15 reach +# Timing Score 0 (1 closes at +0.020 ns worst slack, 15 at +0.015, guard band +# below already discounted); the next best is 4 at score 1266 and most of the +# rest run into the thousands: +# t1=0 t2=17860 t3=13073 t4=1266 t5=11369 t6=6983 t7=21682 t8=6692 +# t9=7719 t10=16642 t11=6946 t12=4311 t13=7967 t14=3821 t15=0 t16=31890 +# t17=27838 t18=11569 t19=30913 t20=23230 t21=17365 t22=5738 t23=15009 +# 1 is pinned -- it is also what sd2snes_sdd1 uses -- and it lives in the .xise +# ("Starting Placer Cost Table", Map AND Par). It is a choice of seed, not a +# relaxation: every constraint in this file is met. +# The .xise also sets Synthesis "Optimization Goal = Area" (sd2snes_sdd1 uses +# Speed). Both fit and both close: measured on this netlist, Speed gives +# 3299/3584 slices and Timing Score 0 at +0.003 ns, Area 3322/3584 and Score 0 +# at +0.020 ns. Area is kept because it is the seed the sweep above was run +# under -- changing the goal re-rolls the placement and invalidates that table, +# and it gives 6.7x the slack on a domain that is only signed with the guard +# band added by hand. Do not change it without re-sweeping. +# CONSEQUENCE: ANY edit to the netlist re-rolls this. A change that looks +# harmless can come back with a score in the thousands, and the honest answer +# is to sweep the cost table again, not to reach for IGNORE_TIMING. +TIMESPEC TS_RTC_W_CDC = FROM "CLK2_GRP" TO "CLKIN" TIG; +TIMESPEC TS_RTC_R_CDC = FROM "CLKIN" TO "CLK2_GRP" TIG; + +NET "p113_out" IOSTANDARD = LVCMOS33; +NET "p113_out" LOC = P113; + +NET "SPI_SCK" LOC = P71; +NET "SPI_SCK" CLOCK_DEDICATED_ROUTE = FALSE; +NET "SPI_SCK" TNM_NET = "SPI_SCK"; +TIMESPEC TS_SPI_SCK = PERIOD "SPI_SCK" 48.1MHz HIGH 50 %; + +NET "SPI_SCK" IOSTANDARD = LVCMOS33; +NET "SPI_SCK" DRIVE = 8; +NET "SPI_SCK" PULLUP; + +NET "SNES_ROMSEL_IN" IOSTANDARD = LVCMOS33; +NET "SNES_READ_IN" IOSTANDARD = LVCMOS33; +NET "SNES_WRITE_IN" IOSTANDARD = LVCMOS33; +NET "SNES_CPU_CLK_IN" IOSTANDARD = LVCMOS33; +NET "SNES_REFRESH" IOSTANDARD = LVCMOS33; + +NET "CLKIN" IOSTANDARD = LVCMOS33; +//NET "CLKIN" PULLUP; +NET "SPI_SS" PULLUP; +//NET "DCM_RST" LOC = P46; +//NET "DCM_RST" IOSTANDARD = LVCMOS33; +NET "SNES_DATABUS_DIR" IOSTANDARD = LVCMOS33; +NET "SNES_DATABUS_OE" IOSTANDARD = LVCMOS33; +NET "SNES_IRQ" IOSTANDARD = LVCMOS33; + +NET "ROM_CE" LOC = P172; +NET "ROM_CE" IOSTANDARD = LVCMOS33; +NET "ROM_CE" DRIVE = 8; + +NET "SNES_ADDR_IN[0]" LOC = P119; +NET "SNES_ADDR_IN[10]" LOC = P146; +NET "SNES_ADDR_IN[11]" LOC = P148; +NET "SNES_ADDR_IN[12]" LOC = P147; +NET "SNES_ADDR_IN[13]" LOC = P144; +NET "SNES_ADDR_IN[14]" LOC = P141; +NET "SNES_ADDR_IN[15]" LOC = P139; +NET "SNES_ADDR_IN[16]" LOC = P137; +NET "SNES_ADDR_IN[17]" LOC = P133; +NET "SNES_ADDR_IN[18]" LOC = P131; +NET "SNES_ADDR_IN[19]" LOC = P128; +NET "SNES_ADDR_IN[1]" LOC = P122; +NET "SNES_ADDR_IN[20]" LOC = P125; +NET "SNES_ADDR_IN[21]" LOC = P123; +NET "SNES_ADDR_IN[22]" LOC = P120; +NET "SNES_ADDR_IN[23]" LOC = P117; +NET "SNES_ADDR_IN[2]" LOC = P124; +NET "SNES_ADDR_IN[3]" LOC = P126; +NET "SNES_ADDR_IN[4]" LOC = P130; +NET "SNES_ADDR_IN[5]" LOC = P132; +NET "SNES_ADDR_IN[6]" LOC = P135; +NET "SNES_ADDR_IN[7]" LOC = P138; +NET "SNES_ADDR_IN[8]" LOC = P140; +NET "SNES_ADDR_IN[9]" LOC = P143; +NET "SNES_DATA[0]" LOC = P107; +NET "SNES_DATA[1]" LOC = P102; +NET "SNES_DATA[2]" LOC = P100; +NET "SNES_DATA[3]" LOC = P96; +NET "SNES_DATA[4]" LOC = P108; +NET "SNES_DATA[5]" LOC = P106; +NET "SNES_DATA[6]" LOC = P101; +NET "SNES_DATA[7]" LOC = P97; + + +NET "CLKIN" LOC = P80; +// NET "RST" LOC = P113; +NET "MCU_OVR" LOC = P92; + + +NET "MCU_OVR" IOSTANDARD = LVCMOS33; +NET "MCU_OVR" DRIVE = 8; + +NET "MCU_RDY" LOC = P83; +NET "MCU_RDY" IOSTANDARD = LVCMOS33; +NET "MCU_RDY" DRIVE = 8; + + +NET "ROM_ADDR[0]" LOC = P166; + + +NET "ROM_ADDR[0]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[0]" DRIVE = 8; + + +NET "ROM_ADDR[10]" LOC = P197; + + +NET "ROM_ADDR[10]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[10]" DRIVE = 8; + + +NET "ROM_ADDR[11]" LOC = P196; + + +NET "ROM_ADDR[11]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[11]" DRIVE = 8; + + +NET "ROM_ADDR[12]" LOC = P2; + + +NET "ROM_ADDR[12]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[12]" DRIVE = 8; + + +NET "ROM_ADDR[13]" LOC = P194; + + +NET "ROM_ADDR[13]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[13]" DRIVE = 8; + + +NET "ROM_ADDR[14]" LOC = P200; + + +NET "ROM_ADDR[14]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[14]" DRIVE = 8; + + +NET "ROM_ADDR[15]" LOC = P184; + + +NET "ROM_ADDR[15]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[15]" DRIVE = 8; + + +NET "ROM_ADDR[16]" LOC = P199; + + +NET "ROM_ADDR[16]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[16]" DRIVE = 8; + + +NET "ROM_ADDR[17]" LOC = P11; + + +NET "ROM_ADDR[17]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[17]" DRIVE = 8; + + +NET "ROM_ADDR[18]" LOC = P3; + + +NET "ROM_ADDR[18]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[18]" DRIVE = 8; + + +NET "ROM_ADDR[19]" LOC = P4; + + +NET "ROM_ADDR[19]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[19]" DRIVE = 8; + + +NET "ROM_ADDR[1]" LOC = P168; + + +NET "ROM_ADDR[1]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[1]" DRIVE = 8; + + +NET "ROM_ADDR[20]" LOC = P191; + + +NET "ROM_ADDR[20]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[20]" DRIVE = 8; + + +NET "ROM_ADDR[21]" LOC = P203; + + +NET "ROM_ADDR[21]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[21]" DRIVE = 8; + + +NET "ROM_ADDR[22]" LOC = P198; + + +NET "ROM_ADDR[22]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[22]" DRIVE = 8; + + +NET "ROM_ADDR[2]" LOC = P171; + + +NET "ROM_ADDR[2]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[2]" DRIVE = 8; + + +NET "ROM_ADDR[3]" LOC = P165; + + +NET "ROM_ADDR[3]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[3]" DRIVE = 8; + + +NET "ROM_ADDR[4]" LOC = P169; + + +NET "ROM_ADDR[4]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[4]" DRIVE = 8; + + +NET "ROM_ADDR[5]" LOC = P18; + + +NET "ROM_ADDR[5]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[5]" DRIVE = 8; + + +NET "ROM_ADDR[6]" LOC = P175; + + +NET "ROM_ADDR[6]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[6]" DRIVE = 8; + + +NET "ROM_ADDR[7]" LOC = P167; + + +NET "ROM_ADDR[7]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[7]" DRIVE = 8; + + +NET "ROM_ADDR[8]" LOC = P205; + + +NET "ROM_ADDR[8]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[8]" DRIVE = 8; + + +NET "ROM_ADDR[9]" LOC = P204; + + +NET "ROM_ADDR[9]" IOSTANDARD = LVCMOS33; +NET "ROM_ADDR[9]" DRIVE = 8; + + +NET "ROM_BHE" LOC = P161; + + +NET "ROM_BHE" IOSTANDARD = LVCMOS33; +NET "ROM_BHE" DRIVE = 8; + + +NET "ROM_BLE" LOC = P156; + + +NET "ROM_BLE" IOSTANDARD = LVCMOS33; +NET "ROM_BLE" DRIVE = 8; + + +NET "ROM_DATA[0]" LOC = P176; + + +NET "ROM_DATA[0]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[0]" DRIVE = 8; + + +NET "ROM_DATA[10]" LOC = P15; + + +NET "ROM_DATA[10]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[10]" DRIVE = 8; + + +NET "ROM_DATA[11]" LOC = P12; + + +NET "ROM_DATA[11]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[11]" DRIVE = 8; + + +NET "ROM_DATA[12]" LOC = P10; + + +NET "ROM_DATA[12]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[12]" DRIVE = 8; + + +NET "ROM_DATA[13]" LOC = P7; + + +NET "ROM_DATA[13]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[13]" DRIVE = 8; + + +NET "ROM_DATA[14]" LOC = P9; + + +NET "ROM_DATA[14]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[14]" DRIVE = 8; + + +NET "ROM_DATA[15]" LOC = P5; + + +NET "ROM_DATA[15]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[15]" DRIVE = 8; + + +NET "ROM_DATA[1]" LOC = P178; + + +NET "ROM_DATA[1]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[1]" DRIVE = 8; + + +NET "ROM_DATA[2]" LOC = P181; + + +NET "ROM_DATA[2]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[2]" DRIVE = 8; + + +NET "ROM_DATA[3]" LOC = P182; + + +NET "ROM_DATA[3]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[3]" DRIVE = 8; + + +NET "ROM_DATA[4]" LOC = P183; + + +NET "ROM_DATA[4]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[4]" DRIVE = 8; + + +NET "ROM_DATA[5]" LOC = P187; + + +NET "ROM_DATA[5]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[5]" DRIVE = 8; + + +NET "ROM_DATA[6]" LOC = P185; + + +NET "ROM_DATA[6]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[6]" DRIVE = 8; + + +NET "ROM_DATA[7]" LOC = P189; + + +NET "ROM_DATA[7]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[7]" DRIVE = 8; + + +NET "ROM_DATA[8]" LOC = P16; + + +NET "ROM_DATA[8]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[8]" DRIVE = 8; + + +NET "ROM_DATA[9]" LOC = P13; + + +NET "ROM_DATA[9]" IOSTANDARD = LVCMOS33; +NET "ROM_DATA[9]" DRIVE = 8; + + +NET "ROM_OE" LOC = P162; + + +NET "ROM_OE" IOSTANDARD = LVCMOS33; +NET "ROM_OE" DRIVE = 8; + + +NET "ROM_WE" LOC = P190; + + +NET "ROM_WE" IOSTANDARD = LVCMOS33; +NET "ROM_WE" DRIVE = 8; +NET "SNES_ADDR_IN[0]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[0]" DRIVE = 8; +NET "SNES_ADDR_IN[10]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[10]" DRIVE = 8; +NET "SNES_ADDR_IN[11]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[11]" DRIVE = 8; +NET "SNES_ADDR_IN[12]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[12]" DRIVE = 8; +NET "SNES_ADDR_IN[13]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[13]" DRIVE = 8; +NET "SNES_ADDR_IN[14]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[14]" DRIVE = 8; +NET "SNES_ADDR_IN[15]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[15]" DRIVE = 8; +NET "SNES_ADDR_IN[16]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[16]" DRIVE = 8; +NET "SNES_ADDR_IN[17]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[17]" DRIVE = 8; +NET "SNES_ADDR_IN[18]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[18]" DRIVE = 8; +NET "SNES_ADDR_IN[19]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[19]" DRIVE = 8; +NET "SNES_ADDR_IN[1]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[1]" DRIVE = 8; +NET "SNES_ADDR_IN[20]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[20]" DRIVE = 8; +NET "SNES_ADDR_IN[21]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[21]" DRIVE = 8; +NET "SNES_ADDR_IN[22]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[22]" DRIVE = 8; +NET "SNES_ADDR_IN[23]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[23]" DRIVE = 8; +NET "SNES_ADDR_IN[2]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[2]" DRIVE = 8; +NET "SNES_ADDR_IN[3]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[3]" DRIVE = 8; +NET "SNES_ADDR_IN[4]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[4]" DRIVE = 8; +NET "SNES_ADDR_IN[5]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[5]" DRIVE = 8; +NET "SNES_ADDR_IN[6]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[6]" DRIVE = 8; +NET "SNES_ADDR_IN[7]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[7]" DRIVE = 8; +NET "SNES_ADDR_IN[8]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[8]" DRIVE = 8; +NET "SNES_ADDR_IN[9]" IOSTANDARD = LVCMOS33; +NET "SNES_ADDR_IN[9]" DRIVE = 8; + + +NET "SNES_CPU_CLK_IN" LOC = P95; +NET "SNES_ROMSEL_IN" LOC = P116; +NET "SNES_DATABUS_DIR" LOC = P111; +NET "SNES_DATABUS_OE" LOC = P109; +NET "SNES_DATABUS_DIR" DRIVE = 8; +NET "SNES_DATABUS_OE" DRIVE = 8; + + +NET "SNES_DATA[0]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[0]" DRIVE = 8; +NET "SNES_DATA[1]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[1]" DRIVE = 8; +NET "SNES_DATA[2]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[2]" DRIVE = 8; +NET "SNES_DATA[3]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[3]" DRIVE = 8; +NET "SNES_DATA[4]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[4]" DRIVE = 8; +NET "SNES_DATA[5]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[5]" DRIVE = 8; +NET "SNES_DATA[6]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[6]" DRIVE = 8; +NET "SNES_DATA[7]" IOSTANDARD = LVCMOS33; +NET "SNES_DATA[7]" DRIVE = 8; + + +NET "SNES_IRQ" LOC = P114; +NET "SNES_READ_IN" LOC = P115; +NET "SNES_REFRESH" LOC = P155; +NET "SNES_WRITE_IN" LOC = P94; + +NET "SNES_PA_IN[0]" IOSTANDARD = LVCMOS33; +NET "SNES_PA_IN[0]" LOC = P90; +NET "SNES_PA_IN[1]" IOSTANDARD = LVCMOS33; +NET "SNES_PA_IN[1]" LOC = P93; +NET "SNES_PA_IN[2]" IOSTANDARD = LVCMOS33; +NET "SNES_PA_IN[2]" LOC = P86; +NET "SNES_PA_IN[3]" IOSTANDARD = LVCMOS33; +NET "SNES_PA_IN[3]" LOC = P87; +NET "SNES_PA_IN[4]" IOSTANDARD = LVCMOS33; +NET "SNES_PA_IN[4]" LOC = P81; +NET "SNES_PA_IN[5]" IOSTANDARD = LVCMOS33; +NET "SNES_PA_IN[5]" LOC = P85; +NET "SNES_PA_IN[6]" IOSTANDARD = LVCMOS33; +NET "SNES_PA_IN[6]" LOC = P152; +NET "SNES_PA_IN[7]" IOSTANDARD = LVCMOS33; +NET "SNES_PA_IN[7]" LOC = P154; + +NET "SNES_PARD_IN" IOSTANDARD = LVCMOS33; +NET "SNES_PARD_IN" LOC = P149; +NET "SNES_PAWR_IN" IOSTANDARD = LVCMOS33; +NET "SNES_PAWR_IN" LOC = P150; + +NET "SPI_MISO" LOC = P72; + + +NET "SPI_MISO" IOSTANDARD = LVCMOS33; +NET "SPI_MISO" DRIVE = 8; + + +NET "SPI_MOSI" LOC = P74; + + +NET "SPI_MOSI" IOSTANDARD = LVCMOS33; +NET "SPI_MOSI" DRIVE = 8; + + +NET "SPI_SS" LOC = P68; + + +NET "SPI_SS" IOSTANDARD = LVCMOS33; +NET "SPI_SS" DRIVE = 8; + + +NET "DAC_LRCK" LOC = P77; + + +NET "DAC_LRCK" IOSTANDARD = LVCMOS33; +NET "DAC_LRCK" DRIVE = 8; + + +NET "DAC_MCLK" LOC = P76; + + +NET "DAC_MCLK" IOSTANDARD = LVCMOS33; +NET "DAC_MCLK" DRIVE = 8; + + +NET "DAC_SDOUT" LOC = P78; + + +NET "DAC_SDOUT" IOSTANDARD = LVCMOS33; +NET "DAC_SDOUT" DRIVE = 8; + +# PlanAhead Generated physical constraints + +NET "SD_CLK" LOC = P64; +NET "SD_CMD" LOC = P67; +NET "SD_DAT[0]" LOC = P65; +NET "SD_DAT[1]" LOC = P79; +NET "SD_DAT[2]" LOC = P62; +NET "SD_DAT[3]" LOC = P63; + +# PlanAhead Generated IO constraints + +NET "SD_CLK" IOSTANDARD = LVCMOS33; +NET "SD_CLK" PULLUP; +NET "SD_CMD" IOSTANDARD = LVCMOS33; +NET "SD_DAT[0]" IOSTANDARD = LVCMOS33; +NET "SD_DAT[1]" IOSTANDARD = LVCMOS33; +NET "SD_DAT[2]" IOSTANDARD = LVCMOS33; +NET "SD_DAT[3]" IOSTANDARD = LVCMOS33; + +NET "SNES_SYSCLK" LOC = P180; +NET "SNES_SYSCLK" IOSTANDARD = LVCMOS33; +NET "SNES_SYSCLK" TNM_NET = "SNES_SYSCLK"; +TIMESPEC TS_SNES_SYSCLK = PERIOD "SNES_SYSCLK" 21.5 MHz HIGH 50 %; + +#NET "RAM_DATA[0]" IOSTANDARD = LVCMOS33; +#NET "RAM_DATA[0]" DRIVE = 8; +#NET "RAM_DATA[0]" LOC = P26; +#NET "RAM_DATA[1]" IOSTANDARD = LVCMOS33; +#NET "RAM_DATA[1]" DRIVE = 8; +#NET "RAM_DATA[1]" LOC = P22; +#NET "RAM_DATA[2]" IOSTANDARD = LVCMOS33; +#NET "RAM_DATA[2]" DRIVE = 8; +#NET "RAM_DATA[2]" LOC = P20; +#NET "RAM_DATA[3]" IOSTANDARD = LVCMOS33; +#NET "RAM_DATA[3]" DRIVE = 8; +#NET "RAM_DATA[3]" LOC = P19; +#NET "RAM_DATA[4]" IOSTANDARD = LVCMOS33; +#NET "RAM_DATA[4]" DRIVE = 8; +#NET "RAM_DATA[4]" LOC = P21; +#NET "RAM_DATA[5]" IOSTANDARD = LVCMOS33; +#NET "RAM_DATA[5]" DRIVE = 8; +#NET "RAM_DATA[5]" LOC = P24; +#NET "RAM_DATA[6]" IOSTANDARD = LVCMOS33; +#NET "RAM_DATA[6]" DRIVE = 8; +#NET "RAM_DATA[6]" LOC = P27; +#NET "RAM_DATA[7]" IOSTANDARD = LVCMOS33; +#NET "RAM_DATA[7]" DRIVE = 8; +#NET "RAM_DATA[7]" LOC = P29; +# +#NET "RAM_OE" IOSTANDARD = LVCMOS33; +#NET "RAM_OE" DRIVE = 8; +#NET "RAM_OE" LOC = P36; +#NET "RAM_WE" IOSTANDARD = LVCMOS33; +#NET "RAM_WE" DRIVE = 8; +#NET "RAM_WE" LOC = P50; +# +#NET "RAM_ADDR[0]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[0]" DRIVE = 8; +#NET "RAM_ADDR[0]" LOC = P28; +#NET "RAM_ADDR[1]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[1]" DRIVE = 8; +#NET "RAM_ADDR[1]" LOC = P31; +#NET "RAM_ADDR[2]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[2]" DRIVE = 8; +#NET "RAM_ADDR[2]" LOC = P33; +#NET "RAM_ADDR[3]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[3]" DRIVE = 8; +#NET "RAM_ADDR[3]" LOC = P35; +#NET "RAM_ADDR[4]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[4]" DRIVE = 8; +#NET "RAM_ADDR[4]" LOC = P37; +#NET "RAM_ADDR[5]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[5]" DRIVE = 8; +#NET "RAM_ADDR[5]" LOC = P40; +#NET "RAM_ADDR[6]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[6]" DRIVE = 8; +#NET "RAM_ADDR[6]" LOC = P43; +#NET "RAM_ADDR[7]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[7]" DRIVE = 8; +#NET "RAM_ADDR[7]" LOC = P45; +#NET "RAM_ADDR[8]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[8]" DRIVE = 8; +#NET "RAM_ADDR[8]" LOC = P44; +#NET "RAM_ADDR[9]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[9]" DRIVE = 8; +#NET "RAM_ADDR[9]" LOC = P42; +#NET "RAM_ADDR[10]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[10]" DRIVE = 8; +#NET "RAM_ADDR[10]" LOC = P34; +#NET "RAM_ADDR[11]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[11]" DRIVE = 8; +#NET "RAM_ADDR[11]" LOC = P39; +#NET "RAM_ADDR[12]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[12]" DRIVE = 8; +#NET "RAM_ADDR[12]" LOC = P48; +#NET "RAM_ADDR[13]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[13]" DRIVE = 8; +#NET "RAM_ADDR[13]" LOC = P46; +#NET "RAM_ADDR[14]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[14]" DRIVE = 8; +#NET "RAM_ADDR[14]" LOC = P51; +#NET "RAM_ADDR[15]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[15]" DRIVE = 8; +#NET "RAM_ADDR[15]" LOC = P58; +#NET "RAM_ADDR[16]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[16]" DRIVE = 8; +#NET "RAM_ADDR[16]" LOC = P57; +#NET "RAM_ADDR[17]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[17]" DRIVE = 8; +#NET "RAM_ADDR[17]" LOC = P61; +#NET "RAM_ADDR[18]" IOSTANDARD = LVCMOS33; +#NET "RAM_ADDR[18]" DRIVE = 8; +#NET "RAM_ADDR[18]" LOC = P52; diff --git a/verilog/sd2snes_spc7110/main.v b/verilog/sd2snes_spc7110/main.v new file mode 100644 index 000000000..420af10f9 --- /dev/null +++ b/verilog/sd2snes_spc7110/main.v @@ -0,0 +1,1135 @@ +`timescale 1 ns / 1 ns +////////////////////////////////////////////////////////////////////////////////// +// Company: Rehkopf +// Engineer: Rehkopf +// +// Create Date: 01:13:46 05/09/2009 +// Design Name: +// Module Name: main +// Project Name: +// Target Devices: +// Tool versions: +// Description: Master Control FSM +// +// Dependencies: address +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module main( +`ifdef MK2 + /* Bus 1: PSRAM, 128Mbit, 16bit, 70ns */ + output [22:0] ROM_ADDR, + output ROM_CE, + input MCU_OVR, + /* debug */ + output p113_out, +`endif +`ifdef MK3 + input SNES_CIC_CLK, + /* Bus 1: 2x PSRAM, 64Mbit, 16bit, 70ns */ + output [21:0] ROM_ADDR, + output ROM_1CE, + output ROM_2CE, + output ROM_ZZ, + /* debug */ + output PM6_out, + output PN6_out, + input PT5_in, +`endif +// SPC7110 behaviour referenced here follows the ares implementation (ISC); +// the full ISC notice is carried in spc7110_map.v / spc7110_dcu.v. + + /* input clock */ + input CLKIN, + + /* SNES signals */ + input [23:0] SNES_ADDR_IN, + input SNES_READ_IN, + input SNES_WRITE_IN, + input SNES_ROMSEL_IN, + inout [7:0] SNES_DATA, + input SNES_CPU_CLK_IN, + input SNES_REFRESH, + output SNES_IRQ, + output SNES_DATABUS_OE, + output SNES_DATABUS_DIR, + input SNES_SYSCLK, + + input [7:0] SNES_PA_IN, + input SNES_PARD_IN, + input SNES_PAWR_IN, + + /* SRAM signals */ + inout [15:0] ROM_DATA, + output ROM_OE, + output ROM_WE, + output ROM_BHE, + output ROM_BLE, + + /* Bus 2: SRAM, 4Mbit, 8bit, 45ns -> NOT USED; Backup RAM mapped to $E0:0000 in PSRAM */ + inout [7:0] RAM_DATA, + output [18:0] RAM_ADDR, + output RAM_OE, + output RAM_WE, + + /* MCU signals */ + input SPI_MOSI, + inout SPI_MISO, + input SPI_SS, + input SPI_SCK, + + output MCU_RDY, + + output DAC_MCLK, + output DAC_LRCK, + output DAC_SDOUT, + + /* SD signals */ + input [3:0] SD_DAT, + inout SD_CMD, + inout SD_CLK +); + +wire CLK2; + +wire dspx_dp_enable; + +wire [7:0] spi_cmd_data; +wire [7:0] spi_param_data; +wire [7:0] spi_input_data; +wire [31:0] spi_byte_cnt; +wire [2:0] spi_bit_cnt; +wire [23:0] MCU_ADDR; +wire [2:0] MAPPER; +wire [23:0] SAVERAM_MASK; +wire [23:0] ROM_MASK; +/* SPC7110 data ROM window in PSRAM, programmed by the MCU (see mcu_cmd.v) */ +wire [23:0] DROM_BASE; +wire [23:0] DROM_MASK; +wire [7:0] SD_DMA_SRAM_DATA; +wire [1:0] SD_DMA_TGT; +wire [10:0] SD_DMA_PARTIAL_START; +wire [10:0] SD_DMA_PARTIAL_END; + +wire [10:0] dac_addr; +wire [2:0] dac_vol_select_out; +wire [8:0] dac_ptr_addr; +//wire [7:0] dac_volume; +wire [7:0] msu_volumerq_out; +wire [7:0] msu_status_out; +wire [31:0] msu_addressrq_out; +wire [15:0] msu_trackrq_out; +wire [13:0] msu_write_addr; +wire [13:0] msu_ptr_addr; +wire [7:0] MSU_SNES_DATA_IN; +wire [7:0] MSU_SNES_DATA_OUT; +wire [5:0] msu_status_reset_bits; +wire [5:0] msu_status_set_bits; + +wire [15:0] featurebits; +wire feat_cmd_unlock = featurebits[5]; + +wire [23:0] MAPPED_SNES_ADDR; +wire ROM_ADDR0; + +wire [8:0] snescmd_addr_mcu; +wire [7:0] snescmd_data_out_mcu; +wire [7:0] snescmd_data_in_mcu; + +reg [7:0] SNES_PARDr = 8'b11111111; +reg [7:0] SNES_PAWRr = 8'b11111111; +reg [7:0] SNES_READr = 8'b11111111; +reg [7:0] SNES_WRITEr = 8'b11111111; +reg [7:0] SNES_CPU_CLKr = 8'b00000000; +reg [7:0] SNES_ROMSELr = 8'b11111111; +reg [7:0] SNES_PULSEr = 8'b11111111; +reg [23:0] SNES_ADDRr [6:0]; +reg [7:0] SNES_PAr [6:0]; +reg [7:0] SNES_DATAr [4:0]; + +reg SNES_DEADr = 1; +reg SNES_reset_strobe = 0; + +reg free_strobe = 0; + +// snes address bus delayed by 6 cycles, 4 respect SNES_READ and SNES_WRITE +wire [23:0] SNES_ADDR = (SNES_ADDRr[5] & SNES_ADDRr[4]); +wire [7:0] SNES_PA = (SNES_PAr[5] & SNES_PAr[4]); +wire [7:0] SNES_DATA_IN = (SNES_DATAr[3] & SNES_DATAr[2]); + +wire SNES_PULSE_IN = SNES_READ_IN & SNES_WRITE_IN & ~SNES_CPU_CLK_IN; + +wire SNES_PULSE_end = (SNES_PULSEr[6:1] == 6'b000011); +wire SNES_PARD_start = (SNES_PARDr[6:1] == 6'b111110); +wire SNES_PARD_end = (SNES_PARDr[6:1] == 6'b000001); +// Sample PAWR data earlier on CPU accesses, later on DMA accesses... +wire SNES_PAWR_start = (SNES_PAWRr[7:1] == (({SNES_ADDR[22], SNES_ADDR[15:0]} == 17'h02100) ? 7'b1110000 : 7'b1000000)); +wire SNES_PAWR_end = (SNES_PAWRr[6:1] == 6'b000001); +wire SNES_RD_start = (SNES_READr[6:1] == 6'b111110); +wire SNES_RD_end = (SNES_READr[6:1] == 6'b000001); +wire SNES_WR_end = (SNES_WRITEr[6:1] == 6'b000001); +wire SNES_cycle_start = (SNES_CPU_CLKr[6:1] == 6'b000001); +wire SNES_cycle_end = (SNES_CPU_CLKr[6:1] == 6'b111110); +// active low write signal from SNES delayed 2 cycles (96MHz clock) +wire SNES_WRITE = SNES_WRITEr[2] & SNES_WRITEr[1]; +// active low read signal from SNES delayed 2 cycles (96MHz clock) +wire SNES_READ = SNES_READr[2] & SNES_READr[1]; +wire SNES_READ_late = SNES_READr[5] & SNES_READr[4]; +wire SNES_READ_narrow = SNES_READ | SNES_READ_late; +wire SNES_CPU_CLK = SNES_CPU_CLKr[2] & SNES_CPU_CLKr[1]; +wire SNES_PARD = SNES_PARDr[2] & SNES_PARDr[1]; +wire SNES_PAWR = SNES_PAWRr[2] & SNES_PAWRr[1]; + +// active low ROM-select signal from SNES delayed 5 cycles (96MHz clock) +wire SNES_ROMSEL = (SNES_ROMSELr[5] & SNES_ROMSELr[4]); + +reg [7:0] BUS_DATA; + +// if SNES CPU is reading, register data bus +// if SNES CPU is writing, register data bus delayed 4 cycles +always @(posedge CLK2) begin + if(~SNES_READ) BUS_DATA <= SNES_DATA; + else if(~SNES_WRITE) BUS_DATA <= SNES_DATA_IN; +end + +wire SD_DMA_TO_ROM; +wire free_slot = (SNES_PULSE_end | free_strobe) & ~SD_DMA_TO_ROM; + +wire ROM_HIT; + +assign DCM_RST=0; + +always @(posedge CLK2) begin + free_strobe <= 1'b0; + if(SNES_cycle_start) free_strobe <= ~ROM_HIT; +end + +// register all interface signals from SNES with 96MHz clock +always @(posedge CLK2) begin + SNES_PULSEr <= {SNES_PULSEr[6:0], SNES_PULSE_IN}; + SNES_PARDr <= {SNES_PARDr[6:0], SNES_PARD_IN}; + SNES_PAWRr <= {SNES_PAWRr[6:0], SNES_PAWR_IN}; + // 8-cycle pipeline for /SNES_RD + SNES_READr <= {SNES_READr[6:0], SNES_READ_IN}; + // 8-cycle pipeline for /SNES_WR + SNES_WRITEr <= {SNES_WRITEr[6:0], SNES_WRITE_IN}; + SNES_CPU_CLKr <= {SNES_CPU_CLKr[6:0], SNES_CPU_CLK_IN}; + SNES_ROMSELr <= {SNES_ROMSELr[6:0], SNES_ROMSEL_IN}; + // 7 cycles pipeline for full address bus (24 bits) + SNES_ADDRr[6] <= SNES_ADDRr[5]; + SNES_ADDRr[5] <= SNES_ADDRr[4]; + SNES_ADDRr[4] <= SNES_ADDRr[3]; + SNES_ADDRr[3] <= SNES_ADDRr[2]; + SNES_ADDRr[2] <= SNES_ADDRr[1]; + SNES_ADDRr[1] <= SNES_ADDRr[0]; + SNES_ADDRr[0] <= SNES_ADDR_IN; + SNES_PAr[6] <= SNES_PAr[5]; + SNES_PAr[5] <= SNES_PAr[4]; + SNES_PAr[4] <= SNES_PAr[3]; + SNES_PAr[3] <= SNES_PAr[2]; + SNES_PAr[2] <= SNES_PAr[1]; + SNES_PAr[1] <= SNES_PAr[0]; + SNES_PAr[0] <= SNES_PA_IN; + SNES_DATAr[4] <= SNES_DATAr[3]; + SNES_DATAr[3] <= SNES_DATAr[2]; + SNES_DATAr[2] <= SNES_DATAr[1]; + SNES_DATAr[1] <= SNES_DATAr[0]; + SNES_DATAr[0] <= SNES_DATA; +end + +parameter ST_IDLE = 5'b00001; +parameter ST_MCU_RD_ADDR = 5'b00010; +parameter ST_MCU_RD_END = 5'b00100; +parameter ST_MCU_WR_ADDR = 5'b01000; +parameter ST_MCU_WR_END = 5'b10000; + +parameter SNES_DEAD_TIMEOUT = 17'd96000; // 1ms + +parameter ROM_CYCLE_LEN = 4'd7; + +/* Length of a data ROM fetch, in CLK2 cycles minus one (the counter is loaded + * with this value and the byte is sampled on the edge that retires it, so the + * address is held for DROM_CYCLE_LEN+1 cycles). + * + * It is LONGER than ROM_CYCLE_LEN on purpose, and the difference is the reason + * this parameter exists at all. The PSRAM is asynchronous, so the real budget + * of an access is + * + * hold >= (clock to ROM_ADDR pad) + (array access time) + (ROM_DATA setup) + * + * and NONE of those three terms is covered by a timing constraint: the external + * bus carries no OFFSET IN/OUT in main.ucf (in any core of the fork), so a fit + * that reports "all constraints met" has said nothing about it. Measured on + * the placed and routed mk2 design (trce data sheet report plus a FROM:TO + * attribution per source), 96.04 MHz, xc3s400-4: + * + * clock to ROM_ADDR pad, MCU source (ROM_ADDRr, a plain register): 10.0-11.2 ns + * clock to ROM_ADDR pad, fetch source (drom_addr): 17.5-18.1 ns + * ROM_DATA pad to the capture flip-flop, setup: up to 8.2 ns + * + * The fetch is ~7 ns slower than the MCU read because its address reaches the + * pins through the 24-bit adder that biases the linear data ROM offset by the + * window base -- the MCU drives the pins straight out of a register. With the + * MCU's eight cycles (83.3 ns) that leaves 83.3 - 18.1 - 8.2 = 57.0 ns for the + * array, BELOW what the 70 ns part guarantees and close enough to what it + * actually does that some addresses read the previous word: a byte from a + * neighbouring address, deterministic, and on screen a handful of corrupted + * tiles in an otherwise correct image. Ten cycles (104.1 ns) leave 77.8 ns, + * which clears the datasheet number with margin. + * + * The same budget computed from the trce data sheet report's WORST clock-to-pad + * of the whole fit (24.91 ns, which includes pads the fetch never drives) and + * the worst ROM_DATA setup (6.54 ns) gives 51.9 ns at eight cycles and 72.7 ns + * at ten -- a more conservative +2.7 ns margin. Both attributions agree on the + * decision (eight cycles is short of 70 ns, ten clears it); quote either, but + * know which one you are quoting: per-path FROM:TO above, whole-fit worst here. + * + * Do not lower this back to ROM_CYCLE_LEN. If the fetch ever has to get + * shorter, take the adder out of the path from drom_addr to the pins first and + * re-measure -- the number here is a hardware budget, not a preference. + */ +parameter DROM_CYCLE_LEN = 4'd9; + +reg [4:0] STATE; +initial STATE = ST_IDLE; + +assign MSU_SNES_DATA_IN = BUS_DATA; + +sd_dma snes_sd_dma( + .CLK(CLK2), + .SD_DAT(SD_DAT), + .SD_CLK(SD_CLK), + .SD_DMA_EN(SD_DMA_EN), + .SD_DMA_STATUS(SD_DMA_STATUS), + .SD_DMA_SRAM_WE(SD_DMA_SRAM_WE), + .SD_DMA_SRAM_DATA(SD_DMA_SRAM_DATA), + .SD_DMA_NEXTADDR(SD_DMA_NEXTADDR), + .SD_DMA_PARTIAL(SD_DMA_PARTIAL), + .SD_DMA_PARTIAL_START(SD_DMA_PARTIAL_START), + .SD_DMA_PARTIAL_END(SD_DMA_PARTIAL_END), + .SD_DMA_START_MID_BLOCK(SD_DMA_START_MID_BLOCK), + .SD_DMA_END_MID_BLOCK(SD_DMA_END_MID_BLOCK), + .DBG_cyclecnt(SD_DMA_DBG_cyclecnt), + .DBG_clkcnt(SD_DMA_DBG_clkcnt) +); + +assign SD_DMA_TO_ROM = (SD_DMA_STATUS && (SD_DMA_TGT == 2'b00)); + +dac snes_dac( + .clkin(CLK2), + .sysclk(SNES_SYSCLK), + .mclk_out(DAC_MCLK), + .lrck_out(DAC_LRCK), + .sdout(DAC_SDOUT), + .we(SD_DMA_TGT==2'b01 ? SD_DMA_SRAM_WE : 1'b1), + .pgm_address(dac_addr), + .pgm_data(SD_DMA_SRAM_DATA), + .DAC_STATUS(DAC_STATUS), + .volume(msu_volumerq_out), + .vol_latch(msu_volume_latch_out), + .vol_select(dac_vol_select_out), + .palmode(dac_palmode_out), + .play(dac_play), + .reset(dac_reset), + .dac_address_ext(dac_ptr_addr) +); + +msu snes_msu ( + .clkin(CLK2), + .enable(msu_enable), + .pgm_address(msu_write_addr), + .pgm_data(SD_DMA_SRAM_DATA), + .pgm_we(SD_DMA_TGT==2'b10 ? SD_DMA_SRAM_WE : 1'b1), + .reg_addr(SNES_ADDR[2:0]), + .reg_data_in(MSU_SNES_DATA_IN), + .reg_data_out(MSU_SNES_DATA_OUT), + .reg_oe_falling(SNES_RD_start), + .reg_oe_rising(SNES_RD_end), + .reg_we_rising(SNES_WR_end), + .status_out(msu_status_out), + .volume_out(msu_volumerq_out), + .volume_latch_out(msu_volume_latch_out), + .addr_out(msu_addressrq_out), + .track_out(msu_trackrq_out), + .status_reset_bits(msu_status_reset_bits), + .status_set_bits(msu_status_set_bits), + .status_reset_we(msu_status_reset_we), + .msu_address_ext(msu_ptr_addr), + .msu_address_ext_write(msu_addr_reset), + .DBG_msu_reg_oe_rising(DBG_msu_reg_oe_rising), + .DBG_msu_reg_oe_falling(DBG_msu_reg_oe_falling), + .DBG_msu_reg_we_rising(DBG_msu_reg_we_rising), + .DBG_msu_address(DBG_msu_address), + .DBG_msu_address_ext_write_rising(DBG_msu_address_ext_write_rising) +); + +spi snes_spi( + .clk(CLK2), + .MOSI(SPI_MOSI), + .MISO(SPI_MISO), + .SSEL(SPI_SS), + .SCK(SPI_SCK), + .cmd_ready(spi_cmd_ready), + .param_ready(spi_param_ready), + .cmd_data(spi_cmd_data), + .param_data(spi_param_data), + .endmessage(spi_endmessage), + .startmessage(spi_startmessage), + .input_data(spi_input_data), + .byte_cnt(spi_byte_cnt), + .bit_cnt(spi_bit_cnt) +); + +wire [15:0] dsp_feat; + +/***************************************************************************** + * SPC7110 register block + * + * The chip answers at $4800-$484F in banks $00-$3F / $80-$BF (bank groups with + * address bit 22 clear, hence the ~SNES_ADDR[22] term): + * $4800-$480F decompression unit + * $4810-$481F data port + * $4820-$482F ALU (mul/div) + * $4830-$483F bank/window mapping + SRAM enable + * $4840-$484F RTC-4513 interface + * + * Plus the two whole-bank aliases of the reference, which is how a game aims a + * DMA channel straight at the chip without going through the B bus: EVERY + * address in bank $50 is $4800 and EVERY address in bank $58 is $4808 (the + * offset inside the bank is ignored, so the DMA may walk it or hold it still). + * The reference tests the bank exactly, so $D0/$D8 are NOT aliases. Those two + * banks carry address bit 22 and are claimed by nothing else in this core: the + * cartridge windows of spc7110_map are $00-3f/$80-bf:8000-ffff and + * $c0-ff:0000-ffff, its SRAM window needs bit 22 clear, and the hook identity + * window needs bits 23:22 both set -- so the alias cannot punch a hole in any + * of them. + * + * This block decodes the window and folds the alias into a register index; the + * register file itself is spc7110_regs.v. + *****************************************************************************/ +reg spc7110_enable; +reg spc7110_reg_enable; +reg [7:0] spc7110_reg_addr; + +wire spc7110_win_regs = ~SNES_ADDR[22] & (SNES_ADDR[15:8] == 8'h48) + & ((SNES_ADDR[7:6] == 2'b00) | (SNES_ADDR[7:4] == 4'h4)); +wire spc7110_win_4800 = (SNES_ADDR[23:16] == 8'h50); // bank alias of $4800 +wire spc7110_win_4808 = (SNES_ADDR[23:16] == 8'h58); // bank alias of $4808 + +// '1' when accessing any SPC7110 register $4800-$484F, or one of the two bank +// aliases; spc7110_reg_addr is the register index the access resolves to. +always @(posedge CLK2) +begin + if( SNES_DEADr == 1'b1 ) + begin + spc7110_enable <= 1'b0; + spc7110_reg_enable <= 1'b0; + spc7110_reg_addr <= 8'h00; + end + else if( MAPPER == 3'b101 ) + begin + spc7110_enable <= 1'b1; + spc7110_reg_enable <= spc7110_win_regs | spc7110_win_4800 | spc7110_win_4808; + spc7110_reg_addr <= spc7110_win_4800 ? 8'h00 + : spc7110_win_4808 ? 8'h08 + : SNES_ADDR[7:0]; + end + else + begin + spc7110_enable <= 1'b0; + spc7110_reg_enable <= 1'b0; + spc7110_reg_addr <= 8'h00; + end +end + +// Register read-back ($4800-$484F), combinational: a register read never waits +// for a data ROM fetch. +wire [7:0] spc7110_dout; + +/* Cartridge address mapping produced by spc7110_map (inside spc7110_regs) and + * consumed by address.v, which turns it into the final PSRAM address. */ +wire SPC7110_ROM_HIT; +wire [23:0] SPC7110_PSRAM_ADDR; +wire SPC7110_IS_SRAM; + +/* RTC-4513 (the cartridge with the real time clock). Time keeping is the + * shared rtc.v of the base core, written either by the MCU (SPI command $e5) or + * by the game through $4840-$4842, exactly the way srtc.v drives it for the + * S-RTC. */ +wire [59:0] rtc_data; +wire [55:0] rtc_data_in; +wire rtc_pgm_we; +wire [59:0] rtcif_rtc_data_wr; +wire rtcif_rtc_we; +/* RTC-4513 battery backup: read by the MCU with SPI $e6, restored with $e7 */ +`ifndef MK2 +wire [59:0] rtc_bkp_time; +wire [1:0] rtc_bkp_flags; +wire [59:0] rtc_bkp_time_in; +wire [1:0] rtc_bkp_flags_in; +wire rtc_bkp_we; +`endif + +/***************************************************************************** + * Data ROM (DROM) fetch path + * + * The data port and the decompressor read the compressed data ROM one byte at a + * time through a req/ack handshake with arbitrary latency. The fetch steals a + * PSRAM slot from the SNES exactly the way the S-DD1 prefetch did: the SNES owns + * the bus during its own read cycle, the second half of every cycle is free, and + * a started access runs to completion (DROM_CYCLE_LEN+1 clocks at 96 MHz, see + * the parameter for the hardware budget behind that number) before the address + * may change. + * + * drom_req level, held by the requester until its ack + * drom_addr LINEAR offset inside the data ROM (0-based), latched at arm time + * drom_ack one clock, drom_data valid, one per requester + * drom_data the fetched byte, registered + * drom_busy '1' while the fetch owns the PSRAM bus; it also keeps the MCU + * off the bus below, the same role the S-DD1's "Idle" output had + * + * The state machine itself sits further down, next to the MCU request registers + * it has to arbitrate against. + *****************************************************************************/ +wire data_drom_req; +wire [23:0] data_drom_addr; +wire dcu_drom_req; +wire [23:0] dcu_drom_addr; + +// Fixed priority, data port above the decompressor: a $4810 read is synchronous +// to the SNES (the game may be running a DMA straight out of it) while the +// decompressor has a tile buffer and tolerates the wait. Neither requester +// bursts -- one byte in flight each, by contract -- so a fixed priority is +// enough and costs nothing. +wire drom_req = data_drom_req | dcu_drom_req; +wire drom_sel_dcu = ~data_drom_req & dcu_drom_req; +wire [23:0] drom_sel_addr = data_drom_req ? data_drom_addr : dcu_drom_addr; + +reg drom_busy; +reg drom_owner; // owner of the running fetch: 0 = data port, 1 = DCU +reg [23:0] drom_addr; +reg [3:0] drom_cnt; +reg drom_ack; +reg [7:0] drom_data; + +initial begin + drom_busy = 1'b0; + drom_owner = 1'b0; + drom_addr = 24'h000000; + drom_cnt = 4'h0; + drom_ack = 1'b0; + drom_data = 8'h00; +end + +wire data_drom_ack = drom_ack & ~drom_owner; +wire dcu_drom_ack = drom_ack & drom_owner; + +// PSRAM address of the byte being fetched: linear offset wrapped inside the data +// ROM, then biased by its base. Both come from the MCU (SPI commands $d7/$d8). +// NOTE the power-up defaults (base 0, mask $ffffff) provide no wrap at all: the +// firmware must program $d7/$d8 before the first fetch or reads walk from +// PSRAM 0 unbounded. +wire [23:0] drom_psram_addr = DROM_BASE + (drom_addr & DROM_MASK); + +spc7110_regs snes_spc7110( + .clkin(CLK2), + .rst(SNES_DEADr), + .reg_enable(spc7110_reg_enable), + .reg_addr(spc7110_reg_addr), + .reg_din(BUS_DATA), + .wr_end(SNES_WR_end), + .rd_end(SNES_RD_end), + .reg_dout(spc7110_dout), + .snes_addr(SNES_ADDR), + .drom_base(DROM_BASE), + .drom_mask(DROM_MASK), + .map_rom_hit(SPC7110_ROM_HIT), + .map_psram_addr(SPC7110_PSRAM_ADDR), + .map_is_sram(SPC7110_IS_SRAM), + .data_drom_req(data_drom_req), + .data_drom_addr(data_drom_addr), + .data_drom_ack(data_drom_ack), + .dcu_drom_req(dcu_drom_req), + .dcu_drom_addr(dcu_drom_addr), + .dcu_drom_ack(dcu_drom_ack), + .drom_data(drom_data), + .rtc_data(rtc_data), + .rtc_we(rtcif_rtc_we), +`ifndef MK2 + .rtc_data_wr(rtcif_rtc_data_wr), + .bkp_time(rtc_bkp_time), + .bkp_flags(rtc_bkp_flags), + .bkp_we(rtc_bkp_we), + .bkp_time_in(rtc_bkp_time_in), + .bkp_flags_in(rtc_bkp_flags_in) +`else + .rtc_data_wr(rtcif_rtc_data_wr) +`endif +); + +// Time keeping. Runs on CLKIN, not CLK2 -- same as the base core. The +// crossing into rtc_data_r is a quasi-static CDC and is cut in main.sdc. +// On mk2 the DCM wrapper owns the IBUFG of the CLKIN pad, so every other +// consumer has to take the buffered copy (a pad may feed its input buffer and +// nothing else); mk3 has no such wrapper and uses the pin directly. +`ifdef MK2 +wire clkin_rtc; +`else +wire clkin_rtc = CLKIN; +`endif +rtc snes_rtc ( + .clkin(clkin_rtc), + .rtc_data(rtc_data), + .rtc_data_in(rtc_data_in), + .pgm_we(rtc_pgm_we), + .rtc_data_in1(rtcif_rtc_data_wr), + .we1(rtcif_rtc_we) +); + +reg [7:0] MCU_DINr; +wire [7:0] MCU_DOUT; +wire [31:0] cheat_pgm_data; +wire [7:0] cheat_data_out; +wire [2:0] cheat_pgm_idx; + +mcu_cmd snes_mcu_cmd( + .clk(CLK2), + .snes_sysclk(SNES_SYSCLK), + .cmd_ready(spi_cmd_ready), + .param_ready(spi_param_ready), + .cmd_data(spi_cmd_data), + .param_data(spi_param_data), + .mcu_mapper(MAPPER), + .mcu_write(MCU_WRITE), + .mcu_data_in(MCU_DINr), + .mcu_data_out(MCU_DOUT), + .spi_byte_cnt(spi_byte_cnt), + .spi_bit_cnt(spi_bit_cnt), + .spi_data_out(spi_input_data), + .addr_out(MCU_ADDR), + .saveram_mask_out(SAVERAM_MASK), + .rom_mask_out(ROM_MASK), + .drom_base_out(DROM_BASE), + .drom_mask_out(DROM_MASK), + .rtc_data_out(rtc_data_in), + .rtc_pgm_we(rtc_pgm_we), +`ifndef MK2 + .rtc_bkp_in(rtc_bkp_time), + .rtc_bkp_flags_in(rtc_bkp_flags), + .rtc_bkp_out(rtc_bkp_time_in), + .rtc_bkp_flags_out(rtc_bkp_flags_in), + .rtc_bkp_we(rtc_bkp_we), +`endif + .SD_DMA_EN(SD_DMA_EN), + .SD_DMA_STATUS(SD_DMA_STATUS), + .SD_DMA_NEXTADDR(SD_DMA_NEXTADDR), + .SD_DMA_SRAM_DATA(SD_DMA_SRAM_DATA), + .SD_DMA_SRAM_WE(SD_DMA_SRAM_WE), + .SD_DMA_TGT(SD_DMA_TGT), + .SD_DMA_PARTIAL(SD_DMA_PARTIAL), + .SD_DMA_PARTIAL_START(SD_DMA_PARTIAL_START), + .SD_DMA_PARTIAL_END(SD_DMA_PARTIAL_END), + .SD_DMA_START_MID_BLOCK(SD_DMA_START_MID_BLOCK), + .SD_DMA_END_MID_BLOCK(SD_DMA_END_MID_BLOCK), + .dac_addr_out(dac_addr), + .DAC_STATUS(DAC_STATUS), + .dac_play_out(dac_play), + .dac_reset_out(dac_reset), + .dac_vol_select_out(dac_vol_select_out), + .dac_palmode_out(dac_palmode_out), + .dac_ptr_out(dac_ptr_addr), + .msu_addr_out(msu_write_addr), + .MSU_STATUS(msu_status_out), + .msu_status_reset_out(msu_status_reset_bits), + .msu_status_set_out(msu_status_set_bits), + .msu_status_reset_we(msu_status_reset_we), + .msu_volumerq(msu_volumerq_out), + .msu_addressrq(msu_addressrq_out), + .msu_trackrq(msu_trackrq_out), + .msu_ptr_out(msu_ptr_addr), + .msu_reset_out(msu_addr_reset), + .featurebits_out(featurebits), + .mcu_rrq(MCU_RRQ), + .mcu_wrq(MCU_WRQ), + .mcu_rq_rdy(MCU_RDY), + .region_out(mcu_region), + .snescmd_addr_out(snescmd_addr_mcu), + .snescmd_we_out(snescmd_we_mcu), + .snescmd_data_out(snescmd_data_out_mcu), + .snescmd_data_in(snescmd_data_in_mcu), + .cheat_pgm_idx_out(cheat_pgm_idx), + .cheat_pgm_data_out(cheat_pgm_data), + .cheat_pgm_we_out(cheat_pgm_we), + .dsp_feat_out(dsp_feat) +); + +address snes_addr( + .featurebits(featurebits), + .SNES_ADDR(SNES_ADDR), // requested address from SNES + .SNES_PA(SNES_PA), + .SNES_ROMSEL(SNES_ROMSEL), + // Address to read/write from PSRAM + .ROM_ADDR(MAPPED_SNES_ADDR), + // '1' when SNES request to access ROM, Backup RAM or BS-X RAM (stored at PSRAM) + .ROM_HIT(ROM_HIT), + // '1' when SNES request to access backup RAM (stored linearly at PSRAM $E0:0000) + .IS_SAVERAM(IS_SAVERAM), + // '1' when SNES request to access ROM (stored linearly at PSRAM $00:0000) + .IS_ROM(IS_ROM), + // '1' when SNES request to access to PSRAM writable range (Backup RAM or BS-X RAM) + .IS_WRITABLE(IS_WRITABLE), + .SAVERAM_MASK(SAVERAM_MASK), + .ROM_MASK(ROM_MASK), + // cartridge mapping from spc7110_map: the four $4830-$4834 selected windows + .SPC7110_ROM_HIT(SPC7110_ROM_HIT), + .SPC7110_PSRAM_ADDR(SPC7110_PSRAM_ADDR), + .SPC7110_IS_SRAM(SPC7110_IS_SRAM), + //MSU-1 + .msu_enable(msu_enable), + .r213f_enable(r213f_enable), + .r2100_hit(r2100_hit), + .snescmd_enable(snescmd_enable), + .nmicmd_enable(nmicmd_enable), + .return_vector_enable(return_vector_enable), + .branch1_enable(branch1_enable), + .branch2_enable(branch2_enable), + .branch3_enable(branch3_enable) +); + +reg pad_latch = 0; +reg [4:0] pad_cnt = 0; + +reg snes_ajr = 0; + +cheat snes_cheat( + .clk(CLK2), + .SNES_ADDR(SNES_ADDR), + .SNES_PA(SNES_PA), + .SNES_DATA(SNES_DATA), + .SNES_reset_strobe(SNES_reset_strobe), + .SNES_wr_strobe(SNES_WR_end), + .SNES_rd_strobe(SNES_RD_start), + .snescmd_enable(snescmd_enable), + .nmicmd_enable(nmicmd_enable), + .return_vector_enable(return_vector_enable), + .branch1_enable(branch1_enable), + .branch2_enable(branch2_enable), + .branch3_enable(branch3_enable), + .pad_latch(pad_latch), + .snes_ajr(snes_ajr), + .SNES_cycle_start(SNES_cycle_start), + .pgm_idx(cheat_pgm_idx), + .pgm_we(cheat_pgm_we), + .pgm_in(cheat_pgm_data), + .data_out(cheat_data_out), + .cheat_hit(cheat_hit), + .snescmd_unlock(snescmd_unlock) +); + +wire [7:0] snescmd_dout; + +parameter ST_R213F_ARMED = 4'b0001; +parameter ST_R213F_WAITBUS = 4'b0010; +parameter ST_R213F_OVERRIDE = 4'b0100; +parameter ST_R213F_HOLD = 4'b1000; + +reg [7:0] r213fr; +reg r213f_forceread; +reg [2:0] r213f_delay; +reg [1:0] r213f_state; +initial r213fr = 8'h55; +initial r213f_forceread = 0; +initial r213f_state = 2'b01; +initial r213f_delay = 3'b000; + +reg [7:0] r2100r = 0; +reg r2100_forcewrite = 0; +reg r2100_forcewrite_pre = 0; +wire [3:0] r2100_limit = featurebits[10:7]; +wire [3:0] r2100_limited = (SNES_DATA[3:0] > r2100_limit) ? r2100_limit : SNES_DATA[3:0]; +wire r2100_patch = featurebits[6]; +wire r2100_enable = r2100_hit & (r2100_patch | ~(&r2100_limit)); + +wire snoop_4200_enable = {SNES_ADDR[22], SNES_ADDR[15:0]} == 17'h04200; +wire r4016_enable = {SNES_ADDR[22], SNES_ADDR[15:0]} == 17'h04016; + +always @(posedge CLK2) begin + r2100_forcewrite <= r2100_forcewrite_pre; +end + +always @(posedge CLK2) begin + if(SNES_WR_end & snoop_4200_enable) begin + snes_ajr <= SNES_DATA[0]; + end +end + +always @(posedge CLK2) begin + if(SNES_WR_end & r4016_enable) begin + pad_latch <= 1'b1; + pad_cnt <= 5'h0; + end + if(SNES_RD_start & r4016_enable) begin + pad_cnt <= pad_cnt + 1; + if(&pad_cnt[3:0]) begin + pad_latch <= 1'b0; + end + end +end + +// data from FPGA to SNES CPU when it is reading +assign SNES_DATA = (r213f_enable & ~SNES_PARD & ~r213f_forceread) ? r213fr + :(r2100_enable & ~SNES_PAWR & r2100_forcewrite) ? r2100r + :(~SNES_READ ^ (r213f_forceread & r213f_enable & ~SNES_PARD)) ? + ( msu_enable ? MSU_SNES_DATA_OUT + :(cheat_hit & ~feat_cmd_unlock) ? cheat_data_out + :((snescmd_unlock | feat_cmd_unlock) & snescmd_enable) ? snescmd_dout + // SPC7110 register read-back ($4800-$484F) + :(spc7110_enable & spc7110_reg_enable) ? spc7110_dout + :(ROM_ADDR0 ? ROM_DATA[7:0] : ROM_DATA[15:8])) + : 8'bZ; + +reg [3:0] ST_MEM_DELAYr; +reg MCU_RD_PENDr = 0; +reg MCU_WR_PENDr = 0; +reg [23:0] ROM_ADDRr; + +/* DROM fetch state machine (the path is declared with the register block above). + * + * Invariants, all inherited from the way the S-DD1 prefetch shared the bus: + * - arm ONLY in a free slot. Taking the bus while the SNES is driving an + * access swaps the address under a read in flight. + * - once armed, hold the bus until the counter runs out even if the slot ends + * (the S-DD1 did the same with ROM_Access_Cnt /= 0); PSRAM is asynchronous + * and an address glitch mid-access corrupts the byte. + * - do NOT arm while the MCU has a request pending. free_slot is a ONE clock + * pulse and the MCU gate below tests ~drom_busy, which is still 0 on the + * clock this machine arms, so both would take the same slot -- and the + * address mux gives the MCU priority, so this fetch would silently sample + * the MCU's address. Deferring here matches the priority already in the mux. + * - drom_busy alone owns the address mux for the whole access. Gating the mux + * with anything combinational over the SNES address, or with the mapper + * enable, would drop the fetch mid-access -- silently, whenever that term + * moved. + * - drom_ack is a REGISTERED one clock pulse and drom_data is already latched + * when it fires. The requesters drop drom_req combinationally on their ack, + * so an ack derived combinationally from the request would close a loop. + * - SNES_DEADr drops everything: the MCU needs the whole bus to load a ROM. + */ +always @(posedge CLK2) begin + if(SNES_DEADr) begin + drom_busy <= 1'b0; + drom_ack <= 1'b0; + drom_cnt <= 4'h0; + end else begin + drom_ack <= 1'b0; + if(~drom_busy) begin + if(drom_req & free_slot & ~(MCU_RD_PENDr | MCU_WR_PENDr)) begin + drom_busy <= 1'b1; + drom_owner <= drom_sel_dcu; + drom_addr <= drom_sel_addr; + drom_cnt <= DROM_CYCLE_LEN; + end + end else begin + drom_cnt <= drom_cnt - 1'b1; + drom_data <= drom_psram_addr[0] ? ROM_DATA[7:0] : ROM_DATA[15:8]; + if(drom_cnt == 0) begin + drom_busy <= 1'b0; + drom_ack <= 1'b1; + end + end + end +end + +reg RQ_MCU_RDYr; +initial RQ_MCU_RDYr = 1'b1; +assign MCU_RDY = RQ_MCU_RDYr; + +wire MCU_WE_HIT = |(STATE & ST_MCU_WR_ADDR); +wire MCU_WR_HIT = |(STATE & (ST_MCU_WR_ADDR | ST_MCU_WR_END)); +wire MCU_RD_HIT = |(STATE & (ST_MCU_RD_ADDR | ST_MCU_RD_END)); +wire MCU_HIT = MCU_WR_HIT | MCU_RD_HIT; + +// final address to PSRAM where ROM and SRAM is stored +`ifdef MK2 +DCM_Scope snes_dcm( + .CLKIN_IN(CLKIN), + .CLKFX_OUT(CLK2), + .CLKDV_OUT(CLK_SCOPE), + .CLKIN_IBUFG_OUT(clkin_rtc), + .CLK0_OUT(), + .LOCKED_OUT(DCM_LOCKED), + .RST_IN(DCM_RST) +); +assign ROM_ADDR = (SD_DMA_TO_ROM) ? MCU_ADDR[23:1] + : MCU_HIT ? ROM_ADDRr[23:1] // keep the MCU above the chip so it can use the free slot during normal SNES accesses + : drom_busy ? drom_psram_addr[23:1] + : MAPPED_SNES_ADDR[23:1]; + + +assign ROM_CE = 1'b0; + +assign p113_out = 1'b0; + +snescmd_buf snescmd ( + .clka(CLK2), // input clka + .wea(SNES_WR_end & ((snescmd_unlock | feat_cmd_unlock) & snescmd_enable)), // input [0 : 0] wea + .addra(SNES_ADDR[8:0]), // input [8 : 0] addra + .dina(SNES_DATA), // input [7 : 0] dina + .douta(snescmd_dout), // output [7 : 0] douta + .clkb(CLK2), // input clkb + .web(snescmd_we_mcu), // input [0 : 0] web + .addrb(snescmd_addr_mcu), // input [8 : 0] addrb + .dinb(snescmd_data_out_mcu), // input [7 : 0] dinb + .doutb(snescmd_data_in_mcu) // output [7 : 0] doutb +); +`endif +`ifdef MK3 +pll snes_pll( + .inclk0(CLKIN), + .c0(CLK2), + .locked(DCM_LOCKED), + .areset(DCM_RST) +); + +assign ROM_ADDR22 = (SD_DMA_TO_ROM) ? MCU_ADDR[1] + : MCU_HIT ? ROM_ADDRr[1] // keep the MCU above the chip so it can use the free slot during normal SNES accesses + : drom_busy ? drom_psram_addr[1] + : MAPPED_SNES_ADDR[1]; + +assign ROM_ADDR = (SD_DMA_TO_ROM) ? MCU_ADDR[23:2] + : MCU_HIT ? ROM_ADDRr[23:2] // keep the MCU above the chip so it can use the free slot during normal SNES accesses + : drom_busy ? drom_psram_addr[23:2] + : MAPPED_SNES_ADDR[23:2]; + +assign ROM_ZZ = 1'b1; +assign ROM_1CE = ROM_ADDR22; +assign ROM_2CE = ~ROM_ADDR22; + +snescmd_buf snescmd ( + .clock(CLK2), // input clka + .wren_a(SNES_WR_end & ((snescmd_unlock | feat_cmd_unlock) & snescmd_enable)), // input [0 : 0] wea + .address_a(SNES_ADDR[8:0]), // input [8 : 0] addra + .data_a(SNES_DATA), // input [7 : 0] dina + .q_a(snescmd_dout), // output [7 : 0] douta + .wren_b(snescmd_we_mcu), // input [0 : 0] web + .address_b(snescmd_addr_mcu), // input [8 : 0] addrb + .data_b(snescmd_data_out_mcu), // input [7 : 0] dinb + .q_b(snescmd_data_in_mcu) // output [7 : 0] doutb +); +`endif + +// OE always active. Overridden by WE when needed. +assign ROM_OE = 1'b0; + +// lower address bit to select [7:0] (ROM_ADDR0 = '1') or [15:8] (ROM_ADDR0 = '0') byte in the 16-bit word read from PSRAM +assign ROM_ADDR0 = (SD_DMA_TO_ROM) ? MCU_ADDR[0] + : MCU_HIT ? ROM_ADDRr[0] // keep the MCU above the chip so it can use the free slot during normal SNES accesses + : drom_busy ? drom_psram_addr[0] + : MAPPED_SNES_ADDR[0]; + +reg[17:0] SNES_DEAD_CNTr; +initial SNES_DEAD_CNTr = 0; + +// MCU r/w request +always @(posedge CLK2) begin + if(MCU_RRQ) begin + MCU_RD_PENDr <= 1'b1; + RQ_MCU_RDYr <= 1'b0; + ROM_ADDRr <= MCU_ADDR; + end else if(MCU_WRQ) begin + MCU_WR_PENDr <= 1'b1; + RQ_MCU_RDYr <= 1'b0; + ROM_ADDRr <= MCU_ADDR; + end else if(STATE & (ST_MCU_RD_END | ST_MCU_WR_END)) begin + MCU_RD_PENDr <= 1'b0; + MCU_WR_PENDr <= 1'b0; + RQ_MCU_RDYr <= 1'b1; + end +end + +always @(posedge CLK2) begin + if(~SNES_CPU_CLKr[1]) SNES_DEAD_CNTr <= SNES_DEAD_CNTr + 1; + else SNES_DEAD_CNTr <= 17'h0; +end + +always @(posedge CLK2) begin + SNES_reset_strobe <= 1'b0; + if(SNES_CPU_CLKr[1]) begin + SNES_DEADr <= 1'b0; + if(SNES_DEADr) SNES_reset_strobe <= 1'b1; + end + else if(SNES_DEAD_CNTr > SNES_DEAD_TIMEOUT) SNES_DEADr <= 1'b1; +end + +always @(posedge CLK2) begin + if(SNES_DEADr & SNES_CPU_CLKr[1]) STATE <= ST_IDLE; // interrupt+restart an ongoing MCU access when the SNES comes alive + else + case(STATE) + ST_IDLE: begin + STATE <= ST_IDLE; + // make sure the MCU doesn't touch the PSRAM while a DROM fetch owns the bus + if((free_slot & ~drom_busy) | SNES_DEADr) begin + if(MCU_RD_PENDr) begin + STATE <= ST_MCU_RD_ADDR; + ST_MEM_DELAYr <= ROM_CYCLE_LEN; + end + else if(MCU_WR_PENDr) begin + STATE <= ST_MCU_WR_ADDR; + ST_MEM_DELAYr <= ROM_CYCLE_LEN; + end + end + end + ST_MCU_RD_ADDR: begin + STATE <= ST_MCU_RD_ADDR; + ST_MEM_DELAYr <= ST_MEM_DELAYr - 1; + if(ST_MEM_DELAYr == 0) STATE <= ST_MCU_RD_END; + MCU_DINr <= (ROM_ADDR0 ? ROM_DATA[7:0] : ROM_DATA[15:8]); + end + ST_MCU_WR_ADDR: begin + STATE <= ST_MCU_WR_ADDR; + ST_MEM_DELAYr <= ST_MEM_DELAYr - 1; + if(ST_MEM_DELAYr == 0) STATE <= ST_MCU_WR_END; + end + ST_MCU_RD_END, ST_MCU_WR_END: begin + STATE <= ST_IDLE; + end + endcase +end + +/*********************** + * R213F read patching * + ***********************/ +always @(posedge CLK2) begin + case(r213f_state) + ST_R213F_HOLD: begin + r213f_state <= ST_R213F_HOLD; + if(SNES_PULSE_end) begin + r213f_forceread <= 1'b1; + r213f_state <= ST_R213F_ARMED; + end + end + ST_R213F_ARMED: begin + r213f_state <= ST_R213F_ARMED; + if(SNES_PARD_start & r213f_enable) begin + r213f_delay <= 3'b001; + r213f_state <= ST_R213F_WAITBUS; + end + end + ST_R213F_WAITBUS: begin + r213f_state <= ST_R213F_WAITBUS; + r213f_delay <= r213f_delay - 1; + if(r213f_delay == 3'b000) begin + r213f_state <= ST_R213F_OVERRIDE; + r213fr <= {SNES_DATA[7:5], mcu_region, SNES_DATA[3:0]}; + end + end + ST_R213F_OVERRIDE: begin + r213f_state <= ST_R213F_HOLD; + r213f_forceread <= 1'b0; + end + endcase +end + +/********************************* + * R2100 patching (experimental) * + *********************************/ +reg [3:0] r2100_bright = 0; +reg [3:0] r2100_bright_orig = 0; + +always @(posedge CLK2) begin + if(SNES_PULSE_end) r2100_forcewrite_pre <= 1'b0; + else if(SNES_PAWR_start & r2100_hit) begin + if(r2100_patch & SNES_DATA[7]) begin + // keep previous brightness during forced blanking so there is no DAC step + r2100_forcewrite_pre <= 1'b1; + r2100r <= {SNES_DATA[7], 3'b010, r2100_bright}; // 0xAx + end else if (r2100_patch && SNES_DATA == 8'h00 && r2100r[7]) begin + // extend forced blanking when game goes from blanking to brightness 0 + r2100_forcewrite_pre <= 1'b1; + r2100r <= {1'b1, 3'b111, r2100_bright}; // 0xFx + end else if (r2100_patch && SNES_DATA[3:0] < 4'h8 && r2100_bright_orig > 4'hd) begin + // substitute big brightness changes with brightness 0 (so it is visible on 1CHIP) + r2100_forcewrite_pre <= 1'b1; + r2100r <= {SNES_DATA[7], 3'b011, 4'h0}; // 0x3x / 0xBx(!) + end else if (r2100_patch | ~(&r2100_limit)) begin + // save brightness, limit brightness + r2100_bright <= r2100_limited; + r2100_bright_orig <= SNES_DATA[3:0]; + if (~(&r2100_limit) && SNES_DATA[3:0] > r2100_limit) begin + r2100_forcewrite_pre <= 1'b1; + r2100r <= {SNES_DATA[7], 3'b100, r2100_limited}; // 0x4x / 0xCx + end + end + end +end + +reg MCU_WRITE_1; +always @(posedge CLK2) MCU_WRITE_1<= MCU_WRITE; + +// data to write to PSRAM (ROM file at boot, backup RAM when game running). +// The chip itself never writes to PSRAM, it only reads (data ROM fetches), so the +// bus is tri-stated during a DROM access. +assign ROM_DATA[7:0] = ROM_ADDR0 ? + // if ROM_ADDR[0] = '1' + (SD_DMA_TO_ROM ? (!MCU_WRITE_1 ? MCU_DOUT : 8'bZ) + : MCU_WR_HIT ? MCU_DOUT + // DROM fetch in flight: read only, never drive + : drom_busy ? 8'bZ + // if writing to ROM or backup RAM (both stored in PSRAM) + : (ROM_HIT & ~SNES_WRITE) ? SNES_DATA + : 8'bZ ) + // if ROM_ADDR[0] = '0' + :8'bZ; + +assign ROM_DATA[15:8] = ROM_ADDR0 ? 8'bZ + // if ROM_ADDR[0] = '0' + : (SD_DMA_TO_ROM ? (!MCU_WRITE_1 ? MCU_DOUT : 8'bZ) + : MCU_WR_HIT ? MCU_DOUT + // DROM fetch in flight: read only, never drive + : drom_busy ? 8'bZ + // if writing to ROM or backup RAM (both stored in PSRAM) + : (ROM_HIT & ~SNES_WRITE) ? SNES_DATA + : 8'bZ ); + + +// write enable for PSRAM +assign ROM_WE = SD_DMA_TO_ROM ? MCU_WRITE + : MCU_WE_HIT ? 1'b0 + : drom_busy ? 1'b1 + : (ROM_HIT & IS_WRITABLE & SNES_CPU_CLK) ? SNES_WRITE + : 1'b1; + +// byte selector for PSRAM output. DROM fetches are one byte per transaction, so +// they use the same byte select as every other access. +// '0' when accessing high byte +assign ROM_BHE = ROM_ADDR0; +// '0' when accessing low byte +assign ROM_BLE = !ROM_ADDR0; + +// active low signal to enable level converters' output; it enables output in both sides of the chip +assign SNES_DATABUS_OE = msu_enable & ~(SNES_READ_narrow & SNES_WRITE) ? 1'b0 : + snescmd_enable & ~(SNES_READ_narrow & SNES_WRITE) ? ~(snescmd_unlock | feat_cmd_unlock) : + spc7110_reg_enable ? 1'b0 : + (r213f_enable & ~SNES_PARD) ? 1'b0 : + (r2100_enable & ~SNES_PAWR) ? 1'b0 : + snoop_4200_enable ? SNES_WRITE : + ((IS_ROM & SNES_ROMSEL) | (!IS_ROM & !IS_SAVERAM & !IS_WRITABLE) | (SNES_READ_narrow & SNES_WRITE) + ); + +/* data bus direction: 0 = SNES -> FPGA; 1 = FPGA -> SNES + * data bus is always SNES -> FPGA to avoid fighting except when: + * a) the SNES wants to read + * b) we want to force a value on the bus + */ +assign SNES_DATABUS_DIR = (~SNES_READ | (~SNES_PARD & (r213f_enable))) ? + (1'b1 ^ (r213f_forceread & r213f_enable & ~SNES_PARD) + ^ (r2100_enable & ~SNES_PAWR & ~r2100_forcewrite & ~IS_ROM & ~IS_WRITABLE)) + : ((~SNES_PAWR & r2100_enable) ? r2100_forcewrite + : 1'b0); + +assign SNES_IRQ = 1'b0; + +endmodule diff --git a/verilog/sd2snes_spc7110/mcu_cmd.v b/verilog/sd2snes_spc7110/mcu_cmd.v new file mode 100644 index 000000000..0cf0bbd55 --- /dev/null +++ b/verilog/sd2snes_spc7110/mcu_cmd.v @@ -0,0 +1,597 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 21:57:50 08/25/2009 +// Design Name: +// Module Name: mcu_cmd +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module mcu_cmd( + input clk, + input cmd_ready, + input param_ready, + input [7:0] cmd_data, + input [7:0] param_data, + output [2:0] mcu_mapper, + output reg mcu_rrq = 0, + output mcu_write, + output reg mcu_wrq = 0, + input mcu_rq_rdy, + output [7:0] mcu_data_out, + input [7:0] mcu_data_in, + output [7:0] spi_data_out, + input [31:0] spi_byte_cnt, + input [2:0] spi_bit_cnt, + output [23:0] addr_out, + output [23:0] saveram_mask_out, + output [23:0] rom_mask_out, + /* SPC7110 data ROM window in PSRAM (SPI commands $d7 / $d8, see below) */ + output [23:0] drom_base_out, + output [23:0] drom_mask_out, + /* RTC-4513 time, pushed by the MCU with SPI command $e5 */ + output [55:0] rtc_data_out, + output rtc_pgm_we, +`ifndef MK2 + /* RTC-4513 battery backup, SPI commands $e6 (read) and $e7 (restore). + Layout in both directions is one status byte followed by the seven time + bytes of $e5: + byte 0 {2'b0, stopped, dow_owned, day-of-week[3:0]} + byte 1..7 packed BCD time, most significant byte first + $e6 is FPGA_CMD_RTCGET, which no other core implements for this core's + chip; $e7 is free here (the SPC7110 has no S-RTC to reset). */ + input [59:0] rtc_bkp_in, + input [1:0] rtc_bkp_flags_in, + output [59:0] rtc_bkp_out, + output [1:0] rtc_bkp_flags_out, + output rtc_bkp_we, +`endif + + // SD "DMA" extension + output SD_DMA_EN, + input SD_DMA_STATUS, + input SD_DMA_NEXTADDR, + input [7:0] SD_DMA_SRAM_DATA, + input SD_DMA_SRAM_WE, + output [1:0] SD_DMA_TGT, + output SD_DMA_PARTIAL, + output [10:0] SD_DMA_PARTIAL_START, + output [10:0] SD_DMA_PARTIAL_END, + output reg SD_DMA_START_MID_BLOCK, + output reg SD_DMA_END_MID_BLOCK, + + // DAC + output [10:0] dac_addr_out, + input DAC_STATUS, + output reg dac_play_out = 0, + output reg dac_reset_out = 0, + output reg [2:0] dac_vol_select_out = 3'b000, + output reg dac_palmode_out = 0, + output reg [8:0] dac_ptr_out = 0, + + // MSU data + output [13:0] msu_addr_out, + input [7:0] MSU_STATUS, + output [5:0] msu_status_reset_out, + output [5:0] msu_status_set_out, + output msu_status_reset_we, + input [31:0] msu_addressrq, + input [15:0] msu_trackrq, + input [7:0] msu_volumerq, + output [13:0] msu_ptr_out, + output msu_reset_out, + + // feature enable + output reg [15:0] featurebits_out, + + output reg region_out, + // SNES sync/clk + input snes_sysclk, + + // snes cmd interface + input [7:0] snescmd_data_in, + output reg [7:0] snescmd_data_out, + output reg [8:0] snescmd_addr_out, + output reg snescmd_we_out, + + // cheat configuration + output reg [7:0] cheat_pgm_idx_out, + output reg [31:0] cheat_pgm_data_out, + output reg cheat_pgm_we_out, + + // DSP core features + output reg [15:0] dsp_feat_out = 16'h0000 +); + +initial begin + region_out = 0; + SD_DMA_START_MID_BLOCK = 0; + SD_DMA_END_MID_BLOCK = 0; +end + +reg [2:0] MAPPER_BUF; +reg [23:0] ADDR_OUT_BUF; +reg [10:0] DAC_ADDR_OUT_BUF; +reg [7:0] DAC_VOL_OUT_BUF; +reg [13:0] MSU_ADDR_OUT_BUF; +reg [13:0] MSU_PTR_OUT_BUF; +reg [5:0] msu_status_set_out_buf; +reg [5:0] msu_status_reset_out_buf; +reg msu_status_reset_we_buf = 0; +reg MSU_RESET_OUT_BUF; + +reg [7:0] MCU_DATA_OUT_BUF; +reg [7:0] MCU_DATA_IN_BUF; +reg [2:0] mcu_nextaddr_buf; + +reg [7:0] dsp_feat_tmp; +reg [7:0] feat_tmp; + +wire mcu_nextaddr; + +reg DAC_STATUSr; +reg SD_DMA_STATUSr; +reg [7:0] MSU_STATUSr; +always @(posedge clk) begin + DAC_STATUSr <= DAC_STATUS; + SD_DMA_STATUSr <= SD_DMA_STATUS; + MSU_STATUSr <= MSU_STATUS; +end + +reg SD_DMA_PARTIALr; +assign SD_DMA_PARTIAL = SD_DMA_PARTIALr; + +reg SD_DMA_ENr; +assign SD_DMA_EN = SD_DMA_ENr; + +reg [1:0] SD_DMA_TGTr; +assign SD_DMA_TGT = SD_DMA_TGTr; + +reg [10:0] SD_DMA_PARTIAL_STARTr; +reg [10:0] SD_DMA_PARTIAL_ENDr; +assign SD_DMA_PARTIAL_START = SD_DMA_PARTIAL_STARTr; +assign SD_DMA_PARTIAL_END = SD_DMA_PARTIAL_ENDr; + +reg [23:0] SAVERAM_MASK; +reg [23:0] ROM_MASK; +/* SPC7110 data ROM: PSRAM base address of the DROM image and the wrap mask + applied to the linear DROM offset before the base is added. Both are + computed by the MCU from the ROM header (the DROM follows the program ROM in + the PSRAM image) and pushed with the two commands below. Neutral defaults: + base 0 / mask all-ones, i.e. straight linear addressing until programmed. */ +reg [23:0] DROM_BASE = 24'h000000; +reg [23:0] DROM_MASK = 24'hffffff; + +/* RTC time as the MCU packs it for command $e5: seven bytes MSB first, BCD + nibbles in the SPC7110 order plus the thousands of the year. pgm_we goes up + on the last data byte and back down on the next one, which is the pulse + rtc.v edge-detects on its own (8 MHz) clock. */ +reg [55:0] rtc_data_out_buf; +reg rtc_pgm_we_buf; + +/* $e6 read shadow and $e7 restore buffers. The shadow is taken in one clock so + the eight bytes the MCU reads cannot straddle a tick of the clock. */ +`ifndef MK2 +reg [59:0] rtc_bkp_in_buf; +reg [1:0] rtc_bkp_in_flags_buf; +reg [59:0] rtc_bkp_out_buf; +reg [1:0] rtc_bkp_out_flags_buf; +reg rtc_bkp_we_buf; +`endif + +assign spi_data_out = MCU_DATA_IN_BUF; + +initial begin + ADDR_OUT_BUF = 0; + DAC_ADDR_OUT_BUF = 0; + MSU_ADDR_OUT_BUF = 0; + SD_DMA_ENr = 0; + MAPPER_BUF = 1; + SD_DMA_PARTIALr = 0; +end + +// command interpretation +always @(posedge clk) begin + snescmd_we_out <= 1'b0; + cheat_pgm_we_out <= 1'b0; + dac_reset_out <= 1'b0; + MSU_RESET_OUT_BUF <= 1'b0; + + if (cmd_ready) begin + case (cmd_data[7:4]) + 4'h3: // select mapper + MAPPER_BUF <= cmd_data[2:0]; + 4'h4: begin// SD DMA + SD_DMA_ENr <= 1; + SD_DMA_TGTr <= cmd_data[1:0]; + SD_DMA_PARTIALr <= cmd_data[2]; + end + 4'h8: SD_DMA_TGTr <= 2'b00; + 4'h9: SD_DMA_TGTr <= 2'b00; // cmd_data[1:0]; // not implemented +// 4'hE: +// select memory unit + endcase + end else if (param_ready) begin + casex (cmd_data[7:0]) + 8'h1x: + case (spi_byte_cnt) + 32'h2: + ROM_MASK[23:16] <= param_data; + 32'h3: + ROM_MASK[15:8] <= param_data; + 32'h4: + ROM_MASK[7:0] <= param_data; + endcase + 8'h2x: + case (spi_byte_cnt) + 32'h2: + SAVERAM_MASK[23:16] <= param_data; + 32'h3: + SAVERAM_MASK[15:8] <= param_data; + 32'h4: + SAVERAM_MASK[7:0] <= param_data; + endcase + /* $d7 bbhhll: set SPC7110 data ROM base address in PSRAM. + $d8 bbhhll: set SPC7110 data ROM wrap mask. + Opcodes $d7/$d8 are free in every other core (base/sa1 stop at $d6), so + a future firmware may send them unconditionally; cores that do not + decode them simply drop the bytes. */ + 8'hd7: + case (spi_byte_cnt) + 32'h2: + DROM_BASE[23:16] <= param_data; + 32'h3: + DROM_BASE[15:8] <= param_data; + 32'h4: + DROM_BASE[7:0] <= param_data; + endcase + 8'hd8: + case (spi_byte_cnt) + 32'h2: + DROM_MASK[23:16] <= param_data; + 32'h3: + DROM_MASK[15:8] <= param_data; + 32'h4: + DROM_MASK[7:0] <= param_data; + endcase + 8'h4x: + SD_DMA_ENr <= 1'b0; + 8'h6x: + case (spi_byte_cnt) + 32'h2: begin + SD_DMA_START_MID_BLOCK <= param_data[7]; + SD_DMA_PARTIAL_STARTr[10:9] <= param_data[1:0]; + end + 32'h3: + SD_DMA_PARTIAL_STARTr[8:0] <= {param_data, 1'b0}; + 32'h4: begin + SD_DMA_END_MID_BLOCK <= param_data[7]; + SD_DMA_PARTIAL_ENDr[10:9] <= param_data[1:0]; + end + 32'h5: + SD_DMA_PARTIAL_ENDr[8:0] <= {param_data, 1'b0}; + endcase + 8'h9x: + MCU_DATA_OUT_BUF <= param_data; + 8'hd0: + case (spi_byte_cnt) + 32'h2: + snescmd_addr_out[7:0] <= param_data; + 32'h3: + snescmd_addr_out[8] <= param_data[0]; + endcase + 8'hd1: + snescmd_addr_out <= snescmd_addr_out + 1; + 8'hd2: begin + case (spi_byte_cnt) + 32'h2: + snescmd_we_out <= 1'b1; + 32'h3: + snescmd_addr_out <= snescmd_addr_out + 1; + endcase + snescmd_data_out <= param_data; + end + 8'hd3: begin + case (spi_byte_cnt) + 32'h2: + cheat_pgm_idx_out <= param_data[2:0]; + 32'h3: + cheat_pgm_data_out[31:24] <= param_data; + 32'h4: + cheat_pgm_data_out[23:16] <= param_data; + 32'h5: + cheat_pgm_data_out[15:8] <= param_data; + 32'h6: begin + cheat_pgm_data_out[7:0] <= param_data; + cheat_pgm_we_out <= 1'b1; + end + endcase + end + 8'he0: + case (spi_byte_cnt) + 32'h2: begin + msu_status_set_out_buf <= param_data[5:0]; + end + 32'h3: begin + msu_status_reset_out_buf <= param_data[5:0]; + msu_status_reset_we_buf <= 1'b1; + end + 32'h4: + msu_status_reset_we_buf <= 1'b0; + endcase + 8'he1: // pause DAC + dac_play_out <= 1'b0; + 8'he2: // resume DAC + dac_play_out <= 1'b1; + 8'he3: // reset DAC (set DAC playback address = 0) + case (spi_byte_cnt) + 32'h2: + dac_ptr_out[8] <= param_data[0]; + 32'h3: begin + dac_ptr_out[7:0] <= param_data; + dac_reset_out <= 1'b1; // reset by default value, see above + end + endcase + 8'he4: // reset MSU read buffer pointer + case (spi_byte_cnt) + 32'h2: begin + MSU_PTR_OUT_BUF[13:8] <= param_data[5:0]; + MSU_PTR_OUT_BUF[7:0] <= 8'h0; + end + 32'h3: begin + MSU_PTR_OUT_BUF[7:0] <= param_data; + MSU_RESET_OUT_BUF <= 1'b1; + end + endcase + 8'he5: // set RTC (SPC7110 format + 1000s of year, nibbles packed) + case (spi_byte_cnt) + 32'h2: + rtc_data_out_buf[55:48] <= param_data; + 32'h3: + rtc_data_out_buf[47:40] <= param_data; + 32'h4: + rtc_data_out_buf[39:32] <= param_data; + 32'h5: + rtc_data_out_buf[31:24] <= param_data; + 32'h6: + rtc_data_out_buf[23:16] <= param_data; + 32'h7: + rtc_data_out_buf[15:8] <= param_data; + 32'h8: begin + rtc_data_out_buf[7:0] <= param_data; + rtc_pgm_we_buf <= 1'b1; + end + 32'h9: + rtc_pgm_we_buf <= 1'b0; + endcase +`ifndef MK2 + 8'he7: // restore RTC battery backup (status byte + the seven $e5 bytes) + case (spi_byte_cnt) + 32'h2: begin + rtc_bkp_out_flags_buf <= param_data[5:4]; + rtc_bkp_out_buf[59:56] <= param_data[3:0]; + end + 32'h3: + rtc_bkp_out_buf[55:48] <= param_data; + 32'h4: + rtc_bkp_out_buf[47:40] <= param_data; + 32'h5: + rtc_bkp_out_buf[39:32] <= param_data; + 32'h6: + rtc_bkp_out_buf[31:24] <= param_data; + 32'h7: + rtc_bkp_out_buf[23:16] <= param_data; + 32'h8: + rtc_bkp_out_buf[15:8] <= param_data; + 32'h9: begin + rtc_bkp_out_buf[7:0] <= param_data; + rtc_bkp_we_buf <= 1'b1; + end + 32'ha: + rtc_bkp_we_buf <= 1'b0; + endcase +`endif + 8'hec: + begin // set DAC properties + dac_vol_select_out <= param_data[2:0]; + dac_palmode_out <= param_data[7]; + end + 8'hed: + case (spi_byte_cnt) + 32'h2: feat_tmp <= param_data; + 32'h3: featurebits_out <= {feat_tmp, param_data}; + endcase + 8'hee: + region_out <= param_data[0]; + 8'hef: + case (spi_byte_cnt) + 32'h2: dsp_feat_tmp <= param_data[7:0]; + 32'h3: begin + dsp_feat_out <= {dsp_feat_tmp, param_data[7:0]}; + end + endcase + endcase + end +end + +always @(posedge clk) begin + if(param_ready && cmd_data[7:4] == 4'h0) begin + case (cmd_data[1:0]) + 2'b01: begin + case (spi_byte_cnt) + 32'h2: begin + DAC_ADDR_OUT_BUF[10:8] <= param_data[2:0]; + DAC_ADDR_OUT_BUF[7:0] <= 8'b0; + end + 32'h3: + DAC_ADDR_OUT_BUF[7:0] <= param_data; + endcase + end + 2'b10: begin + case (spi_byte_cnt) + 32'h2: begin + MSU_ADDR_OUT_BUF[13:8] <= param_data[5:0]; + MSU_ADDR_OUT_BUF[7:0] <= 8'b0; + end + 32'h3: + MSU_ADDR_OUT_BUF[7:0] <= param_data; + endcase + end + default: + case (spi_byte_cnt) + 32'h2: begin + ADDR_OUT_BUF[23:16] <= param_data; + ADDR_OUT_BUF[15:0] <= 16'b0; + end + 32'h3: + ADDR_OUT_BUF[15:8] <= param_data; + 32'h4: + ADDR_OUT_BUF[7:0] <= param_data; + endcase + endcase + end else if (SD_DMA_NEXTADDR | (mcu_nextaddr & (cmd_data[7:5] == 3'h4) + && (cmd_data[3]) + && (spi_byte_cnt >= (32'h1+cmd_data[4]))) + ) begin + case (SD_DMA_TGTr) + 2'b00: ADDR_OUT_BUF <= ADDR_OUT_BUF + 1; + 2'b01: DAC_ADDR_OUT_BUF <= DAC_ADDR_OUT_BUF + 1; + 2'b10: MSU_ADDR_OUT_BUF <= MSU_ADDR_OUT_BUF + 1; + endcase + end +end + +// value fetch during last SPI bit +always @(posedge clk) begin + if (cmd_data[7:4] == 4'h8 && mcu_nextaddr) + MCU_DATA_IN_BUF <= mcu_data_in; + else if (cmd_ready | param_ready /* bit_cnt == 7 */) begin + if (cmd_data[7:4] == 4'hA) + MCU_DATA_IN_BUF <= snescmd_data_in; +`ifndef MK2 + if (cmd_data[7:0] == 8'hE6) + case (spi_byte_cnt) + 32'h1: begin + rtc_bkp_in_buf <= rtc_bkp_in; + rtc_bkp_in_flags_buf <= rtc_bkp_flags_in; + end + 32'h2: + MCU_DATA_IN_BUF <= {2'b00, rtc_bkp_in_flags_buf, rtc_bkp_in_buf[59:56]}; + 32'h3: + MCU_DATA_IN_BUF <= rtc_bkp_in_buf[55:48]; + 32'h4: + MCU_DATA_IN_BUF <= rtc_bkp_in_buf[47:40]; + 32'h5: + MCU_DATA_IN_BUF <= rtc_bkp_in_buf[39:32]; + 32'h6: + MCU_DATA_IN_BUF <= rtc_bkp_in_buf[31:24]; + 32'h7: + MCU_DATA_IN_BUF <= rtc_bkp_in_buf[23:16]; + 32'h8: + MCU_DATA_IN_BUF <= rtc_bkp_in_buf[15:8]; + 32'h9: + MCU_DATA_IN_BUF <= rtc_bkp_in_buf[7:0]; + endcase +`endif + + if (cmd_data[7:0] == 8'hF0) + MCU_DATA_IN_BUF <= 8'hA5; + else if (cmd_data[7:0] == 8'hF1) + case (spi_byte_cnt[0]) + 1'b1: // buffer status (1st byte) + MCU_DATA_IN_BUF <= {SD_DMA_STATUSr, DAC_STATUSr, MSU_STATUSr[7], 5'b0}; + 1'b0: // control status (2nd byte) + MCU_DATA_IN_BUF <= {1'b0, MSU_STATUSr[6:0]}; + endcase + else if (cmd_data[7:0] == 8'hF2) + case (spi_byte_cnt) + 32'h1: + MCU_DATA_IN_BUF <= msu_addressrq[31:24]; + 32'h2: + MCU_DATA_IN_BUF <= msu_addressrq[23:16]; + 32'h3: + MCU_DATA_IN_BUF <= msu_addressrq[15:8]; + 32'h4: + MCU_DATA_IN_BUF <= msu_addressrq[7:0]; + endcase + else if (cmd_data[7:0] == 8'hF3) + case (spi_byte_cnt) + 32'h1: + MCU_DATA_IN_BUF <= msu_trackrq[15:8]; + 32'h2: + MCU_DATA_IN_BUF <= msu_trackrq[7:0]; + endcase + else if (cmd_data[7:0] == 8'hF4) + MCU_DATA_IN_BUF <= msu_volumerq; + else if (cmd_data[7:0] == 8'hFF) + MCU_DATA_IN_BUF <= param_data; + else if (cmd_data[7:0] == 8'hD1) + MCU_DATA_IN_BUF <= snescmd_data_in; + end +end + +// nextaddr pulse generation +always @(posedge clk) begin + mcu_nextaddr_buf <= {mcu_nextaddr_buf[1:0], mcu_rq_rdy}; +end + +always @(posedge clk) begin + mcu_rrq <= 1'b0; + if((param_ready | cmd_ready) && cmd_data[7:4] == 4'h8) begin + mcu_rrq <= 1'b1; + end +end + +always @(posedge clk) begin + mcu_wrq <= 1'b0; + if(param_ready && cmd_data[7:4] == 4'h9) begin + mcu_wrq <= 1'b1; + end +end + +// trigger for nextaddr +assign mcu_nextaddr = mcu_nextaddr_buf == 2'b01; + +assign mcu_write = SD_DMA_STATUS + ?(SD_DMA_TGTr == 2'b00 + ? SD_DMA_SRAM_WE + : 1'b1 + ) + : 1'b1; + +assign addr_out = ADDR_OUT_BUF; +assign dac_addr_out = DAC_ADDR_OUT_BUF; +assign msu_addr_out = MSU_ADDR_OUT_BUF; +assign msu_status_reset_we = msu_status_reset_we_buf; +assign msu_status_reset_out = msu_status_reset_out_buf; +assign msu_status_set_out = msu_status_set_out_buf; +assign msu_reset_out = MSU_RESET_OUT_BUF; +assign msu_ptr_out = MSU_PTR_OUT_BUF; + +assign mcu_data_out = SD_DMA_STATUS ? SD_DMA_SRAM_DATA : MCU_DATA_OUT_BUF; +assign mcu_mapper = MAPPER_BUF; +assign rom_mask_out = ROM_MASK; +assign saveram_mask_out = SAVERAM_MASK; +assign drom_base_out = DROM_BASE; +assign drom_mask_out = DROM_MASK; +assign rtc_data_out = rtc_data_out_buf; +assign rtc_pgm_we = rtc_pgm_we_buf; +`ifndef MK2 +assign rtc_bkp_out = rtc_bkp_out_buf; +assign rtc_bkp_flags_out = rtc_bkp_out_flags_buf; +assign rtc_bkp_we = rtc_bkp_we_buf; +`endif + +assign DBG_mcu_nextaddr = mcu_nextaddr; +endmodule diff --git a/verilog/sd2snes_spc7110/msu.v b/verilog/sd2snes_spc7110/msu.v new file mode 100644 index 000000000..6fb19a3ac --- /dev/null +++ b/verilog/sd2snes_spc7110/msu.v @@ -0,0 +1,215 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 14:55:04 12/14/2010 +// Design Name: +// Module Name: msu +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module msu( + input clkin, + input enable, + input [13:0] pgm_address, + input [7:0] pgm_data, + input pgm_we, + input [2:0] reg_addr, + input [7:0] reg_data_in, + output [7:0] reg_data_out, + input reg_oe_falling, + input reg_oe_rising, + input reg_we_rising, + output [7:0] status_out, + output [7:0] volume_out, + output volume_latch_out, + output [31:0] addr_out, + output [15:0] track_out, + input [5:0] status_reset_bits, + input [5:0] status_set_bits, + input status_reset_we, + input [13:0] msu_address_ext, + input msu_address_ext_write, + + output DBG_msu_reg_oe_rising, + output DBG_msu_reg_oe_falling, + output DBG_msu_reg_we_rising, + output [13:0] DBG_msu_address, + output DBG_msu_address_ext_write_rising +); + +reg msu_addr_inc_arm = 0; + +reg [1:0] status_reset_we_r; +always @(posedge clkin) status_reset_we_r = {status_reset_we_r[0], status_reset_we}; +wire status_reset_en = (status_reset_we_r == 2'b01); + +reg [13:0] msu_address_r; +wire [13:0] msu_address = msu_address_r; +initial msu_address_r = 13'b0; + +wire [7:0] msu_data; +reg [7:0] msu_data_r; + +reg [2:0] msu_address_ext_write_sreg; +always @(posedge clkin) + msu_address_ext_write_sreg <= {msu_address_ext_write_sreg[1:0], msu_address_ext_write}; +wire msu_address_ext_write_rising = (msu_address_ext_write_sreg[2:1] == 2'b01); + +reg [31:0] addr_out_r; +assign addr_out = addr_out_r; + +reg [15:0] track_out_r; +assign track_out = track_out_r; + +reg [7:0] volume_r; +assign volume_out = volume_r; + +reg volume_start_r; +assign volume_latch_out = volume_start_r; + +reg audio_start_r; +reg audio_busy_r; +reg data_start_r; +reg data_busy_r; +reg ctrl_start_r; +reg audio_error_r; +reg [2:0] audio_ctrl_r; +reg [1:0] audio_status_r; + +initial begin + audio_busy_r = 1'b1; + data_busy_r = 1'b1; + audio_error_r = 1'b0; + volume_r = 8'h00; + addr_out_r = 32'h00000000; + track_out_r = 16'h0000; + data_start_r = 1'b0; + audio_start_r = 1'b0; +end + +assign DBG_msu_address = msu_address; +assign DBG_msu_reg_oe_rising = reg_oe_rising; +assign DBG_msu_reg_oe_falling = reg_oe_falling; +assign DBG_msu_reg_we_rising = reg_we_rising; +assign DBG_msu_address_ext_write_rising = msu_address_ext_write_rising; + +assign status_out = {msu_address_r[13], // 7 + audio_start_r, // 6 + data_start_r, // 5 + volume_start_r, // 4 + audio_ctrl_r, // 3:1 + ctrl_start_r}; // 0 + +initial msu_address_r = 14'h1234; + +`ifdef MK2 +msu_databuf snes_msu_databuf ( + .clka(clkin), + .wea(~pgm_we), // Bus [0 : 0] + .addra(pgm_address), // Bus [13 : 0] + .dina(pgm_data), // Bus [7 : 0] + .clkb(clkin), + .addrb(msu_address), // Bus [13 : 0] + .doutb(msu_data) +); // Bus [7 : 0] +`endif +`ifdef MK3 +msu_databuf snes_msu_databuf ( + .clock(clkin), + .wren(~pgm_we), // Bus [0 : 0] + .wraddress(pgm_address), // Bus [13 : 0] + .data(pgm_data), // Bus [7 : 0] + .rdaddress(msu_address), // Bus [13 : 0] + .q(msu_data) +); // Bus [7 : 0] +`endif + +reg [7:0] data_out_r; +assign reg_data_out = data_out_r; + +always @(posedge clkin) begin + if(msu_address_ext_write_rising) begin + msu_address_r <= msu_address_ext; + end else if(reg_oe_rising & msu_addr_inc_arm) begin + msu_address_r <= msu_address_r + 1; + end +end + +always @(posedge clkin) begin + if(reg_oe_falling & enable & (reg_addr == 3'h1)) begin + msu_addr_inc_arm <= 1'b1; + end else if(reg_oe_rising) begin + msu_addr_inc_arm <= 1'b0; + end +end + +always @(posedge clkin) begin + if(reg_oe_falling & enable) + case(reg_addr) + 3'h0: data_out_r <= {data_busy_r, audio_busy_r, audio_status_r, audio_error_r, 3'b010}; + 3'h1: data_out_r <= msu_data; + 3'h2: data_out_r <= 8'h53; + 3'h3: data_out_r <= 8'h2d; + 3'h4: data_out_r <= 8'h4d; + 3'h5: data_out_r <= 8'h53; + 3'h6: data_out_r <= 8'h55; + 3'h7: data_out_r <= 8'h31; + endcase +end + +always @(posedge clkin) begin + if(reg_we_rising & enable) begin + case(reg_addr) + 3'h0: addr_out_r[7:0] <= reg_data_in; + 3'h1: addr_out_r[15:8] <= reg_data_in; + 3'h2: addr_out_r[23:16] <= reg_data_in; + 3'h3: begin + addr_out_r[31:24] <= reg_data_in; + data_start_r <= 1'b1; + data_busy_r <= 1'b1; + end + 3'h4: begin + track_out_r[7:0] <= reg_data_in; + end + 3'h5: begin + track_out_r[15:8] <= reg_data_in; + audio_start_r <= 1'b1; + audio_busy_r <= 1'b1; + end + 3'h6: begin + volume_r <= reg_data_in; + volume_start_r <= 1'b1; + end + 3'h7: begin + if(!audio_busy_r) begin + audio_ctrl_r <= reg_data_in[2:0]; + ctrl_start_r <= 1'b1; + end + end + endcase + end else if (status_reset_en) begin + audio_busy_r <= (audio_busy_r | status_set_bits[5]) & ~status_reset_bits[5]; + if(status_reset_bits[5]) audio_start_r <= 1'b0; + data_busy_r <= (data_busy_r | status_set_bits[4]) & ~status_reset_bits[4]; + if(status_reset_bits[4]) data_start_r <= 1'b0; + audio_error_r <= (audio_error_r | status_set_bits[3]) & ~status_reset_bits[3]; + audio_status_r <= (audio_status_r | status_set_bits[2:1]) & ~status_reset_bits[2:1]; + ctrl_start_r <= (ctrl_start_r | status_set_bits[0]) & ~status_reset_bits[0]; + end else begin + volume_start_r <= 1'b0; + end +end + + +endmodule diff --git a/verilog/sd2snes_spc7110/pll.ppf b/verilog/sd2snes_spc7110/pll.ppf new file mode 100644 index 000000000..b8f2c8e9a --- /dev/null +++ b/verilog/sd2snes_spc7110/pll.ppf @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/verilog/sd2snes_spc7110/rtc.v b/verilog/sd2snes_spc7110/rtc.v new file mode 100644 index 000000000..6e794b627 --- /dev/null +++ b/verilog/sd2snes_spc7110/rtc.v @@ -0,0 +1,472 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 23:32:12 01/08/2011 +// Design Name: +// Module Name: rtc_srtc +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +`ifdef MK2 + `define SECOND_PERIOD 24000000 +`else + `define SECOND_PERIOD 8000000 +`endif +module rtc ( + input clkin, + input pgm_we, + input [55:0] rtc_data_in, + input we1, + input [59:0] rtc_data_in1, + output [59:0] rtc_data +); + +reg [59:0] rtc_data_r; +reg [59:0] rtc_data_out_r; + +reg [1:0] pgm_we_sreg; +always @(posedge clkin) pgm_we_sreg <= {pgm_we_sreg[0], pgm_we}; +wire pgm_we_rising = (pgm_we_sreg[1:0] == 2'b01); + +reg [2:0] we1_sreg; +always @(posedge clkin) we1_sreg <= {we1_sreg[1:0], we1}; +wire we1_rising = (we1_sreg[2:1] == 2'b01); + +reg [31:0] tick_cnt; + +always @(posedge clkin) begin + tick_cnt <= tick_cnt + 1; + if((tick_cnt == `SECOND_PERIOD) || pgm_we_rising) tick_cnt <= 0; +end + +assign rtc_data = rtc_data_out_r; + +reg [31:0] rtc_state; +/* Carry between the digits of one per-second pass. It has to be cleared + whenever the counter is loaded from outside: the load takes priority for that + clock but the REST of the pass still runs, and every state after STATE_SEC1 + increments on carry alone. A load that landed mid pass therefore used to + apply the leftover carry to the digits it had just been handed - a write of + 23:59:59 arriving after the seconds had rolled came out as 00:10:02 - which + is a fraction of a percent of loads, i.e. exactly the kind of thing that only + ever shows up on somebody else's console. */ +reg carry; + +reg [3:0] dom1[11:0]; +reg [3:0] dom10[11:0]; +// month / year are derived combinationally from rtc_data_r. They used to be +// registers loaded in STATE_YEAR1, i.e. one full scan AFTER the STATE_DAY1 that +// consumes them: right after an SNES side write that changes the month +// (rtc_data_in1 / we1) the day rollover of the very next scan still used the +// PREVIOUS month's length, so writing 31 Dec while the clock stood in a 28 or +// 30 day month produced day 32. +wire [7:0] year_bin = {rtc_data_r[47:44], 3'b000} + {rtc_data_r[47:44], 1'b0} + + rtc_data_r[43:40]; +wire [3:0] month = rtc_data_r[35:32] + (rtc_data_r[36] ? 4'd10 : 4'd0) - 4'd1; +wire [1:0] year = year_bin[1:0]; + +reg [4:0] dow_day; +reg [3:0] dow_month; +reg [13:0] dow_year; +reg [6:0] dow_year1; +reg [6:0] dow_year100; +reg [15:0] dow_tmp; +reg [15:0] dow_year_tmp; + +parameter [31:0] + STATE_SEC1 = 32'b00000000000000000000000000000001, + STATE_SEC10 = 32'b00000000000000000000000000000010, + STATE_MIN1 = 32'b00000000000000000000000000000100, + STATE_MIN10 = 32'b00000000000000000000000000001000, + STATE_HOUR1 = 32'b00000000000000000000000000010000, + STATE_HOUR10 = 32'b00000000000000000000000000100000, + STATE_DAY1 = 32'b00000000000000000000000001000000, + STATE_DAY10 = 32'b00000000000000000000000010000000, + STATE_MON1 = 32'b00000000000000000000000100000000, + STATE_MON10 = 32'b00000000000000000000001000000000, + STATE_YEAR1 = 32'b00000000000000000000010000000000, + STATE_YEAR10 = 32'b00000000000000000000100000000000, + STATE_YEAR100 = 32'b00000000000000000001000000000000, + STATE_YEAR1000 = 32'b00000000000000000010000000000000, + STATE_DOW0_0 = 32'b00000000000000000100000000000000, + STATE_DOW0_1 = 32'b00000000000000001000000000000000, + STATE_DOW0_2 = 32'b00000000000000010000000000000000, + STATE_DOW1_0_0 = 32'b00000000000000100000000000000000, + STATE_DOW1_0_1 = 32'b00000000000001000000000000000000, + STATE_DOW1_0_2 = 32'b00000000000010000000000000000000, + STATE_DOW1_0_3 = 32'b00000000000100000000000000000000, + STATE_DOW1_0_4 = 32'b00000000001000000000000000000000, + STATE_DOW1_1_0 = 32'b00000000010000000000000000000000, + STATE_DOW1_1_1 = 32'b00000000100000000000000000000000, + STATE_DOW1_1_2 = 32'b00000001000000000000000000000000, + STATE_DOW1_1_3 = 32'b00000010000000000000000000000000, + STATE_DOW2 = 32'b00000100000000000000000000000000, + STATE_DOW3 = 32'b00001000000000000000000000000000, + STATE_DOW4 = 32'b00010000000000000000000000000000, + STATE_DOW5 = 32'b00100000000000000000000000000000, + STATE_LATCH = 32'b01000000000000000000000000000000, + STATE_IDLE = 32'b10000000000000000000000000000000; + +initial begin + rtc_state = STATE_IDLE; + dom1[0] = 1; dom10[0] = 3; + dom1[1] = 8; dom10[1] = 2; + dom1[2] = 1; dom10[2] = 3; + dom1[3] = 0; dom10[3] = 3; + dom1[4] = 1; dom10[4] = 3; + dom1[5] = 0; dom10[5] = 3; + dom1[6] = 1; dom10[6] = 3; + dom1[7] = 1; dom10[7] = 3; + dom1[8] = 0; dom10[8] = 3; + dom1[9] = 1; dom10[9] = 3; + dom1[10] = 0; dom10[10] = 3; + dom1[11] = 1; dom10[11] = 3; + rtc_data_r = 60'h220110301000000; + tick_cnt = 0; + dow_year_tmp = 0; +end + +wire is_leapyear_feb = (month == 1) && (year[1:0] == 2'b00); + +always @(posedge clkin) begin + if(!tick_cnt) begin + rtc_state <= STATE_SEC1; + end else begin + case (rtc_state) + STATE_SEC1: + rtc_state <= STATE_SEC10; + STATE_SEC10: + rtc_state <= STATE_MIN1; + STATE_MIN1: + rtc_state <= STATE_MIN10; + STATE_MIN10: + rtc_state <= STATE_HOUR1; + STATE_HOUR1: + rtc_state <= STATE_HOUR10; + STATE_HOUR10: + rtc_state <= STATE_DAY1; + STATE_DAY1: + rtc_state <= STATE_DAY10; + STATE_DAY10: + rtc_state <= STATE_MON1; + STATE_MON1: + rtc_state <= STATE_MON10; + STATE_MON10: + rtc_state <= STATE_YEAR1; + STATE_YEAR1: + rtc_state <= STATE_YEAR10; + STATE_YEAR10: + rtc_state <= STATE_YEAR100; + STATE_YEAR100: + rtc_state <= STATE_YEAR1000; + STATE_YEAR1000: + rtc_state <= STATE_DOW0_0; + STATE_DOW0_0: + rtc_state <= STATE_DOW0_1; + STATE_DOW0_1: + rtc_state <= STATE_DOW0_2; + STATE_DOW0_2: + if(dow_month <= 2) + rtc_state <= STATE_DOW1_0_0; + else + rtc_state <= STATE_DOW1_1_0; + STATE_DOW1_0_0: + rtc_state <= STATE_DOW1_0_1; + STATE_DOW1_0_1: + rtc_state <= STATE_DOW1_0_2; + STATE_DOW1_0_2: + rtc_state <= STATE_DOW1_0_3; + STATE_DOW1_0_3: + rtc_state <= STATE_DOW1_0_4; + STATE_DOW1_1_0: + rtc_state <= STATE_DOW1_1_1; + STATE_DOW1_1_1: + rtc_state <= STATE_DOW1_1_2; + STATE_DOW1_1_2: + rtc_state <= STATE_DOW1_1_3; + STATE_DOW1_0_4, + STATE_DOW1_1_3: + rtc_state <= STATE_DOW2; + STATE_DOW2: + rtc_state <= STATE_DOW3; + STATE_DOW3: + rtc_state <= STATE_DOW4; + STATE_DOW4: + if(dow_tmp > 13) + rtc_state <= STATE_DOW4; + else + rtc_state <= STATE_DOW5; + STATE_DOW5: + rtc_state <= STATE_LATCH; + STATE_LATCH: + rtc_state <= STATE_IDLE; + default: + rtc_state <= STATE_IDLE; + endcase + end +end + +always @(posedge clkin) begin + if(pgm_we_rising) begin + rtc_data_r[55:0] <= rtc_data_in; + carry <= 0; + end else if (we1_rising) begin + rtc_data_r <= rtc_data_in1; + carry <= 0; + end else begin + case(rtc_state) + STATE_SEC1: begin + if(rtc_data_r[3:0] == 9) begin + rtc_data_r[3:0] <= 0; + carry <= 1; + end else begin + rtc_data_r[3:0] <= rtc_data_r[3:0] + 1; + carry <= 0; + end + end + STATE_SEC10: begin + if(carry) begin + if(rtc_data_r[7:4] == 5) begin + rtc_data_r[7:4] <= 0; + carry <= 1; + end else begin + rtc_data_r[7:4] <= rtc_data_r[7:4] + 1; + carry <= 0; + end + end + end + STATE_MIN1: begin + if(carry) begin + if(rtc_data_r[11:8] == 9) begin + rtc_data_r[11:8] <= 0; + carry <= 1; + end else begin + rtc_data_r[11:8] <= rtc_data_r[11:8] + 1; + carry <= 0; + end + end + end + STATE_MIN10: begin + if(carry) begin + if(rtc_data_r[15:12] == 5) begin + rtc_data_r[15:12] <= 0; + carry <= 1; + end else begin + rtc_data_r[15:12] <= rtc_data_r[15:12] + 1; + carry <= 0; + end + end + end + STATE_HOUR1: begin + if(carry) begin + if(rtc_data_r[23:20] == 2 && rtc_data_r[19:16] == 3) begin + rtc_data_r[19:16] <= 0; + carry <= 1; + end else if (rtc_data_r[19:16] == 9) begin + rtc_data_r[19:16] <= 0; + carry <= 1; + end else begin + rtc_data_r[19:16] <= rtc_data_r[19:16] + 1; + carry <= 0; + end + end + end + STATE_HOUR10: begin + if(carry) begin + if(rtc_data_r[23:20] == 2) begin + rtc_data_r[23:20] <= 0; + carry <= 1; + end else begin + rtc_data_r[23:20] <= rtc_data_r[23:20] + 1; + carry <= 0; + end + end + end + STATE_DAY1: begin + if(carry) begin + if(rtc_data_r[31:28] == dom10[month] + && rtc_data_r[27:24] == dom1[month] + is_leapyear_feb) begin + rtc_data_r[27:24] <= 0; + carry <= 1; + end else if (rtc_data_r[27:24] == 9) begin + rtc_data_r[27:24] <= 0; + carry <= 1; + end else begin + rtc_data_r[27:24] <= rtc_data_r[27:24] + 1; + carry <= 0; + end + end + end + STATE_DAY10: begin + if(carry) begin + if(rtc_data_r[31:28] == dom10[month]) begin + rtc_data_r[31:28] <= 0; + rtc_data_r[27:24] <= 1; + carry <= 1; + end else begin + rtc_data_r[31:28] <= rtc_data_r[31:28] + 1; + carry <= 0; + end + end + end + STATE_MON1: begin + if(carry) begin + if(rtc_data_r[39:36] == 1 && rtc_data_r[35:32] == 2) begin + rtc_data_r[35:32] <= 1; + carry <= 1; + end else if (rtc_data_r[35:32] == 9) begin + rtc_data_r[35:32] <= 0; + carry <= 1; + end else begin + rtc_data_r[35:32] <= rtc_data_r[35:32] + 1; + carry <= 0; + end + end + end + STATE_MON10: begin + if(carry) begin + if(rtc_data_r[39:36] == 1) begin + rtc_data_r[39:36] <= 0; + carry <= 1; + end else begin + rtc_data_r[39:36] <= rtc_data_r[39:36] + 1; + carry <= 0; + end + end + end + STATE_YEAR1: begin + if(carry) begin + if(rtc_data_r[43:40] == 9) begin + rtc_data_r[43:40] <= 0; + carry <= 1; + end else begin + rtc_data_r[43:40] <= rtc_data_r[43:40] + 1; + carry <= 0; + end + end + end + STATE_YEAR10: begin + if(carry) begin + if(rtc_data_r[47:44] == 9) begin + rtc_data_r[47:44] <= 0; + carry <= 1; + end else begin + rtc_data_r[47:44] <= rtc_data_r[47:44] + 1; + carry <= 0; + end + end + end + STATE_YEAR100: begin + if(carry) begin + if(rtc_data_r[51:48] == 9) begin + rtc_data_r[51:48] <= 0; + carry <= 1; + end else begin + rtc_data_r[51:48] <= rtc_data_r[51:48] + 1; + carry <= 0; + end + end + end + STATE_YEAR1000: begin + if(carry) begin + if(rtc_data_r[55:52] == 9) begin + rtc_data_r[55:52] <= 0; + carry <= 1; + end else begin + rtc_data_r[55:52] <= rtc_data_r[55:52] + 1; + carry <= 0; + end + end + end + STATE_DOW0_0: begin + dow_year1 <= rtc_data_r[43:40]; + dow_year100 <= rtc_data_r[51:48]; + dow_month <= month + 1; + dow_day <= rtc_data_r[27:24]; + end + STATE_DOW0_1: begin + dow_year1 <= dow_year1 + (rtc_data_r[47:44] << 1); + dow_year100 <= dow_year100 + (rtc_data_r[55:52] << 1); + dow_day <= dow_day + (rtc_data_r[31:28] << 1); // parens: + binds tighter than << in Verilog; without them the accumulator itself was doubled (day-of-week wrong for 84% of dates) + end + STATE_DOW0_2: begin + dow_year1 <= dow_year1 + (rtc_data_r[47:44] << 3); + dow_year100 <= dow_year100 + (rtc_data_r[55:52] << 3); + dow_day <= dow_day + (rtc_data_r[31:28] << 3); + end + STATE_DOW1_0_0: begin + dow_month <= dow_month + 10; + dow_year <= dow_year1; + end + STATE_DOW1_0_1: begin + dow_year <= dow_year + (dow_year100 << 2); + end + STATE_DOW1_0_2: begin + dow_year <= dow_year + (dow_year100 << 5); + end + STATE_DOW1_0_3: begin + dow_year <= dow_year + (dow_year100 << 6) - 1; + end + STATE_DOW1_0_4: begin + if(dow_year1) + dow_year1 <= dow_year1 - 1; + else begin + dow_year1 <= 99; + dow_year100 <= dow_year100 - 1; + end + end + STATE_DOW1_1_0: begin + dow_month <= dow_month - 2; + dow_year <= dow_year1; + end + STATE_DOW1_1_1: begin + dow_year <= dow_year + (dow_year100 << 2); + end + STATE_DOW1_1_2: begin + dow_year <= dow_year + (dow_year100 << 5); + end + STATE_DOW1_1_3: begin + dow_year <= dow_year + (dow_year100 << 6); + end + + STATE_DOW2: begin + dow_tmp <= (83 * dow_month); + dow_year_tmp <= dow_year + + (dow_year >> 2) + - (dow_year100) + + (dow_year100 >> 2); + end + STATE_DOW3: begin + dow_tmp <= (dow_tmp >> 5) + + dow_day + + dow_year_tmp; + //+ dow_year + //+ (dow_year >> 2) + //- (dow_year100) + //+ (dow_year100 >> 2); + end + STATE_DOW4: begin + dow_tmp <= dow_tmp - 7; + end + STATE_DOW5: begin + rtc_data_r[59:56] <= {1'b0, dow_tmp[2:0]}; + end + STATE_LATCH: begin + rtc_data_out_r <= rtc_data_r; + end + endcase + end +end + +endmodule diff --git a/verilog/sd2snes_spc7110/sd2snes_spc7110.qpf b/verilog/sd2snes_spc7110/sd2snes_spc7110.qpf new file mode 100644 index 000000000..143b1c6ab --- /dev/null +++ b/verilog/sd2snes_spc7110/sd2snes_spc7110.qpf @@ -0,0 +1,30 @@ +# -------------------------------------------------------------------------- # +# +# Copyright (C) 2018 Intel Corporation. All rights reserved. +# Your use of Intel 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 Intel Program License +# Subscription Agreement, the Intel Quartus Prime License Agreement, +# the Intel FPGA IP License Agreement, or other applicable license +# agreement, including, without limitation, that your use is for +# the sole purpose of programming logic devices manufactured by +# Intel and sold by Intel or its authorized distributors. Please +# refer to the applicable agreement for further details. +# +# -------------------------------------------------------------------------- # +# +# Quartus Prime +# Version 18.1.0 Build 625 09/12/2018 SJ Lite Edition +# Date created = 05:02:17 March 24, 2019 +# +# -------------------------------------------------------------------------- # + +QUARTUS_VERSION = "18.1" +DATE = "05:02:17 March 24, 2019" + +# Revisions + +PROJECT_REVISION = "main" diff --git a/verilog/sd2snes_spc7110/sd2snes_spc7110.xise b/verilog/sd2snes_spc7110/sd2snes_spc7110.xise new file mode 100644 index 000000000..55b2efeaf --- /dev/null +++ b/verilog/sd2snes_spc7110/sd2snes_spc7110.xise @@ -0,0 +1,451 @@ + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/verilog/sd2snes_spc7110/sd_dma.v b/verilog/sd2snes_spc7110/sd_dma.v new file mode 100644 index 000000000..b482a2de1 --- /dev/null +++ b/verilog/sd2snes_spc7110/sd_dma.v @@ -0,0 +1,155 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 19:19:08 12/01/2010 +// Design Name: +// Module Name: sd_dma +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module sd_dma( + input [3:0] SD_DAT, + inout SD_CLK, + input CLK, + input SD_DMA_EN, + output SD_DMA_STATUS, + output SD_DMA_SRAM_WE, + output SD_DMA_NEXTADDR, + output [7:0] SD_DMA_SRAM_DATA, + input SD_DMA_PARTIAL, + input [10:0] SD_DMA_PARTIAL_START, + input [10:0] SD_DMA_PARTIAL_END, + input SD_DMA_START_MID_BLOCK, + input SD_DMA_END_MID_BLOCK, + + output [10:0] DBG_cyclecnt, + output [2:0] DBG_clkcnt +); + +reg [10:0] SD_DMA_STARTr; +reg [10:0] SD_DMA_ENDr; +reg SD_DMA_PARTIALr; +always @(posedge CLK) SD_DMA_PARTIALr <= SD_DMA_PARTIAL; + +reg SD_DMA_DONEr; +reg[1:0] SD_DMA_DONEr2; +initial begin + SD_DMA_DONEr2 = 2'b00; + SD_DMA_DONEr = 1'b0; +end +always @(posedge CLK) SD_DMA_DONEr2 <= {SD_DMA_DONEr2[0], SD_DMA_DONEr}; +wire SD_DMA_DONE_rising = (SD_DMA_DONEr2[1:0] == 2'b01); + +reg [1:0] SD_DMA_ENr; +initial SD_DMA_ENr = 2'b00; +always @(posedge CLK) SD_DMA_ENr <= {SD_DMA_ENr[0], SD_DMA_EN}; +wire SD_DMA_EN_rising = (SD_DMA_ENr [1:0] == 2'b01); + +reg SD_DMA_STATUSr; +assign SD_DMA_STATUS = SD_DMA_STATUSr; + +reg SD_DMA_CLKMASKr = 1'b1; + +// we need 1042 cycles (startbit + 1024 nibbles + 16 crc + stopbit) +reg [10:0] cyclecnt; +initial cyclecnt = 11'd0; + +reg SD_DMA_SRAM_WEr; +initial SD_DMA_SRAM_WEr = 1'b1; +assign SD_DMA_SRAM_WE = (cyclecnt < 1025 && SD_DMA_STATUSr) ? SD_DMA_SRAM_WEr : 1'b1; + +reg SD_DMA_NEXTADDRr; +assign SD_DMA_NEXTADDR = (cyclecnt < 1025 && SD_DMA_STATUSr) ? SD_DMA_NEXTADDRr : 1'b0; + +reg[7:0] SD_DMA_SRAM_DATAr; +assign SD_DMA_SRAM_DATA = SD_DMA_SRAM_DATAr; + +// we have 4 internal cycles per SD clock, 8 per RAM byte write +reg [2:0] clkcnt; +initial clkcnt = 3'b000; +reg [1:0] SD_CLKr; +initial SD_CLKr = 3'b111; +always @(posedge CLK) + if(SD_DMA_EN_rising) SD_CLKr <= 3'b111; + else SD_CLKr <= {SD_CLKr[0], clkcnt[1]}; + +assign SD_CLK = SD_DMA_CLKMASKr ? 1'bZ : SD_CLKr[1]; + +always @(posedge CLK) begin + if(SD_DMA_EN_rising) begin + SD_DMA_STATUSr <= 1'b1; + SD_DMA_STARTr <= (SD_DMA_PARTIALr ? SD_DMA_PARTIAL_START : 11'h0); + SD_DMA_ENDr <= (SD_DMA_PARTIALr ? SD_DMA_PARTIAL_END : 11'd1024); + end + else if (SD_DMA_DONE_rising) SD_DMA_STATUSr <= 1'b0; +end + +always @(posedge CLK) begin + if(SD_DMA_EN_rising) begin + SD_DMA_CLKMASKr <= 1'b0; + end + else if (SD_DMA_DONEr) begin + SD_DMA_CLKMASKr <= 1'b1; + end +end +always @(posedge CLK) begin + if(cyclecnt == 1042 + || ((SD_DMA_END_MID_BLOCK & SD_DMA_PARTIALr) && cyclecnt == SD_DMA_PARTIAL_END)) + SD_DMA_DONEr <= 1; + else SD_DMA_DONEr <= 0; +end + +always @(posedge CLK) begin + if(SD_DMA_EN_rising || !SD_DMA_STATUSr) begin + clkcnt <= 0; + end else begin + if(SD_DMA_STATUSr) begin + clkcnt <= clkcnt + 1; + end + end +end + +always @(posedge CLK) begin + if(SD_DMA_EN_rising) + cyclecnt <= (SD_DMA_PARTIALr && SD_DMA_START_MID_BLOCK) ? SD_DMA_PARTIAL_START : 0; + else if(!SD_DMA_STATUSr) cyclecnt <= 0; + else if(clkcnt[1:0] == 2'b10) cyclecnt <= cyclecnt + 1; +end + +// we have 8 clk cycles to complete one RAM write +// (4 clk cycles per SD_CLK; 2 SD_CLK cycles per byte) +always @(posedge CLK) begin + if(SD_DMA_STATUSr) begin + case(clkcnt[2:0]) + 3'h0: begin + SD_DMA_SRAM_DATAr[7:4] <= SD_DAT; + if(cyclecnt>SD_DMA_STARTr && cyclecnt <= SD_DMA_ENDr) SD_DMA_NEXTADDRr <= 1'b1; + end + 3'h1: begin + SD_DMA_NEXTADDRr <= 1'b0; + end + 3'h2: if(cyclecnt>=SD_DMA_STARTr && cyclecnt < SD_DMA_ENDr) SD_DMA_SRAM_WEr <= 1'b0; +// 3'h3: + 3'h4: + SD_DMA_SRAM_DATAr[3:0] <= SD_DAT; +// 3'h5: +// 3'h6: + 3'h7: + SD_DMA_SRAM_WEr <= 1'b1; + endcase + end +end + +endmodule + diff --git a/verilog/sd2snes_spc7110/spc7110_alu.v b/verilog/sd2snes_spc7110/spc7110_alu.v new file mode 100644 index 000000000..6992c4baf --- /dev/null +++ b/verilog/sd2snes_spc7110/spc7110_alu.v @@ -0,0 +1,258 @@ +// --------------------------------------------------------------------------- +// spc7110_alu.v -- SPC7110 arithmetic logic unit: 16x16 multiply and 32/16 +// divide, signed or unsigned, behind the $4820-$482F register block. +// +// Copyright (c) 2004-2025 ares team, Near et al +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Adapted for RTL from the ares SPC7110 implementation (ISC licensed). +// +// Behaviour (ares alu.cpp + the $4820-$482F cases of spc7110.cpp): +// - a write to $4825 latches the multiplier high byte, sets r482f |= 0x81 +// and queues a multiply; a write to $4827 latches the divisor high byte, +// sets r482f |= 0x80 and queues a divide; either unit clears ONLY bit 7 +// when it finishes (r482f &= 0x7f), so bit 0 stays set forever once the +// first multiply has been triggered. +// Bit 7 is therefore not a plain flip-flop: it reads +// "busy | mul_pending | div_pending", so a unit that +// finishes while the OTHER one is still queued cannot report idle with +// the queued operands not yet consumed. Bit 0 is the only stored bit. +// - $482E keeps bit 0 only; that bit selects signed arithmetic. +// - divide by zero yields quotient 0 and remainder = dividend[15:0]. +// - signed divide truncates toward zero and the remainder takes the sign of +// the dividend, which is what the C++ operators of the reference do. +// 0x80000000 / -1 has no representable quotient: the restoring divider +// wraps to 0x80000000 with remainder 0 (two's complement; what a restoring +// divider produces naturally -- unverified on real silicon). +// +// Interface assumption shared with spc7110_regs: every we_* strobe is a ONE +// cycle pulse and at most one of them is high at a time (they come from a +// single decoded SNES write). A strobe held high for several cycles would +// queue one operation per cycle. +// +// Structure: +// - multiply : one registered signed 17x17 product (fits an embedded 18x18 +// multiplier of the Cyclone IV), 3 clocks end to end. +// - divide : restoring shift/subtract, 1 quotient bit per clock, 32 +// iterations, ~36 clocks end to end. Both are far inside the +// the design budgets (multiply <= 64 clocks, divide <= 96). +// --------------------------------------------------------------------------- +module spc7110_alu( + input clkin, + input rst, + + // register writes ($4820-$4827, $482E) + input we_4820, + input we_4821, + input we_4822, + input we_4823, + input we_4824, + input we_4825, + input we_4826, + input we_4827, + input we_482e, + input [7:0] wdata, + + // read-back + output [7:0] r4820, + output [7:0] r4821, + output [7:0] r4822, + output [7:0] r4823, + output [7:0] r4824, + output [7:0] r4825, + output [7:0] r4826, + output [7:0] r4827, + output [7:0] r4828, + output [7:0] r4829, + output [7:0] r482a, + output [7:0] r482b, + output [7:0] r482c, + output [7:0] r482d, + output [7:0] r482e, + output [7:0] r482f +); + + // ------------------------------------------------------------------------- + // operand / result registers + // ------------------------------------------------------------------------- + reg [7:0] q4820, q4821, q4822, q4823; // multiplicand B0-B1 / dividend + reg [7:0] q4824, q4825; // multiplier + reg [7:0] q4826, q4827; // divisor + reg [31:0] res; // $4828-$482B (product / quotient) + reg [15:0] rmd; // $482C-$482D (remainder) + reg sgn; // $482E bit 0 + reg sticky0; // $482F bit 0: set by $4825 forever + + assign r4820 = q4820; + assign r4821 = q4821; + assign r4822 = q4822; + assign r4823 = q4823; + assign r4824 = q4824; + assign r4825 = q4825; + assign r4826 = q4826; + assign r4827 = q4827; + assign r4828 = res[7:0]; + assign r4829 = res[15:8]; + assign r482a = res[23:16]; + assign r482b = res[31:24]; + assign r482c = rmd[7:0]; + assign r482d = rmd[15:8]; + assign r482e = {7'b0, sgn}; + // r482f is assigned below, next to the sequencer state it reports + + // ------------------------------------------------------------------------- + // operand views, taken at the moment an operation starts + // ------------------------------------------------------------------------- + wire [31:0] dividend = {q4823, q4822, q4821, q4820}; + wire [15:0] divisor = {q4827, q4826}; + wire [15:0] mcand = {q4821, q4820}; + wire [15:0] mplier = {q4825, q4824}; + + wire dvd_neg = sgn & dividend[31]; + wire dsr_neg = sgn & divisor[15]; + wire [31:0] dvd_mag = dvd_neg ? (~dividend + 32'd1) : dividend; + wire [15:0] dsr_mag = dsr_neg ? (~divisor + 16'd1) : divisor; + + // ------------------------------------------------------------------------- + // sequencer + // ------------------------------------------------------------------------- + localparam [2:0] ST_IDLE = 3'd0, + ST_MUL0 = 3'd1, + ST_MUL1 = 3'd2, + ST_DIV = 3'd3, + ST_FIX = 3'd4; + + reg [2:0] state; + reg mul_pending, div_pending; + wire busy = (state != ST_IDLE); + + // $482F: bit 7 = busy or queued, bit 0 sticks once a multiply was requested + assign r482f = {busy | mul_pending | div_pending, 6'b0, sticky0}; + + // multiply datapath -- signed 17x17 covers both signed and unsigned 16x16 + reg signed [16:0] ma, mb; + reg signed [33:0] mprod; + + // divide datapath + reg [31:0] dvd; // quotient accumulates from the LSB + reg [16:0] rem; + reg [15:0] dsr; + reg [5:0] cnt; + reg q_neg, r_neg; + + wire [16:0] rem_shift = {rem[15:0], dvd[31]}; + wire [17:0] rem_diff = {1'b0, rem_shift} - {2'b0, dsr}; + wire rem_ge = ~rem_diff[17]; + + always @(posedge clkin) begin + if(rst) begin + state <= ST_IDLE; + mul_pending <= 1'b0; + div_pending <= 1'b0; + sticky0 <= 1'b0; + ma <= 17'sd0; + mb <= 17'sd0; + mprod <= 34'sd0; + dvd <= 32'h0; + rem <= 17'h0; + dsr <= 16'h0; + cnt <= 6'd0; + q_neg <= 1'b0; + r_neg <= 1'b0; + res <= 32'h0; + rmd <= 16'h0; + q4820 <= 8'h00; + q4821 <= 8'h00; + q4822 <= 8'h00; + q4823 <= 8'h00; + q4824 <= 8'h00; + q4825 <= 8'h00; + q4826 <= 8'h00; + q4827 <= 8'h00; + sgn <= 1'b0; + end else begin + case(state) + ST_IDLE: begin + // ares main(): the multiply is serviced before the divide + if(mul_pending) begin + mul_pending <= 1'b0; + ma <= {sgn & mcand[15], mcand}; + mb <= {sgn & mplier[15], mplier}; + state <= ST_MUL0; + end else if(div_pending) begin + div_pending <= 1'b0; + q_neg <= dvd_neg ^ dsr_neg; + r_neg <= dvd_neg; + dsr <= dsr_mag; + if(divisor == 16'h0000) begin + // illegal division by zero: quotient 0, remainder = dividend + dvd <= 32'h0; + rem <= {1'b0, dividend[15:0]}; + q_neg <= 1'b0; + r_neg <= 1'b0; + cnt <= 6'd0; + end else begin + dvd <= dvd_mag; + rem <= 17'h0; + cnt <= 6'd32; + end + state <= ST_DIV; + end + end + + ST_MUL0: begin + mprod <= ma * mb; + state <= ST_MUL1; + end + + ST_MUL1: begin + res <= mprod[31:0]; + state <= ST_IDLE; + end + + ST_DIV: begin + if(cnt == 6'd0) begin + state <= ST_FIX; + end else begin + rem <= rem_ge ? rem_diff[16:0] : rem_shift; + dvd <= {dvd[30:0], rem_ge}; + cnt <= cnt - 6'd1; + end + end + + ST_FIX: begin + // the result registers land on the same edge that drops busy, so + // $482F bit 7 never falls before $4828-$482D are stable + res <= q_neg ? (~dvd + 32'd1) : dvd; + rmd <= r_neg ? (~rem[15:0] + 16'd1) : rem[15:0]; + state <= ST_IDLE; + end + + default: state <= ST_IDLE; + endcase + + // ----- register writes ------------------------------------------------- + if(we_4820) q4820 <= wdata; + if(we_4821) q4821 <= wdata; + if(we_4822) q4822 <= wdata; + if(we_4823) q4823 <= wdata; + if(we_4824) q4824 <= wdata; + if(we_4825) begin q4825 <= wdata; mul_pending <= 1'b1; sticky0 <= 1'b1; end + if(we_4826) q4826 <= wdata; + if(we_4827) begin q4827 <= wdata; div_pending <= 1'b1; end + if(we_482e) sgn <= wdata[0]; + end + end + +endmodule diff --git a/verilog/sd2snes_spc7110/spc7110_data.v b/verilog/sd2snes_spc7110/spc7110_data.v new file mode 100644 index 000000000..138d9e7b4 --- /dev/null +++ b/verilog/sd2snes_spc7110/spc7110_data.v @@ -0,0 +1,314 @@ +// --------------------------------------------------------------------------- +// spc7110_data.v -- SPC7110 data port unit: 23 bit offset, adjust, stride and +// the four auto-increment sources behind the $4810-$481A register block. +// +// Copyright (c) 2004-2025 ares team, Near et al +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Adapted for RTL from the ares SPC7110 implementation (ISC licensed). +// +// Behaviour (ares data.cpp + the $4810-$481A cases of spc7110.cpp): +// +// r4818 bit 0 stride enabled (otherwise the stride is 1) +// bit 1 the adjust is added to the offset at READ time +// bit 2 the stride is signed +// bit 3 the adjust is signed +// bit 4 the auto-increment target is the ADJUST, not the offset +// bits 6:5 auto-increment source: +// 0 = only a $4810 read +// 1 = also a $4814 write +// 2 = also a $4815 write +// 3 = also a $481A read +// (a write to $4818 keeps 7 bits and always re-reads) +// +// Every event listed below updates the registers in its own cycle and then +// launches ONE data ROM read in the background; $4810 always returns the +// byte that is already buffered, so a register read never stalls the SNES +// bus (reads always serve the already-buffered byte). +// +// write $4813 : offset high byte (7 bits), then read +// write $4814 : adjust low byte, then offset += adjust when source == 1 +// write $4815 : adjust high byte, then offset += adjust when source == 2; +// when the source is not 2 the write still re-reads if bit 1 +// is set. (The reference performs that first read even when +// source == 2, but the increment immediately reads again and +// overwrites it, so only the second read is issued here.) +// write $4818 : settings, then read +// read $4810 : offset += stride (or adjust += stride when bit 4), read +// read $481A : offset += adjust when source == 3, then read +// +// The dataromRead() pre-masking of the reference lives HERE (inside the +// section 2): the offset is masked with (0x100000 << drom_size) - 1, and an +// address carrying 0x400000 with a data ROM smaller than 8 MB serves 0x00 +// without touching the bus at all. The mirroring of the PHYSICAL data ROM +// image is the integration's DROM_MASK, outside this module. +// +// PIPELINE (mk2 timing pass). The read address is produced ONE CYCLE after +// the event that asks for it. The reason is timing: computing it in the +// event cycle means "offset + stride" feeding "+ adjust", two 23 bit adders +// in series behind the stride and settings muxes -- 12 levels of logic, the +// worst path of the Spartan-3 flavor. Since dataPortRead() reads with the +// offset and the settings the event LEAVES BEHIND, the address is simply +// "off + adjust" over the registers on the next cycle, so the two adders +// land in different cycles and neither feeds the other. +// The extra clock only delays when drom_req goes up; the data ROM latency +// is arbitrary by contract, $4810 still answers from the buffered byte, and +// no register read-back is delayed by it. Back to back events keep the +// "last event wins the pending slot" behaviour of the unpipelined version. +// +// Interface assumption shared with spc7110_regs: every we_*/rd_* strobe is +// a ONE cycle pulse and at most one of them is high at a time (they come +// from a single decoded SNES access). When two would overlap the case +// below resolves them in the listed order, but the SNES bus cannot produce +// that. +// +// Widths that matter: r4813 is 7 bits, so the stored offset is 23 bits and +// both offset arithmetic and the read address wrap at that boundary exactly +// like the n24 -> setDataOffset() path of the reference. +// --------------------------------------------------------------------------- +module spc7110_data( + input clkin, + input rst, + + // register writes ($4811-$4818, $481A) + input we_4811, + input we_4812, + input we_4813, + input we_4814, + input we_4815, + input we_4816, + input we_4817, + input we_4818, + input we_481a, // no register behind it; kept for the decoder + input [7:0] wdata, + + // read strobes with a side effect + input rd_4810, + input rd_481a, + + // r4834[1:0]: data ROM size, drives the dataromRead() pre-masking + input [1:0] drom_size, + + // read-back + output [7:0] r4810, + output [7:0] r4811, + output [7:0] r4812, + output [7:0] r4813, + output [7:0] r4814, + output [7:0] r4815, + output [7:0] r4816, + output [7:0] r4817, + output [7:0] r4818, + output [7:0] r481a, + + // data ROM port (req/ack handshake, one byte per transaction) + output drom_req, + output [23:0] drom_addr, + input drom_ack, + input [7:0] drom_data +); + + // ------------------------------------------------------------------------- + // state + // ------------------------------------------------------------------------- + reg [22:0] off; // $4811-$4813 (the high byte keeps 7 bits) + reg [15:0] adj; // $4814-$4815 + reg [15:0] strd; // $4816-$4817 + reg [6:0] cfg; // $4818 + reg [7:0] dbyte; // $4810 + + assign r4810 = dbyte; + assign r4811 = off[7:0]; + assign r4812 = off[15:8]; + assign r4813 = {1'b0, off[22:16]}; + assign r4814 = adj[7:0]; + assign r4815 = adj[15:8]; + assign r4816 = strd[7:0]; + assign r4817 = strd[15:8]; + assign r4818 = {1'b0, cfg}; + assign r481a = 8'h00; // the reference always returns 0x00 here + + // ------------------------------------------------------------------------- + // combinational event decode + // ------------------------------------------------------------------------- + // stride, sign extended when bit 2 is set + wire [15:0] strd_sel = cfg[0] ? strd : 16'h0001; + wire [22:0] strd_ext = cfg[2] ? {{7{strd_sel[15]}}, strd_sel} + : {7'h00, strd_sel}; + + reg [15:0] adj_wr; // $4814/$4815 after the byte write of THIS event + reg [15:0] adj_nx; // next value of $4814-$4815 + reg [22:0] off_nx; // next value of $4811-$4813 + reg ev_read; // this event launches a data ROM read + + // The adjust that an event adds to the OFFSET is always the byte-written + // one, never the stride-incremented one: the increment belongs to the + // $4810 read strobe and the offset-plus-adjust events are other strobes. + // Keeping the two apart is what stops synthesis from stringing the 16 bit + // adjust adder in front of the 23 bit offset adder. + wire [22:0] adj_off = cfg[3] ? {{7{adj_wr[15]}}, adj_wr} : {7'h00, adj_wr}; + + always @* begin + // ---- $4814/$4815 byte writes and the bit 4 increment target ------------ + adj_wr = adj; + if(we_4814) adj_wr = {adj[15:8], wdata}; + if(we_4815) adj_wr = {wdata, adj[7:0]}; + + adj_nx = (rd_4810 && cfg[4]) ? (adj + strd_ext[15:0]) : adj_wr; + + // ---- offset update and read launch ------------------------------------- + off_nx = off; + ev_read = 1'b0; + + case(1'b1) + we_4811: off_nx = {off[22:8], wdata}; + we_4812: off_nx = {off[22:16], wdata, off[7:0]}; + we_4813: begin + off_nx = {wdata[6:0], off[15:0]}; + ev_read = 1'b1; + end + we_4814: begin + if(cfg[6:5] == 2'd1) begin + off_nx = off + adj_off; + ev_read = 1'b1; + end + end + we_4815: begin + if(cfg[6:5] == 2'd2) begin + off_nx = off + adj_off; + ev_read = 1'b1; + end else if(cfg[1]) begin + ev_read = 1'b1; + end + end + we_4818: ev_read = 1'b1; + rd_4810: begin + if(!cfg[4]) off_nx = off + strd_ext; + ev_read = 1'b1; + end + rd_481a: begin + if(cfg[6:5] == 2'd3) begin + off_nx = off + adj_off; + ev_read = 1'b1; + end + end + default: ; + endcase + + end + + // ------------------------------------------------------------------------- + // read address, ONE PIPELINE STAGE BEHIND THE EVENT + // ------------------------------------------------------------------------- + // dataPortRead() is dataromRead(offset + adjust) with the offset and the + // settings the event LEAVES BEHIND, so the address is exactly what the + // registers hold on the cycle AFTER the event -- no need to compute it from + // the pre-event state through a second adder in the same cycle. The + // reference truncates the sum to 24 bits, but bit 23 is dead downstream + // (see size_mask), so the whole path is kept 23 bits wide. + wire [22:0] adj_rd = cfg[3] ? {{7{adj[15]}}, adj} : {7'h00, adj}; + wire [22:0] radj_rd = cfg[1] ? adj_rd : 23'h000000; + wire [22:0] addr_c = off + radj_rd; + + // ------------------------------------------------------------------------- + // dataromRead() pre-masking (see the ares reference) + // ------------------------------------------------------------------------- + // 23 bits: every mask clears bit 23 of the read address and only bit 22 + // drives the 0x400000 guard, so bit 23 of "offset + adjust" is dead and is + // never carried around. + reg [22:0] size_mask; + always @* begin + case(drom_size) + 2'd0: size_mask = 23'h0fffff; // 8 Mbit + 2'd1: size_mask = 23'h1fffff; // 16 Mbit + 2'd2: size_mask = 23'h3fffff; // 32 Mbit + default: size_mask = 23'h7fffff; // 64 Mbit + endcase + end + + // ------------------------------------------------------------------------- + // background data ROM engine: at most one request in flight, request held + // until ack, address stable while the request is up + // ------------------------------------------------------------------------- + reg rd_arm; // stage 1: an event asked for a read this cycle + reg rd_pend; // stage 2: rd_addr holds the address to fetch + reg [22:0] rd_addr; + reg busy; + reg req_v; + reg [22:0] req_addr; + + // "if((r4834 & 3) != 3 && (address & 0x400000)) return 0x00;" + wire rd_kill = (drom_size != 2'd3) & rd_addr[22]; + + // Handshake rule: the request is a level held until the ack, and it + // drops COMBINATIONALLY on the ack cycle -- a level sensitive arbiter must + // never see the same request twice. + assign drom_req = req_v & ~drom_ack; + assign drom_addr = {1'b0, req_addr}; + + always @(posedge clkin) begin + if(rst) begin + off <= 23'h000000; + adj <= 16'h0000; + strd <= 16'h0000; + cfg <= 7'h00; + dbyte <= 8'h00; + rd_arm <= 1'b0; + rd_pend <= 1'b0; + rd_addr <= 23'h000000; + busy <= 1'b0; + req_v <= 1'b0; + req_addr <= 23'h000000; + end else begin + off <= off_nx; + adj <= adj_nx; + if(we_4816) strd[7:0] <= wdata; + if(we_4817) strd[15:8] <= wdata; + if(we_4818) cfg <= wdata[6:0]; + + // ---- engine --------------------------------------------------------- + if(busy) begin + if(drom_ack) begin + dbyte <= drom_data; + busy <= 1'b0; + req_v <= 1'b0; + end + end else if(rd_pend) begin + rd_pend <= 1'b0; + if(rd_kill) begin + // outside the visible data ROM: serve 0x00, never touch the bus + dbyte <= 8'h00; + end else begin + req_v <= 1'b1; + req_addr <= rd_addr & size_mask; + busy <= 1'b1; + end + end + + // ---- pipeline stage 1 -> 2 ------------------------------------------- + // One cycle after the event, off/adj/cfg already hold the post-event + // state, so addr_c IS the address dataPortRead() would compute. This + // is the split that keeps the two 23 bit adders (offset update and + // offset+adjust) in different cycles instead of in series. + // The assignments sit AFTER the engine block so an address latched in + // the very cycle the engine consumed the previous one is not lost. + if(rd_arm) begin + rd_pend <= 1'b1; + rd_addr <= addr_c; + end + rd_arm <= ev_read; + end + end + +endmodule diff --git a/verilog/sd2snes_spc7110/spc7110_dcu.v b/verilog/sd2snes_spc7110/spc7110_dcu.v new file mode 100644 index 000000000..f62114536 --- /dev/null +++ b/verilog/sd2snes_spc7110/spc7110_dcu.v @@ -0,0 +1,1146 @@ +// --------------------------------------------------------------------------- +// spc7110_dcu.v -- SPC7110 decompression unit (DCU): arithmetic decoder, +// context model, colour map and tile buffer for the $4800-$480C register block. +// +// Copyright (c) 2004-2025 ares team, Near et al +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Adapted for RTL from the ares SPC7110 implementation (ISC licensed); +// original decompressor by neviksti, optimized by talarubi. +// +// Structure (C++ -> RTL map): +// - sequencer FSM : $4806 begin-transfer, seek loop, 8-row tile fill +// - decoder FSM : one decode() call = 8 pixels x bpp planes +// - move-to-front : one shared combinational unit, 4 uses per pixel +// - context store : 80 x 21 bit block RAM holding the UNPACKED model +// - evolution ROM : 64 x 20 bit block RAM (53 used) +// - byte prefetch : 8 deep FIFO in front of the data ROM port, so the +// arithmetic coder never waits on data ROM latency +// - tile buffer : two 32 byte buffers; $4800 is served from the buffer the +// SNES owns while the other one is refilled in the +// background, so a register read never stalls the bus. +// +// ACCEPTED DIVERGENCE from the reference model (deliberate, documented): because +// the refill runs ahead, r4807 and r480b bit0 are sampled when a tile STARTS +// being decoded, up to one tile earlier than the reference, which consumes them +// lazily on the first read of the tile. Real silicon also decodes ahead -- the +// ready bit of $480C exists for that reason -- and no known driver rewrites the +// stride between the $4806 trigger and the end of the transfer. A game that +// did would show one tile decoded with the previous stride; vectors that +// exercise it are kept as documentation outside the gate. +// --------------------------------------------------------------------------- + +module spc7110_dcu( + input clkin, + input rst, + + // register writes ($4801-$480B) + input we_4801, + input we_4802, + input we_4803, + input we_4804, + input we_4805, + input we_4806, + input we_4807, + input we_4808, + input we_4809, + input we_480a, + input we_480b, + input [7:0] wdata, + + // $4800 read strobe: one byte consumed by the SNES + input rd_4800, + + // r4834[1:0]: data ROM size, drives the dataromRead() pre-masking below + input [1:0] drom_size, + + output [7:0] dcu_data, + output dcu_ready, + + // register read-back + output [7:0] r4801, + output [7:0] r4802, + output [7:0] r4803, + output [7:0] r4804, + output [7:0] r4805, + output [7:0] r4806, + output [7:0] r4807, + output [7:0] r4808, + output [7:0] r4809, + output [7:0] r480a, + output [7:0] r480b, + output [7:0] r480c, + + // data ROM port (contract section 1): level request, 1 clk ack, 1 byte + output drom_req, + output [23:0] drom_addr, + input drom_ack, + input [7:0] drom_data +); + + // ------------------------------------------------------------------------- + // control registers + // ------------------------------------------------------------------------- + reg [7:0] q4801, q4802; + reg [6:0] q4803; // n7 in the reference: bit 7 is dropped + reg [7:0] q4804, q4805, q4806, q4807; + reg [15:0] qcount; // {r480a, r4809}, decremented per $4800 read + reg [1:0] q480b; // written as (data & 3) + reg qdone; // $480C bit 7 + + assign r4801 = q4801; + assign r4802 = q4802; + assign r4803 = {1'b0, q4803}; + assign r4804 = q4804; + assign r4805 = q4805; + assign r4806 = q4806; + assign r4807 = q4807; + assign r4808 = 8'h00; + assign r4809 = qcount[7:0]; + assign r480a = qcount[15:8]; + assign r480b = {6'b000000, q480b}; + assign r480c = {qdone, 7'b0000000}; + + // ------------------------------------------------------------------------- + // functions + // ------------------------------------------------------------------------- + + // moveToFront(), split in two pipeline stages. Fused, the nibble search and + // the 64-bit rotate shared one clock and formed the critical path of the + // whole core (the scan/apply split below is what keeps it off the critical path). + // + // stage A -- which nibble matches: + // Written without a variable part-select on purpose: XST rejects those on a + // signal ("Variable index is not supported in signal"), so the per-nibble + // compare is done on the whole word and the 16 results are picked out with + // constant bit-selects. e = bitwise equality, a2 bit 4j = "nibble j matches". + function [15:0] mtf_hit; + input [63:0] list; + input [3:0] nib; + reg [63:0] e, a1, a2; + begin + e = ~(list ^ {16{nib}}); + a1 = e & (e >> 1); + a2 = a1 & (a1 >> 2); + mtf_hit = {a2[60], a2[56], a2[52], a2[48], a2[44], a2[40], a2[36], a2[32], + a2[28], a2[24], a2[20], a2[16], a2[12], a2[ 8], a2[ 4], a2[ 0]}; + end + endfunction + + // stage B -- rotate the list up to the match. pfx[j] is "nibble j sits + // ABOVE the first match", so it keeps its value; everything at or below the + // match shifts up one nibble and the match itself lands in the low four bits. + // Same shape as the reference implementation: (list & mask) | (list << 4 & + // ~mask), with the nibble dropped into the low four bits. keep is pfx + // widened to one bit per nibble, built by a static concatenation so no + // variable index reaches a signal. + function [63:0] mtf_apply; + input [63:0] list; + input [15:0] pfx; + input [3:0] nib; + input any; + reg [63:0] keep; + begin + keep = {{4{pfx[15]}}, {4{pfx[14]}}, {4{pfx[13]}}, {4{pfx[12]}}, + {4{pfx[11]}}, {4{pfx[10]}}, {4{pfx[ 9]}}, {4{pfx[ 8]}}, + {4{pfx[ 7]}}, {4{pfx[ 6]}}, {4{pfx[ 5]}}, {4{pfx[ 4]}}, + {4{pfx[ 3]}}, {4{pfx[ 2]}}, {4{pfx[ 1]}}, {4{pfx[ 0]}}}; + if(!any) begin + mtf_apply = list; // not in the list: unchanged (as in C) + end else begin + mtf_apply = (list & keep) | ((list << 4) & ~keep); + mtf_apply[3:0] = nib; + end + end + endfunction + + // number of left shifts needed to bring range back above Max/2 (=127); + // replaces the "while(range <= Max/2)" loop with one barrel shift. + function [3:0] norm_k; + input [8:0] r; + begin + if (r[8]) norm_k = 4'd0; + else if(r[7]) norm_k = 4'd0; + else if(r[6]) norm_k = 4'd1; + else if(r[5]) norm_k = 4'd2; + else if(r[4]) norm_k = 4'd3; + else if(r[3]) norm_k = 4'd4; + else if(r[2]) norm_k = 4'd5; + else if(r[1]) norm_k = 4'd6; + else norm_k = 4'd7; // r[0] or r == 0 (unreachable) + end + endfunction + + // dataromRead() address mask: size = 1 << (r4834 & 3) megabytes. + function [23:0] dsz_mask; + input [1:0] sz; + begin + case(sz) + 2'd0: dsz_mask = 24'h0fffff; + 2'd1: dsz_mask = 24'h1fffff; + 2'd2: dsz_mask = 24'h3fffff; + default: dsz_mask = 24'h7fffff; + endcase + end + endfunction + + // ------------------------------------------------------------------------- + // data ROM port: one request in flight, load-address unit has priority over + // the decompressor byte prefetch. Both users go through dataromRead(), so + // the size mask and the $400000 hole are applied here, before the request + // leaves the module: an address in the hole is answered with 00 and never + // reaches the bus. + // ------------------------------------------------------------------------- + reg req_v; + reg req_owner; // 1 = load address unit, 0 = prefetch + reg [23:0] req_addr; + reg la_kill, pf_kill; // discard the answer of a stale request + + // Handshake rule: the request must drop combinationally with the ack (a + // held request would be re-sampled by the arbiter as a second transaction, + // issuing the very same read twice). + assign drom_req = req_v && !drom_ack; + assign drom_addr = req_addr; + + // load address unit ($4804 write): 4 sequential bytes at table + index*4 + // la_cur is the address of the byte la_st is fetching, kept as its own + // counter instead of being recomputed as base + (la_st - 1): that adder put + // a full 24 bit carry chain between la_st and the $400000 hole test, and the + // hole test drives the D input of dcu_addr_r. Equivalent by construction -- + // it is loaded with the base when la_st is armed and steps on exactly the + // events that step la_st. + reg [2:0] la_st; // 0 idle, 1..4 = fetching byte 0..3 + reg [23:0] la_cur; + reg [1:0] dcu_mode_r; + reg [22:0] dcu_addr_r; + wire la_busy = (la_st != 3'd0); + + // byte prefetch FIFO. Occupancy is its own up/down counter rather than + // (fifo_wp - fifo_rp): that subtractor sat in front of fifo_empty -> + // ar2_go -> the clock enable of the whole arithmetic step, so the write + // pointer reached range/input/bits through two extra levels of logic. The + // counter is exact by construction -- it moves on the very conditions that + // move the two pointers, and the transfer restart zeroes it with the same + // priority it zeroes them. It never leaves 0..8, so bit 3 alone means full. + reg [7:0] fifo_mem [0:7]; + reg [3:0] fifo_wp, fifo_rp; + reg [3:0] fifo_cnt; + reg [23:0] pf_addr; + reg pf_en; + wire fifo_empty = (fifo_cnt == 4'd0); + wire [7:0] fifo_dout = fifo_mem[fifo_rp[2:0]]; + wire pf_need = pf_en && !fifo_cnt[3]; + + // dataromRead() of the next byte each user wants + wire [23:0] la_masked = la_cur & dsz_mask(drom_size); + wire la_hole = (drom_size != 2'd3) && la_cur[22]; + wire [23:0] pf_masked = pf_addr & dsz_mask(drom_size); + wire pf_hole = (drom_size != 2'd3) && pf_addr[22]; + + // ------------------------------------------------------------------------- + // decompressor state + // ------------------------------------------------------------------------- + reg [1:0] mode; // 0 = 1bpp, 1 = 2bpp, 2 = 4bpp + wire bpp1 = (mode == 2'd0); + wire bpp2 = (mode == 2'd1); + wire bpp4 = (mode == 2'd2); + + reg [3:0] bits_r; // bits remaining in input (1..8) + reg [8:0] range_r; // arithmetic range (Max+1 = 256 at init) + reg [15:0] input_r; + reg [7:0] output_r; + reg [63:0] pixels_r; + reg [63:0] colormap_r; + reg [63:0] map_r; + reg [31:0] result_r; + + localparam [63:0] CMAP_INIT = 64'hfedcba9876543210; + + // context store: {swap, probability[7:0], next0[5:0], next1[5:0]} + // Two invariants every reader of this table may rely on: (1) the read + // address is bounded by construction -- set_w <= 4 (diff_w is 0..4) and + // idx_w <= 14, so ctx_raddr <= 78 never leaves ctxmem[0:79]; (2) ST_BT_CLR + // rewrites entries 0..79 with CTX_INIT before every transfer, including + // after an abort, so every probability a decode can observe is a row of the + // evolution table (all reachable probabilities lie in [8'h01, 8'h5a]). + reg [20:0] ctxmem [0:79]; + reg [20:0] ctx_q; + reg [6:0] ctx_raddr; + reg ctx_re; + reg ctx_we; + reg [6:0] ctx_waddr; + reg [20:0] ctx_wdata; + wire [5:0] c_next0 = ctx_q[11:6]; + wire [5:0] c_next1 = ctx_q[ 5:0]; + + localparam [20:0] CTX_INIT = {1'b0, 20'h5a041}; // swap 0, evolution[0] + + // evolution ROM: {probability[7:0], next0[5:0], next1[5:0]}. Both successor + // states are read at once and the decoded symbol picks one a clock later: + // addressing the ROM with (symbol ? next1 : next0) put the whole + // context-read -> subtract -> compare chain in front of a block RAM address + // pin, which was the worst path of the core on Spartan-3. The two reads are + // the two ports of one ROM, so the second one is free. + // XST warns "Signal is used but never assigned" for this + // initial-block ROM; the warning is spurious -- the INIT strings in the + // generated bitstream carry the table below verbatim. + reg [19:0] evo [0:63]; + reg [19:0] evo_q0, evo_q1; + + initial begin + evo[ 0] = 20'h5a041; evo[ 1] = 20'h25086; evo[ 2] = 20'h110c8; + evo[ 3] = 20'h0810a; evo[ 4] = 20'h0314c; evo[ 5] = 20'h0114f; + evo[ 6] = 20'h5a1c7; evo[ 7] = 20'h3f213; evo[ 8] = 20'h2c255; + evo[ 9] = 20'h20296; evo[10] = 20'h172d7; evo[11] = 20'h11319; + evo[12] = 20'h0c35a; evo[13] = 20'h0939c; evo[14] = 20'h073dd; + evo[15] = 20'h0541f; evo[16] = 20'h04460; evo[17] = 20'h034a2; + evo[18] = 20'h02163; evo[19] = 20'h5a514; evo[20] = 20'h48567; + evo[21] = 20'h3a5a8; evo[22] = 20'h2e5ea; evo[23] = 20'h2662c; + evo[24] = 20'h1f66d; evo[25] = 20'h196ae; evo[26] = 20'h156d9; + evo[27] = 20'h1171a; evo[28] = 20'h0e75a; evo[29] = 20'h0b79b; + evo[30] = 20'h097dc; evo[31] = 20'h0881d; evo[32] = 20'h0785e; + evo[33] = 20'h0589f; evo[34] = 20'h048e1; evo[35] = 20'h04921; + evo[36] = 20'h03962; evo[37] = 20'h029a3; evo[38] = 20'h02164; + evo[39] = 20'h58a27; evo[40] = 20'h4da6f; evo[41] = 20'h43ab0; + evo[42] = 20'h3baf1; evo[43] = 20'h34b32; evo[44] = 20'h2eb73; + evo[45] = 20'h29bac; evo[46] = 20'h2562d; evo[47] = 20'h56c2f; + evo[48] = 20'h4fc6f; evo[49] = 20'h47cb0; evo[50] = 20'h41cf1; + evo[51] = 20'h3cd32; evo[52] = 20'h37af3; evo[53] = 20'h00000; + evo[54] = 20'h00000; evo[55] = 20'h00000; evo[56] = 20'h00000; + evo[57] = 20'h00000; evo[58] = 20'h00000; evo[59] = 20'h00000; + evo[60] = 20'h00000; evo[61] = 20'h00000; evo[62] = 20'h00000; + evo[63] = 20'h00000; + end + + always @(posedge clkin) begin + if(ctx_we) ctxmem[ctx_waddr] <= ctx_wdata; + if(ctx_re) ctx_q <= ctxmem[ctx_raddr]; + evo_q0 <= evo[c_next0]; + evo_q1 <= evo[c_next1]; + end + + // ------------------------------------------------------------------------- + // sequencer / decoder state + // ------------------------------------------------------------------------- + localparam [3:0] ST_IDLE = 4'd0; + localparam [3:0] ST_BT_CHK = 4'd1; + localparam [3:0] ST_BT_CLR = 4'd2; + localparam [3:0] ST_BT_INIT = 4'd3; + localparam [3:0] ST_BT_IN0 = 4'd4; + localparam [3:0] ST_BT_IN1 = 4'd5; + localparam [3:0] ST_BT_DEC = 4'd6; + localparam [3:0] ST_BT_DECW = 4'd7; + localparam [3:0] ST_BT_SEEK = 4'd8; + localparam [3:0] ST_BT_SKW = 4'd9; + localparam [3:0] ST_BT_ARM = 4'd10; + localparam [3:0] ST_F_ROW = 4'd11; + localparam [3:0] ST_F_SEEK = 4'd12; + localparam [3:0] ST_F_SKW = 4'd13; + + localparam [2:0] DC_IDLE = 3'd0; + localparam [2:0] DC_PIX = 3'd1; + localparam [2:0] DC_CTX = 3'd2; + localparam [2:0] DC_AR1 = 3'd3; + localparam [2:0] DC_AR2 = 3'd4; + localparam [2:0] DC_END = 3'd5; + + reg [3:0] state; + reg [2:0] dc_state; + reg dc_busy; + reg bt_pending; + reg xfer_active; + reg [6:0] clr_cnt; + reg [15:0] seek_cnt; + reg [2:0] row; + reg [7:0] row_seek; + + // Tile buffers: two 32 byte pages. Byte ADDRESSED rather than one flat + // vector: a variable index into a memory is the form XST accepts, while a + // variable part-select of a signal is not. + // + // Each page is split into four eight entry banks by (address bit 4, address + // bit 0), which is exactly how a row is laid out: a row writes byte 2r to + // bank a, 2r+1 to bank b, 2r+16 to bank c and 2r+17 to bank d, so no bank + // ever takes more than one byte per row and each of them has a single write + // port. That is what lets the pages live in eight small memories instead of + // 512 flip flops behind a 32:1 read mux -- the flip flop form spread over + // the whole die and its clock enable was the longest route in the core. + // 1bpp writes one byte per row, alternating between banks a and b. + // + // THE READ IS ASYNCHRONOUS AND HAS TO STAY THAT WAY. dcu_data is combinational + // out of these banks, so they must be LUT RAM (or flip flops), never block RAM. + // Left to itself XST spends whatever block RAM is still free on them and + // reports what it does with it: "The RAM will be implemented as a + // BLOCK RAM, absorbing the following register(s): ". Absorbing rd_off + // means the read address stops being a wire into an asynchronous array and + // becomes the block RAM's own address register -- and on silicon that register + // never advances: the three banks that landed in block RAM returned entry 0 for + // all eight indices, every time. Measured on a Mk.II with a probe cartridge + // that reads a known transfer out of $4800 four times at four different paces: + // tb0a-d and tb1a (LUT RAM) byte exact, tb1b/tb1c/tb1d (block RAM) every index + // equal to index 0. On screen that is a handful of tiles wrong in an otherwise + // correct image, identical in every fit, which is what it looked like. + // An earlier revision of this comment claimed the two forms were equivalent + // because a page is only written while its buf_valid bit is clear. That + // argument only ever covered read-during-write; it says nothing about the + // address register, and it was wrong. + // Cyclone IV has neither form and keeps the flip flops, so the attribute is + // scoped to the flavour that needs it and the mk3 netlist is untouched. +`ifdef MK2 + (* ram_style = "distributed" *) +`endif + reg [7:0] tb0a [0:7], tb0b [0:7], tb0c [0:7], tb0d [0:7]; +`ifdef MK2 + (* ram_style = "distributed" *) +`endif + reg [7:0] tb1a [0:7], tb1b [0:7], tb1c [0:7], tb1d [0:7]; + reg [1:0] buf_valid; + reg rd_sel, fill_sel; + reg [4:0] rd_off; + + // decode() iteration state + reg [2:0] pix; + reg [1:0] plane; + reg [2:0] diff_r; + reg [3:0] pa_r, pb_r, pc_r; + reg [6:0] ctx_addr_r; + reg [3:0] k_r; + reg [15:0] input_a_r; + reg [8:0] range_a_r; + reg norm_r; + reg swap_nx_r; + reg sym_r; // decoded symbol, picks the successor state + + // move-to-front chain, two stages deep. A pixel needs four moves: three that + // build "map" from the OLD colormap (pc, pb, pa) and one that advances the + // colormap itself (pa). The colormap move is independent of the map chain, + // so it is interleaved into the slots the dependent chain leaves free, and + // the four moves still finish in six clocks -- the same window the fused + // version used, which is what keeps 2bpp at seven clocks per pixel: + // + // clk 1 A(map <- cm , pc) + // clk 2 B(map) A(cm <- cm , pa) + // clk 3 B(cm) A(map <- map, pb) + // clk 4 B(map) + // clk 5 A(map <- map, pa) + // clk 6 B(map) <- map final, read by the pixel epilogue on clk 7 + // + // Invariant that makes the interleave safe: no write to the register a + // stage A read lands between that A and its own B. (Note the clk1->clk2 + // pair: A reads colormap_r while its B writes map_r -- the colormap write + // of clk 3 comes after, which is exactly the reference's "map is a copy of + // colormap taken BEFORE the colormap advances".) + // + // Which slot runs on the next clock is known a full clock ahead, so the + // nibble and the two select bits of stage A are REGISTERS, loaded by the + // decoder block below. The lists themselves are still read in the stage A + // cycle, so the invariant above is untouched -- only the operand select + // moves. + reg [2:0] mtf_ph; // 0 idle, 2..6 = clk 2..6 above + reg b_valid; // stage B has work this cycle + reg [15:0] b_pfx; + reg b_any; + reg [3:0] b_nib; + reg b_lsel; // stage B list: 0 = colormap_r, 1 = map_r + reg b_dest; // stage B target: 0 = colormap_r, 1 = map_r + wire mtf_busy = (mtf_ph != 3'd0) || b_valid; + + wire bt_abort = bt_pending && !la_busy; + + // ------------------------------------------------------------------------- + // combinational decode datapath + // ------------------------------------------------------------------------- + + // pixel setup: pa/pb/pc and the "which of the three neighbours differ" code + wire [3:0] pa_w = bpp2 ? {2'b00, pixels_r[3:2]} : pixels_r[3:0]; + wire [3:0] pb_w = bpp2 ? {2'b00, pixels_r[15:14]} : pixels_r[31:28]; + wire [3:0] pc_w = bpp2 ? {2'b00, pixels_r[17:16]} : pixels_r[35:32]; + wire [3:0] match_w = pa_w ^ pb_w ^ pc_w; + wire [2:0] diff_w = ((pa_w == pb_w) && (pb_w == pc_w)) ? 3'd0 : + ((match_w ^ pa_w) == 4'd0) ? 3'd1 : + ((match_w ^ pb_w) == 4'd0) ? 3'd2 : + ((match_w ^ pc_w) == 4'd0) ? 3'd3 : 3'd4; + + // Stage A operand: REGISTERS, loaded one clock before the stage A that uses + // them (the schedule is known that early -- see the decoder block). Decoding + // them out of mtf_ph in the same cycle put a two level nibble multiplexer AND + // the fan-out-14 mtf_ph net in front of mtf_hit -> prefix-OR -> b_pfx, which + // was the worst path of the whole core; the registered form starts that chain + // at a flip flop instead. Nothing about the schedule changes: the values + // presented here are exactly what the combinational decode produced. + // + // Do NOT pin these down with register_balancing = "no". XST retimes them + // backward across their own input multiplexer and fans a_nib out into ten + // replicas placed next to the sixteen nibble comparators; the multiplexer it + // leaves behind is local and cheap, and the replication is worth far more + // than the level it costs. Measured on this design: the attribute (or + // turning balancing off project-wide) costs 1.2 ns / 2.4 ns of worst-case + // slack respectively. Registering the operand is still what makes that + // possible -- with the schedule decoded in the same cycle, the multiplexer + // hung off mtf_ph, a sequencer register the balancer cannot replicate. + reg a_fire; + reg [3:0] a_nib; + reg a_lsel, a_dest; + + wire [63:0] a_list = a_lsel ? map_r : colormap_r; + wire [15:0] a_hit = mtf_hit(a_list, a_nib); + // mask of everything strictly above the lowest set bit, as an explicit + // 4-level prefix-OR tree. pfx_or8[i] = |a_hit[i:0], so shifting left once + // gives "some bit set strictly below i" = the wanted mask; a_hit == 0 + // collapses to all-zero. The earlier form (a_hit & -a_hit, then + // (a_low << 1) - 1) is equivalent but maps to two 16-bit carry chains in + // series, which dominated this path on Spartan-3. + wire [15:0] pfx_or1 = a_hit | {a_hit[14:0], 1'b0}; + wire [15:0] pfx_or2 = pfx_or1 | {pfx_or1[13:0], 2'b0}; + wire [15:0] pfx_or4 = pfx_or2 | {pfx_or2[11:0], 4'b0}; + wire [15:0] pfx_or8 = pfx_or4 | {pfx_or4[7:0], 8'b0}; + wire [15:0] a_pfx = {pfx_or8[14:0], 1'b0}; + + wire [63:0] b_list = b_lsel ? map_r : colormap_r; + wire [63:0] mtf_res = mtf_apply(b_list, b_pfx, b_nib, b_any); + + // context selection for the current plane + wire [3:0] bit_w = bpp1 ? (4'd1 << pix[1:0]) : (4'd1 << plane); + wire [3:0] hist_w = (bit_w - 4'd1) & output_r[3:0]; + wire [2:0] set_w = bpp1 ? {2'b00, pix[2]} : + bpp2 ? diff_r : + ((plane[1]) && (hist_w <= 4'd1)) ? diff_r : 3'd0; + wire [3:0] idx_w = bit_w + hist_w - 4'd1; + + // arithmetic step + wire c_swap = ctx_q[20]; + wire [7:0] c_prob = ctx_q[19:12]; + wire [8:0] lps_sub = range_r - {1'b0, c_prob}; + wire [7:0] lps_off = lps_sub[7:0]; + + // symbol is "input[15:8] >= lps_off", stated as "probability >= range - + // input[15:8]" so that the probability -- the one operand that arrives late, + // straight out of the context store -- crosses ONE carry chain instead of the + // subtractor and the comparator in series. The threshold is built from + // registers only, so it is ready long before the context read lands. The + // +256 bias keeps both sides unsigned and ten bits wide, so nothing wraps: + // X >= R - P <=> P >= R - X <=> 256+P >= 256+R-X + // Same function as the original whenever R - P fits the eight bits of + // lps_off, which the decoder guarantees at every step: renormalisation + // leaves range in [128,256] and every probability the evolution table can + // reach is in [1,0x5a], so R - P stays in [38,255]. The equivalence and + // both bounds were enumerated exhaustively against the original expression + // over the whole operating domain. + wire [9:0] sym_thr = {1'b0, range_r} + 10'd256 - {2'b00, input_r[15:8]}; + wire symbol = ({2'b01, c_prob} >= sym_thr); + + // range - lps_off is exactly {lps_sub[8], c_prob}: taking the low eight bits + // of (range - probability) back out of range returns the probability, plus + // the 256 the truncation removed. True for every 9 bit range and 8 bit + // probability, so the second subtractor of the old form is pure wiring. + wire [8:0] range_a = symbol ? {lps_sub[8], c_prob} : {1'b0, lps_off}; + wire [15:0] input_a = symbol ? (input_r - {lps_off, 8'h00}) : input_r; + wire [3:0] k_w = norm_k(range_a); + wire swap_nx = c_swap ^ (symbol & (c_prob > 8'h55)); + + // successor state, chosen after the fact from the two that were read + wire [19:0] evo_q = sym_r ? evo_q1 : evo_q0; + + // normalisation / input refill + // The reference merges the refilled byte with "input += read()", but the two + // operands can never overlap, so this is an OR -- which takes a 16 bit carry + // chain off the end of the refill path. Proof by induction on the invariant + // "the low (8 - bits) bits of input are zero", which holds after the two + // initial reads (bits = 8, nothing required) and is preserved by every step: + // * a batch of k shifts without a refill leaves (8 - bits) + k zeros and + // sets bits to bits - k, so (8 - bits_new) = (8 - bits) + k; + // * a batch WITH a refill shifts by k = bits + kb, so the shifted input + // ends with (8 - bits) + bits + kb = 8 + kb zeros while the byte lands in + // bits [kb, kb+7] -- disjoint -- and bits_new = 8 - kb leaves exactly the + // kb zeros the invariant then asks for; + // * the MPS/LPS rescale only subtracts from the top byte. + // k is at most 7 and bits at least 1, so a batch needs at most one byte. + // The disjointness is also asserted on live operands at every refill of the + // decode test suite. + wire need_byte = (k_r != 4'd0) && (k_r >= bits_r); + wire [3:0] kb = k_r - bits_r; + wire [15:0] input_new = (input_a_r << k_r) | + (need_byte ? ({8'h00, fifo_dout} << kb) : 16'h0000); + wire [3:0] bits_new = need_byte ? (4'd8 - kb) : (bits_r - k_r); + wire [8:0] range_new = range_a_r << k_r; + + wire last_plane = bpp1 ? 1'b1 : bpp2 ? (plane == 2'd1) : (plane == 2'd3); + wire ar2_go = !(need_byte && fifo_empty) && !(last_plane && mtf_busy); + wire dec_pop = (dc_state == DC_AR2) && ar2_go && need_byte; + wire seq_pop = ((state == ST_BT_IN0) || (state == ST_BT_IN1)) && !fifo_empty; + + // the three events that move the FIFO pointers in the arbiter below, named + // here so the occupancy counter tracks them exactly + wire fifo_push = (req_v && drom_ack && !req_owner && !pf_kill) || + (!req_v && !la_busy && pf_need && pf_hole); + wire fifo_pop = seq_pop || dec_pop; + + // pixel epilogue + wire [3:0] pe_raw = bpp1 ? {3'b000, output_r[0]} : + bpp2 ? {2'b00, output_r[1:0]} : output_r[3:0]; + wire [3:0] pe_index = bpp1 ? {3'b000, pe_raw[0] ^ pixels_r[15]} : pe_raw; + wire [63:0] pe_shift = map_r >> {pe_index, 2'b00}; + wire [3:0] pe_nib = pe_shift[3:0]; + wire [63:0] pixels_new = bpp1 ? {pixels_r[62:0], pe_nib[0]} : + bpp2 ? {pixels_r[61:0], pe_nib[1:0]} : + {pixels_r[59:0], pe_nib}; + + // deinterleave: pure wiring (inverse Morton transform of the reference) + wire [15:0] px16 = pixels_r[15:0]; + wire [31:0] px32 = pixels_r[31:0]; + wire [15:0] deint2 = {px16[14], px16[12], px16[10], px16[ 8], + px16[ 6], px16[ 4], px16[ 2], px16[ 0], + px16[15], px16[13], px16[11], px16[ 9], + px16[ 7], px16[ 5], px16[ 3], px16[ 1]}; + wire [31:0] deint4 = {px32[28], px32[24], px32[20], px32[16], + px32[12], px32[ 8], px32[ 4], px32[ 0], + px32[29], px32[25], px32[21], px32[17], + px32[13], px32[ 9], px32[ 5], px32[ 1], + px32[30], px32[26], px32[22], px32[18], + px32[14], px32[10], px32[ 6], px32[ 2], + px32[31], px32[27], px32[23], px32[19], + px32[15], px32[11], px32[ 7], px32[ 3]}; + + // decode() start pulse, asserted combinationally so the decoder starts on the + // same edge the sequencer enters its wait state + wire dec_start = (state == ST_BT_DEC) || + ((state == ST_BT_SEEK) && (seek_cnt != 16'd0)) || + ((state == ST_F_SEEK) && (row_seek != 8'd0)); + + wire fill_needed = xfer_active && !(fill_sel ? buf_valid[1] : buf_valid[0]); + + // Clock 1 of the move-to-front schedule is the DC_PIX cycle itself, so its + // stage A operand has to be latched during the cycle BEFORE it -- either the + // DC_IDLE that starts a decode, or the last DC_AR2 of the previous pixel. + // In the DC_AR2 case pixels_r is shifted on the very same edge, and the + // shift makes the pc nibble of the next cycle identical to the pb nibble of + // this one: pixels_new[17:16] == pixels_r[15:14] in 2bpp and + // pixels_new[35:32] == pixels_r[31:28] in 4bpp, which is pb_w in both. So + // no part of pixels_new -- and in particular not the pixel epilogue that + // produces it -- reaches this register. + wire [3:0] pc_nxt = (dc_state == DC_AR2) ? pb_w : pc_w; + wire nxt_is_pix = !bpp1 && + (((dc_state == DC_IDLE) && dec_start) || + ((dc_state == DC_AR2) && ar2_go && last_plane && + (pix != 3'd7))); + + // $4800 read path: bank by (bit 4, bit 0), index with the bits between + wire [4:0] off_mask = bpp1 ? 5'd7 : bpp2 ? 5'd15 : 5'd31; + wire [2:0] rd_idx = rd_off[3:1]; + wire [7:0] tb0_lo = rd_off[0] ? tb0b[rd_idx] : tb0a[rd_idx]; + wire [7:0] tb0_hi = rd_off[0] ? tb0d[rd_idx] : tb0c[rd_idx]; + wire [7:0] tb1_lo = rd_off[0] ? tb1b[rd_idx] : tb1a[rd_idx]; + wire [7:0] tb1_hi = rd_off[0] ? tb1d[rd_idx] : tb1c[rd_idx]; + wire [7:0] tb0_byte = rd_off[4] ? tb0_hi : tb0_lo; + wire [7:0] tb1_byte = rd_off[4] ? tb1_hi : tb1_lo; + assign dcu_ready = qdone && (rd_sel ? buf_valid[1] : buf_valid[0]); + assign dcu_data = dcu_ready ? (rd_sel ? tb1_byte : tb0_byte) : 8'h00; + wire rd_take = rd_4800 && dcu_ready; + wire rd_wrap = rd_take && (rd_off == off_mask); + + // Tile buffer fill. The four byte offsets of a row -- 2r, 2r+1, 2r+16 and + // 2r+17 -- are one per bank, so the bank index is the row itself; 1bpp puts + // its single byte at offset r, which lands in bank a or b at r/2. + wire fill_wr = (state == ST_F_ROW); + wire [2:0] fill_idx = bpp1 ? {1'b0, row[2:1]} : row; + wire fill_a = fill_wr && (!bpp1 || !row[0]); + wire fill_b = fill_wr && (!bpp1 || row[0]); + wire fill_cd = fill_wr && bpp4; + wire [7:0] fill_db = bpp1 ? result_r[7:0] : result_r[15:8]; + + // Deliberately outside the sequencer's abort guard: a transfer restart + // clears buf_valid and refills every readable byte from row 0 before the + // page can be read again, so a byte written into an aborted fill is + // overwritten before it is reachable. Keeping the abort out of the write + // enable is also what keeps these banks off the sequencer's fan-out. No + // reset either: nothing reads a page before buf_valid says it is filled. + always @(posedge clkin) begin + if(!fill_sel) begin + if(fill_a) tb0a[fill_idx] <= result_r[7:0]; + if(fill_b) tb0b[fill_idx] <= fill_db; + if(fill_cd) tb0c[row] <= result_r[23:16]; + if(fill_cd) tb0d[row] <= result_r[31:24]; + end else begin + if(fill_a) tb1a[fill_idx] <= result_r[7:0]; + if(fill_b) tb1b[fill_idx] <= fill_db; + if(fill_cd) tb1c[row] <= result_r[23:16]; + if(fill_cd) tb1d[row] <= result_r[31:24]; + end + end + + // ------------------------------------------------------------------------- + // context / evolution port muxing + // ------------------------------------------------------------------------- + always @* begin + ctx_re = 1'b0; + ctx_raddr = {set_w, idx_w}; + ctx_we = 1'b0; + ctx_waddr = ctx_addr_r; + ctx_wdata = norm_r ? {swap_nx_r, evo_q} : {swap_nx_r, ctx_q[19:0]}; + if(state == ST_BT_CLR) begin + ctx_we = 1'b1; + ctx_waddr = clr_cnt; + ctx_wdata = CTX_INIT; + end else begin + if(dc_state == DC_CTX) ctx_re = 1'b1; + if((dc_state == DC_AR2) && ar2_go) ctx_we = 1'b1; + end + end + + // ------------------------------------------------------------------------- + // data ROM arbiter, load address unit, prefetch FIFO + // ------------------------------------------------------------------------- + always @(posedge clkin) begin + if(rst) begin + req_v <= 1'b0; + req_owner <= 1'b0; + req_addr <= 24'h000000; + la_kill <= 1'b0; + pf_kill <= 1'b0; + la_st <= 3'd0; + la_cur <= 24'h000000; + dcu_mode_r <= 2'd0; + dcu_addr_r <= 23'd0; + fifo_wp <= 4'd0; + fifo_rp <= 4'd0; + fifo_cnt <= 4'd0; + pf_addr <= 24'h000000; + pf_en <= 1'b0; + end else begin + if(req_v && drom_ack) begin + req_v <= 1'b0; + if(req_owner) begin + if(!la_kill) begin + case(la_st) + 3'd1: dcu_mode_r <= drom_data[1:0]; + 3'd2: dcu_addr_r[22:16] <= drom_data[6:0]; + 3'd3: dcu_addr_r[15:8] <= drom_data; + 3'd4: dcu_addr_r[7:0] <= drom_data; + default: ; + endcase + la_st <= (la_st == 3'd4) ? 3'd0 : (la_st + 3'd1); + la_cur <= la_cur + 24'd1; + end + la_kill <= 1'b0; + end else begin + if(!pf_kill) begin + fifo_mem[fifo_wp[2:0]] <= drom_data; + fifo_wp <= fifo_wp + 4'd1; + end + pf_kill <= 1'b0; + end + end else if(!req_v) begin + if(la_st != 3'd0) begin + if(la_hole) begin + case(la_st) + 3'd1: dcu_mode_r <= 2'd0; + 3'd2: dcu_addr_r[22:16] <= 7'd0; + 3'd3: dcu_addr_r[15:8] <= 8'h00; + 3'd4: dcu_addr_r[7:0] <= 8'h00; + default: ; + endcase + la_st <= (la_st == 3'd4) ? 3'd0 : (la_st + 3'd1); + la_cur <= la_cur + 24'd1; + end else begin + req_v <= 1'b1; + req_owner <= 1'b1; + req_addr <= la_masked; + end + end else if(pf_need) begin + if(pf_hole) begin + fifo_mem[fifo_wp[2:0]] <= 8'h00; + fifo_wp <= fifo_wp + 4'd1; + pf_addr <= pf_addr + 24'd1; + end else begin + req_v <= 1'b1; + req_owner <= 1'b0; + req_addr <= pf_masked; + pf_addr <= pf_addr + 24'd1; + end + end + end + + if(seq_pop || dec_pop) fifo_rp <= fifo_rp + 4'd1; + + // $4804 write (re)starts the load address unit. Last write to la_cur in + // the block, so it beats the step above exactly as it already beats the + // la_st advance next to it. + if(we_4804) begin + la_cur <= {1'b0, q4803, q4802, q4801} + {14'd0, wdata, 2'b00}; + la_st <= 3'd1; + la_kill <= req_v && req_owner && !drom_ack; + end + + // initialize(): the byte stream restarts at the decompression origin + if(state == ST_BT_INIT) begin + fifo_wp <= 4'd0; + fifo_rp <= 4'd0; + pf_addr <= {1'b0, dcu_addr_r}; + pf_en <= 1'b1; + pf_kill <= req_v && !req_owner && !drom_ack; + end + + // occupancy, with the restart winning over a push or a pop exactly as it + // does over the two pointers above + if(state == ST_BT_INIT) fifo_cnt <= 4'd0; + else if(fifo_push && !fifo_pop) fifo_cnt <= fifo_cnt + 4'd1; + else if(fifo_pop && !fifo_push) fifo_cnt <= fifo_cnt - 4'd1; + end + end + + // ------------------------------------------------------------------------- + // register writes, $4800 read side effects + // ------------------------------------------------------------------------- + always @(posedge clkin) begin + if(rst) begin + q4801 <= 8'h00; q4802 <= 8'h00; q4803 <= 7'h00; + q4804 <= 8'h00; q4805 <= 8'h00; q4806 <= 8'h00; q4807 <= 8'h00; + qcount <= 16'h0000; + q480b <= 2'b00; + end else begin + if(we_4801) q4801 <= wdata; + if(we_4802) q4802 <= wdata; + if(we_4803) q4803 <= wdata[6:0]; + if(we_4804) q4804 <= wdata; + if(we_4805) q4805 <= wdata; + if(we_4806) q4806 <= wdata; + if(we_4807) q4807 <= wdata; + if(we_4809) qcount[7:0] <= wdata; + if(we_480a) qcount[15:8] <= wdata; + if(we_480b) q480b <= wdata[1:0]; + // the counter decrements on every $4800 read, done or not + if(rd_4800 && !we_4809 && !we_480a) qcount <= qcount - 16'd1; + end + end + + // ------------------------------------------------------------------------- + // sequencer: begin transfer, seek, tile fill, buffer bookkeeping + // ------------------------------------------------------------------------- + always @(posedge clkin) begin + if(rst) begin + state <= ST_IDLE; + bt_pending <= 1'b0; + xfer_active <= 1'b0; + qdone <= 1'b0; + mode <= 2'd0; + clr_cnt <= 7'd0; + seek_cnt <= 16'd0; + row <= 3'd0; + row_seek <= 8'd0; + buf_valid <= 2'b00; + rd_sel <= 1'b0; + fill_sel <= 1'b0; + rd_off <= 5'd0; + bits_r <= 4'd8; + range_r <= 9'd256; + input_r <= 16'h0000; + output_r <= 8'h00; + pixels_r <= 64'd0; + colormap_r <= CMAP_INIT; + map_r <= CMAP_INIT; + result_r <= 32'd0; + end else begin + if(bt_abort) begin + // a new transfer discards every decoder and buffer state, so aborting + // an in-flight background fill here is safe + bt_pending <= 1'b0; + xfer_active <= 1'b0; + buf_valid <= 2'b00; + state <= ST_BT_CHK; + end else begin + case(state) + ST_IDLE: begin + if(fill_needed) begin + row <= 3'd0; + state <= ST_F_ROW; + end + end + + // dcuBeginTransfer(): mode 3 is invalid, nothing happens at all + ST_BT_CHK: begin + if(dcu_mode_r == 2'd3) begin + state <= ST_IDLE; + end else begin + mode <= dcu_mode_r; + clr_cnt <= 7'd0; + state <= ST_BT_CLR; + end + end + + // initialize(): clear the context model + ST_BT_CLR: begin + clr_cnt <= clr_cnt + 7'd1; + if(clr_cnt == 7'd79) state <= ST_BT_INIT; + end + + ST_BT_INIT: begin + bits_r <= 4'd8; + range_r <= 9'd256; + output_r <= 8'h00; + pixels_r <= 64'd0; + colormap_r <= CMAP_INIT; + map_r <= CMAP_INIT; + result_r <= 32'd0; + state <= ST_BT_IN0; + end + + // input = read() << 8 | read() + ST_BT_IN0: begin + if(!fifo_empty) begin + input_r[15:8] <= fifo_dout; + state <= ST_BT_IN1; + end + end + ST_BT_IN1: begin + if(!fifo_empty) begin + input_r[7:0] <= fifo_dout; + seek_cnt <= q480b[1] ? {q4806, q4805} : 16'd0; + state <= ST_BT_DEC; + end + end + + ST_BT_DEC: state <= ST_BT_DECW; + ST_BT_DECW: if(!dc_busy) state <= ST_BT_SEEK; + + // while(seek--) decode(); + ST_BT_SEEK: begin + if(seek_cnt == 16'd0) begin + state <= ST_BT_ARM; + end else begin + seek_cnt <= seek_cnt - 16'd1; + state <= ST_BT_SKW; + end + end + ST_BT_SKW: if(!dc_busy) state <= ST_BT_SEEK; + + ST_BT_ARM: begin + xfer_active <= 1'b1; + buf_valid <= 2'b00; + rd_sel <= 1'b0; + fill_sel <= 1'b0; + rd_off <= 5'd0; + state <= ST_IDLE; + end + + // dcuRead() tile refill: the row is stored by the tile buffer block + // above, this only arms the seek to the next one + ST_F_ROW: begin + row_seek <= q480b[0] ? q4807 : 8'd1; + state <= ST_F_SEEK; + end + + ST_F_SEEK: begin + if(row_seek == 8'd0) begin + if(row == 3'd7) begin + if(fill_sel) buf_valid[1] <= 1'b1; + else buf_valid[0] <= 1'b1; + fill_sel <= ~fill_sel; + qdone <= 1'b1; + state <= ST_IDLE; + end else begin + row <= row + 3'd1; + state <= ST_F_ROW; + end + end else begin + row_seek <= row_seek - 8'd1; + state <= ST_F_SKW; + end + end + ST_F_SKW: if(!dc_busy) state <= ST_F_SEEK; + + default: state <= ST_IDLE; + endcase + + // ------------------------------------------------------------------ + // decoder writes into the shared decompressor state + // ------------------------------------------------------------------ + if(b_valid) begin + if(b_dest) map_r <= mtf_res; + else colormap_r <= mtf_res; + end + + if(dc_state == DC_AR1) begin + output_r <= {output_r[6:0], symbol ^ c_swap}; + end + + if((dc_state == DC_AR2) && ar2_go) begin + range_r <= range_new; + input_r <= input_new; + bits_r <= bits_new; + if(last_plane) pixels_r <= pixels_new; + end + + if(dc_state == DC_END) begin + result_r <= bpp1 ? pixels_r[31:0] : + bpp2 ? {16'h0000, deint2} : deint4; + end + end + + // ------------------------------------------------------------------ + // $4800 consumption: never blocks, always serves the buffered byte + // ------------------------------------------------------------------ + if(rd_take) begin + if(rd_wrap) begin + rd_off <= 5'd0; + if(rd_sel) buf_valid[1] <= 1'b0; + else buf_valid[0] <= 1'b0; + rd_sel <= ~rd_sel; + end else begin + rd_off <= rd_off + 5'd1; + end + end + + // $4806 write arms a transfer and clears the done flag. Last in the + // block on purpose: it must override a fill that completes in the very + // same cycle, and it must re-arm bt_pending even when an abort is being + // taken right now. + if(we_4806) begin + qdone <= 1'b0; + bt_pending <= 1'b1; + end + end + end + + // ------------------------------------------------------------------------- + // decoder: one decode() call + // ------------------------------------------------------------------------- + always @(posedge clkin) begin + if(rst) begin + dc_state <= DC_IDLE; + dc_busy <= 1'b0; + mtf_ph <= 3'd0; + b_valid <= 1'b0; + a_fire <= 1'b0; + a_nib <= 4'd0; + a_lsel <= 1'b0; + a_dest <= 1'b0; + pix <= 3'd0; + plane <= 2'd0; + diff_r <= 3'd0; + pa_r <= 4'd0; + pb_r <= 4'd0; + pc_r <= 4'd0; + ctx_addr_r<= 7'd0; + k_r <= 4'd0; + input_a_r <= 16'h0000; + range_a_r <= 9'd0; + norm_r <= 1'b0; + swap_nx_r <= 1'b0; + sym_r <= 1'b0; + end else if(bt_abort) begin + dc_state <= DC_IDLE; + dc_busy <= 1'b0; + mtf_ph <= 3'd0; + b_valid <= 1'b0; + a_fire <= 1'b0; + end else begin + // move-to-front chain, running beside the plane loop + if(dc_state == DC_PIX) mtf_ph <= 3'd2; + else if(mtf_ph != 3'd0) mtf_ph <= (mtf_ph == 3'd6) ? 3'd0 : (mtf_ph + 3'd1); + + // Stage A operand of the NEXT clock. The four arms are the four clocks + // of the schedule above, each decoded one clock early: the arm that fires + // clock 1 runs in the cycle before DC_PIX, the arm that fires clock 2 + // runs in DC_PIX itself (pa_w is what pa_r is being loaded with on this + // same edge), and clocks 3 and 5 come off mtf_ph 2 and 4. The four are + // mutually exclusive by construction: DC_PIX is only ever entered with + // the chain idle, because ar2_go holds the last plane of a pixel back + // while mtf_busy is set, so mtf_ph is 0 in DC_PIX and in the cycle before + // it. + if(nxt_is_pix) begin + a_fire <= 1'b1; a_nib <= pc_nxt; a_lsel <= 1'b0; a_dest <= 1'b1; + end else if(dc_state == DC_PIX) begin + a_fire <= 1'b1; a_nib <= pa_w; a_lsel <= 1'b0; a_dest <= 1'b0; + end else if(mtf_ph == 3'd2) begin + a_fire <= 1'b1; a_nib <= pb_r; a_lsel <= 1'b1; a_dest <= 1'b1; + end else if(mtf_ph == 3'd4) begin + a_fire <= 1'b1; a_nib <= pa_r; a_lsel <= 1'b1; a_dest <= 1'b1; + end else begin + a_fire <= 1'b0; + end + + // stage A latches everything stage B needs on the next clock + b_valid <= a_fire; + if(a_fire) begin + b_pfx <= a_pfx; + b_any <= |a_hit; + b_nib <= a_nib; + b_lsel <= a_lsel; + b_dest <= a_dest; + end + + case(dc_state) + DC_IDLE: begin + if(dec_start) begin + dc_busy <= 1'b1; + pix <= 3'd0; + plane <= 2'd0; + dc_state <= bpp1 ? DC_CTX : DC_PIX; + end + end + + // pixel setup: latch pa/pb/pc/diff and update the colormap + DC_PIX: begin + pa_r <= pa_w; + pb_r <= pb_w; + pc_r <= pc_w; + diff_r <= diff_w; + plane <= 2'd0; + dc_state <= DC_CTX; + end + + // issue the context read + DC_CTX: begin + ctx_addr_r <= {set_w, idx_w}; + dc_state <= DC_AR1; + end + + // symbol decision; both evolution ROM reads for the writeback land at + // the end of this state and sym_r picks between them + DC_AR1: begin + k_r <= k_w; + input_a_r <= input_a; + range_a_r <= range_a; + norm_r <= (k_w != 4'd0); + swap_nx_r <= swap_nx; + sym_r <= symbol; + dc_state <= DC_AR2; + end + + // renormalisation, refill, context writeback, pixel epilogue + DC_AR2: begin + if(ar2_go) begin + if(!last_plane) begin + plane <= plane + 2'd1; + dc_state <= DC_CTX; + end else if(pix != 3'd7) begin + pix <= pix + 3'd1; + dc_state <= bpp1 ? DC_CTX : DC_PIX; + end else begin + dc_state <= DC_END; + end + end + end + + DC_END: begin + dc_busy <= 1'b0; + dc_state <= DC_IDLE; + end + + default: dc_state <= DC_IDLE; + endcase + end + end + +endmodule diff --git a/verilog/sd2snes_spc7110/spc7110_map.v b/verilog/sd2snes_spc7110/spc7110_map.v new file mode 100644 index 000000000..e9740744c --- /dev/null +++ b/verilog/sd2snes_spc7110/spc7110_map.v @@ -0,0 +1,145 @@ +// --------------------------------------------------------------------------- +// spc7110_map.v -- SPC7110 memory control unit: the four 1 MB cartridge +// windows selected by $4830-$4834 plus the $6000-7FFF SRAM gate. +// +// Copyright (c) 2004-2025 ares team, Near et al +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Adapted for RTL from the ares SPC7110 implementation (ISC licensed). +// +// Behaviour (ares mcuromRead()/mcuramRead()). The two +// SNES ranges the cartridge claims fold into one 23 bit linear address: +// +// $00-3f,80-bf:8000-ffff mask 0x800000 -> lin = snes_addr[22:0] +// $c0-ff:0000-ffff mask 0xc00000 -> lin = snes_addr[21:0] +// +// lin[21:20] then picks the window and lin[19:0] is the offset inside it: +// +// window 0 PROM 0x000000 when a PROM exists, else data ROM block r4830 & 7 +// window 1 PROM 0x100000 when r4834 & 4, else data ROM block r4831 & 7. +// The reference tests ONLY the flag there, so with the flag set +// and no PROM present the access resolves to nothing: rom_hit = 0, +// never a fallback to r4831 (the ares window-1 branch is unconditional). +// window 2 data ROM block r4832 & 7 +// window 3 data ROM block r4833 & 7 +// +// Every data ROM path goes through the same dataromRead() pre-masking as +// spc7110_data: the block address is masked with (0x100000 << (r4834 & 3)) - 1 +// and an address carrying 0x400000 with a data ROM smaller than 8 MB reads +// 0x00, which is reported here as rom_hit = 0 (nothing to fetch from PSRAM). +// +// PSRAM layout: the cartridge image is contiguous with +// the PROM at 0x000000, so drom_base IS the PROM size. The module derives +// "a PROM exists" from drom_base != 0 and the PROM mirror mask from +// drom_base - 1; every SPC7110 cartridge has power of two PROM and data ROM, +// which is the same assumption the drom_mask input already makes. +// +// psram_addr is forced to 0 when rom_hit is 0 (including the SRAM window), so +// a consumer can never latch a stale ROM address on a cycle that has none. +// The SRAM offset itself is not produced here: is_sram only reports that the +// access belongs to the save RAM, which the integration maps at its own base. +// --------------------------------------------------------------------------- +module spc7110_map( + input [23:0] snes_addr, + + input [7:0] r4830, + input [7:0] r4831, + input [7:0] r4832, + input [7:0] r4833, + input [7:0] r4834, + + input [23:0] drom_base, // = PROM size; data ROM origin in PSRAM + input [23:0] drom_mask, // physical data ROM mirror mask + + output rom_hit, + output [23:0] psram_addr, + output is_sram +); + + // ------------------------------------------------------------------------- + // address decode + // ------------------------------------------------------------------------- + // banks $00-3f and $80-bf are exactly the banks with bit 22 clear + wire lo_bank = ~snes_addr[22]; + wire hi_bank = snes_addr[23] & snes_addr[22]; // $c0-ff + + wire rom_win = (lo_bank & snes_addr[15]) | hi_bank; + + // Both folds land on the same 22 bits: removing bit 23 from $00-3f/$80-bf + // and bits 23:22 from $c0-ff leaves snes_addr[21:0] either way, because the + // banks of the first range always have bit 22 clear. + wire [1:0] win = snes_addr[21:20]; + wire [19:0] off20 = snes_addr[19:0]; + + // ------------------------------------------------------------------------- + // program ROM + // ------------------------------------------------------------------------- + wire prom_present = |drom_base; + wire [23:0] prom_mask = drom_base - 24'd1; + + // Which branch the reference takes. Window 0 asks "does a PROM exist" and + // falls back to the data ROM when it does not; window 1 asks ONLY about + // r4834 bit 2 and takes the PROM branch unconditionally when it is set -- + // there is no fallback to r4831 there. With the bit set and no PROM the + // access therefore resolves to nothing at all (matching the reference). + wire prom_branch = ((win == 2'd0) & prom_present) + | ((win == 2'd1) & r4834[2]); + + wire use_prom = rom_win & prom_branch & prom_present; + + // 0x000000 for window 0, 0x100000 for window 1 + wire [23:0] prom_lin = {3'b000, (win == 2'd1), off20}; + wire [23:0] prom_off = prom_lin & prom_mask; + + // ------------------------------------------------------------------------- + // data ROM + // ------------------------------------------------------------------------- + reg [2:0] blk; + always @* begin + case(win) + 2'd0: blk = r4830[2:0]; + 2'd1: blk = r4831[2:0]; + 2'd2: blk = r4832[2:0]; + default: blk = r4833[2:0]; + endcase + end + + reg [22:0] size_mask; + always @* begin + case(r4834[1:0]) + 2'd0: size_mask = 23'h0fffff; // 8 Mbit + 2'd1: size_mask = 23'h1fffff; // 16 Mbit + 2'd2: size_mask = 23'h3fffff; // 32 Mbit + default: size_mask = 23'h7fffff; // 64 Mbit + endcase + end + + wire [22:0] drom_lin = {blk, off20}; + wire drom_kill = (r4834[1:0] != 2'b11) & drom_lin[22]; + wire [23:0] drom_off = {1'b0, drom_lin & size_mask} & drom_mask; + + // ------------------------------------------------------------------------- + // outputs + // ------------------------------------------------------------------------- + assign is_sram = lo_bank & r4830[7] & (snes_addr[15:13] == 3'b011); + + // the PROM branch answers only if a PROM is there, the data ROM branch only + // if the 0x400000 guard of dataromRead() does not fire + assign rom_hit = rom_win & (prom_branch ? prom_present : ~drom_kill); + + assign psram_addr = ~rom_hit ? 24'h000000 : + use_prom ? prom_off : + (drom_base + drom_off); + +endmodule diff --git a/verilog/sd2snes_spc7110/spc7110_regs.v b/verilog/sd2snes_spc7110/spc7110_regs.v new file mode 100644 index 000000000..57783feae --- /dev/null +++ b/verilog/sd2snes_spc7110/spc7110_regs.v @@ -0,0 +1,504 @@ +`timescale 1 ns / 1 ns +////////////////////////////////////////////////////////////////////////////////// +// Module Name: spc7110_regs +// Project Name: sd2snes +// +// Description: +// SPC7110 register block. Owns the fine decode of $4800-$484F, the memory +// control registers $4830-$4834, and the routing of access strobes and +// read-back to the five units that implement the chip: +// +// $4800-$480C spc7110_dcu decompression unit +// $4810-$481A spc7110_data data port +// $4820-$482F spc7110_alu multiply / divide +// $4830-$4834 here bank windows + SRAM enable (spc7110_map reads +// them and produces the cartridge address) +// $4840-$4842 spc7110_rtcif RTC-4513 interface +// +// The frame (main.v) decodes the window itself and passes reg_enable plus the +// register index; this module never sees a bank. That is also where the two +// whole-bank aliases of the reference are resolved ($50:xxxx -> $4800, +// $58:xxxx -> $4808), so they arrive here indistinguishable from a direct +// access. Registers with no unit behind them read back $00 and drop writes; +// $4808 is one of them, so a write through the $58 alias is dropped too. +// +// Strobes. we_* comes from the END of the SNES write cycle and rd_* from the +// END of the read cycle, so exactly one strobe is high for exactly one clock +// per SNES access. That is a hard requirement of the units: they queue one +// operation per strobe cycle, and the read-back of the data port, the DCU and +// the RTC interface is combinational, so a strobe taken at the START of a read +// would auto-increment the pointer under the byte the SNES is latching. +// +// The reference model folds $4840-$484F back onto $4800-$483F (it has no RTC); +// real hardware does not, and neither does this decode. +// +// Reset is synchronous and active high (SNES_DEADr in the frame): the chip +// comes up with the power-on register values of the reference and the units +// drop whatever they were doing, which is what the MCU needs while it loads a +// ROM into PSRAM. +////////////////////////////////////////////////////////////////////////////////// +module spc7110_regs( + input clkin, + input rst, +// SPC7110 behaviour referenced here follows the ares implementation (ISC); +// the full ISC notice is carried in spc7110_map.v / spc7110_dcu.v. + + /* SNES register window $4800-$484F, decoded by the frame */ + input reg_enable, + input [7:0] reg_addr, // SNES_ADDR[7:0] + input [7:0] reg_din, // registered SNES data bus + input wr_end, // SNES_WR_end + input rd_end, // SNES_RD_end + output [7:0] reg_dout, + + /* cartridge address mapping (spc7110_map) */ + input [23:0] snes_addr, + input [23:0] drom_base, + input [23:0] drom_mask, + output map_rom_hit, + output [23:0] map_psram_addr, + output map_is_sram, + + /* data ROM ports, one per requester; the frame arbitrates and fetches */ + output data_drom_req, + output [23:0] data_drom_addr, + input data_drom_ack, + output dcu_drom_req, + output [23:0] dcu_drom_addr, + input dcu_drom_ack, + input [7:0] drom_data, + + /* RTC-4513 time keeping (rtc.v) */ + input [59:0] rtc_data, + output rtc_we, +`ifndef MK2 + output [59:0] rtc_data_wr, + + /* RTC-4513 battery backup state, straight through to spc7110_rtcif (SPI $e6 + reads it, $e7 restores it) */ + output [59:0] bkp_time, + output [1:0] bkp_flags, + input bkp_we, + input [59:0] bkp_time_in, + input [1:0] bkp_flags_in +`else + /* mk2 flavor: no virtual battery (contract $0Ca) */ + output [59:0] rtc_data_wr +`endif +); + +// ----------------------------------------------------------------------------- +// access strobes +// +// The decode below is combinational ("_d") and is REGISTERED before it reaches +// the units. SNES_ADDR arrives from a shift register the fitter maps into block +// RAM, whose clock-to-output is slow; decoding it and then running the data +// port's two chained 23 bit adders inside the same clock misses the 96 MHz +// period by about 0.9 ns. With the strobe registered, the adders start from a +// plain flip-flop and the decode gets a clock of its own. +// +// The extra clock changes nothing that is observable: the strobes are still one +// clock pulses with at most one high at a time, and they already fire at the END +// of the SNES access, long after the byte was latched off the bus. +// +// No reset term is needed on the registered strobes: reg_enable (the frame's +// spc7110_reg_enable) is held at 0 for as long as rst is asserted, so wr and rd +// are 0 through the whole reset and on the clock it releases. +// ----------------------------------------------------------------------------- +wire wr = reg_enable & wr_end; +wire rd = reg_enable & rd_end; + +wire [7:0] a = reg_addr; + +wire blk_48_0x = (a[7:4] == 4'h0); +wire blk_48_1x = (a[7:4] == 4'h1); +wire blk_48_2x = (a[7:4] == 4'h2); +wire blk_48_3x = (a[7:4] == 4'h3); +wire blk_48_4x = (a[7:4] == 4'h4); + +// decompression unit ($4800-$480C). $4800 and $4808 take no write. +wire we_4801_d = wr & blk_48_0x & (a[3:0] == 4'h1); +wire we_4802_d = wr & blk_48_0x & (a[3:0] == 4'h2); +wire we_4803_d = wr & blk_48_0x & (a[3:0] == 4'h3); +wire we_4804_d = wr & blk_48_0x & (a[3:0] == 4'h4); +wire we_4805_d = wr & blk_48_0x & (a[3:0] == 4'h5); +wire we_4806_d = wr & blk_48_0x & (a[3:0] == 4'h6); +wire we_4807_d = wr & blk_48_0x & (a[3:0] == 4'h7); +wire we_4808_d = wr & blk_48_0x & (a[3:0] == 4'h8); +wire we_4809_d = wr & blk_48_0x & (a[3:0] == 4'h9); +wire we_480a_d = wr & blk_48_0x & (a[3:0] == 4'ha); +wire we_480b_d = wr & blk_48_0x & (a[3:0] == 4'hb); +// a read of $4800 consumes one decompressed byte and decrements {r480a,r4809} +wire rd_4800_d = rd & blk_48_0x & (a[3:0] == 4'h0); + +// data port ($4810-$481A). $4810 and $481A also have a read side effect. +wire we_4811_d = wr & blk_48_1x & (a[3:0] == 4'h1); +wire we_4812_d = wr & blk_48_1x & (a[3:0] == 4'h2); +wire we_4813_d = wr & blk_48_1x & (a[3:0] == 4'h3); +wire we_4814_d = wr & blk_48_1x & (a[3:0] == 4'h4); +wire we_4815_d = wr & blk_48_1x & (a[3:0] == 4'h5); +wire we_4816_d = wr & blk_48_1x & (a[3:0] == 4'h6); +wire we_4817_d = wr & blk_48_1x & (a[3:0] == 4'h7); +wire we_4818_d = wr & blk_48_1x & (a[3:0] == 4'h8); +wire we_481a_d = wr & blk_48_1x & (a[3:0] == 4'ha); +wire rd_4810_d = rd & blk_48_1x & (a[3:0] == 4'h0); +wire rd_481a_d = rd & blk_48_1x & (a[3:0] == 4'ha); + +// arithmetic logic unit ($4820-$482F) +wire we_4820_d = wr & blk_48_2x & (a[3:0] == 4'h0); +wire we_4821_d = wr & blk_48_2x & (a[3:0] == 4'h1); +wire we_4822_d = wr & blk_48_2x & (a[3:0] == 4'h2); +wire we_4823_d = wr & blk_48_2x & (a[3:0] == 4'h3); +wire we_4824_d = wr & blk_48_2x & (a[3:0] == 4'h4); +wire we_4825_d = wr & blk_48_2x & (a[3:0] == 4'h5); +wire we_4826_d = wr & blk_48_2x & (a[3:0] == 4'h6); +wire we_4827_d = wr & blk_48_2x & (a[3:0] == 4'h7); +wire we_482e_d = wr & blk_48_2x & (a[3:0] == 4'he); + +// memory control unit ($4830-$4834), the register file below +wire we_4830_d = wr & blk_48_3x & (a[3:0] == 4'h0); +wire we_4831_d = wr & blk_48_3x & (a[3:0] == 4'h1); +wire we_4832_d = wr & blk_48_3x & (a[3:0] == 4'h2); +wire we_4833_d = wr & blk_48_3x & (a[3:0] == 4'h3); +wire we_4834_d = wr & blk_48_3x & (a[3:0] == 4'h4); + +// RTC-4513 interface ($4840-$4842) +wire we_4840_d = wr & blk_48_4x & (a[3:0] == 4'h0); +wire we_4841_d = wr & blk_48_4x & (a[3:0] == 4'h1); +wire we_4842_d = wr & blk_48_4x & (a[3:0] == 4'h2); +wire rd_4840_d = rd & blk_48_4x & (a[3:0] == 4'h0); +wire rd_4841_d = rd & blk_48_4x & (a[3:0] == 4'h1); +wire rd_4842_d = rd & blk_48_4x & (a[3:0] == 4'h2); + + +// registered strobes: this is what the units see +reg [7:0] wdata; +reg we_4801, we_4802, we_4803, we_4804; +reg we_4805, we_4806, we_4807, we_4808; +reg we_4809, we_480a, we_480b, rd_4800; +reg we_4811, we_4812, we_4813, we_4814; +reg we_4815, we_4816, we_4817, we_4818; +reg we_481a, rd_4810, rd_481a, we_4820; +reg we_4821, we_4822, we_4823, we_4824; +reg we_4825, we_4826, we_4827, we_482e; +reg we_4830, we_4831, we_4832, we_4833; +reg we_4834, we_4840, we_4841, we_4842; +reg rd_4840, rd_4841, rd_4842; + +always @(posedge clkin) begin + wdata <= reg_din; + we_4801 <= we_4801_d; + we_4802 <= we_4802_d; + we_4803 <= we_4803_d; + we_4804 <= we_4804_d; + we_4805 <= we_4805_d; + we_4806 <= we_4806_d; + we_4807 <= we_4807_d; + we_4808 <= we_4808_d; + we_4809 <= we_4809_d; + we_480a <= we_480a_d; + we_480b <= we_480b_d; + rd_4800 <= rd_4800_d; + we_4811 <= we_4811_d; + we_4812 <= we_4812_d; + we_4813 <= we_4813_d; + we_4814 <= we_4814_d; + we_4815 <= we_4815_d; + we_4816 <= we_4816_d; + we_4817 <= we_4817_d; + we_4818 <= we_4818_d; + we_481a <= we_481a_d; + rd_4810 <= rd_4810_d; + rd_481a <= rd_481a_d; + we_4820 <= we_4820_d; + we_4821 <= we_4821_d; + we_4822 <= we_4822_d; + we_4823 <= we_4823_d; + we_4824 <= we_4824_d; + we_4825 <= we_4825_d; + we_4826 <= we_4826_d; + we_4827 <= we_4827_d; + we_482e <= we_482e_d; + we_4830 <= we_4830_d; + we_4831 <= we_4831_d; + we_4832 <= we_4832_d; + we_4833 <= we_4833_d; + we_4834 <= we_4834_d; + we_4840 <= we_4840_d; + we_4841 <= we_4841_d; + we_4842 <= we_4842_d; + rd_4840 <= rd_4840_d; + rd_4841 <= rd_4841_d; + rd_4842 <= rd_4842_d; +end + +// ----------------------------------------------------------------------------- +// memory control registers $4830-$4834 +// +// Write masks and power-on values are the reference ones: $4830 keeps the SRAM +// enable plus the three block bits, the other four keep three bits each. The +// masked bits read back as 0, which is what the reference reports too. +// ----------------------------------------------------------------------------- +reg [7:0] q4830; +reg [7:0] q4831; +reg [7:0] q4832; +reg [7:0] q4833; +reg [7:0] q4834; + +initial begin + q4830 = 8'h00; + q4831 = 8'h00; + q4832 = 8'h01; + q4833 = 8'h02; + q4834 = 8'h00; +end + +always @(posedge clkin) begin + if(rst) begin + q4830 <= 8'h00; + q4831 <= 8'h00; + q4832 <= 8'h01; + q4833 <= 8'h02; + q4834 <= 8'h00; + end else begin + if(we_4830) q4830 <= wdata & 8'h87; + if(we_4831) q4831 <= wdata & 8'h07; + if(we_4832) q4832 <= wdata & 8'h07; + if(we_4833) q4833 <= wdata & 8'h07; + if(we_4834) q4834 <= wdata & 8'h07; + end +end + +// data ROM size, shared by the two units that pre-mask their own reads +wire [1:0] drom_size = q4834[1:0]; + +// ----------------------------------------------------------------------------- +// units +// ----------------------------------------------------------------------------- +wire [7:0] dcu_data; +wire [7:0] d4801, d4802, d4803, d4804, d4805, d4806, d4807; +wire [7:0] d4808, d4809, d480a, d480b, d480c; + +spc7110_dcu spc7110_dcu_i( + .clkin(clkin), + .rst(rst), + .we_4801(we_4801), + .we_4802(we_4802), + .we_4803(we_4803), + .we_4804(we_4804), + .we_4805(we_4805), + .we_4806(we_4806), + .we_4807(we_4807), + .we_4808(we_4808), + .we_4809(we_4809), + .we_480a(we_480a), + .we_480b(we_480b), + .wdata(wdata), + .rd_4800(rd_4800), + .drom_size(drom_size), + .dcu_data(dcu_data), + // dcu_ready is not part of the register map: dcu_data already reads $00 while + // the buffer the SNES owns is being refilled, and $480C bit7 is the flag the + // game polls. Left open on purpose. + .dcu_ready(), + .r4801(d4801), + .r4802(d4802), + .r4803(d4803), + .r4804(d4804), + .r4805(d4805), + .r4806(d4806), + .r4807(d4807), + .r4808(d4808), + .r4809(d4809), + .r480a(d480a), + .r480b(d480b), + .r480c(d480c), + .drom_req(dcu_drom_req), + .drom_addr(dcu_drom_addr), + .drom_ack(dcu_drom_ack), + .drom_data(drom_data) +); + +wire [7:0] p4810, p4811, p4812, p4813, p4814; +wire [7:0] p4815, p4816, p4817, p4818, p481a; + +spc7110_data spc7110_data_i( + .clkin(clkin), + .rst(rst), + .we_4811(we_4811), + .we_4812(we_4812), + .we_4813(we_4813), + .we_4814(we_4814), + .we_4815(we_4815), + .we_4816(we_4816), + .we_4817(we_4817), + .we_4818(we_4818), + .we_481a(we_481a), + .wdata(wdata), + .rd_4810(rd_4810), + .rd_481a(rd_481a), + .drom_size(drom_size), + .r4810(p4810), + .r4811(p4811), + .r4812(p4812), + .r4813(p4813), + .r4814(p4814), + .r4815(p4815), + .r4816(p4816), + .r4817(p4817), + .r4818(p4818), + .r481a(p481a), + .drom_req(data_drom_req), + .drom_addr(data_drom_addr), + .drom_ack(data_drom_ack), + .drom_data(drom_data) +); + +wire [7:0] m4820, m4821, m4822, m4823, m4824, m4825, m4826, m4827; +wire [7:0] m4828, m4829, m482a, m482b, m482c, m482d, m482e, m482f; + +spc7110_alu spc7110_alu_i( + .clkin(clkin), + .rst(rst), + .we_4820(we_4820), + .we_4821(we_4821), + .we_4822(we_4822), + .we_4823(we_4823), + .we_4824(we_4824), + .we_4825(we_4825), + .we_4826(we_4826), + .we_4827(we_4827), + .we_482e(we_482e), + .wdata(wdata), + .r4820(m4820), + .r4821(m4821), + .r4822(m4822), + .r4823(m4823), + .r4824(m4824), + .r4825(m4825), + .r4826(m4826), + .r4827(m4827), + .r4828(m4828), + .r4829(m4829), + .r482a(m482a), + .r482b(m482b), + .r482c(m482c), + .r482d(m482d), + .r482e(m482e), + .r482f(m482f) +); + +spc7110_map spc7110_map_i( + .snes_addr(snes_addr), + .r4830(q4830), + .r4831(q4831), + .r4832(q4832), + .r4833(q4833), + .r4834(q4834), + .drom_base(drom_base), + .drom_mask(drom_mask), + .rom_hit(map_rom_hit), + .psram_addr(map_psram_addr), + .is_sram(map_is_sram) +); + +wire [7:0] c4840, c4841, c4842; + +spc7110_rtcif spc7110_rtcif_i( + .clkin(clkin), + .rst(rst), + .we_4840(we_4840), + .we_4841(we_4841), + .we_4842(we_4842), + .wdata(wdata), + .rd_4840(rd_4840), + .rd_4841(rd_4841), + .rd_4842(rd_4842), + .r4840(c4840), + .r4841(c4841), + .r4842(c4842), + .rtc_data(rtc_data), + .rtc_we(rtc_we), +`ifndef MK2 + .rtc_data_wr(rtc_data_wr), + .bkp_time(bkp_time), + .bkp_flags(bkp_flags), + .bkp_we(bkp_we), + .bkp_time_in(bkp_time_in), + .bkp_flags_in(bkp_flags_in) +`else + .rtc_data_wr(rtc_data_wr) +`endif +); + +// ----------------------------------------------------------------------------- +// read-back mux +// +// Purely combinational, as the invariant of the register block demands: a read +// never waits for a data ROM fetch. $4800 is served from the tile buffer the +// SNES owns ($00 while it is still being refilled, which is what the DCU already +// drives on dcu_data), and $4810 from the byte the data port fetched ahead. +// ----------------------------------------------------------------------------- +reg [7:0] dout; +always @* begin + dout = 8'h00; + case(a) + 8'h00: dout = dcu_data; + 8'h01: dout = d4801; + 8'h02: dout = d4802; + 8'h03: dout = d4803; + 8'h04: dout = d4804; + 8'h05: dout = d4805; + 8'h06: dout = d4806; + 8'h07: dout = d4807; + 8'h08: dout = d4808; + 8'h09: dout = d4809; + 8'h0a: dout = d480a; + 8'h0b: dout = d480b; + 8'h0c: dout = d480c; + + 8'h10: dout = p4810; + 8'h11: dout = p4811; + 8'h12: dout = p4812; + 8'h13: dout = p4813; + 8'h14: dout = p4814; + 8'h15: dout = p4815; + 8'h16: dout = p4816; + 8'h17: dout = p4817; + 8'h18: dout = p4818; + 8'h1a: dout = p481a; + + 8'h20: dout = m4820; + 8'h21: dout = m4821; + 8'h22: dout = m4822; + 8'h23: dout = m4823; + 8'h24: dout = m4824; + 8'h25: dout = m4825; + 8'h26: dout = m4826; + 8'h27: dout = m4827; + 8'h28: dout = m4828; + 8'h29: dout = m4829; + 8'h2a: dout = m482a; + 8'h2b: dout = m482b; + 8'h2c: dout = m482c; + 8'h2d: dout = m482d; + 8'h2e: dout = m482e; + 8'h2f: dout = m482f; + + 8'h30: dout = q4830; + 8'h31: dout = q4831; + 8'h32: dout = q4832; + 8'h33: dout = q4833; + 8'h34: dout = q4834; + + 8'h40: dout = c4840; + 8'h41: dout = c4841; + 8'h42: dout = c4842; + + default: dout = 8'h00; + endcase +end + +assign reg_dout = dout; + +endmodule diff --git a/verilog/sd2snes_spc7110/spc7110_rtcif.v b/verilog/sd2snes_spc7110/spc7110_rtcif.v new file mode 100644 index 000000000..d7c010240 --- /dev/null +++ b/verilog/sd2snes_spc7110/spc7110_rtcif.v @@ -0,0 +1,604 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Module Name: spc7110_rtcif +// Project Name: sd2snes +// +// Description: +// SPC7110 real time clock interface: the $4840-$4842 register block through +// which the SNES talks to the external Epson RTC-4513 found on the Far East +// of Eden Zero (Tengai Makyou Zero) cartridge. +// +// This module implements the register/command protocol only. Time keeping +// stays where it already is: rtc.v from sd2snes_base counts in exactly the +// packed nibble format the MCU uses for SPI command $e5, and this module +// snapshots that counter on chip select, serves nibbles out of the snapshot, +// and writes a modified snapshot back through the same port srtc.v uses +// (we1 / rtc_data_in1). There is deliberately no second seconds counter in +// here. +// +// Protocol sources (public documentation only): +// - nocash "fullsnes", section "SNES Cart SPC7110 with RTC-4513 Real Time +// Clock": port list, transfer sequence (switch CE from LOW to HIGH, send +// command, send starting index, read or write one or more 4bit data +// units with automatic index increment and wrap from 0Fh to 00h, finally +// switch CE back LOW), commands 03h/0Ch, and the RTC-4513 register table. +// - nesdev forum topic 4106, Dark Force's SPC7110 register notes: $4841 +// takes the command byte as the first write after RTC enable, then the +// index of the register to access, then data; the index auto-increments +// after each subsequent read/write; $4842 bit7 is the ready flag the game +// tests before reading RTC data. +// +// RTC-4513 register index <-> rtc.v rtc_data[59:0] nibble map: +// +// idx RTC-4513 register rtc_data mask non-counter bits +// 0 seconds, ones [3:0] f - +// 1 seconds, tens [7:4] 7 bit3 = LOST +// 2 minutes, ones [11:8] f - +// 3 minutes, tens [15:12] 7 bit3 = WRAP +// 4 hours, ones [19:16] f - +// 5 hours, tens [23:20] 3 bit3 = WRAP, bit2 = PM/AM +// 6 day of month, ones [27:24] f - +// 7 day of month, tens [31:28] 3 bit3 = WRAP, bit2 = RAM +// 8 month, ones [35:32] f - +// 9 month, tens [39:36] 1 bit3 = WRAP, bit2..1 = RAM +// a year, ones [43:40] f - +// b year, tens [47:44] f - +// c day of week (0 = Sunday) [59:56] 7 bit3 = WRAP +// d control register D - f kept locally, reset 1 +// e control register E - f kept locally, reset f +// f control register F - f kept locally, reset 6 +// +// The "mask" column is what a write keeps: everything outside it is a flag or +// user RAM on the RTC-4513, not part of the BCD counter, and must not reach +// rtc.v (see the write case). Those bits read back as 0; the three user RAM +// bits at indices 7 and 9 are not stored. +// +// Two control bits do have an effect and are carried out at the moment the +// register is written: RESET (F bit0) zeroes the seconds, 30ADJ (D bit3) +// zeroes the seconds and adds one minute when the seconds were >= 30. The +// rest of D/E/F is plain read/write storage. +// +// rtc_data[51:48] (century, ones) and [55:52] (century, thousands) have no +// RTC-4513 index: the chip only keeps a two digit year. A write therefore +// starts from the snapshot instead of from zero, which carries the century - +// and the day of week rtc.v computes - through untouched. +// +// The WRAP bits ("time changed during access") are always reported as 0: the +// whole session is served from one snapshot taken at chip select, so the time +// cannot change underneath a running transfer and the game never has to +// deselect and read again. Same reasoning for LOST: the MCU programs the +// clock on every game load, so the time is never lost. +// +// $4842 bit7 is the ready flag. On real hardware the SPC7110 moves the +// nibble over a 4 bit serial bus to the RTC and the flag is low while that +// runs; Dark Force adds that it is "cleared after successful read". Here the +// data is available immediately, so the flag is modelled as a consumable: a +// data read at $4841 arms it, reading $4842 consumes it. Both driver shapes +// terminate - "poll until ready, then read" costs at most one extra poll, and +// "read, then poll until the flag clears" ends on the first poll. +// +// Bus interface notes for the integration (spc7110_regs.v): +// - we_484x / rd_484x are one clock strobes. r4840..r4842 are +// combinational, so rd_4841 has to be the END of the SNES read cycle +// (SNES_RD_end, the way srtc.v is wired): the index auto-increment must +// not change the byte the SNES is latching. +// - rtc_we / rtc_data_wr go to rtc.v's we1 / rtc_data_in1. rtc.v samples +// we1 through a 3 stage shift register on its own clock (CLKIN, 8 MHz on +// mk3) while this module runs on CLK2, so the strobe is held WE_PULSE +// clocks - long enough to survive that clock ratio. +// +////////////////////////////////////////////////////////////////////////////////// +module spc7110_rtcif( + input clkin, + input rst, + + // register writes (one clock strobes, data on wdata) + input we_4840, + input we_4841, + input we_4842, + input [7:0] wdata, + + // register reads (one clock strobes at the end of the SNES read cycle) + input rd_4840, + input rd_4841, + input rd_4842, + + // read-back + output [7:0] r4840, + output [7:0] r4841, + output [7:0] r4842, + + // time keeping (rtc.v) + input [59:0] rtc_data, + output rtc_we, +`ifndef MK2 + output [59:0] rtc_data_wr, + + // Battery backup state (SPI $e6 reads it, $e7 restores it). bkp_time is the + // time the cartridge shows - the freeze register while the clock is stopped, + // rtc.v otherwise - with the day of week counter already substituted into + // [59:56]. bkp_flags is {stopped, dow_owned}. bkp_we is a one clock strobe + // that loads all of it back. + output [59:0] bkp_time, + output [1:0] bkp_flags, + input bkp_we, + input [59:0] bkp_time_in, + input [1:0] bkp_flags_in +`else + // mk2 flavor: no virtual battery ($e6/$e7 handover gated out, contract $0Ca). + // The freeze register and STOP stay - they are cheap and the game uses them + // inside a session; what goes is only the handover to and from the MCU. + output [59:0] rtc_data_wr +`endif +); + +// Width of the rtc.v write strobe, in clkin cycles. rtc.v edge-detects we1 +// with a three stage shift register clocked at CLKIN; 64 CLK2 cycles are +// slightly over five CLKIN periods at the mk3 96/8 MHz ratio. +parameter WE_PULSE = 7'd64; + +// A time write is normally committed when the game deselects the chip. If a +// game ever leaves CE high after writing, this watchdog commits it anyway +// after 2^STALE_BITS idle clocks (~44 ms at 96 MHz). +parameter STALE_BITS = 22; + +localparam CMD_WRITE = 4'h3; // fullsnes: 03h = write mode +localparam CMD_READ = 4'hc; // fullsnes: 0Ch = read mode + +localparam ST_CMD = 2'd0; // waiting for the command byte +localparam ST_INDEX = 2'd1; // waiting for the starting index +localparam ST_XFER = 2'd2; // data nibbles, index auto-increments + +reg ce; // $4840 bit0: chip select +reg [1:0] state; +reg [3:0] index; +reg [3:0] cmd; + +reg [59:0] snap; // coherent copy of rtc_data for this session +reg [59:0] wr_hold; // what rtc.v gets to see while rtc_we is up +reg [3:0] ctrl_d, ctrl_e, ctrl_f; + +reg dirty; // a time register was written this session +reg rtc_we_r; +reg [6:0] we_cnt; +reg [STALE_BITS-1:0] idle_cnt; + +// rtc.v only publishes rtc_data once per second (its STATE_LATCH), so for up +// to a second after a write the counter output still shows the old time. A +// session opened in that window must not snapshot it, or a game that writes +// the clock and reads it straight back would see its write disappear. +// wr_pending says "the last thing written is newer than what rtc_data shows"; +// it clears as soon as rtc.v publishes a new second. +reg wr_pending; +reg [59:0] rtc_prev; + +// $4842 bit7. On the real cartridge the SPC7110 shifts the nibble to the RTC +// over a 4 bit serial bus, so the flag drops while a transfer is running and +// the game polls it; Dark Force's notes add "high bit cleared after successful +// read". Here the data is available immediately, so the flag is modelled as a +// consumable: a data read at $4841 arms it and reading $4842 consumes it. +// That satisfies BOTH shapes of driver loop - "poll until ready, then read" +// falls through after at most one extra poll, and "read, then poll until the +// flag clears" terminates on the first poll. An always-ready $4842 would hang +// the second shape forever. +reg busy_r; + +// Day of week (index Ch). On the RTC-4513 this register is a plain modulo 7 +// counter: the host loads it with whatever value it likes and the chip steps it +// by one at midnight - it is NOT derived from the date. rtc.v, by contrast, +// recomputes the weekday from the calendar on every one second pass and +// overwrites whatever was written, so a value the game puts there survives less +// than a second. +// +// The cartridge's own factory check program proves the counter semantics: it +// sets the clock to 1999-12-31 23:59:59 with weekday 6, while 1999-12-31 was a +// Friday (5), and then expects to read weekday 0 after the roll into +// 2000-01-01, which was a Saturday (6). The expected value is deliberately one +// step off the real calendar, so no date-derived weekday can ever satisfy it. +// +// dow_r is that counter. Until the game writes index Ch, dow_owned is 0 and it +// simply mirrors rtc.v, which is what gives a sensible weekday for the wall +// clock the MCU programs at load time. The first write takes ownership; from +// then on the value is kept here and stepped at midnight. date_ref is the date +// dow_r is in step with, century included: without the century the roll +// 2000-01-01 -> 2100-01-01 this test can produce would be invisible. +// +// dow_ss is the per session copy, so index Ch stays as coherent as every other +// index. +reg [3:0] dow_r; +reg [3:0] dow_ss; +reg dow_owned; +reg [31:0] date_ref; + +// STOP (control register F, bit1) and the freeze register. +// +// The polarity of this bit is the one the cartridge's own factory check program +// demonstrates, not the one fullsnes prints: bit1 = 1 STOPS the clock, bit1 = 0 +// lets it run. The program writes F=$07 to freeze, programs the calendar, +// writes F=$04 and only then does the clock advance; it finishes by writing +// F=$06, and the MODE 2 test that follows a power cycle requires the calendar +// to have stayed exactly where it was. fullsnes ("0=Stop, 1=Normal") is +// inverted; Dark Force ("1 - stop timer") is right. +// +// While stopped, every session is served from frz instead of from rtc.v, which +// keeps counting underneath - stopping it is not possible from here, and would +// be wrong anyway, since the MCU reprograms it on every load. Clearing STOP +// commits the frozen time back through the normal write path, so the clock +// resumes where it was left rather than where rtc.v happens to be. +// +// stopped is NOT ctrl_f[1] itself. ctrl_f keeps its documented power-up value +// of 6, which has bit1 set, but a freshly configured core has no battery state +// to defend: it has just been handed the wall clock by the MCU and must run. +// So the effective flag starts at 0 and only follows the game's own writes to +// register F (or an $e7 restore) - a game that never touches the RTC keeps the +// wall clock it was given. +reg stopped; +reg [59:0] frz; + +`ifndef MK2 +// The state the MCU hands back with $e7, kept in its own registers. The module +// sits in reset (SNES_DEADr) for the whole time the MCU is setting the +// cartridge up, so the restore has to be latched here and the reset branch has +// to seed the working registers from it - otherwise the reset that is still +// running would throw it away before the SNES ever starts. bkp_valid is only +// cleared by FPGA configuration, i.e. once per game load, which is exactly when +// "there is no backup" is the right answer. +reg [59:0] bkp_hold; +reg [1:0] bkp_hold_f; +reg bkp_valid; +`endif +// Whether that seed has already been planted. rst here is SNES_DEADr, which +// goes high for every console reset and not just for the load, but the RTC-4513 +// is battery backed: it sits outside the console's reset domain and a reset +// does not touch the calendar, the weekday, STOP or the control registers. So +// the seed is applied only while a handover is outstanding - from the moment +// $e7 arrives until the console actually starts running - and every reset after +// that leaves the chip alone. +reg bkp_applied; + +// what the cartridge shows: the freeze register, or the live counter +wire [59:0] time_src = stopped ? frz : rtc_data; + +initial begin + ce = 1'b0; + state = ST_CMD; + index = 4'h0; + cmd = 4'h0; + snap = 60'h0; + wr_hold = 60'h0; + ctrl_d = 4'h1; + ctrl_e = 4'hf; + ctrl_f = 4'h6; + dirty = 1'b0; + rtc_we_r = 1'b0; + we_cnt = 7'd0; + idle_cnt = {STALE_BITS{1'b0}}; + wr_pending = 1'b0; + rtc_prev = 60'h0; + busy_r = 1'b0; + dow_r = 4'h0; + dow_ss = 4'h0; + dow_owned = 1'b0; + date_ref = 32'h0; + stopped = 1'b0; + frz = 60'h0; +`ifndef MK2 + bkp_hold = 60'h0; + bkp_hold_f = 2'b00; + bkp_valid = 1'b0; +`endif + bkp_applied = 1'b0; +end + +// --------------------------------------------------------------------------- +// read-back +// --------------------------------------------------------------------------- +reg [3:0] nibble; +always @* begin + case (index) + 4'h0: nibble = snap[3:0]; + 4'h1: nibble = snap[7:4]; + 4'h2: nibble = snap[11:8]; + 4'h3: nibble = snap[15:12]; + 4'h4: nibble = snap[19:16]; + 4'h5: nibble = snap[23:20]; + 4'h6: nibble = snap[27:24]; + 4'h7: nibble = snap[31:28]; + 4'h8: nibble = snap[35:32]; + 4'h9: nibble = snap[39:36]; + 4'ha: nibble = snap[43:40]; + 4'hb: nibble = snap[47:44]; + 4'hc: nibble = dow_ss; + 4'hd: nibble = ctrl_d; + 4'he: nibble = ctrl_e; + default: nibble = ctrl_f; + endcase +end + +assign r4840 = {7'h00, ce}; +assign r4841 = ce ? {4'h0, nibble} : 8'h00; +assign r4842 = {~busy_r, 7'h00}; // bit7 = ready + +assign rtc_we = rtc_we_r; +assign rtc_data_wr = wr_hold; + +`ifndef MK2 +assign bkp_time = {dow_owned ? dow_r : time_src[59:56], time_src[55:0]}; +assign bkp_flags = {stopped, dow_owned}; +`endif + +// --------------------------------------------------------------------------- +// command / index / data machine +// --------------------------------------------------------------------------- +wire ce_rising = wdata[0] & ~ce; +wire ce_falling = ~wdata[0] & ce; +wire stale = dirty & ce & (idle_cnt == {STALE_BITS{1'b1}}); + +always @(posedge clkin) begin +`ifndef MK2 + // accepted in reset as well as out of it - see bkp_hold above + if (bkp_we) begin + bkp_hold <= bkp_time_in; + bkp_hold_f <= bkp_flags_in; + bkp_valid <= 1'b1; + bkp_applied <= 1'b0; // a fresh handover, waiting to be planted + end + +`endif + + if (rst) begin + // Bus and session state only. The console going away drops chip select, so + // whatever transfer was open is abandoned - and abandoned WITHOUT being + // committed, which is what dirty <= 0 says: a half written time never + // reaches the clock. Everything that belongs to the chip rather than to + // the bus is deliberately left alone. + ce <= 1'b0; + state <= ST_CMD; + index <= 4'h0; + cmd <= 4'h0; + dirty <= 1'b0; + rtc_we_r <= 1'b0; + we_cnt <= 7'd0; + idle_cnt <= {STALE_BITS{1'b0}}; + busy_r <= 1'b0; + + // The handover from the MCU. The MCU holds the console in reset for the + // whole time it is setting the cartridge up, so this keeps tracking until + // the console starts; from then on bkp_applied is set and a player pressing + // reset finds the clock exactly as the game left it. +`ifndef MK2 + if (!bkp_applied) begin + snap <= bkp_valid ? bkp_hold : rtc_data; + wr_hold <= rtc_data; + ctrl_d <= 4'h1; + ctrl_e <= 4'hf; + ctrl_f <= 4'h6; + wr_pending <= 1'b0; + rtc_prev <= rtc_data; + dow_r <= bkp_valid ? bkp_hold[59:56] : rtc_data[59:56]; + dow_ss <= bkp_valid ? bkp_hold[59:56] : rtc_data[59:56]; + dow_owned <= bkp_valid ? bkp_hold_f[0] : 1'b0; + date_ref <= bkp_valid ? bkp_hold[55:24] : rtc_data[55:24]; + stopped <= bkp_valid ? bkp_hold_f[1] : 1'b0; + frz <= bkp_valid ? bkp_hold : rtc_data; + end +`else + // Same block with the handover selections collapsed: with no backup to + // plant, every seed comes from the wall clock the MCU programmed ($e5). + // bkp_applied itself stays - it is what makes a console reset leave the + // clock alone once the game is running, which is RTC-4513 behaviour and + // not part of the handover. + if (!bkp_applied) begin + snap <= rtc_data; + wr_hold <= rtc_data; + ctrl_d <= 4'h1; + ctrl_e <= 4'hf; + ctrl_f <= 4'h6; + wr_pending <= 1'b0; + rtc_prev <= rtc_data; + dow_r <= rtc_data[59:56]; + dow_ss <= rtc_data[59:56]; + dow_owned <= 1'b0; + date_ref <= rtc_data[55:24]; + stopped <= 1'b0; + frz <= rtc_data; + end +`endif + end else begin + // the console is running: the handover is done with + bkp_applied <= 1'b1; + + // $4842: armed by a data read, consumed by reading the status + if (rd_4842) busy_r <= 1'b0; + if (ce & rd_4841) busy_r <= 1'b1; + + // write strobe timer (a commit below may restart it) + if (we_cnt != 7'd0) begin + we_cnt <= we_cnt - 7'd1; + if (we_cnt == 7'd1) rtc_we_r <= 1'b0; + end + + // rtc.v published something new (a new second, or a fresh time from the + // MCU's $e5): what it shows is current again. Comparing the whole word + // and not just the seconds also covers a reprogram that lands on the same + // seconds digit. + rtc_prev <= rtc_data; + if (rtc_data != rtc_prev) wr_pending <= 1'b0; + + // Day of week step: rtc.v moved onto a date the counter is not in step + // with, i.e. one midnight, so one step. Both halves of the condition earn + // their keep. It has to be an EDGE on rtc_data, because a commit points + // date_ref at the date that was just written and rtc_data has not published + // it yet - a plain level compare would fire on that gap. And it has to + // compare against date_ref, because rtc.v publishes only once per second: + // depending on where the write lands in that pass it either publishes the + // written 23:59:59 first and rolls over on the following pass, or increments + // it straight away so that the write becoming visible and the midnight roll + // are the SAME transition. With both halves either order steps exactly + // once. Until the game claims the register the value just follows rtc.v. + if (!stopped && (rtc_data[55:24] != rtc_prev[55:24]) + && (rtc_data[55:24] != date_ref)) begin + date_ref <= rtc_data[55:24]; + dow_r <= (dow_r == 4'd6) ? 4'd0 : dow_r + 4'd1; + end + if (!dow_owned) dow_r <= time_src[59:56]; + + // watchdog for a session that never deselects + if (dirty & ce) idle_cnt <= idle_cnt + {{(STALE_BITS-1){1'b0}}, 1'b1}; + else idle_cnt <= {STALE_BITS{1'b0}}; + + if (we_4840) begin + ce <= wdata[0]; + if (ce_rising) begin + // start of a session: take the snapshot the whole transfer works on. + // While a write has not shown up on rtc_data yet, keep serving it. + state <= ST_CMD; + index <= 4'h0; + cmd <= 4'h0; + busy_r <= 1'b0; + // frozen: frz is authoritative, so wr_pending does not apply + if (stopped) snap <= frz; + else if (!wr_pending) snap <= rtc_data; + dow_ss <= dow_owned ? dow_r : time_src[59:56]; + end + if (ce_falling) begin + state <= ST_CMD; + busy_r <= 1'b0; + // fullsnes: TEST (bit3) and RESET (bit0) auto-clear on CE = LOW + ctrl_f <= ctrl_f & 4'b0110; + if (dirty) begin + wr_hold <= snap; + rtc_we_r <= 1'b1; + we_cnt <= WE_PULSE; + dirty <= 1'b0; + wr_pending <= 1'b1; + date_ref <= snap[55:24]; // dow_r is in step with what we wrote + end + // still stopped at the end of the session: whatever the session leaves + // in snap is what the chip goes on showing + if (stopped) frz <= snap; + end + end else if (ce & we_4841) begin + idle_cnt <= {STALE_BITS{1'b0}}; + case (state) + ST_CMD: begin + cmd <= wdata[3:0]; + // only the two documented commands open a transfer; anything else is + // ignored rather than left to wedge the machine + if (wdata[3:0] == CMD_WRITE || wdata[3:0] == CMD_READ) + state <= ST_INDEX; + end + ST_INDEX: begin + // the starting index itself does not increment + index <= wdata[3:0]; + state <= ST_XFER; + end + default: begin + index <= index + 4'h1; // wraps 0f -> 00 by construction + // Only the counter bits of each index reach the clock. The rest of + // the nibble is a flag or user RAM on the RTC-4513 (see the map in + // the header), and letting it through would push a non-BCD digit + // into rtc.v: writing 8 to index 3 (the WRAP bit) would give a + // minutes-tens digit of 8, writing 4 to index 5 (PM/AM) an + // hours-tens digit of 4. Masked-off bits read back as 0; the three + // user RAM bits are not stored. + case (index) + 4'h0: begin snap[3:0] <= wdata[3:0]; dirty <= 1'b1; end + 4'h1: begin snap[7:4] <= wdata[3:0] & 4'h7; dirty <= 1'b1; end + 4'h2: begin snap[11:8] <= wdata[3:0]; dirty <= 1'b1; end + 4'h3: begin snap[15:12] <= wdata[3:0] & 4'h7; dirty <= 1'b1; end + 4'h4: begin snap[19:16] <= wdata[3:0]; dirty <= 1'b1; end + 4'h5: begin snap[23:20] <= wdata[3:0] & 4'h3; dirty <= 1'b1; end + 4'h6: begin snap[27:24] <= wdata[3:0]; dirty <= 1'b1; end + 4'h7: begin snap[31:28] <= wdata[3:0] & 4'h3; dirty <= 1'b1; end + 4'h8: begin snap[35:32] <= wdata[3:0]; dirty <= 1'b1; end + 4'h9: begin snap[39:36] <= wdata[3:0] & 4'h1; dirty <= 1'b1; end + 4'ha: begin snap[43:40] <= wdata[3:0]; dirty <= 1'b1; end + 4'hb: begin snap[47:44] <= wdata[3:0]; dirty <= 1'b1; end + 4'hc: begin + snap[59:56] <= wdata[3:0] & 4'h7; + dow_r <= wdata[3:0] & 4'h7; + dow_ss <= wdata[3:0] & 4'h7; + dow_owned <= 1'b1; + dirty <= 1'b1; + end + 4'hd: begin + ctrl_d <= wdata[3:0]; + // 30ADJ (bit3): "Set seconds to zero, and, if seconds was >= 30, + // increase minutes" (fullsnes). The carry stops at the minute - + // the sources only ask for the minute, and cascading is rtc.v's + // job - so minute 59 SATURATES: the seconds still go to zero but + // the minute stays put. Wrapping it to 00 instead would move the + // clock 59 minutes BACKWARDS, an error about sixty times larger + // than the one minute the saturation costs. + if (wdata[3]) begin + snap[7:0] <= 8'h00; + dirty <= 1'b1; + if (snap[7:4] >= 4'h3) begin + if (snap[11:8] == 4'h9) begin + if (snap[15:12] != 4'h5) begin // not 59: carry + snap[11:8] <= 4'h0; + snap[15:12] <= snap[15:12] + 4'h1; + end + end else + snap[11:8] <= snap[11:8] + 4'h1; + end + end + end + 4'he: ctrl_e <= wdata[3:0]; + default: begin + ctrl_f <= wdata[3:0]; + // RESET (bit0): "Stop clock and reset seconds to 00h" (fullsnes); + // Dark Force reads it the same way. The clock itself is rtc.v + // and cannot be stopped from here, so only the seconds are + // zeroed. The bit auto-clears on CE = LOW. + if (wdata[0]) begin + snap[7:0] <= 8'h00; + dirty <= 1'b1; + end + // STOP (bit1). Clearing it resumes: mark the session dirty so + // the frozen time - which snap was loaded from at chip select - + // is committed back into rtc.v by the normal write path. + stopped <= wdata[1]; + if (stopped && !wdata[1]) dirty <= 1'b1; + end + endcase + end + endcase + end else if (ce & rd_4841) begin + idle_cnt <= {STALE_BITS{1'b0}}; + if (state == ST_XFER) index <= index + 4'h1; + end else if (stale) begin + wr_hold <= snap; + rtc_we_r <= 1'b1; + we_cnt <= WE_PULSE; + dirty <= 1'b0; + wr_pending <= 1'b1; + date_ref <= snap[55:24]; + if (stopped) frz <= snap; + end + +`ifndef MK2 + // MCU restore of the backed up state ($e7). Last in the block on purpose: + // it overrides anything the SNES side did in the same clock. The time + // itself also goes to rtc.v, through $e5, before this arrives; frz is what + // makes a stopped clock come back at exactly the second it was left on. + if (bkp_we) begin + frz <= bkp_time_in; + snap <= bkp_time_in; + dow_r <= bkp_time_in[59:56]; + dow_ss <= bkp_time_in[59:56]; + dow_owned <= bkp_flags_in[0]; + stopped <= bkp_flags_in[1]; + date_ref <= bkp_time_in[55:24]; + wr_pending <= 1'b0; + dirty <= 1'b0; + end +`endif + end +end + +// we_4842 is accepted and ignored ($4842 is status only); rd_4840 and rd_4842 +// have no side effects on the RTC-4513. + +endmodule diff --git a/verilog/sd2snes_spc7110/spi.v b/verilog/sd2snes_spc7110/spi.v new file mode 100644 index 000000000..37280bac7 --- /dev/null +++ b/verilog/sd2snes_spc7110/spi.v @@ -0,0 +1,127 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: +// Engineer: +// +// Create Date: 21:16:09 07/10/2009 +// Design Name: +// Module Name: spi +// Project Name: +// Target Devices: +// Tool versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// + +////////////////////////////////////////////////////////////////////////////////// +module spi( + input clk, + input SCK, + input MOSI, + inout MISO, + input SSEL, + output cmd_ready, + output param_ready, + output [7:0] cmd_data, + output [7:0] param_data, + output endmessage, + output startmessage, + input [7:0] input_data, + output [31:0] byte_cnt, + output [2:0] bit_cnt +); + +reg [7:0] cmd_data_r; +reg [7:0] param_data_r; + +reg [2:0] SSELr; +reg [2:0] SSELSCKr; + +always @(posedge clk) SSELr <= {SSELr[1:0], SSEL}; +always @(posedge SCK) SSELSCKr <= {SSELSCKr[1:0], SSEL}; + +wire SSEL_inactive = SSELr[1]; +wire SSEL_active = ~SSELr[1]; // SSEL is active low +wire SSEL_startmessage = (SSELr[2:1]==2'b10); // message starts at falling edge +wire SSEL_endmessage = (SSELr[2:1]==2'b01); // message stops at rising edge +assign endmessage = SSEL_endmessage; +assign startmessage = SSEL_startmessage; + +// bit count for one SPI byte + byte count for the message +reg [2:0] bitcnt; +initial bitcnt = 3'b000; +wire bitcnt_msb = bitcnt[2]; +reg [2:0] bitcnt_wrap_r; +always @(posedge clk) bitcnt_wrap_r <= {bitcnt_wrap_r[1:0], bitcnt_msb}; +wire byte_received_sync = (bitcnt_wrap_r[2:1] == 2'b10); + +reg [31:0] byte_cnt_r; + +reg byte_received; // high when a byte has been received +reg [7:0] byte_data_received; + +assign bit_cnt = bitcnt; + +always @(posedge SCK) begin + if(SSELSCKr[1]) bitcnt <= 3'b000; + else bitcnt <= bitcnt + 3'b001; +end + +always @(posedge SCK) begin + if(~SSELSCKr[1]) + byte_data_received <= {byte_data_received[6:0], MOSI}; + if(~SSELSCKr[1] && bitcnt==3'b111) + byte_received <= 1'b1; + else byte_received <= 1'b0; +end + +//reg [2:0] byte_received_r; +//always @(posedge clk) byte_received_r <= {byte_received_r[1:0], byte_received}; +//wire byte_received_sync = (byte_received_r[2:1] == 2'b01); + +always @(posedge clk) begin + if(SSEL_inactive) + byte_cnt_r <= 16'h0000; + else if(byte_received_sync) + byte_cnt_r <= byte_cnt_r + 16'h0001; +end + +reg [7:0] byte_data_sent; + +assign MISO = ~SSEL ? input_data[7-bitcnt] : 1'bZ; // send MSB first + +reg cmd_ready_r; +reg param_ready_r; +reg cmd_ready_r2; +reg param_ready_r2; +assign cmd_ready = cmd_ready_r; +assign param_ready = param_ready_r; +assign cmd_data = cmd_data_r; +assign param_data = param_data_r; +assign byte_cnt = byte_cnt_r; + +always @(posedge clk) cmd_ready_r2 = byte_received_sync && byte_cnt_r == 32'h0; +always @(posedge clk) param_ready_r2 = byte_received_sync && byte_cnt_r > 32'h0; + +// fill registers +always @(posedge clk) begin + if (SSEL_startmessage) + cmd_data_r <= 8'h00; + else if(cmd_ready_r2) + cmd_data_r <= byte_data_received; + else if(param_ready_r2) + param_data_r <= byte_data_received; +end + +// delay ready signals by one clock +always @(posedge clk) begin + cmd_ready_r <= cmd_ready_r2; + param_ready_r <= param_ready_r2; +end + +endmodule From c36a066b0a213b66a003efcb44820469089f0245 Mon Sep 17 00:00:00 2001 From: Luan Freitas Date: Thu, 20 Aug 2026 02:40:08 -0300 Subject: [PATCH 3/4] SPC7110: firmware support (data ROM window, RTC-4513 with virtual battery) --- src/Makefile | 3 + src/fpga_spi.c | 49 +++++++ src/fpga_spi.h | 16 ++- src/memory.c | 56 ++++++++ src/msu1.c | 10 ++ src/smc.c | 4 +- src/smc.h | 1 + src/snes.c | 8 ++ src/spc7110rtc.c | 355 +++++++++++++++++++++++++++++++++++++++++++++++ src/spc7110rtc.h | 69 +++++++++ 10 files changed, 568 insertions(+), 3 deletions(-) create mode 100644 src/spc7110rtc.c create mode 100644 src/spc7110rtc.h diff --git a/src/Makefile b/src/Makefile index 4192ebeb9..4e810affd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -81,6 +81,9 @@ SRC += printf.c fileops.c fpga.c fpga_spi.c snes.c smc.c SRC += memory.c filetypes.c faulthandler.c sort.c crc32.c cic.c SRC += cli.c ymodem.c rle.c msu1.c crc16.c sysinfo.c SRC += cfg.c cheat.c yaml.c savestate.c sgb.c hwinfo.c +# SPC7110 RTC-4513 battery backup. mk3 only in practice: the call sites are +# gated on CONFIG_MK2, so --gc-sections drops the whole object on mk2. +SRC += spc7110rtc.c SRC += usbdesc.c usbcore.c usbuser.c cdcuser.c usbinterface.c diff --git a/src/fpga_spi.c b/src/fpga_spi.c index 32d03362e..ba102b0fd 100644 --- a/src/fpga_spi.c +++ b/src/fpga_spi.c @@ -79,7 +79,12 @@ nibbles packed) eg 0x20111210094816 is 2011-12-10, 9:48:16 E6 ssrr set/reset BS-X status register [7:0] + E6 -{8} read SPC7110 RTC-4513 battery backup + (SPC7110 core only: status byte + the seven + time bytes of E5) E7 - reset SRTC state + E7 ff tt{7} restore SPC7110 RTC-4513 battery backup + (SPC7110 core only) E8 - reset DSP program and data ROM write pointers E9 hhmmllxxxx write+incr. DSP program ROM (xxxx=dummy writes) EA hhllxxxx write+incr. DSP data ROM (xxxx=dummy writes) @@ -222,6 +227,28 @@ void set_rom_mask(uint32_t mask) { FPGA_DESELECT(); } +/* SPC7110: the data ROM window in PSRAM. The core computes + psram_addr = base + (offset & mask), so the mask must span only the DROM + (power of two); the firmware programs both before the SNES is released. + Every other core ignores $d7/$d8. */ +void set_drom_base(uint32_t base) { + FPGA_SELECT(); + FPGA_TX_BYTE(FPGA_CMD_SETDROMBASE); + FPGA_TX_BYTE((base >> 16) & 0xff); + FPGA_TX_BYTE((base >> 8) & 0xff); + FPGA_TX_BYTE((base) & 0xff); + FPGA_DESELECT(); +} + +void set_drom_mask(uint32_t mask) { + FPGA_SELECT(); + FPGA_TX_BYTE(FPGA_CMD_SETDROMMASK); + FPGA_TX_BYTE((mask >> 16) & 0xff); + FPGA_TX_BYTE((mask >> 8) & 0xff); + FPGA_TX_BYTE((mask) & 0xff); + FPGA_DESELECT(); +} + void set_mapper(uint8_t val) { FPGA_SELECT(); FPGA_TX_BYTE(FPGA_CMD_SETMAPPER(val)); @@ -379,6 +406,28 @@ uint64_t get_fpga_time() { return result; } +/* SPC7110 RTC-4513 battery backup, FPGA_SPC7110_RTC_LEN bytes: the status byte + followed by the seven packed BCD time bytes. Only the SPC7110 core answers; + the shadow the core hands out is taken in one clock, so the eight bytes can + never straddle a tick. */ +void get_spc7110_rtc(uint8_t *state) { + int i; + FPGA_SELECT(); + FPGA_TX_BYTE(FPGA_CMD_SPC7110RTCGET); + FPGA_TX_BYTE(0x00); /* dummy (latch the current state into the shadow) */ + for(i = 0; i < FPGA_SPC7110_RTC_LEN; i++) state[i] = FPGA_RX_BYTE(); + FPGA_DESELECT(); +} + +void set_spc7110_rtc(const uint8_t *state) { + int i; + FPGA_SELECT(); + FPGA_TX_BYTE(FPGA_CMD_SPC7110RTCSET); + for(i = 0; i < FPGA_SPC7110_RTC_LEN; i++) FPGA_TX_BYTE(state[i]); + FPGA_TX_BYTE(0x00); /* latch */ + FPGA_DESELECT(); +} + void set_fpga_time(uint64_t time) { FPGA_SELECT(); FPGA_TX_BYTE(FPGA_CMD_RTCSET); diff --git a/src/fpga_spi.h b/src/fpga_spi.h index 88969856e..f7a733b31 100644 --- a/src/fpga_spi.h +++ b/src/fpga_spi.h @@ -79,15 +79,25 @@ #define FPGA_CMD_SNESCMD_READ (0xd1) #define FPGA_CMD_SNESCMD_WRITE (0xd2) #define FPGA_CMD_CHEAT_WRITE (0xd3) +#define FPGA_CMD_SETDROMBASE (0xd7) /* SPC7110 core only: 3 param bytes MSB first -> PSRAM base of the data ROM (every other core drops the bytes) */ +#define FPGA_CMD_SETDROMMASK (0xd8) /* SPC7110 core only: 3 param bytes MSB first -> power-of-two size mask of the data ROM */ #define FPGA_CMD_MSUSETBITS (0xe0) #define FPGA_CMD_DACPAUSE (0xe1) #define FPGA_CMD_DACPLAY (0xe2) #define FPGA_CMD_DACSETPTR (0xe3) #define FPGA_CMD_MSUSETPTR (0xe4) #define FPGA_CMD_RTCSET (0xe5) -#define FPGA_CMD_RTCGET (0xe6) /* TODO remap - SGB only */ +#define FPGA_CMD_RTCGET (0xe6) /* TODO remap - SGB, and the SPC7110 core (see below) */ #define FPGA_CMD_BSXSETBITS (0xe6) #define FPGA_CMD_SRTCRESET (0xe7) +/* SPC7110 core only: RTC-4513 battery backup, eight bytes in both directions - + one status byte {stopped, weekday written, weekday} followed by the seven + packed BCD time bytes of $e5. $e6 reads the state the cartridge is showing, + $e7 puts it back; every other core drops the bytes ($e7 there is the S-RTC + reset, which the SPC7110 core does not have). */ +#define FPGA_CMD_SPC7110RTCGET (0xe6) +#define FPGA_CMD_SPC7110RTCSET (0xe7) +#define FPGA_SPC7110_RTC_LEN (8) #define FPGA_CMD_DSPRESETPTR (0xe8) #define FPGA_CMD_DSPWRITEPGM (0xe9) #define FPGA_CMD_DSPWRITEDAT (0xea) @@ -124,6 +134,8 @@ void set_msu_status(uint16_t status); void set_saveram_base(uint8_t); void set_saveram_mask(uint32_t); void set_rom_mask(uint32_t); +void set_drom_base(uint32_t); +void set_drom_mask(uint32_t); void set_mapper(uint8_t val); void fpga_sddma(uint8_t tgt, uint8_t partial); void fpga_set_sddma_range(uint16_t start, uint16_t end); @@ -133,6 +145,8 @@ uint32_t get_msu_offset(void); uint32_t get_snes_sysclk(void); void set_fpga_time(uint64_t time); uint64_t get_fpga_time(void); +void get_spc7110_rtc(uint8_t *state); +void set_spc7110_rtc(const uint8_t *state); void set_bsx_regs(uint8_t set, uint8_t reset); void fpga_reset_srtc_state(void); void fpga_reset_dspx_addr(void); diff --git a/src/memory.c b/src/memory.c index 285ddfc0e..45c56c029 100644 --- a/src/memory.c +++ b/src/memory.c @@ -50,6 +50,7 @@ memory.c: RAM operations #include "rtc.h" #include "savestate.h" #include "sgb.h" +#include "spc7110rtc.h" #include char* hex = "0123456789ABCDEF"; @@ -434,6 +435,39 @@ uint32_t load_rom(uint8_t* filename, uint32_t base_addr, uint8_t flags) { set_saveram_base(ramslot); } set_rom_mask(rommask); + if(romprops.has_spc7110) { + /* SPC7110: the ROM image is the program ROM followed by the data ROM. + Hand the core the DROM window over PSRAM as base + power-of-two mask + (the core masks the linear offset first, then adds the base). The + power-up defaults are neutral, so this MUST run before the SNES is + released. + Image-shape premise, deliberate: a headerless .sfc does not carry the + PROM size (emulators take it from an external manifest), so the PROM is + assumed 1 MB and the DROM a power of two -- true for every released + SPC7110 cart (Tengai Makyou Zero 1+4 MB, Momotarou Dentetsu Happy + 1+2 MB, Super Power League 4 1+1 MB). A hypothetical 2 MB-PROM cart + (the r4834 bit2 mode) or a non-power-of-two data ROM is out of scope + and would map incorrectly. */ + uint32_t drombase = 0x100000; + uint32_t dromspan = (romprops.romsize_bytes > drombase) + ? (romprops.romsize_bytes - drombase) : drombase; + uint32_t dromsize = 1; + while(dromsize < dromspan) dromsize <<= 1; + set_drom_base(drombase); + set_drom_mask(dromsize - 1); + /* The RTC-4513 powers up with an invalid BCD calendar (month/day 00) and + games retry forever on it, so the clock is always programmed before boot. + With a .rtc sidecar next to the save this restores what the cartridge + battery would have kept - including a clock the game stopped - and + without one it falls back to the console's own time. */ +#ifndef CONFIG_MK2 + spc7110_rtc_load(filename); +#else + /* the Mk.II core carries no virtual battery (see verilog `ifndef MK2); + wall clock only */ + set_fpga_time(get_bcdtime()); +#endif + } readled(0); printf("gsu=%x sa1=%x srambase=%lx sramsize=%lx\n", romprops.has_gsu, romprops.has_sa1, romprops.srambase, romprops.sramsize_bytes); @@ -444,6 +478,28 @@ uint32_t load_rom(uint8_t* filename, uint32_t base_addr, uint8_t flags) { if (romprops.sramsize_bytes) migrate_and_load_srm(filename, SRAM_SAVE_ADDR); /* file not found error is ok (SRM file might not exist yet) */ if(file_res == FR_NO_FILE) file_res = 0; +#ifdef CONFIG_MK2 + /* SPC7110 on Mk.II has no virtual battery, so the factory check + program's RTC BACKUP test can never pass there. Plant the retail + signature that the check program itself writes on success ("SPC7110 + CHECK OK", last 16 bytes of SRAM) whenever it is absent, so a fresh + cart boots retail instead of looping the factory ritual. Only the RTC + cart (carttype 0xf9, Tengai Makyou Zero) carries the check program -- + on the plain SPC7110 carts those 16 bytes are ordinary save data and + must not be touched. A save that already carries the signature is + untouched; the seed lands in the initial CRC, so it reaches the card + only together with a real save. */ + if(romprops.has_spc7110_rtc && romprops.sramsize_bytes >= 16) { + static const uint8_t spc7110_sig[16] = { 'S','P','C','7','1','1','0',' ', + 'C','H','E','C','K',' ','O','K' }; + uint8_t sigcur[16]; + uint32_t sigaddr = SRAM_SAVE_ADDR + romprops.sramsize_bytes - 16; + sram_readblock(sigcur, sigaddr, 16); + if(memcmp(sigcur, spc7110_sig, 16)) { + sram_writeblock((void*)spc7110_sig, sigaddr, 16); + } + } +#endif saveram_crc_old = calc_sram_crc(SRAM_SAVE_ADDR + romprops.srambase, romprops.sramsize_bytes, 0); saveram_crc = 0; saveram_offset = 0; diff --git a/src/msu1.c b/src/msu1.c index 69aa33b1c..dc1b17739 100644 --- a/src/msu1.c +++ b/src/msu1.c @@ -17,6 +17,7 @@ #include "usbinterface.h" #include "savestate.h" #include "cfg.h" +#include "spc7110rtc.h" FIL msudata; FIL msuaudio; @@ -71,6 +72,15 @@ int is_msu_free_to_save() { */ void msu_savecheck(int immediate) { uint32_t currentcrc; +#ifndef CONFIG_MK2 + /* Keep the SPC7110 RTC-4513 backup in step here too. msu1_loop is the second + game loop and the switch(cmd) of the normal one never runs for an MSU-1 + title, so anything that only lives there is silently dead in these games. + Deliberately ABOVE the autosave gate: the RTC backup is not SRAM autosave + and must not be switched off with it. Costs one branch when the cartridge + is not an SPC7110, and writes the card only on a real change. */ + spc7110_rtc_save(file_lfn); +#endif if(!cfg_is_msu1_autosave_enabled()) { return; } diff --git a/src/smc.c b/src/smc.c index a5c28e8fb..aeb7fd8a8 100644 --- a/src/smc.c +++ b/src/smc.c @@ -72,6 +72,7 @@ void smc_id(snes_romprops_t* props, uint32_t file_offset) { props->has_st0018 = 0; props->has_msu1 = 0; props->has_spc7110 = 0; + props->has_spc7110_rtc = 0; props->has_cx4 = 0; props->has_obc1 = 0; props->has_gsu = 0; @@ -263,8 +264,7 @@ void smc_id(snes_romprops_t* props, uint32_t file_offset) { case 0x2a: /* SPC7110 */ if(header->carttype == 0xf5 || header->carttype == 0xf9) { props->has_spc7110 = 1; - props->error = MENU_ERR_NOIMPL; - props->error_param = (uint8_t*)"SPC7110"; + props->has_spc7110_rtc = (header->carttype == 0xf9); props->fpga_conf = FPGA_SPC7110; props->mapper_id = 5; } diff --git a/src/smc.h b/src/smc.h index abd09c5e1..d656d4575 100644 --- a/src/smc.h +++ b/src/smc.h @@ -89,6 +89,7 @@ typedef struct __attribute__ ((__packed__)) { uint8_t has_sa1; /* SA-1 presence flag */ uint8_t has_sdd1; /* S-DD1 presence flag */ uint8_t has_spc7110; /* SPC7110 presence flag */ + uint8_t has_spc7110_rtc; /* SPC7110 + RTC-4513 (carttype 0xf9) */ uint8_t has_combo; /* Multi game presence flag */ uint32_t srambase; /* saveram base address */ uint32_t sramsize_bytes; /* saveram size in bytes */ diff --git a/src/snes.c b/src/snes.c index 595e38828..40146a09a 100644 --- a/src/snes.c +++ b/src/snes.c @@ -43,6 +43,7 @@ #include "cfg.h" #include "usbinterface.h" #include "sgb.h" +#include "spc7110rtc.h" #include "version.h" #include "hwinfo.h" @@ -289,6 +290,13 @@ uint8_t snes_main_loop() { /* save the GB RTC if enabled */ sgb_gtc_save(file_lfn); +#ifndef CONFIG_MK2 + /* keep the SPC7110 RTC-4513 backup in step; writes the card only on a change + (the virtual battery is not built into the Mk.II core, and this gate is + what lets --gc-sections drop the whole object there) */ + spc7110_rtc_save(file_lfn); +#endif + if(romprops.sramsize_bytes && CFG.enable_autosave) { uint32_t crc_bytes = min(romprops.sramsize_bytes - saveram_offset, SRAM_REGION_SIZE); saveram_crc = calc_sram_crc(SRAM_SAVE_ADDR + romprops.srambase + saveram_offset, crc_bytes, saveram_crc); diff --git a/src/spc7110rtc.c b/src/spc7110rtc.c new file mode 100644 index 000000000..79e7e6093 --- /dev/null +++ b/src/spc7110rtc.c @@ -0,0 +1,355 @@ +/* SPC7110 / RTC-4513 battery backup. See spc7110rtc.h for what this is for. */ + +#include +#include + +#include "spc7110rtc.h" + +#ifndef SPC7110_RTC_HOSTTEST +#include "config.h" +#include "fileops.h" +#include "fpga_spi.h" +#include "memory.h" +#include "rtc.h" +#include "smc.h" +#include "uart.h" +#include "led.h" + +/* the loaded ROM's properties (defined in smc.c, declared per-user like the + other consumers -- cheat.c, msu1.c, memory.c do the same) */ +extern snes_romprops_t romprops; +#endif + +#define SECS_PER_DAY (86400) + +/* ------------------------------------------------------------------------ */ +/* calendar */ +/* */ +/* Days are counted from an epoch of 0000-03-01 with the year starting in */ +/* March, so February - the only month whose length moves - is always the */ +/* last one and no leap year special case is needed anywhere else. Both */ +/* directions are exact for every year the RTC-4513 can express (0000-9999), */ +/* including 2000 and 2400 (leap) against 1900 and 2100 (not). */ +/* ------------------------------------------------------------------------ */ + +/* Gregorian day-count conversion below follows Howard Hinnant's public-domain + "chrono-Compatible Low-Level Date Algorithms" (era/yoe/doy/doe form). */ +static int32_t days_from_civil(int32_t y, int32_t m, int32_t d) { + int32_t era; + uint32_t yoe, doy, doe; + + y -= (m <= 2); + era = (y >= 0 ? y : y - 399) / 400; + yoe = (uint32_t)(y - era * 400); /* 0..399 */ + doy = (153u * (uint32_t)(m + (m > 2 ? -3 : 9)) + 2u) / 5u + (uint32_t)d - 1u; + doe = yoe * 365u + yoe / 4u - yoe / 100u + doy; /* 0..146096 */ + return era * 146097 + (int32_t)doe; +} + +static void civil_from_days(int32_t z, int32_t *py, int32_t *pm, int32_t *pd) { + int32_t era, y; + uint32_t doe, yoe, doy, mp, d, m; + + era = (z >= 0 ? z : z - 146096) / 146097; + doe = (uint32_t)(z - era * 146097); /* 0..146096 */ + yoe = (doe - doe / 1460u + doe / 36524u - doe / 146096u) / 365u; + y = (int32_t)yoe + era * 400; + doy = doe - (365u * yoe + yoe / 4u - yoe / 100u); + mp = (5u * doy + 2u) / 153u; + d = doy - (153u * mp + 2u) / 5u + 1u; + m = mp + (mp < 10u ? 3u : (uint32_t)-9); + *py = y + (m <= 2); + *pm = (int32_t)m; + *pd = (int32_t)d; +} + +static int bcd2bin(uint8_t b, int32_t *v) { + if ((b & 0x0f) > 9 || (b >> 4) > 9) return -1; + *v = (int32_t)(b >> 4) * 10 + (int32_t)(b & 0x0f); + return 0; +} + +static uint8_t bin2bcd(int32_t v) { + return (uint8_t)((((v / 10) % 10) << 4) | (v % 10)); +} + +int spc7110_rtc_unpack(const uint8_t *time, spc7110_instant_t *out) { + int32_t cent, year, mon, day, hour, min, sec; + + if (bcd2bin(time[0], ¢) || bcd2bin(time[1], &year) + || bcd2bin(time[2], &mon) || bcd2bin(time[3], &day) + || bcd2bin(time[4], &hour) || bcd2bin(time[5], &min) + || bcd2bin(time[6], &sec)) return -1; + /* the day is only range checked, not checked against the month: a corrupt + sidecar normalises (30 February becomes 2 March) instead of being thrown + away, which is the friendlier failure for a clock */ + if (mon < 1 || mon > 12 || day < 1 || day > 31 + || hour > 23 || min > 59 || sec > 59) return -1; + + out->days = days_from_civil(cent * 100 + year, mon, day); + out->secs = hour * 3600 + min * 60 + sec; + return 0; +} + +void spc7110_rtc_pack(const spc7110_instant_t *in, uint8_t *time) { + int32_t y, m, d, s = in->secs; + + civil_from_days(in->days, &y, &m, &d); + y %= 10000; + if (y < 0) y += 10000; + time[0] = bin2bcd(y / 100); + time[1] = bin2bcd(y % 100); + time[2] = bin2bcd(m); + time[3] = bin2bcd(d); + time[4] = bin2bcd(s / 3600); + time[5] = bin2bcd((s / 60) % 60); + time[6] = bin2bcd(s % 60); +} + +static void normalise(spc7110_instant_t *t) { + while (t->secs >= SECS_PER_DAY) { t->secs -= SECS_PER_DAY; t->days += 1; } + while (t->secs < 0) { t->secs += SECS_PER_DAY; t->days -= 1; } +} + +void spc7110_rtc_diff(const spc7110_instant_t *a, const spc7110_instant_t *b, + spc7110_instant_t *d) { + d->days = a->days - b->days; + d->secs = a->secs - b->secs; + normalise(d); +} + +void spc7110_rtc_add(const spc7110_instant_t *a, const spc7110_instant_t *d, + spc7110_instant_t *r) { + r->days = a->days + d->days; + r->secs = a->secs + d->secs; + normalise(r); +} + +int32_t spc7110_rtc_midnights(const spc7110_instant_t *from, + const spc7110_instant_t *to) { + return to->days - from->days; +} + +#ifndef SPC7110_RTC_HOSTTEST + +/* ------------------------------------------------------------------------ */ +/* sidecar */ +/* ------------------------------------------------------------------------ */ + +#define SPC7110_RTC_VERSION (1) + +typedef struct { + uint8_t magic[4]; /* "S7RT" */ + uint8_t version; + uint8_t reserved[3]; + uint8_t flags; /* spc7110_rtc_state_t, flattened so */ + uint8_t time[SPC7110_RTC_TIMELEN]; /* the layout is fixed by this file */ + uint8_t wall[SPC7110_RTC_TIMELEN]; /* console clock when this was saved */ + uint8_t pad; +} spc7110_rtc_file_t; + +/* the layout above is the on-card format; a change has to bump the version */ +typedef char spc7110_rtc_file_size_check[(sizeof(spc7110_rtc_file_t) == 24) ? 1 : -1]; + +static const uint8_t spc7110_rtc_magic[4] = { 'S', '7', 'R', 'T' }; + +/* last state written out, so the game loop only touches the card on a change */ +static spc7110_instant_t spc7110_rtc_last_delta; +static uint8_t spc7110_rtc_last_flags; +static uint8_t spc7110_rtc_known; + +/* how far the two clocks may disagree before the sidecar is rewritten. The + cartridge clock and the console clock are different crystals, so their + seconds drift apart and the difference jitters by one either way; without a + dead band that alone would write the card every few seconds. */ +#define SPC7110_RTC_SLACK (2) + +static void bcdtime_to_bytes(uint64_t t, uint8_t *b) { + int i; + for (i = 0; i < SPC7110_RTC_TIMELEN; i++) + b[i] = (uint8_t)(t >> (8 * (SPC7110_RTC_TIMELEN - 1 - i))); +} + +static uint64_t bytes_to_bcdtime(const uint8_t *b, uint8_t dow) { + uint64_t t = ((uint64_t)(dow & 7)) << 56; + int i; + for (i = 0; i < SPC7110_RTC_TIMELEN; i++) + t |= ((uint64_t)b[i]) << (8 * (SPC7110_RTC_TIMELEN - 1 - i)); + return t; +} + +/* the SPI helpers speak eight plain bytes; marshal rather than rely on how the + struct happens to be laid out */ +static void spc7110_rtc_get(spc7110_rtc_state_t *st) { + uint8_t b[FPGA_SPC7110_RTC_LEN]; + get_spc7110_rtc(b); + st->flags = b[0]; + memcpy(st->time, b + 1, SPC7110_RTC_TIMELEN); +} + +/* `shown` is the instant the cartridge has to report from now on; `prog` is + what rtc.v is handed, which is one second less when the clock is running + because programming it makes it run a pass straight away. */ +static void spc7110_rtc_program(const spc7110_rtc_state_t *st, + const uint8_t *prog) { + uint8_t b[FPGA_SPC7110_RTC_LEN]; + set_fpga_time(bytes_to_bcdtime(prog, st->flags & SPC7110_RTC_DOW_MASK)); + b[0] = st->flags; + memcpy(b + 1, st->time, SPC7110_RTC_TIMELEN); + set_spc7110_rtc(b); +} + +/* no usable backup: behave exactly as before this file existed */ +static void spc7110_rtc_wallclock(void) { + set_fpga_time(get_bcdtime()); + spc7110_rtc_known = 0; +} + +void spc7110_rtc_load(uint8_t *filename) { + spc7110_rtc_file_t f; + spc7110_rtc_state_t st; + spc7110_instant_t stored, saved, now, delta, cur, prog; + uint8_t wall[SPC7110_RTC_TIMELEN]; + uint8_t progtime[SPC7110_RTC_TIMELEN]; + char rtcfile[256] = SAVE_BASEDIR; + uint32_t got; + + spc7110_rtc_known = 0; + bcdtime_to_bytes(get_bcdtime(), wall); + + /* read path: name the sidecar, never create the folder here */ + append_file_basename(rtcfile, (char *)filename, ".rtc", sizeof(rtcfile)); + file_open((uint8_t *)rtcfile, FA_READ); + if (file_res) { file_close(); spc7110_rtc_wallclock(); return; } + got = file_readblock(&f, 0, sizeof(f)); + file_close(); + + /* a truncated file is a broken file: fall back rather than read the tail of + whatever the buffer happened to hold */ + if (got != sizeof(f) + || memcmp(f.magic, spc7110_rtc_magic, sizeof(f.magic)) + || f.version != SPC7110_RTC_VERSION) { + printf("SPC7110 RTC: unusable sidecar, using the console clock\n"); + spc7110_rtc_wallclock(); + return; + } + + st.flags = f.flags; + memcpy(st.time, f.time, SPC7110_RTC_TIMELEN); + + /* both are needed on either branch: `now` seeds the change detector even for + a stopped clock, because that is what the save path will be comparing */ + if (spc7110_rtc_unpack(st.time, &stored) || spc7110_rtc_unpack(wall, &now)) { + spc7110_rtc_wallclock(); + return; + } + + if (st.flags & SPC7110_RTC_STOPPED) { + /* stopped clocks do not age. The core serves this out of its freeze + register, so the second it comes back on is the second it was left on. */ + cur = stored; + } else { + if (spc7110_rtc_unpack(f.wall, &saved)) { + spc7110_rtc_wallclock(); + return; + } + spc7110_rtc_diff(&now, &saved, &delta); + /* console clock moved backwards (it was corrected, or never set): the + cartridge clock cannot run backwards, so it simply does not advance */ + if (delta.days < 0) { delta.days = 0; delta.secs = 0; } + spc7110_rtc_add(&stored, &delta, &cur); + + /* the weekday is a counter, not a function of the date: advance it by the + number of midnights that went by, and by nothing else */ + st.flags = (uint8_t)((st.flags & ~SPC7110_RTC_DOW_MASK) + | (uint8_t)(((uint32_t)(st.flags & SPC7110_RTC_DOW_MASK) + + (uint32_t)spc7110_rtc_midnights(&stored, &cur)) % 7)); + + spc7110_rtc_pack(&cur, st.time); + } + + /* rtc.v runs one per-second pass the moment it is programmed, so what it + publishes is the value it was handed plus one second. That second comes + off here, and only here: `cur` stays the instant the cartridge shows. A + stopped clock is served out of the freeze register instead, so it needs no + correction. */ + prog = cur; + if (!(st.flags & SPC7110_RTC_STOPPED)) { + prog.secs -= 1; + if (prog.secs < 0) { prog.secs += SECS_PER_DAY; prog.days -= 1; } + } + spc7110_rtc_pack(&prog, progtime); + + spc7110_rtc_program(&st, progtime); + + /* Seed the change detector with EXACTLY what the save path will compare + against, so the first pass of the game loop finds nothing to do: the frozen + instant while stopped, the offset between the two clocks while running. */ + spc7110_rtc_last_flags = (uint8_t)(st.flags & ~SPC7110_RTC_DOW_MASK); + if (st.flags & SPC7110_RTC_STOPPED) spc7110_rtc_last_delta = cur; + else spc7110_rtc_diff(&cur, &now, &spc7110_rtc_last_delta); + spc7110_rtc_known = 1; + + printf("SPC7110 RTC: restored %s, %02x%02x-%02x-%02x %02x:%02x:%02x\n", + (st.flags & SPC7110_RTC_STOPPED) ? "stopped" : "running", + st.time[0], st.time[1], st.time[2], st.time[3], + st.time[4], st.time[5], st.time[6]); +} + +void spc7110_rtc_save(uint8_t *filename) { + spc7110_rtc_file_t f; + spc7110_rtc_state_t st; + spc7110_instant_t cart, now, delta; + uint8_t wall[SPC7110_RTC_TIMELEN]; + char rtcfile[256] = SAVE_BASEDIR; + uint8_t flags_stable; + + if (!romprops.has_spc7110) return; + + spc7110_rtc_get(&st); + bcdtime_to_bytes(get_bcdtime(), wall); + if (spc7110_rtc_unpack(st.time, &cart) || spc7110_rtc_unpack(wall, &now)) + return; + + /* What has to stay the same for the backup to still describe reality: the + control flags, and either the frozen time (stopped) or the offset between + the two clocks (running). A clock that simply runs keeps both constant, + so the card is never touched. */ + flags_stable = (uint8_t)(st.flags & ~SPC7110_RTC_DOW_MASK); + if (st.flags & SPC7110_RTC_STOPPED) { + delta = cart; + } else { + spc7110_rtc_diff(&cart, &now, &delta); + } + + if (spc7110_rtc_known + && flags_stable == spc7110_rtc_last_flags + && delta.days == spc7110_rtc_last_delta.days + && (delta.secs - spc7110_rtc_last_delta.secs) <= SPC7110_RTC_SLACK + && (spc7110_rtc_last_delta.secs - delta.secs) <= SPC7110_RTC_SLACK) + return; + + check_or_create_folder(SAVE_BASEDIR); + append_file_basename(rtcfile, (char *)filename, ".rtc", sizeof(rtcfile)); + + memcpy(f.magic, spc7110_rtc_magic, sizeof(f.magic)); + f.version = SPC7110_RTC_VERSION; + f.reserved[0] = f.reserved[1] = f.reserved[2] = 0; + f.flags = st.flags; + memcpy(f.time, st.time, SPC7110_RTC_TIMELEN); + memcpy(f.wall, wall, SPC7110_RTC_TIMELEN); + f.pad = 0; + + file_open((uint8_t *)rtcfile, FA_CREATE_ALWAYS | FA_WRITE); + if (file_res) { file_close(); return; } + writeled(1); + file_writeblock(&f, 0, sizeof(f)); + writeled(0); + file_close(); + + spc7110_rtc_last_flags = flags_stable; + spc7110_rtc_last_delta = delta; + spc7110_rtc_known = 1; +} + +#endif /* SPC7110_RTC_HOSTTEST */ diff --git a/src/spc7110rtc.h b/src/spc7110rtc.h new file mode 100644 index 000000000..fb12fea2e --- /dev/null +++ b/src/spc7110rtc.h @@ -0,0 +1,69 @@ +/* SPC7110 / RTC-4513 battery backup. + * + * The RTC-4513 on the Far East of Eden Zero cartridge is kept alive by a coin + * cell: the calendar it holds - and whether it is running at all - survives a + * power cycle. Nothing on this board does: the FPGA is reconfigured on every + * game load and the clock inside it is programmed from the console's own clock. + * + * The cartridge's factory check program needs the real behaviour. Its second + * pass programs the clock, stops it, and expects to find it on the same second + * after the console has been switched off and on again. So the state is kept + * in a sidecar next to the save file and handed back to the core on load, the + * same way the Super Game Boy keeps the MBC3 clock in a .gtc file. + * + * Sidecar: /sd2snes/saves/.rtc, next to the .srm, versioned, 24 bytes. + */ +#ifndef SPC7110RTC_H +#define SPC7110RTC_H + +#include + +/* Status byte of the backup, identical over SPI (command $e6 reads it, $e7 + restores it) and in the sidecar. */ +#define SPC7110_RTC_STOPPED (0x20) /* control register F bit1 was set */ +#define SPC7110_RTC_DOW_OWNED (0x10) /* the game has written the weekday */ +#define SPC7110_RTC_DOW_MASK (0x0f) + +/* Seven packed BCD bytes, most significant first, exactly the payload of SPI + command $e5: century, year, month, day, hour, minute, second. */ +#define SPC7110_RTC_TIMELEN (7) + +typedef struct { + uint8_t flags; + uint8_t time[SPC7110_RTC_TIMELEN]; +} spc7110_rtc_state_t; + +/* A calendar instant, split so that no intermediate result can overflow: whole + days since the internal epoch, plus seconds inside the day. Two SPC7110 + dates can be seventy years apart (the factory test drives the clock to 2100 + while the console sits in the 2020s), which is more seconds than a 32 bit + count holds. */ +typedef struct { + int32_t days; + int32_t secs; +} spc7110_instant_t; + +/* Calendar helpers. No hardware, no files - pure arithmetic seams that a + host-side self test can exercise. */ +int spc7110_rtc_unpack(const uint8_t *time, spc7110_instant_t *out); +void spc7110_rtc_pack(const spc7110_instant_t *in, uint8_t *time); +void spc7110_rtc_diff(const spc7110_instant_t *a, const spc7110_instant_t *b, + spc7110_instant_t *d); +void spc7110_rtc_add(const spc7110_instant_t *a, const spc7110_instant_t *d, + spc7110_instant_t *r); +/* days between two instants, i.e. how many midnights were crossed */ +int32_t spc7110_rtc_midnights(const spc7110_instant_t *from, + const spc7110_instant_t *to); + +#ifndef SPC7110_RTC_HOSTTEST +/* Called from load_rom once the core is configured and before the SNES runs. + Always programs the clock: with the backed up state when there is one, with + the console's own time when there is not. */ +void spc7110_rtc_load(uint8_t *filename); +/* Called from the game loop. Reads the core's state over SPI and writes the + sidecar only when it actually changed, so a free running clock costs no + card writes at all. */ +void spc7110_rtc_save(uint8_t *filename); +#endif + +#endif From 7f7ca3315f2696ee74c86f1193e8742eadb52a50 Mon Sep 17 00:00:00 2001 From: Luan Freitas Date: Tue, 25 Aug 2026 02:51:07 -0300 Subject: [PATCH 4/4] feat(spc7110): map the expansion ROM at banks $40-4f Based on @terminator2k2's fix --- src/fpga_spi.c | 15 +++++++++++ src/fpga_spi.h | 2 ++ src/memory.c | 4 +++ verilog/sd2snes_spc7110/main.v | 3 +++ verilog/sd2snes_spc7110/mcu_cmd.v | 21 +++++++++++++++ verilog/sd2snes_spc7110/sd2snes_spc7110.xise | 2 +- verilog/sd2snes_spc7110/spc7110_map.v | 28 ++++++++++++++++++-- verilog/sd2snes_spc7110/spc7110_regs.v | 2 ++ 8 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/fpga_spi.c b/src/fpga_spi.c index ba102b0fd..776551083 100644 --- a/src/fpga_spi.c +++ b/src/fpga_spi.c @@ -249,6 +249,21 @@ void set_drom_mask(uint32_t mask) { FPGA_DESELECT(); } +/* SPC7110: PSRAM base of the expansion ROM the Tengai Makyou Zero + translations add at banks $40-4f (flat, outside the $483x window logic). + 1 MB-aligned by contract -- the core concatenates base[23:20] with the + bank offset -- and 0 means no expansion chip, so it is sent on every + SPC7110 load like $d7/$d8 and never carries a stale value over. Every + other core ignores $db. */ +void set_exp_base(uint32_t base) { + FPGA_SELECT(); + FPGA_TX_BYTE(FPGA_CMD_SETEXPBASE); + FPGA_TX_BYTE((base >> 16) & 0xff); + FPGA_TX_BYTE((base >> 8) & 0xff); + FPGA_TX_BYTE((base) & 0xff); + FPGA_DESELECT(); +} + void set_mapper(uint8_t val) { FPGA_SELECT(); FPGA_TX_BYTE(FPGA_CMD_SETMAPPER(val)); diff --git a/src/fpga_spi.h b/src/fpga_spi.h index f7a733b31..26f62ae7e 100644 --- a/src/fpga_spi.h +++ b/src/fpga_spi.h @@ -81,6 +81,7 @@ #define FPGA_CMD_CHEAT_WRITE (0xd3) #define FPGA_CMD_SETDROMBASE (0xd7) /* SPC7110 core only: 3 param bytes MSB first -> PSRAM base of the data ROM (every other core drops the bytes) */ #define FPGA_CMD_SETDROMMASK (0xd8) /* SPC7110 core only: 3 param bytes MSB first -> power-of-two size mask of the data ROM */ +#define FPGA_CMD_SETEXPBASE (0xdb) /* SPC7110 core only: 3 param bytes MSB first -> PSRAM base of the expansion ROM decoded flat at banks $40-4f (a third chip only the Tengai Makyou Zero translations populate). 1 MB-aligned by contract -- the core concatenates base[23:20] with the bank offset instead of adding. 0 = chip absent; sent on every SPC7110 load so a stale base never lingers; every other core drops the bytes. */ #define FPGA_CMD_MSUSETBITS (0xe0) #define FPGA_CMD_DACPAUSE (0xe1) #define FPGA_CMD_DACPLAY (0xe2) @@ -136,6 +137,7 @@ void set_saveram_mask(uint32_t); void set_rom_mask(uint32_t); void set_drom_base(uint32_t); void set_drom_mask(uint32_t); +void set_exp_base(uint32_t); void set_mapper(uint8_t val); void fpga_sddma(uint8_t tgt, uint8_t partial); void fpga_set_sddma_range(uint16_t start, uint16_t end); diff --git a/src/memory.c b/src/memory.c index 45c56c029..961d365d5 100644 --- a/src/memory.c +++ b/src/memory.c @@ -455,6 +455,10 @@ uint32_t load_rom(uint8_t* filename, uint32_t base_addr, uint8_t flags) { while(dromsize < dromspan) dromsize <<= 1; set_drom_base(drombase); set_drom_mask(dromsize - 1); + /* Expansion ROM for SPC7110 translations. + 7 MB ROMs use the extra 1 MB at PSRAM 0x600000. + Set its base when present, or 0 otherwise. */ + set_exp_base(romprops.romsize_bytes > 0x600000 ? 0x600000 : 0); /* The RTC-4513 powers up with an invalid BCD calendar (month/day 00) and games retry forever on it, so the clock is always programmed before boot. With a .rtc sidecar next to the save this restores what the cartridge diff --git a/verilog/sd2snes_spc7110/main.v b/verilog/sd2snes_spc7110/main.v index 420af10f9..3a8c2aec3 100644 --- a/verilog/sd2snes_spc7110/main.v +++ b/verilog/sd2snes_spc7110/main.v @@ -109,6 +109,7 @@ wire [23:0] ROM_MASK; /* SPC7110 data ROM window in PSRAM, programmed by the MCU (see mcu_cmd.v) */ wire [23:0] DROM_BASE; wire [23:0] DROM_MASK; +wire [23:0] EXP_BASE; wire [7:0] SD_DMA_SRAM_DATA; wire [1:0] SD_DMA_TGT; wire [10:0] SD_DMA_PARTIAL_START; @@ -546,6 +547,7 @@ spc7110_regs snes_spc7110( .snes_addr(SNES_ADDR), .drom_base(DROM_BASE), .drom_mask(DROM_MASK), + .exp_base(EXP_BASE), .map_rom_hit(SPC7110_ROM_HIT), .map_psram_addr(SPC7110_PSRAM_ADDR), .map_is_sram(SPC7110_IS_SRAM), @@ -614,6 +616,7 @@ mcu_cmd snes_mcu_cmd( .rom_mask_out(ROM_MASK), .drom_base_out(DROM_BASE), .drom_mask_out(DROM_MASK), + .exp_base_out(EXP_BASE), .rtc_data_out(rtc_data_in), .rtc_pgm_we(rtc_pgm_we), `ifndef MK2 diff --git a/verilog/sd2snes_spc7110/mcu_cmd.v b/verilog/sd2snes_spc7110/mcu_cmd.v index 0cf0bbd55..ff521f14c 100644 --- a/verilog/sd2snes_spc7110/mcu_cmd.v +++ b/verilog/sd2snes_spc7110/mcu_cmd.v @@ -40,6 +40,8 @@ module mcu_cmd( /* SPC7110 data ROM window in PSRAM (SPI commands $d7 / $d8, see below) */ output [23:0] drom_base_out, output [23:0] drom_mask_out, + /* SPC7110 expansion ROM base (SPI command $db, see below) */ + output [23:0] exp_base_out, /* RTC-4513 time, pushed by the MCU with SPI command $e5 */ output [55:0] rtc_data_out, output rtc_pgm_we, @@ -173,6 +175,13 @@ reg [23:0] ROM_MASK; reg [23:0] DROM_BASE = 24'h000000; reg [23:0] DROM_MASK = 24'hffffff; +/* SPC7110 expansion ROM at banks $40-4f (flat, no window/block indirection -- + see spc7110_map.v). PSRAM base of the chip, 1 MB-aligned by contract; 0 = + no third chip (no retail SPC7110 cart has one, the Tengai Makyou Zero + translations do). The MCU sends this on every load, like DROM_BASE/ + DROM_MASK, so it never carries a stale value from a previous game. */ +reg [23:0] EXP_BASE = 24'h000000; + /* RTC time as the MCU packs it for command $e5: seven bytes MSB first, BCD nibbles in the SPC7110 order plus the thousands of the year. pgm_we goes up on the last data byte and back down on the next one, which is the pulse @@ -265,6 +274,17 @@ always @(posedge clk) begin 32'h4: DROM_MASK[7:0] <= param_data; endcase + /* $db bbhhll: set SPC7110 expansion ROM base in PSRAM (banks $40-4f). + $db is free in every other core, same rationale as $d7/$d8. */ + 8'hdb: + case (spi_byte_cnt) + 32'h2: + EXP_BASE[23:16] <= param_data; + 32'h3: + EXP_BASE[15:8] <= param_data; + 32'h4: + EXP_BASE[7:0] <= param_data; + endcase 8'h4x: SD_DMA_ENr <= 1'b0; 8'h6x: @@ -585,6 +605,7 @@ assign rom_mask_out = ROM_MASK; assign saveram_mask_out = SAVERAM_MASK; assign drom_base_out = DROM_BASE; assign drom_mask_out = DROM_MASK; +assign exp_base_out = EXP_BASE; assign rtc_data_out = rtc_data_out_buf; assign rtc_pgm_we = rtc_pgm_we_buf; `ifndef MK2 diff --git a/verilog/sd2snes_spc7110/sd2snes_spc7110.xise b/verilog/sd2snes_spc7110/sd2snes_spc7110.xise index 55b2efeaf..8f5a5bf91 100644 --- a/verilog/sd2snes_spc7110/sd2snes_spc7110.xise +++ b/verilog/sd2snes_spc7110/sd2snes_spc7110.xise @@ -237,7 +237,7 @@ - + diff --git a/verilog/sd2snes_spc7110/spc7110_map.v b/verilog/sd2snes_spc7110/spc7110_map.v index e9740744c..a64eebbd1 100644 --- a/verilog/sd2snes_spc7110/spc7110_map.v +++ b/verilog/sd2snes_spc7110/spc7110_map.v @@ -62,6 +62,12 @@ module spc7110_map( input [23:0] drom_base, // = PROM size; data ROM origin in PSRAM input [23:0] drom_mask, // physical data ROM mirror mask + input [23:0] exp_base, // expansion ROM (banks $40-4f) origin in + // PSRAM, 1 MB-aligned by contract; upper + // nibble 0 = chip absent (a real base is + // never below 1 MB, the PROM owns PSRAM 0 + // -- same sentinel idea as prom_present) + output rom_hit, output [23:0] psram_addr, output is_sram @@ -76,6 +82,18 @@ module spc7110_map( wire rom_win = (lo_bank & snes_addr[15]) | hi_bank; + // ------------------------------------------------------------------------- + // expansion ROM: banks $40-4f, flat 1 MB, outside the $483x window/block + // machinery entirely. The board decodes it as a separate chip (its own + // "map address=40-4f:0000-ffff", no mask/mcu column); no retail cart + // populates it, the Tengai Makyou Zero translations do. exp_base is + // 1 MB-aligned by contract, so the PSRAM address is a concatenation -- + // no 24-bit carry chain in the PSRAM address path. + // ------------------------------------------------------------------------- + wire exp_avail = |exp_base[23:20]; + wire exp_sel = (snes_addr[23:20] == 4'h4); + wire [23:0] exp_psram = {exp_base[23:20], snes_addr[19:0]}; + // Both folds land on the same 22 bits: removing bit 23 from $00-3f/$80-bf // and bits 23:22 from $c0-ff leaves snes_addr[21:0] either way, because the // banks of the first range always have bit 22 clear. @@ -135,10 +153,16 @@ module spc7110_map( assign is_sram = lo_bank & r4830[7] & (snes_addr[15:13] == 3'b011); // the PROM branch answers only if a PROM is there, the data ROM branch only - // if the 0x400000 guard of dataromRead() does not fire - assign rom_hit = rom_win & (prom_branch ? prom_present : ~drom_kill); + // if the 0x400000 guard of dataromRead() does not fire. exp_sel sits + // outside rom_win entirely (lo_bank needs bit 22 clear, hi_bank needs + // bit 23 set, exp_sel is bit23=0/bit22=1), so there is nothing to + // arbitrate -- and with no expansion chip present the $40-4f banks stay + // unmapped exactly as before. + assign rom_hit = (exp_sel & exp_avail) + | (rom_win & (prom_branch ? prom_present : ~drom_kill)); assign psram_addr = ~rom_hit ? 24'h000000 : + exp_sel ? exp_psram : use_prom ? prom_off : (drom_base + drom_off); diff --git a/verilog/sd2snes_spc7110/spc7110_regs.v b/verilog/sd2snes_spc7110/spc7110_regs.v index 57783feae..f31201281 100644 --- a/verilog/sd2snes_spc7110/spc7110_regs.v +++ b/verilog/sd2snes_spc7110/spc7110_regs.v @@ -55,6 +55,7 @@ module spc7110_regs( input [23:0] snes_addr, input [23:0] drom_base, input [23:0] drom_mask, + input [23:0] exp_base, output map_rom_hit, output [23:0] map_psram_addr, output map_is_sram, @@ -397,6 +398,7 @@ spc7110_map spc7110_map_i( .r4834(q4834), .drom_base(drom_base), .drom_mask(drom_mask), + .exp_base(exp_base), .rom_hit(map_rom_hit), .psram_addr(map_psram_addr), .is_sram(map_is_sram)