sd2snes/src/fpga_spi.c

434 lines
12 KiB
C

/* sd2snes - SD card based universal cartridge for the SNES
Copyright (C) 2009-2012 Maximilian Rehkopf <otakon@gmx.net>
uC firmware portion
Inspired by and based on code from sd2iec, written by Ingo Korb et al.
See sdcard.c|h, config.h.
FAT file system access based on code by ChaN, Jim Brain, Ingo Korb,
see ff.c|h.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
fpga_spi.h: functions for SPI ctrl, SRAM interfacing and feature configuration
*/
/*
SPI commands
cmd param function
=============================================
0t bbhhll set address to 0xbbhhll
t = target
target: 0 = RAM
1 = MSU Audio buffer
2 = MSU Data buffer
targets 1 & 2 only require 2 address bytes to
be written.
10 bbhhll set SNES input address mask to 0xbbhhll
20 bbhhll set SRAM address mask to 0xbbhhll
3m - set mapper to m
0=HiROM, 1=LoROM, 2=ExHiROM, 6=SF96, 7=Menu
4s - trigger SD DMA (512b from SD to memory)
s: Bit 2 = partial, Bit 1:0 = target
target: see above
60 xsssyeee set SD DMA partial transfer parameters
x: 0 = read from sector start (skip until
start offset reached)
8 = assume mid-sector position and read
immediately
sss = start offset (msb first)
y: 0 = skip rest of SD sector
8 = stop mid-sector if end offset reached
eee = end offset (msb first)
8p - read (RAM only)
p: 0 = no increment after read
8 = increment after read
9p {xx}* write xx
p: i-tt
tt = target (see above)
i = increment (see above)
E0 ssrr set MSU-1 status register (=FPGA status [7:0])
ss = bits to set in status register (1=set)
rr = bits to reset in status register (1=reset)
E1 - pause DAC
E2 - resume/play DAC
E3 - reset DAC playback pointer (0)
E4 hhll set MSU read pointer
E5 tt{7} set RTC (SPC7110 format + 1000s of year,
nibbles packed)
eg 0x20111210094816 is 2011-12-10, 9:48:16
E6 ssrr set/reset BS-X status register [7:0]
E7 - reset SRTC state
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)
EB - put DSP into reset
EC - release DSP from reset
ED - set feature enable bits (see below)
EE - set $213f override value (0=NTSC, 1=PAL)
F0 - receive test token (to see if FPGA is alive)
F1 - receive status (16bit, MSB first), see below
F2 - get MSU data address (32bit, MSB first)
F3 - get MSU audio track no. (16bit, MSB first)
F4 - get MSU volume (8bit)
FE - get SNES master clock frequency (32bit, MSB first)
measured 1x/sec
FF {xx}* echo (returns the sent data in the next byte)
FPGA status word:
bit function
==========================================================================
15 SD DMA busy (0=idle, 1=busy)
14 DAC read pointer MSB
13 MSU read pointer MSB
12 reserved (0)
11 reserved (0)
10 reserved (0)
9 reserved (0)
8 reserved (0)
7 reserved (0)
6 reserved (0)
5 MSU1 Audio request from SNES
4 MSU1 Data request from SNES
3 reserved (0)
2 MSU1 Audio control status: 0=no repeat, 1=repeat
1 MSU1 Audio control status: 0=pause, 1=play
0 MSU1 Audio control request
FPGA feature enable bits:
bit function
==========================================================================
7 -
6 -
5 -
4 enable $213F override
3 enable MSU1 registers
2 enable SRTC registers
1 enable ST0010 mapping
0 enable DSPx mapping
*/
#include <arm/NXP/LPC17xx/LPC17xx.h>
#include "bits.h"
#include "fpga.h"
#include "config.h"
#include "uart.h"
#include "spi.h"
#include "fpga_spi.h"
#include "timer.h"
#include "sdnative.h"
void fpga_spi_init( void )
{
spi_init();
BITBAND( FPGA_MCU_RDY_REG->FIODIR, FPGA_MCU_RDY_BIT ) = 0;
}
void set_msu_addr( uint16_t address )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SETADDR | FPGA_TGT_MSUBUF );
FPGA_TX_BYTE( ( address >> 8 ) & 0xff );
FPGA_TX_BYTE( ( address ) & 0xff );
FPGA_DESELECT();
}
void set_dac_addr( uint16_t address )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SETADDR | FPGA_TGT_DACBUF );
FPGA_TX_BYTE( ( address >> 8 ) & 0xff );
FPGA_TX_BYTE( ( address ) & 0xff );
FPGA_DESELECT();
}
void set_mcu_addr( uint32_t address )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SETADDR | FPGA_TGT_MEM );
FPGA_TX_BYTE( ( address >> 16 ) & 0xff );
FPGA_TX_BYTE( ( address >> 8 ) & 0xff );
FPGA_TX_BYTE( ( address ) & 0xff );
FPGA_DESELECT();
}
void set_saveram_mask( uint32_t mask )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SETRAMMASK );
FPGA_TX_BYTE( ( mask >> 16 ) & 0xff );
FPGA_TX_BYTE( ( mask >> 8 ) & 0xff );
FPGA_TX_BYTE( ( mask ) & 0xff );
FPGA_DESELECT();
}
void set_rom_mask( uint32_t mask )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SETROMMASK );
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 ) );
FPGA_DESELECT();
}
uint8_t fpga_test()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_TEST );
uint8_t result = FPGA_RX_BYTE();
FPGA_DESELECT();
return result;
}
uint16_t fpga_status()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_GETSTATUS );
uint16_t result = ( FPGA_RX_BYTE() ) << 8;
result |= FPGA_RX_BYTE();
FPGA_DESELECT();
return result;
}
void fpga_set_sddma_range( uint16_t start, uint16_t end )
{
printf( "%s %08X -> %08X\n", __func__, start, end );
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SDDMA_RANGE );
FPGA_TX_BYTE( start >> 8 );
FPGA_TX_BYTE( start & 0xff );
FPGA_TX_BYTE( end >> 8 );
FPGA_TX_BYTE( end & 0xff );
FPGA_DESELECT();
}
void fpga_sddma( uint8_t tgt, uint8_t partial )
{
//printf("%s %02X -> %02X\n", __func__, tgt, partial);
//uint32_t test = 0;
//uint8_t status = 0;
BITBAND( SD_CLKREG->FIODIR, SD_CLKPIN ) = 0;
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SDDMA | ( tgt & 3 ) | ( partial ? FPGA_SDDMA_PARTIAL : 0 ) );
FPGA_TX_BYTE( 0x00 ); /* dummy for falling DMA_EN edge */
FPGA_DESELECT();
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_GETSTATUS );
DBG_SD printf( "FPGA DMA request sent, wait for completion..." );
while ( FPGA_RX_BYTE() & 0x80 )
{
FPGA_RX_BYTE(); /* eat the 2nd status byte */
}
DBG_SD printf( "...complete\n" );
FPGA_DESELECT();
BITBAND( SD_CLKREG->FIODIR, SD_CLKPIN ) = 1;
}
void dac_play()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_DACPLAY );
FPGA_TX_BYTE( 0x00 ); /* latch reset */
FPGA_DESELECT();
}
void dac_pause()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_DACPAUSE );
FPGA_TX_BYTE( 0x00 ); /* latch reset */
FPGA_DESELECT();
}
void dac_reset()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_DACRESETPTR );
FPGA_TX_BYTE( 0x00 ); /* latch reset */
FPGA_TX_BYTE( 0x00 ); /* latch reset */
FPGA_DESELECT();
}
void msu_reset( uint16_t address )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_MSUSETPTR );
FPGA_TX_BYTE( ( address >> 8 ) & 0xff ); /* address hi */
FPGA_TX_BYTE( address & 0xff ); /* address lo */
FPGA_TX_BYTE( 0x00 ); /* latch reset */
FPGA_TX_BYTE( 0x00 ); /* latch reset */
FPGA_DESELECT();
}
void set_msu_status( uint8_t set, uint8_t reset )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_MSUSETBITS );
FPGA_TX_BYTE( set );
FPGA_TX_BYTE( reset );
FPGA_TX_BYTE( 0x00 ); /* latch reset */
FPGA_DESELECT();
}
uint16_t get_msu_track()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_MSUGETTRACK );
uint16_t result = ( FPGA_RX_BYTE() ) << 8;
result |= FPGA_RX_BYTE();
FPGA_DESELECT();
return result;
}
uint32_t get_msu_offset()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_MSUGETADDR );
uint32_t result = ( FPGA_RX_BYTE() ) << 24;
result |= ( FPGA_RX_BYTE() ) << 16;
result |= ( FPGA_RX_BYTE() ) << 8;
result |= ( FPGA_RX_BYTE() );
FPGA_DESELECT();
return result;
}
uint32_t get_snes_sysclk()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_GETSYSCLK );
FPGA_TX_BYTE( 0x00 ); /* dummy (copy current sysclk count to register) */
uint32_t result = ( FPGA_RX_BYTE() ) << 24;
result |= ( FPGA_RX_BYTE() ) << 16;
result |= ( FPGA_RX_BYTE() ) << 8;
result |= ( FPGA_RX_BYTE() );
FPGA_DESELECT();
return result;
}
void set_bsx_regs( uint8_t set, uint8_t reset )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_BSXSETBITS );
FPGA_TX_BYTE( set );
FPGA_TX_BYTE( reset );
FPGA_TX_BYTE( 0x00 ); /* latch reset */
FPGA_DESELECT();
}
void set_fpga_time( uint64_t time )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_RTCSET );
FPGA_TX_BYTE( ( time >> 48 ) & 0xff );
FPGA_TX_BYTE( ( time >> 40 ) & 0xff );
FPGA_TX_BYTE( ( time >> 32 ) & 0xff );
FPGA_TX_BYTE( ( time >> 24 ) & 0xff );
FPGA_TX_BYTE( ( time >> 16 ) & 0xff );
FPGA_TX_BYTE( ( time >> 8 ) & 0xff );
FPGA_TX_BYTE( time & 0xff );
FPGA_TX_BYTE( 0x00 );
FPGA_DESELECT();
}
void fpga_reset_srtc_state()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SRTCRESET );
FPGA_TX_BYTE( 0x00 );
FPGA_TX_BYTE( 0x00 );
FPGA_DESELECT();
}
void fpga_reset_dspx_addr()
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_DSPRESETPTR );
FPGA_TX_BYTE( 0x00 );
FPGA_TX_BYTE( 0x00 );
FPGA_DESELECT();
}
void fpga_write_dspx_pgm( uint32_t data )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_DSPWRITEPGM );
FPGA_TX_BYTE( ( data >> 16 ) & 0xff );
FPGA_TX_BYTE( ( data >> 8 ) & 0xff );
FPGA_TX_BYTE( ( data ) & 0xff );
FPGA_TX_BYTE( 0x00 );
FPGA_TX_BYTE( 0x00 );
FPGA_DESELECT();
}
void fpga_write_dspx_dat( uint16_t data )
{
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_DSPWRITEDAT );
FPGA_TX_BYTE( ( data >> 8 ) & 0xff );
FPGA_TX_BYTE( ( data ) & 0xff );
FPGA_TX_BYTE( 0x00 );
FPGA_TX_BYTE( 0x00 );
FPGA_DESELECT();
}
void fpga_dspx_reset( uint8_t reset )
{
FPGA_SELECT();
FPGA_TX_BYTE( reset ? FPGA_CMD_DSPRESET : FPGA_CMD_DSPUNRESET );
FPGA_TX_BYTE( 0x00 );
FPGA_DESELECT();
}
void fpga_set_features( uint8_t feat )
{
printf( "set features: %02x\n", feat );
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SETFEATURE );
FPGA_TX_BYTE( feat );
FPGA_DESELECT();
}
void fpga_set_213f( uint8_t data )
{
printf( "set 213f: %d\n", data );
FPGA_SELECT();
FPGA_TX_BYTE( FPGA_CMD_SET213F );
FPGA_TX_BYTE( data );
FPGA_DESELECT();
}