From fa28f6035eac301c818c7b07a8413b98182fb5ae Mon Sep 17 00:00:00 2001 From: ikari Date: Thu, 23 Sep 2010 00:18:44 +0200 Subject: [PATCH] mk2 fw wip --- src/Makefile | 5 +- src/config.h | 17 +++ src/crc16.c | 51 +++++++ src/crc16.h | 54 ++++++++ src/faulthandler.c | 20 +++ src/fileops.h | 1 + src/filetypes.c | 80 +++++++---- src/filetypes.h | 3 + src/fpga.c | 152 +++++++++++++++++++++ src/fpga.h | 74 ++++++++++ src/fpga_spi.c | 99 ++++++++++++++ src/fpga_spi.h | 59 ++++++++ src/led.c | 16 +++ src/led.h | 3 + src/main.c | 215 +++++++++++++++++++++++++++-- src/memory.c | 329 +++++++++++++++++++++++++++++++++++++++++++++ src/memory.h | 63 +++++++++ src/printf.c | 1 + src/sdcard.c | 40 +++--- src/sdcard.h | 5 + src/smc.c | 173 ++++++++++++++++++++++++ src/smc.h | 62 +++++++++ src/snes.c | 45 +++---- src/snes.h | 4 + src/sort.c | 125 +++++++++++++++++ src/sort.h | 15 +++ src/spi.c | 179 +++++++++++++++--------- src/spi.h | 23 +++- 28 files changed, 1760 insertions(+), 153 deletions(-) create mode 100644 src/crc16.c create mode 100644 src/crc16.h create mode 100644 src/faulthandler.c create mode 100644 src/fpga.c create mode 100644 src/fpga.h create mode 100644 src/fpga_spi.c create mode 100644 src/fpga_spi.h create mode 100644 src/memory.c create mode 100644 src/memory.h create mode 100644 src/smc.c create mode 100644 src/smc.h create mode 100644 src/sort.c create mode 100644 src/sort.h diff --git a/src/Makefile b/src/Makefile index d2629c6..a42250c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -55,7 +55,7 @@ TARGET = $(OBJDIR)/sd2snes # List C source files here. (C dependencies are automatically generated.) -SRC = main.c ff.c ccsbcs.c clock.c uart.c power.c led.c timer.c printf.c sdcard.c spi.c fileops.c rtc.c fpga.c +SRC = main.c ff.c ccsbcs.c clock.c uart.c power.c led.c timer.c printf.c sdcard.c spi.c fileops.c rtc.c fpga.c fpga_spi.c snes.c smc.c memory.c crc16.c filetypes.c faulthandler.c sort.c # List Assembler source files here. @@ -218,6 +218,9 @@ program: bin debug: bin openocd -f openocd-usb.cfg -f lpc1754.cfg +reset: + openocd -f openocd-usb.cfg -f lpc1754.cfg -f reset.cfg + # Display size of file. HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex ELFSIZE = $(SIZE) -A $(TARGET).elf diff --git a/src/config.h b/src/config.h index 7bb6e38..6a673ce 100644 --- a/src/config.h +++ b/src/config.h @@ -1,6 +1,8 @@ #ifndef _CONFIG_H #define _CONFIG_H +#define IN_AHBRAM __attribute__ ((section(".ahbram"))) + #define SDCARD_DETECT (1) #define SDCARD_WP (0) #define SD_SUPPLY_VOLTAGE (1L<<21) /* 3.3V - 3.4V */ @@ -15,4 +17,19 @@ #define CONFIG_UART_TX_BUF_SHIFT 5 #define CONFIG_UART_BAUDRATE 921600 #define CONFIG_UART_DEADLOCKABLE + +#define SPI_SD 1 +#define SPI_FPGA 0 + +#define SSP_CLK_DIVISOR_FAST 4 +#define SSP_CLK_DIVISOR_SLOW 250 +#define SSP_CLK_DIVISOR_FPGA_FAST 6 +#define SSP_CLK_DIVISOR_FPGA_SLOW 16 + +#define SNES_RESET_REG LPC_GPIO1 +#define SNES_RESET_BIT 29 +// Rev.B: 26 + +#define QSORT_MAXELEM 1024 + #endif diff --git a/src/crc16.c b/src/crc16.c new file mode 100644 index 0000000..056d75b --- /dev/null +++ b/src/crc16.c @@ -0,0 +1,51 @@ +/** + * \file stdout + * Functions and types for CRC checks. + * + * Generated on Tue Jun 30 23:02:59 2009, + * by pycrc v0.7.1, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 16 + * Poly = 0x8005 + * XorIn = 0x0000 + * ReflectIn = True + * XorOut = 0x0000 + * ReflectOut = True + * Algorithm = bit-by-bit-fast + * Direct = True + *****************************************************************************/ +#include +#include "crc16.h" +/** + * Update the crc value with new data. + * + * \param crc The current crc value. + * \param data Pointer to a buffer of \a data_len bytes. + * \param data_len Number of bytes in the \a data buffer. + * \return The updated crc value. + *****************************************************************************/ +crc_t crc16_update(crc_t crc, const unsigned char *data, size_t data_len) +{ + unsigned int i; + uint8_t bit; + unsigned char c; + + while (data_len--) { + c = *data++; + for (i = 0x01; i & 0xff; i <<= 1) { + bit = (crc & 0x8000 ? 1 : 0); + if (c & i) { + bit ^= 1; + } + crc <<= 1; + if (bit) { + crc ^= 0x8005; + } + } + crc &= 0xffff; + } + return crc & 0xffff; +} + + + diff --git a/src/crc16.h b/src/crc16.h new file mode 100644 index 0000000..5b5e491 --- /dev/null +++ b/src/crc16.h @@ -0,0 +1,54 @@ +/** + * \file stdout + * Functions and types for CRC checks. + * + * Generated on Tue Jun 30 23:02:56 2009, + * by pycrc v0.7.1, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 16 + * Poly = 0x8005 + * XorIn = 0x0000 + * ReflectIn = True + * XorOut = 0x0000 + * ReflectOut = True + * Algorithm = bit-by-bit-fast + * Direct = True + *****************************************************************************/ +#ifndef __STDOUT__ +#define __STDOUT__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The definition of the used algorithm. + *****************************************************************************/ +#define CRC_ALGO_BIT_BY_BIT_FAST 1 + +/** + * The type of the CRC values. + * + * This type must be big enough to contain at least 16 bits. + *****************************************************************************/ +typedef uint16_t crc_t; + +/** + * Update the crc value with new data. + * + * \param crc The current crc value. + * \param data Pointer to a buffer of \a data_len bytes. + * \param data_len Number of bytes in the \a data buffer. + * \return The updated crc value. + *****************************************************************************/ +crc_t crc16_update(crc_t crc, const unsigned char *data, size_t data_len); + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif + +#endif /* __STDOUT__ */ + diff --git a/src/faulthandler.c b/src/faulthandler.c new file mode 100644 index 0000000..47d595f --- /dev/null +++ b/src/faulthandler.c @@ -0,0 +1,20 @@ +#include +#include "uart.h" + +void HardFault_Handler(void) { + printf("HFSR: %lx\n", SCB->HFSR); + while (1) ; +} + +void MemManage_Handler(void) { + printf("MemManage - CFSR: %lx; MMFAR: %lx\n", SCB->CFSR, SCB->MMFAR); +} + +void BusFault_Handler(void) { + printf("BusFault - CFSR: %lx; BFAR: %lx\n", SCB->CFSR, SCB->BFAR); +} + +void UsageFault_Handler(void) { + printf("UsageFault - CFSR: %lx; BFAR: %lx\n", SCB->CFSR, SCB->BFAR); +} + diff --git a/src/fileops.h b/src/fileops.h index 68b0bf4..4cdee72 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -26,6 +26,7 @@ #ifndef FILEOPS_H #define FILEOPS_H +#include #include "ff.h" BYTE file_buf[512]; diff --git a/src/filetypes.c b/src/filetypes.c index a3618a3..77bae76 100644 --- a/src/filetypes.c +++ b/src/filetypes.c @@ -26,8 +26,6 @@ #include #include -#include -#include #include "config.h" #include "uart.h" #include "filetypes.h" @@ -37,13 +35,14 @@ #include "crc16.h" #include "memory.h" #include "led.h" +#include "sort.h" uint16_t scan_flat(const char* path) { DIR dir; FRESULT res; FILINFO fno; - fno.lfn = NULL; - res = f_opendir(&dir, (unsigned char*)path); + fno.lfname = NULL; + res = f_opendir(&dir, (TCHAR*)path); uint16_t numentries = 0; if (res == FR_OK) { for (;;) { @@ -60,7 +59,7 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { FILINFO fno; FRESULT res; uint8_t len; - unsigned char* fn; + TCHAR* fn; static unsigned char depth = 0; static uint16_t crc; static uint32_t db_tgt; @@ -81,10 +80,11 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { next_subdir_tgt = SRAM_DIR_ADDR; this_dir_tgt = SRAM_DIR_ADDR; parent_tgt = 0; - dprintf("root dir @%lx\n", dir_tgt); + printf("root dir @%lx\n", dir_tgt); } - fno.lfn = file_lfn; + fno.lfsize = 255; + fno.lfname = (TCHAR*)file_lfn; numentries=0; for(pass = 0; pass < 2; pass++) { if(pass) { @@ -95,24 +95,24 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { if(next_subdir_tgt > dir_end) { dir_end = next_subdir_tgt; } - dprintf("path=%s depth=%d ptr=%lx entries=%d parent=%lx next subdir @%lx\n", path, depth, db_tgt, numentries, parent_tgt, next_subdir_tgt /*dir_tgt_next*/); + printf("path=%s depth=%d ptr=%lx entries=%d parent=%lx next subdir @%lx\n", path, depth, db_tgt, numentries, parent_tgt, next_subdir_tgt /*dir_tgt_next*/); // _delay_ms(50); if(mkdb) { - dprintf("d=%d Saving %lX to Address %lX [end]\n", depth, 0L, next_subdir_tgt - 4); + printf("d=%d Saving %lx to Address %lx [end]\n", depth, 0L, next_subdir_tgt - 4); // _delay_ms(50); sram_writelong(0L, next_subdir_tgt - 4); } } - res = f_opendir(&dir, (unsigned char*)path); + res = f_opendir(&dir, (TCHAR*)path); if (res == FR_OK) { if(pass && parent_tgt && mkdb) { // write backlink to parent dir // switch to next bank if record does not fit in current bank if((db_tgt&0xffff) > ((0x10000-(sizeof(next_subdir_tgt)+sizeof(len)+4))&0xffff)) { - dprintf("switch! old=%lx ", db_tgt); + printf("switch! old=%lx ", db_tgt); db_tgt &= 0xffff0000; db_tgt += 0x00010000; - dprintf("new=%lx\n", db_tgt); + printf("new=%lx\n", db_tgt); } sram_writelong((parent_tgt-SRAM_MENU_ADDR), db_tgt); sram_writebyte(0, db_tgt+sizeof(next_subdir_tgt)); @@ -123,7 +123,7 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { } len = strlen((char*)path); for (;;) { - toggle_busy_led(); + toggle_read_led(); res = f_readdir(&dir, &fno); if (res != FR_OK || fno.fname[0] == 0) { if(pass) { @@ -131,8 +131,8 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { } break; } - fn = *fno.lfn ? fno.lfn : fno.fname; - // dprintf("%s\n", fn); + fn = *fno.lfname ? fno.lfname : fno.fname; +// printf("%s\n", fn); // _delay_ms(100); if (*fn == '.') continue; if (fno.fattrib & AM_DIR) { @@ -144,7 +144,7 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { if(mkdb) { uint16_t pathlen = strlen(path); // write element pointer to current dir structure - dprintf("d=%d Saving %lX to Address %lX [dir]\n", depth, db_tgt, dir_tgt); + printf("d=%d Saving %lx to Address %lx [dir]\n", depth, db_tgt, dir_tgt); // _delay_ms(50); sram_writelong((db_tgt-SRAM_MENU_ADDR)|((uint32_t)0x80<<24), dir_tgt); // sram_writeblock((uint8_t*)&db_tgt, dir_tgt_save, sizeof(dir_tgt_save)); @@ -153,12 +153,12 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { // - path name // - pointer to sub dir structure if((db_tgt&0xffff) > ((0x10000-(sizeof(next_subdir_tgt) + sizeof(len) + pathlen + 2))&0xffff)) { - dprintf("switch! old=%lx ", db_tgt); + printf("switch! old=%lx ", db_tgt); db_tgt &= 0xffff0000; db_tgt += 0x00010000; - dprintf("new=%lx\n", db_tgt); + printf("new=%lx\n", db_tgt); } - dprintf(" Saving dir descriptor to %lX, tgt=%lX, path=%s\n", db_tgt, next_subdir_tgt, path); + printf(" Saving dir descriptor to %lx tgt=%lx, path=%s\n", db_tgt, next_subdir_tgt, path); // _delay_ms(100); sram_writelong((next_subdir_tgt-SRAM_MENU_ADDR), db_tgt); sram_writebyte(len+1, db_tgt+sizeof(next_subdir_tgt)); @@ -189,20 +189,20 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { case TYPE_SMC: // XXX file_open_by_filinfo(&fno); // XXX if(file_res){ -// XXX dprintf("ZOMG NOOOO %d\n", file_res); +// XXX printf("ZOMG NOOOO %d\n", file_res); // XXX _delay_ms(30); // XXX } // XXX smc_id(&romprops); // XXX file_close(); // _delay_ms(30); // write element pointer to current dir structure -// dprintf("d=%d Saving %lX to Address %lX [file]\n", depth, db_tgt, dir_tgt); +// printf("d=%d Saving %lX to Address %lX [file]\n", depth, db_tgt, dir_tgt); // _delay_ms(50); if((db_tgt&0xffff) > ((0x10000-(sizeof(romprops) + sizeof(len) + pathlen + 1))&0xffff)) { - dprintf("switch! old=%lx ", db_tgt); + printf("switch! old=%lx ", db_tgt); db_tgt &= 0xffff0000; db_tgt += 0x00010000; - dprintf("new=%lx\n", db_tgt); + printf("new=%lx\n", db_tgt); } sram_writelong((db_tgt-SRAM_MENU_ADDR), dir_tgt); // sram_writeblock((uint8_t*)&db_tgt, dir_tgt, sizeof(db_tgt)); @@ -220,23 +220,23 @@ uint16_t scan_dir(char* path, char mkdb, uint32_t this_dir_tgt) { break; } path[len]=0; - // dprintf("%s ", path); + // printf("%s ", path); // _delay_ms(30); } } else { - unsigned char* sfn = fno.fname; + TCHAR* sfn = fno.fname; while(*sfn != 0) { - crc += crc16_update(crc, sfn++, 1); + crc += crc16_update(crc, (unsigned char*)sfn++, 1); } } } - // dprintf("%s/%s\n", path, fn); + // printf("%s/%s\n", path, fn); // _delay_ms(50); } } } else uart_putc(0x30+res); } -// dprintf("%x\n", crc); +// printf("%x\n", crc); // _delay_ms(50); sram_writelong(db_tgt, SRAM_DB_ADDR+4); sram_writelong(dir_end, SRAM_DB_ADDR+8); @@ -248,7 +248,7 @@ SNES_FTYPE determine_filetype(char* filename) { char* ext = strrchr(filename, '.'); if(ext == NULL) return TYPE_UNKNOWN; - if(!strcasecmp_P(ext+1, PSTR("SMC"))) { + if(!strcasecmp(ext+1, "SMC")) { return TYPE_SMC; }/* later if(!strcasecmp_P(ext+1, PSTR("SRM"))) { @@ -271,3 +271,25 @@ FRESULT get_db_id(uint16_t* id) { } return file_res; } + +int get_num_dirent(uint32_t addr) { + int result = 0; + while(sram_readlong(addr+result*4)) { + result++; + } + return result; +} + +void sort_all_dir(uint32_t endaddr) { + uint32_t entries = 0; + uint32_t current_base = SRAM_DIR_ADDR; + while(current_base<(endaddr)) { + while(sram_readlong(current_base+entries*4)) { + entries++; + } + printf("sorting dir @%lx, entries: %ld\n", current_base, entries); + sort_dir(current_base, entries); + current_base += 4*entries + 4; + entries = 0; + } +} diff --git a/src/filetypes.h b/src/filetypes.h index 6cb443b..3746960 100644 --- a/src/filetypes.h +++ b/src/filetypes.h @@ -39,7 +39,10 @@ typedef enum { char fs_path[256]; SNES_FTYPE determine_filetype(char* filename); //uint32_t scan_fs(); +uint16_t scan_flat(const char* path); uint16_t scan_dir(char* path, char mkdb, uint32_t this_subdir_tgt); FRESULT get_db_id(uint16_t*); +int get_num_dirent(uint32_t addr); +void sort_all_dir(uint32_t endaddr); #endif diff --git a/src/fpga.c b/src/fpga.c new file mode 100644 index 0000000..f0f783f --- /dev/null +++ b/src/fpga.c @@ -0,0 +1,152 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR 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.c: FPGA (re)configuration +*/ + + +/* + FPGA pin mapping + ================ + CCLK P0.11 out + PROG_B P1.10 out + INIT_B P2.9 in + DIN P2.8 out + DONE P0.22 in + */ + +#include +#include "bits.h" + +#include "fpga.h" +#include "fpga_spi.h" +#include "config.h" +#include "uart.h" +#include "sdcard.h" +#include "diskio.h" +#include "integer.h" +#include "ff.h" +#include "fileops.h" +// XXX #include "fpga_spi.h" +#include "spi.h" +#include "led.h" +#include "timer.h" + +/*DWORD get_fattime(void) { + return 0L; +}*/ +void fpga_set_prog_b(uint8_t val) { + if(val) + BITBAND(PROGBREG->FIOSET, PROGBBIT) = 1; + else + BITBAND(PROGBREG->FIOCLR, PROGBBIT) = 1; +} + +void fpga_set_cclk(uint8_t val) { + if(val) + BITBAND(CCLKREG->FIOSET, CCLKBIT) = 1; + else + BITBAND(CCLKREG->FIOCLR, CCLKBIT) = 1; +} + +int fpga_get_initb() { + return BITBAND(INITBREG->FIOPIN, INITBBIT); +} + +void fpga_init() { +/* mainly GPIO directions */ + BITBAND(CCLKREG->FIODIR, CCLKBIT) = 1; /* CCLK */ + BITBAND(DONEREG->FIODIR, DONEBIT) = 0; /* DONE */ + BITBAND(PROGBREG->FIODIR, PROGBBIT) = 1; /* PROG_B */ + BITBAND(DINREG->FIODIR, DINBIT) = 1; /* DIN */ + BITBAND(INITBREG->FIODIR, INITBBIT) = 0; /* INIT_B */ + + LPC_GPIO2->FIOMASK1 = 0; + + SPI_OFFLOAD=0; + fpga_set_cclk(0); /* initial clk=0 */ +} + +int fpga_get_done(void) { + return BITBAND(LPC_GPIO0->FIOPIN, 22); +} + +void fpga_postinit() { + LPC_GPIO2->FIOMASK1 = 0; +} + +void fpga_pgm(uint8_t* filename) { + int MAXRETRIES = 10; + int retries = MAXRETRIES; + do { + fpga_set_prog_b(0); + uart_putc('P'); + fpga_set_prog_b(1); + while(!fpga_get_initb()); + LPC_GPIO2->FIOMASK1 = ~(BV(0)); + uart_putc('p'); + + UINT bytes_read; + + // open configware file + file_open(filename, FA_READ); + if(file_res) { + uart_putc('?'); + uart_putc(0x30+file_res); + return; + } + + for (;;) { + bytes_read = file_read(); + if (file_res || bytes_read == 0) break; // error or eof + for(int i=0; i clock = f/4 +// XXX SPSR = 0; + printf("SPI slow\n"); + } else { // mcu only + FPGA_SPI_FAST(); + CLR_MCU_OVR(); + // Enable SPI double speed mode -> clock = f/2 +// XXX SPSR = _BV(SPI2X); + printf("SPI fast\n"); + } +} + diff --git a/src/fpga.h b/src/fpga.h new file mode 100644 index 0000000..b3e4058 --- /dev/null +++ b/src/fpga.h @@ -0,0 +1,74 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR 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.h: FPGA (re)configuration +*/ + +#ifndef FPGA_H +#define FPGA_H + +#include +#include "bits.h" + +void fpga_set_prog_b(uint8_t val); +void fpga_set_cclk(uint8_t val); +int fpga_get_initb(void); + +void fpga_init(void); +uint8_t fpga_test(void); +void fpga_postinit(void); +void fpga_pgm(uint8_t* filename); + +void set_mcu_ovr(uint8_t val); + + +uint8_t SPI_OFFLOAD; + +#define CCLKREG LPC_GPIO0 +#define PROGBREG LPC_GPIO1 +#define INITBREG LPC_GPIO2 +#define DINREG LPC_GPIO2 +#define DONEREG LPC_GPIO0 + +#define CCLKBIT (11) +#define PROGBBIT (10) +#define INITBBIT (9) +#define DINBIT (8) +#define DONEBIT (22) + + +#define FPGA_TEST_TOKEN (0xa5) + +// some macros for bulk transfers (faster) +#define FPGA_SEND_BYTE_SERIAL(data) do {SET_FPGA_DIN(data>>7); CCLK();\ +SET_FPGA_DIN(data>>6); CCLK(); SET_FPGA_DIN(data>>5); CCLK();\ +SET_FPGA_DIN(data>>4); CCLK(); SET_FPGA_DIN(data>>3); CCLK();\ +SET_FPGA_DIN(data>>2); CCLK(); SET_FPGA_DIN(data>>1); CCLK();\ +SET_FPGA_DIN(data); CCLK();} while (0) +#define SET_CCLK() do {BITBAND(LPC_GPIO0->FIOSET, 11) = 1;} while (0) +#define CLR_CCLK() do {BITBAND(LPC_GPIO0->FIOCLR, 11) = 1;} while (0) +#define CCLK() do {SET_CCLK(); CLR_CCLK();} while (0) +#define SET_MCU_OVR() do {BITBAND(LPC_GPIO2->FIOSET, 8) = 1;} while (0) +#define CLR_MCU_OVR() do {BITBAND(LPC_GPIO2->FIOCLR, 8) = 1;} while (0) +#define SET_FPGA_DIN(data) do {LPC_GPIO2->FIOPIN1 = data;} while (0) +#endif diff --git a/src/fpga_spi.c b/src/fpga_spi.c new file mode 100644 index 0000000..6a6ff60 --- /dev/null +++ b/src/fpga_spi.c @@ -0,0 +1,99 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR 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 + ============================================= + 00 bb[hh[ll]] set address to 0xbb0000, 0xbbhh00, or 0xbbhhll + 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, 7=Menu + 80 - read with increment + 81 - read w/o increment + 90 {xx}* write xx with increment + 91 {xx}* write xx w/o increment + F0 - receive test token (to see if FPGA is alive) + +*/ + +#include +#include "bits.h" +#include "fpga.h" +#include "config.h" +#include "uart.h" +#include "spi.h" +#include "fpga_spi.h" +#include "timer.h" + +void fpga_spi_init(void) { + spi_init(SPI_SPEED_FPGA_FAST, SPI_FPGA); +} + +void set_mcu_addr(uint32_t address) { + FPGA_SELECT(); + FPGA_TX_BYTE(0x00); + 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(0x20); + 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(0x10); + 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(0x30 | (val & 0x0f)); + FPGA_DESELECT(); +} + +uint8_t fpga_test() { + FPGA_SELECT(); + FPGA_TX_BYTE(0xF0); // TEST + FPGA_TX_BYTE(0x00); // dummy + uint8_t result = FPGA_RX_BYTE(); + FPGA_DESELECT(); + return result; +} + diff --git a/src/fpga_spi.h b/src/fpga_spi.h new file mode 100644 index 0000000..9fcb508 --- /dev/null +++ b/src/fpga_spi.h @@ -0,0 +1,59 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR 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 +*/ + +#ifndef _FPGA_SPI_H +#define _FPGA_SPI_H + +#include +#include "bits.h" + +#define FPGA_SS_BIT 16 +#define FPGA_SS_REG LPC_GPIO0 + +#define FPGA_SELECT() do {FPGA_TX_SYNC(); BITBAND(FPGA_SS_REG->FIOCLR, FPGA_SS_BIT) = 1;} while (0) +#define FPGA_DESELECT() do {FPGA_TX_SYNC(); BITBAND(FPGA_SS_REG->FIOSET, FPGA_SS_BIT) = 1;} while (0) + +#define FPGA_TX_SYNC() spi_tx_sync(SPI_FPGA) +#define FPGA_TX_BYTE(x) spi_tx_byte(x, SPI_FPGA) +#define FPGA_RX_BYTE() spi_rx_byte(SPI_FPGA) +#define FPGA_TXRX_BYTE(x) spi_txrx_byte(x, SPI_FPGA) +#define FPGA_TX_BLOCK(x,y) spi_tx_block(x,y,SPI_FPGA) +#define FPGA_RX_BLOCK(x,y) spi_rx_block(x,y,SPI_FPGA) + +#define FPGA_SPI_FAST() spi_set_speed(SPI_SPEED_FPGA_FAST, SPI_FPGA) +#define FPGA_SPI_SLOW() spi_set_speed(SPI_SPEED_FPGA_SLOW, SPI_FPGA) + +void fpga_spi_init(void); +void fpga_spi_test(void); +void spi_fpga(void); +void spi_sd(void); +void spi_none(void); +void set_mcu_addr(uint32_t); +void set_saveram_mask(uint32_t); +void set_rom_mask(uint32_t); +void set_mapper(uint8_t val); + +#endif diff --git a/src/led.c b/src/led.c index 7ca91e4..4b7b206 100644 --- a/src/led.c +++ b/src/led.c @@ -6,6 +6,8 @@ #include "led.h" int led_rdyledstate = 0; +int led_readledstate = 0; +int led_writeledstate = 0; void rdyled(unsigned int state) { BITBAND(LPC_GPIO2->FIODIR, 0) = state; @@ -14,6 +16,12 @@ void rdyled(unsigned int state) { void readled(unsigned int state) { BITBAND(LPC_GPIO2->FIODIR, 1) = state; + led_readledstate = state; +} + +void writeled(unsigned int state) { + BITBAND(LPC_GPIO2->FIODIR, 2) = state; + led_writeledstate = state; } void led_clkout32(uint32_t val) { @@ -31,6 +39,14 @@ void toggle_rdy_led() { rdyled(~led_rdyledstate); } +void toggle_read_led() { + readled(~led_readledstate); +} + +void toggle_write_led() { + writeled(~led_writeledstate); +} + void led_panic() { while(1) { LPC_GPIO2->FIODIR |= BV(0) | BV(1) | BV(2); diff --git a/src/led.h b/src/led.h index 364b41b..5389d5c 100644 --- a/src/led.h +++ b/src/led.h @@ -4,9 +4,12 @@ #define _LED_H void readled(unsigned int state); +void writeled(unsigned int state); void rdyled(unsigned int state); void led_clkout32(uint32_t val); void toggle_rdy_led(void); +void toggle_read_led(void); +void toggle_write_led(void); void led_panic(void); #endif diff --git a/src/main.c b/src/main.c index c8d9559..a3510f7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ #include +#include #include "config.h" #include "clock.h" #include "uart.h" @@ -11,6 +12,12 @@ #include "sdcard.h" #include "fileops.h" #include "fpga.h" +#include "fpga_spi.h" +#include "filetypes.h" +#include "memory.h" +#include "snes.h" +#include "led.h" +#include "sort.h" #define EMC0TOGGLE (3<<4) #define MR0R (1<<1) @@ -23,21 +30,31 @@ volatile enum diskstates disk_state; int main(void) { LPC_GPIO2->FIODIR = BV(0) | BV(1) | BV(2); LPC_GPIO1->FIODIR = 0; - uint32_t p1; + LPC_GPIO0->FIODIR = BV(16); - /* connect UART3 on P0[25:26] + SSP1 on P0[6:9] + MAT3.0 on P0[10] */ - LPC_PINCON->PINSEL1=(0xf<<18); - LPC_PINCON->PINSEL0=BV(13) | BV(15) | BV(17) | BV(19) | BV(20) | BV(21); + /* connect UART3 on P0[25:26] + SSP0 on P0[15:18] SSP1 on P0[6:9] + MAT3.0 on P0[10] */ + LPC_PINCON->PINSEL1 = BV(18) | BV(19) | BV(20) | BV(21) /* UART3 */ + | BV(3) | BV(5); /* SSP0 (FPGA) except SS */ + LPC_PINCON->PINSEL0 = BV(31) /* SSP0 */ + | BV(13) | BV(15) | BV(17) | BV(19) /* SSP1 (SD) */ + | BV(20) | BV(21); /* MAT3.0 */ + /* enable pull-downs for CIC data lines */ + LPC_PINCON->PINMODE3 = BV(18) | BV(19) | BV(20) | BV(21); clock_disconnect(); - + snes_init(); + snes_reset(1); power_init(); timer_init(); uart_init(); - spi_init(SPI_SPEED_SLOW); - sd_init(); + fpga_spi_init(); + spi_preinit(SPI_FPGA); + spi_preinit(SPI_SD); /* do this last because the peripheral init()s change PCLK dividers */ clock_init(); + + sd_init(); + fpga_spi_init(); delay_ms(10); printf("\n\nsd2snes mk.2\n============\ncpu clock: %d Hz\n", CONFIG_CPU_FREQUENCY); file_init(); @@ -57,12 +74,194 @@ int main(void) { LPC_TIM3->MCR=MR0R; LPC_TIM3->MR0=1; LPC_TIM3->TCR=1; - uart_puts("hurr durr derpderpderp\n"); fpga_init(); fpga_pgm((uint8_t*)"/sd2snes/main.bit"); +restart: + rdyled(1); + readled(0); + writeled(0); + set_mcu_ovr(1); // exclusive mode + + *fs_path=0; + uint16_t saved_dir_id; + get_db_id(&saved_dir_id); + + uint16_t mem_dir_id = sram_readshort(SRAM_DIRID); + uint32_t mem_magic = sram_readlong(SRAM_SCRATCHPAD); + + if((mem_magic != 0x12345678) || (mem_dir_id != saved_dir_id)) { + uint16_t curr_dir_id = scan_dir(fs_path, 0, 0); // generate files footprint + printf("curr dir id = %x\n", curr_dir_id); + if((get_db_id(&saved_dir_id) != FR_OK) // no database? + || saved_dir_id != curr_dir_id) { // files changed? // XXX + printf("saved dir id = %x\n", saved_dir_id); + printf("rebuilding database..."); + curr_dir_id = scan_dir(fs_path, 1, 0); // then rebuild database + sram_writeblock(&curr_dir_id, SRAM_DB_ADDR, 2); + uint32_t endaddr, direndaddr; + sram_readblock(&endaddr, SRAM_DB_ADDR+4, 4); + sram_readblock(&direndaddr, SRAM_DB_ADDR+8, 4); + printf("%lx %lx\n", endaddr, direndaddr); + printf("sorting database..."); + sort_all_dir(direndaddr); + printf("done\n"); + save_sram((uint8_t*)"/sd2snes/sd2snes.db", endaddr-SRAM_DB_ADDR, SRAM_DB_ADDR); + save_sram((uint8_t*)"/sd2snes/sd2snes.dir", direndaddr-(SRAM_DIR_ADDR), SRAM_DIR_ADDR); + printf("done\n"); +// sram_hexdump(SRAM_DB_ADDR, 0x400); + } else { + printf("saved dir id = %x\n", saved_dir_id); + printf("different card, consistent db, loading db...\n"); + load_sram((uint8_t*)"/sd2snes/sd2snes.db", SRAM_DB_ADDR); + load_sram((uint8_t*)"/sd2snes/sd2snes.dir", SRAM_DIR_ADDR); + } +// save_sram((uint8_t*)"/debug.smc", 0x400000, 0); +// uart_putc('['); +// load_sram((uint8_t*)"/test.srm", SRAM_SAVE_ADDR); +// uart_putc(']'); + + sram_writeshort(curr_dir_id, SRAM_DIRID); + sram_writelong(0x12345678, SRAM_SCRATCHPAD); + } else { + printf("same card, loading db...\n"); + load_sram((uint8_t*)"/sd2snes/sd2snes.db", SRAM_DB_ADDR); + load_sram((uint8_t*)"/sd2snes/sd2snes.dir", SRAM_DIR_ADDR); + } + + uart_putc('('); + load_rom((uint8_t*)"/sd2snes/menu.bin", SRAM_MENU_ADDR); + set_rom_mask(0x3fffff); // force mirroring off + set_mapper(0x7); // menu mapper XXX + uart_putc(')'); + uart_putcrlf(); +// sram_hexdump(0x7ffff0, 0x10); +// sram_hexdump(0, 0x400); +// save_sram((uint8_t*)"/sd2snes/dump", 65536, 0); + + sram_writebyte(0, SRAM_CMD_ADDR); + + set_mcu_ovr(0); + + printf("SNES GO!\n"); + snes_reset(0); + + uint8_t cmd = 0; + printf("test sram\n"); + while(!sram_reliable()) uart_puts("DERP"); + printf("ok\n"); + +/* uint8_t *teststring=(uint8_t*)"Wer das liest ist doof! 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + uint8_t testbuf[80]; +while(1) { + sram_writeblock(teststring, 0xd00000, 61); + sram_readblock(testbuf, 0xd00000, 61); + testbuf[60]=0; + printf("%p %s\n",testbuf, (char*)testbuf); +} */ + + while(!cmd) { + cmd=menu_main_loop(); + switch(cmd) { + case SNES_CMD_LOADROM: + get_selected_name(file_lfn); +// snes_reset(1); + set_mcu_ovr(1); + printf("Selected name: %s\n", file_lfn); + load_rom(file_lfn, SRAM_ROM_ADDR); +// save_sram((uint8_t*)"/sd2snes/test.smc", romprops.romsize_bytes, 0); + if(romprops.ramsize_bytes) { + strcpy(strrchr((char*)file_lfn, (int)'.'), ".srm"); + printf("SRM file: %s\n", file_lfn); + load_sram(file_lfn, SRAM_SAVE_ADDR); + } else { + printf("No SRAM\n"); + } + set_mcu_ovr(0); + snes_reset(1); + delay_ms(100); + snes_reset(0); + break; + case SNES_CMD_SETRTC: + break; + default: + printf("unknown cmd: %d\n", cmd); + cmd=0; // unknown cmd: stay in loop + break; + } + } + + printf("cmd was %x, going to snes main loop\n", cmd); + cmd=0; + uint8_t snes_reset_prev=0, snes_reset_now=0, snes_reset_state=0; + uint16_t reset_count=0; + while(fpga_test() == FPGA_TEST_TOKEN) { + snes_reset_now=get_snes_reset(); + if(snes_reset_now) { + if(!snes_reset_prev) { + printf("RESET BUTTON DOWN\n"); + snes_reset_state=1; + // reset reset counter + reset_count=0; + } + } else { + if(snes_reset_prev) { + printf("RESET BUTTON UP\n"); + snes_reset_state=0; + } + } + if(snes_reset_state) { + delay_ms(10); + reset_count++; + } else { + sram_reliable(); + snes_main_loop(); + } + if(reset_count>100) { + reset_count=0; + set_mcu_ovr(1); + snes_reset(1); + delay_ms(100); + if(romprops.ramsize_bytes && fpga_test() == 0xa5) { + writeled(1); + save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR); + writeled(0); + } + delay_ms(1000); + goto restart; + } + snes_reset_prev = snes_reset_now; + } + // FPGA TEST FAIL. PANIC. + led_panic(); + +while(1); +} + +void teststuff(void) { + uint8_t i=0, data; + uint32_t p1; + sram_writebyte(0x5e, 0); + sram_writebyte(0x6f, 1); + sram_writelong(0x3451451e, 4); + sram_hexdump(0x0, 0x10); + sram_hexdump(0x620000, 0x200); + +while(i<10) { + FPGA_SELECT(); + FPGA_TX_BYTE(0xff); + FPGA_TX_BYTE(0xff); + data=FPGA_TXRX_BYTE(i); + FPGA_DESELECT(); + printf("%x %x\n", i, data); + i++; +} + while(1) { +// if((data=fpga_test())!=0xa5) printf("Unexpected FPGA test token 0x%x\n", data); + } while (1) { p1 = LPC_GPIO1->FIOPIN; BITBAND(LPC_GPIO2->FIOPIN, 1) = (p1 & BV(29))>>29; } + } diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..2ebfbbb --- /dev/null +++ b/src/memory.c @@ -0,0 +1,329 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR 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 + + memory.c: RAM operations +*/ + + +#include "config.h" +#include "uart.h" +#include "fpga.h" +#include "crc16.h" +#include "ff.h" +#include "fileops.h" +#include "spi.h" +#include "fpga_spi.h" +#include "led.h" +#include "smc.h" +#include "memory.h" +#include "snes.h" +#include "timer.h" + +char* hex = "0123456789ABCDEF"; + +void sram_hexdump(uint32_t addr, uint32_t len) { + static uint8_t buf[16]; + uint32_t ptr; + for(ptr=0; ptr < len; ptr += 16) { + sram_readblock((void*)buf, ptr+addr, 16); + uart_trace(buf, 0, 16); + } +} + +void sram_writebyte(uint8_t val, uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x91); // WRITE + FPGA_TX_BYTE(val); + FPGA_TX_BYTE(0x00); // dummy + FPGA_DESELECT(); +} + +uint8_t sram_readbyte(uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x81); // READ + FPGA_TX_BYTE(0x00); // dummy + uint8_t val = FPGA_TXRX_BYTE(0x00); + FPGA_DESELECT(); + return val; +} + +void sram_writeshort(uint16_t val, uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x91); // WRITE + FPGA_TX_BYTE(val&0xff); // 7-0 + FPGA_TX_BYTE((val>>8)&0xff); // 15-8 + FPGA_TX_BYTE(0x00); // dummy + FPGA_DESELECT(); +} + +void sram_writelong(uint32_t val, uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x91); // WRITE + FPGA_TX_BYTE(val&0xff); // 7-0 + FPGA_TX_BYTE((val>>8)&0xff); // 15-8 + FPGA_TX_BYTE((val>>16)&0xff); // 23-15 + FPGA_TX_BYTE((val>>24)&0xff); // 31-24 + FPGA_TX_BYTE(0x00); // dummy + FPGA_DESELECT(); +} + +uint16_t sram_readshort(uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x81); + FPGA_TX_BYTE(0x00); + + uint32_t val = FPGA_TXRX_BYTE(0x00); + val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<8); + FPGA_DESELECT(); + return val; +} + +uint32_t sram_readlong(uint32_t addr) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x81); + FPGA_TX_BYTE(0x00); + uint32_t val = FPGA_TXRX_BYTE(0x00); + val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<8); + val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<16); + val |= ((uint32_t)FPGA_TXRX_BYTE(0x00)<<24); + FPGA_DESELECT(); + return val; +} + +void sram_readlongblock(uint32_t* buf, uint32_t addr, uint16_t count) { + set_mcu_addr(addr); + FPGA_SELECT(); + FPGA_TX_BYTE(0x81); + FPGA_TX_BYTE(0x00); + uint16_t i=0; + while(i (romprops.romsize_bytes + romprops.offset)) { + romprops.romsize_bytes <<= 1; + } + + if(romprops.header.ramsize == 0) { + rammask = 0; + } else { + rammask = romprops.ramsize_bytes - 1; + } + rommask = romprops.romsize_bytes - 1; + printf("ramsize=%x rammask=%lx\nromsize=%x rommask=%lx\n", romprops.header.ramsize, rammask, romprops.header.romsize, rommask); + set_saveram_mask(rammask); + set_rom_mask(rommask); + + return (uint32_t)filesize; +} + +uint32_t load_sram(uint8_t* filename, uint32_t base_addr) { + set_mcu_addr(base_addr); + UINT bytes_read; + DWORD filesize; + file_open(filename, FA_READ); + filesize = file_handle.fsize; + if(file_res) return 0; + for(;;) { + bytes_read = file_read(); + if (file_res || !bytes_read) break; + FPGA_SELECT(); + FPGA_TX_BYTE(0x91); + for(int j=0; j + AVR 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 + + memory.h: RAM operations +*/ + +#ifndef MEMORY_H +#define MEMORY_H + +#include + +#define SRAM_ROM_ADDR (0x000000L) +#define SRAM_SAVE_ADDR (0xE00000L) + +#define SRAM_MENU_ADDR (0xE00000L) +#define SRAM_DB_ADDR (0xE20000L) +#define SRAM_DIR_ADDR (0xE10000L) +#define SRAM_CMD_ADDR (0xFF1004L) +#define SRAM_PARAM_ADDR (0xFF1000L) +#define SRAM_MENU_SAVE_ADDR (0xFF0000L) +#define SRAM_SCRATCHPAD (0xFFFF00L) +#define SRAM_DIRID (0xFFFFF0L) +#define SRAM_RELIABILITY_SCORE (0x100) + + uint32_t load_rom(uint8_t* filename, uint32_t base_addr); + uint32_t load_sram(uint8_t* filename, uint32_t base_addr); + void sram_hexdump(uint32_t addr, uint32_t len); + uint8_t sram_readbyte(uint32_t addr); + uint16_t sram_readshort(uint32_t addr); + uint32_t sram_readlong(uint32_t addr); + void sram_writebyte(uint8_t val, uint32_t addr); + void sram_writeshort(uint16_t val, uint32_t addr); + void sram_writelong(uint32_t val, uint32_t addr); + void sram_readblock(void* buf, uint32_t addr, uint16_t size); + void sram_readlongblock(uint32_t* buf, uint32_t addr, uint16_t count); + void sram_writeblock(void* buf, uint32_t addr, uint16_t size); + void save_sram(uint8_t* filename, uint32_t sram_size, uint32_t base_addr); + uint32_t calc_sram_crc(uint32_t base_addr, uint32_t size); + uint8_t sram_reliable(void); + +#include "smc.h" + snes_romprops_t romprops; +#endif diff --git a/src/printf.c b/src/printf.c index 79249d7..861c03d 100644 --- a/src/printf.c +++ b/src/printf.c @@ -179,6 +179,7 @@ static int internal_nprintf(void (*output_function)(char c), const char *fmt, va output_function('x'); width -= 2; case 'x': + case 'X': base = 16; flags |= FLAG_UNSIGNED; break; diff --git a/src/sdcard.c b/src/sdcard.c index 3fa2095..f50242e 100644 --- a/src/sdcard.c +++ b/src/sdcard.c @@ -184,7 +184,7 @@ static uint8_t wait_for_response(uint8_t expected) { tick_t timeout = getticks() + HZ/2; while (time_before(getticks(), timeout)) { - uint8_t byte = spi_rx_byte(); + uint8_t byte = spi_rx_byte(1); if (expected == 0 && byte != 0) return 1; @@ -199,7 +199,7 @@ static uint8_t wait_for_response(uint8_t expected) { static void deselectCard(uint8_t card) { // Send 8 clock cycles set_sd_led(0); - spi_rx_byte(); + spi_rx_byte(1); } /** @@ -244,15 +244,15 @@ static int sendCommand(const uint8_t card, #endif // Transfer command - spi_tx_byte(0x40+command); + spi_tx_byte(0x40+command, 1); uint32_t tmp = swap_word(parameter); - spi_tx_block(&tmp, 4); - spi_tx_byte(crc); + spi_tx_block(&tmp, 4, 1); + spi_tx_byte(crc, 1); // Wait for a valid response timeout = getticks() + HZ/2; do { - i = spi_rx_byte(); + i = spi_rx_byte(1); } while (i & 0x80 && time_before(getticks(), timeout)); #ifdef CONFIG_TWINSD @@ -291,7 +291,7 @@ static uint8_t extendedInit(const uint8_t card) { } // No error, continue SDHC initialization - spi_rx_block(&answer, 4); + spi_rx_block(&answer, 4, 1); answer = swap_word(answer); deselectCard(card); @@ -420,16 +420,16 @@ printf("sd_initialize\n"); * IEC lines tied to SPI, so I moved it here to resolve the * conflict. */ - spi_init(SPI_SPEED_SLOW); + spi_init(SPI_SPEED_SLOW, 1); disk_state = DISK_ERROR; cardtype[drv] = 0; set_sd_led(0); - // Send 80 clks + // Send 8000 clks for (counter=0; counter<1000; counter++) { - spi_tx_byte(0xff); + spi_tx_byte(0xff, 1); } // Reset card @@ -455,7 +455,7 @@ printf("sd_initialize\n"); } while (i > 1 && counter-- > 0); if (counter > 0) { - spi_rx_block(&answer, 4); + spi_rx_block(&answer, 4, 1); answer = swap_word(answer); // See if the card likes our supply voltage @@ -501,7 +501,7 @@ printf("sd_initialize\n"); } // Thats it! - spi_set_speed(SPI_SPEED_FAST); + spi_set_speed(SPI_SPEED_FAST, 1); disk_state = DISK_OK; return sd_status(drv); } @@ -554,9 +554,9 @@ DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) { #ifdef CONFIG_SD_BLOCKTRANSFER /* Transfer data first, calculate CRC afterwards */ - spi_rx_block(buffer, 512); + spi_rx_block(buffer, 512, 1); - recvcrc = spi_rx_byte() << 8 | spi_rx_byte(); + recvcrc = spi_rx_byte(1) << 8 | spi_rx_byte(1); #ifdef CONFIG_SD_DATACRC crc = crc_xmodem_block(0, buffer, 512); #endif @@ -653,10 +653,10 @@ DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) { } // Send data token - spi_tx_byte(0xfe); + spi_tx_byte(0xfe, 1); // Send data - spi_tx_block(buffer, 512); + spi_tx_block(buffer, 512, 1); #ifdef CONFIG_SD_DATACRC crc = crc_xmodem_block(0, buffer, 512); #else @@ -664,11 +664,11 @@ DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) { #endif // Send CRC - spi_tx_byte(crc >> 8); - spi_tx_byte(crc & 0xff); + spi_tx_byte(crc >> 8, 1); + spi_tx_byte(crc & 0xff, 1); // Get and check status feedback - status = spi_rx_byte(); + status = spi_rx_byte(1); // Retry if neccessary if ((status & 0x0F) != 0x05) { @@ -725,7 +725,7 @@ DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer) { return RES_ERROR; } - spi_rx_block(buf, 18); + spi_rx_block(buf, 18, 1); deselectCard(drv); if (cardtype[drv] & CARD_SDHC) { diff --git a/src/sdcard.h b/src/sdcard.h index c1f88af..7d6a1a4 100644 --- a/src/sdcard.h +++ b/src/sdcard.h @@ -29,6 +29,11 @@ #include "diskio.h" +#define SD_TX_BYTE(x) spi_tx_byte(x, SPI_SD); +#define SD_RX_BYTE(x) spi_rx_byte(x, SPI_SD); +#define SD_TX_BLOCK(x,y) spi_tx_block(x,y, SPI_SD); +#define SD_RX_BLOCK(x,y) spi_rx_block(x,y, SPI_SD); + /* These functions are weak-aliased to disk_... */ void sd_init(void); DSTATUS sd_status(BYTE drv); diff --git a/src/smc.c b/src/smc.c new file mode 100644 index 0000000..9444c88 --- /dev/null +++ b/src/smc.c @@ -0,0 +1,173 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR 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 + + smc.c: SMC file related operations +*/ + +#include "fileops.h" +#include "config.h" +#include "uart.h" +#include "smc.h" + +uint32_t hdr_addr[6] = {0xffb0, 0x101b0, 0x7fb0, 0x81b0, 0x40ffb0, 0x4101b0}; +uint8_t countAllASCII(uint8_t* data, int size) { + uint8_t res = 0; + do { + size--; + if(data[size] >= 0x20 && data[size] <= 0x7e) { + res++; + } + } while (size); + return res; +} + +uint8_t countAllJISX0201(uint8_t* data, int size) { + uint8_t res = 0; + do { + size--; + if((data[size] >= 0x20 && data[size] <= 0x7e) + ||(data[size] >= 0xa1 && data[size] <= 0xdf)) { + res++; + } + } while (size); + return res; +} + +uint8_t isFixed(uint8_t* data, int size, uint8_t value) { + uint8_t res = 1; + do { + size--; + if(data[size] != value) { + res = 0; + } + } while (size); + return res; +} + +uint8_t checkChksum(uint16_t cchk, uint16_t chk) { + uint32_t sum = cchk + chk; + uint8_t res = 0; + if(sum==0x0000ffff) { + res = 0x10; + } + return res; +} + +void smc_id(snes_romprops_t* props) { + uint8_t score, maxscore=1, score_idx=2; // assume LoROM + + snes_header_t* header = &(props->header); + + for(uint8_t num = 0; num < 6; num++) { + if(!file_readblock(header, hdr_addr[num], sizeof(snes_header_t)) + || file_res) { +// dprintf("uh oh... %d\n", file_res); +// _delay_ms(30); + score = 0; + } else { + score = smc_headerscore(header)/(1+(num&1)); + if((file_handle.fsize & 0x2ff) == 0x200) { + if(num&1) { + score+=20; + } else { + score=0; + } + } else { + if(!(num&1)) { + score+=20; + } else { + score=0; + } + } + } +// dprintf("%d: offset = %lX; score = %d\n", num, hdr_addr[num], score); +// _delay_ms(100); + if(score>=maxscore) { + score_idx=num; + maxscore=score; + } + } + + if(score_idx & 1) { + props->offset = 0x200; + } else { + props->offset = 0; + } + + // restore the chosen one +// dprintf("winner is %d\n", score_idx); +// _delay_ms(30); + file_readblock(header, hdr_addr[score_idx], sizeof(snes_header_t)); + switch(header->map & 0xef) { + case 0x20: + props->mapper_id = 1; + break; + case 0x21: + props->mapper_id = 0; + break; + case 0x25: + props->mapper_id = 2; + break; + default: // invalid/unsupported mapper, use header location + switch(score_idx) { + case 0: + case 1: + props->mapper_id = 0; + break; + case 2: + case 3: + props->mapper_id = 1; + break; + case 4: + case 5: + props->mapper_id = 2; + break; + default: + props->mapper_id = 1; // whatever + } + } + if(header->romsize == 0 || header->romsize > 13) { + header->romsize = 13; + } + props->ramsize_bytes = (uint32_t)1024 << header->ramsize; + props->romsize_bytes = (uint32_t)1024 << header->romsize; + props->expramsize_bytes = (uint32_t)1024 << header->expramsize; +// dprintf("ramsize_bytes: %ld\n", props->ramsize_bytes); + if(props->ramsize_bytes > 32768 || props->ramsize_bytes < 2048) { + props->ramsize_bytes = 0; + } +// dprintf("ramsize_bytes: %ld\n", props->ramsize_bytes); + f_lseek(&file_handle, 0); +} + +uint8_t smc_headerscore(snes_header_t* header) { + uint8_t score=0; + score += countAllASCII(header->maker, sizeof(header->maker)); + score += countAllASCII(header->gamecode, sizeof(header->gamecode)); + score += isFixed(header->fixed_00, sizeof(header->fixed_00), 0x00); + score += countAllJISX0201(header->name, sizeof(header->name)); + score += 3*isFixed(&header->fixed_33, sizeof(header->fixed_33), 0x33); + score += checkChksum(header->cchk, header->chk); + return score; +} + diff --git a/src/smc.h b/src/smc.h new file mode 100644 index 0000000..ae57a5b --- /dev/null +++ b/src/smc.h @@ -0,0 +1,62 @@ +/* sd2snes - SD card based universal cartridge for the SNES + Copyright (C) 2009-2010 Maximilian Rehkopf + AVR 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 + + smc.h: SMC file structures +*/ + +#ifndef SMC_H +#define SMC_H + +typedef struct _snes_header { + uint8_t maker[2]; // 0xB0 + uint8_t gamecode[4]; // 0xB2 + uint8_t fixed_00[7]; // 0xB6 + uint8_t expramsize; // 0xBD + uint8_t specver; // 0xBE + uint8_t carttype2; // 0xBF + uint8_t name[21]; // 0xC0 + uint8_t map; // 0xD5 + uint8_t carttype; // 0xD6 + uint8_t romsize; // 0xD7 + uint8_t ramsize; // 0xD8 + uint8_t destcode; // 0xD9 + uint8_t fixed_33; // 0xDA + uint8_t ver; // 0xDB + uint16_t cchk; // 0xDC + uint16_t chk; // 0xDE +} snes_header_t; + +typedef struct _snes_romprops { + uint16_t offset; // start of actual ROM image + uint8_t mapper_id; // FPGA mapper + uint8_t pad1; // for alignment + uint32_t expramsize_bytes; // ExpRAM size in bytes + uint32_t ramsize_bytes; // CartRAM size in bytes + uint32_t romsize_bytes; // ROM size in bytes (rounded up) + snes_header_t header; // original header from ROM image +} snes_romprops_t; + +void smc_id(snes_romprops_t*); +uint8_t smc_headerscore(snes_header_t*); + +#endif diff --git a/src/snes.c b/src/snes.c index e765fd1..5cc28d8 100644 --- a/src/snes.c +++ b/src/snes.c @@ -24,8 +24,8 @@ snes.c: SNES hardware control and monitoring */ -#include -#include "avrcompat.h" +#include +#include "bits.h" #include "config.h" #include "uart.h" #include "snes.h" @@ -38,8 +38,9 @@ uint8_t initloop=1; uint32_t saveram_crc, saveram_crc_old; void snes_init() { - DDRD |= _BV(PD5); // PD5 = RESET_DIR - DDRD |= _BV(PD6); // PD6 = RESET + /* put reset level on reset pin */ + BITBAND(SNES_RESET_REG->FIOCLR, SNES_RESET_BIT) = 1; + /* reset the SNES */ snes_reset(1); } @@ -49,15 +50,7 @@ void snes_init() { * state: put SNES in reset state when 1, release when 0 */ void snes_reset(int state) { - if(state) { - DDRD |= _BV(PD6); // /RESET pin -> out - PORTD &= ~_BV(PD6); // /RESET = 0 - PORTD |= _BV(PD5); // RESET_DIR = 1; - } else { - PORTD &= ~_BV(PD5); // RESET_DIR = 0; - DDRD &= ~_BV(PD6); // /RESET pin -> in - PORTD |= _BV(PD6); // /RESET = pullup - } + BITBAND(SNES_RESET_REG->FIODIR, SNES_RESET_BIT) = state; } /* @@ -66,9 +59,7 @@ void snes_reset(int state) { * returns: 1 when reset, 0 when not reset */ uint8_t get_snes_reset() { -// DDRD &= ~_BV(PD6); // /RESET pin -> in -// PORTD &= ~_BV(PD5); // RESET_DIR (external buffer) = 0 - return !(PIND & _BV(PD6)); + return !BITBAND(SNES_RESET_REG->FIOPIN, SNES_RESET_BIT); } /* @@ -99,25 +90,23 @@ void snes_main_loop() { samecount++; } if(diffcount>=1 && samecount==5) { - uart_putc('U'); - uart_puthexshort(saveram_crc); - uart_putcrlf(); - set_busy_led(1); + printf("SaveRAM CRC: 0x%04lx; saving\n", saveram_crc); + writeled(1); save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR); - set_busy_led(0); + writeled(0); didnotsave=0; } if(didnotsave>50) { + printf("periodic save (sram contents keep changing...)\n"); diffcount=0; - uart_putc('V'); - set_busy_led(1); + writeled(1); save_sram(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR); didnotsave=0; - set_busy_led(0); + writeled(0); } saveram_crc_old = saveram_crc; } - dprintf("crc_valid=%d sram_valid=%d diffcount=%ld samecount=%ld, didnotsave=%ld\n", crc_valid, sram_valid, diffcount, samecount, didnotsave); + printf("crc_valid=%d sram_valid=%d diffcount=%ld samecount=%ld, didnotsave=%ld\n", crc_valid, sram_valid, diffcount, samecount, didnotsave); } /* @@ -129,7 +118,7 @@ uint8_t menu_main_loop() { sram_writebyte(0, SRAM_CMD_ADDR); while(!cmd) { if(!get_snes_reset()) { - while(!sram_reliable()); + while(!sram_reliable())printf("hurr\n");; cmd = sram_readbyte(SRAM_CMD_ADDR); } if(get_snes_reset()) { @@ -140,7 +129,7 @@ uint8_t menu_main_loop() { } void get_selected_name(uint8_t* fn) { - uint32_t addr = sram_readlong(SRAM_FD_ADDR); - dprintf("fd addr=%lX\n", addr); + uint32_t addr = sram_readlong(SRAM_PARAM_ADDR); + printf("fd addr=%lx\n", addr); sram_readblock(fn, addr+0x41+SRAM_MENU_ADDR, 256); } diff --git a/src/snes.h b/src/snes.h index 48be5d4..57a817b 100644 --- a/src/snes.h +++ b/src/snes.h @@ -26,6 +26,10 @@ #ifndef SNES_H #define SNES_H + +#define SNES_CMD_LOADROM (1) +#define SNES_CMD_SETRTC (2) + uint8_t crc_valid; void snes_init(void); diff --git a/src/sort.c b/src/sort.c new file mode 100644 index 0000000..358f8b3 --- /dev/null +++ b/src/sort.c @@ -0,0 +1,125 @@ + +#include +#include +#include +#include "config.h" +#include "uart.h" +#include "memory.h" +#include "sort.h" + +/* + heap sort algorithm for data located outside RAM + addr: start address of pointer table + i: index (in 32-bit elements) + heapsize: size of heap (in 32-bit elements) +*/ + +uint32_t stat_getstring = 0; +static char sort_str1[21], sort_str2[21]; +uint32_t ptrcache[QSORT_MAXELEM] IN_AHBRAM; + +/* get element from pointer table in external RAM*/ +uint32_t sort_get_elem(uint32_t base, unsigned int index) { + return sram_readlong(base+4*index); +} + +/* put element from pointer table in external RAM */ +void sort_put_elem(uint32_t base, unsigned int index, uint32_t elem) { + sram_writelong(elem, base+4*index); +} + +/* compare strings pointed to by elements of pointer table */ +int sort_cmp_idx(uint32_t base, unsigned int index1, unsigned int index2) { + uint32_t elem1, elem2; + elem1 = sort_get_elem(base, index1); + elem2 = sort_get_elem(base, index2); + return sort_cmp_elem((void*)&elem1, (void*)&elem2); +} + +int sort_cmp_elem(const void* elem1, const void* elem2) { + uint32_t el1 = *(uint32_t*)elem1; + uint32_t el2 = *(uint32_t*)elem2; + sort_getstring_for_dirent(sort_str1, el1); + sort_getstring_for_dirent(sort_str2, el2); +//sort_getlong_for_dirent(sort_long1, elem1); +//sort_getlong_for_dirent(sort_long2, elem2); +// printf("i1=%d i2=%d elem1=%lx elem2=%lx ; compare %s --- %s\n", index1, index2, elem1, elem2, sort_str1, sort_str2); + + if ((el1 & 0x80000000) && !(el2 & 0x80000000)) { + return -1; + } + + if (!(el1 & 0x80000000) && (el2 & 0x80000000)) { + return 1; + } +/* + uint16_t cmp_i; + for(cmp_i=0; cmp_i<8 && sort_long1[cmp_i] == sort_long2[cmp_i]; cmp_i++); + if(cmp_i==8) { + return 0; + } + return sort_long1[cmp_i]-sort_long2[cmp_i];*/ + return strcasecmp(sort_str1, sort_str2); +} + +/* get truncated string from database */ +void sort_getstring_for_dirent(char *ptr, uint32_t addr) { +stat_getstring++; + if(addr & 0x80000000) { + /* is directory link, name offset 6 */ + sram_readblock(ptr, addr+0x6+SRAM_MENU_ADDR, 20); + } else { + /* is file link, name offset 65 */ + sram_readblock(ptr, addr+0x41+SRAM_MENU_ADDR, 20); + } + ptr[20]=0; +} + +void sort_heapify(uint32_t addr, unsigned int i, unsigned int heapsize) +{ + while(1) + { + unsigned int l = 2*i+1; + unsigned int r = 2*i+2; + + unsigned int largest = (l < heapsize && sort_cmp_idx(addr, i, l) < 0) ? l : i; + + if(r < heapsize && sort_cmp_idx(addr, largest, r) < 0) + largest = r; + + if(largest != i) + { + uint32_t tmp = sort_get_elem(addr, i); + sort_put_elem(addr, i, sort_get_elem(addr, largest)); + sort_put_elem(addr, largest, tmp); + i = largest; + } + else break; + } +} + +void sort_dir(uint32_t addr, unsigned int size) +{ +stat_getstring=0; + if(size > QSORT_MAXELEM) { + printf("more than %d dir entries, doing slower in-place sort\n", QSORT_MAXELEM); + ext_heapsort(addr, size); + } else { + /* retrieve, sort, and store dir table */ + sram_readblock(ptrcache, addr, size*4); + qsort((void*)ptrcache, size, 4, sort_cmp_elem); + sram_writeblock(ptrcache, addr, size*4); + } +} + +void ext_heapsort(uint32_t addr, unsigned int size) { + for(unsigned int i = size/2; i > 0;) sort_heapify(addr, --i, size); + + for(unsigned int i = size-1; i>0; --i) { + uint32_t tmp = sort_get_elem(addr, 0); + sort_put_elem(addr, 0, sort_get_elem(addr, i)); + sort_put_elem(addr, i, tmp); + sort_heapify(addr, 0, i); + } +} + diff --git a/src/sort.h b/src/sort.h new file mode 100644 index 0000000..bb3ff00 --- /dev/null +++ b/src/sort.h @@ -0,0 +1,15 @@ +#ifndef _SORT_H +#define _SORT_H + +#include + +uint32_t sort_get_elem(uint32_t base, unsigned int index); +void sort_put_elem(uint32_t base, unsigned int index, uint32_t elem); +int sort_cmp_idx(uint32_t base, unsigned int index1, unsigned int index2); +int sort_cmp_elem(const void* elem1, const void* elem2); +void sort_getstring_for_dirent(char *ptr, uint32_t addr); +void sort_getlong_for_dirent(uint32_t* ptr, uint32_t addr); +void sort_heapify(uint32_t addr, unsigned int i, unsigned int heapsize); +void sort_dir(uint32_t addr, unsigned int size); +void ext_heapsort(uint32_t addr, unsigned int size); +#endif diff --git a/src/spi.c b/src/spi.c index 73f7dc9..a2194bb 100644 --- a/src/spi.c +++ b/src/spi.c @@ -28,6 +28,7 @@ #include "bits.h" #include "config.h" #include "spi.h" +#include "uart.h" #define SSP_TFE 0 // Transmit FIFO empty #define SSP_TNF 1 // Transmit FIFO not full @@ -35,10 +36,6 @@ #define SSP_RFF 3 // Receive FIFO full #define SSP_BSY 4 // Busy -// FIXME: Move to config.h! -#define SSP_CLK_DIVISOR_FAST 4 -#define SSP_CLK_DIVISOR_SLOW 250 - // #define SSP_REGS LPC_SSP1 // #define SSP_PCLKREG PCLKSEL0 /* SSP0: PCLKSEL1 @@ -54,118 +51,173 @@ SSP1: 3 */ typedef struct { - LPC_SSP_TypeDef *SSP_REGS; - LPC_GPDMA_TypeDef *SSP_DMAC; - __IO uint32_t SSP_PCLKREG; - int SSP_PCLKBIT; - int SSP_DMAID_TX; - int SSP_DMAID_RX; + LPC_SSP_TypeDef *SSP_REGS; + LPC_GPDMACH_TypeDef *SSP_DMACH; + uint32_t *SSP_PCLKREG; + int SSP_PCLKBIT; + int SSP_DMAID_TX; + int SSP_DMAID_RX; } ssp_props; static ssp_props SSP_SEL[2] = { - { LPC_SSP0, LPC_GPDMA0, PCLKSEL1, 10, 0, 1 }, /* SSP0 */ - { LPC_SSP1, LPC_GPDMA1, PCLKSEL0, 20, 2, 3 } + { LPC_SSP0, LPC_GPDMACH0, (uint32_t*)&(LPC_SC->PCLKSEL1), 10, 0, 1 }, /* SSP0 */ + { LPC_SSP1, LPC_GPDMACH1, (uint32_t*)&(LPC_SC->PCLKSEL0), 20, 2, 3 } /* SSP1 */ }; -void spi_init(spi_speed_t speed) { +void spi_preinit(int device) { + /* select interface */ + ssp_props *ssp = &(SSP_SEL[device]); + /* Set clock prescaler to 1:1 */ - BITBAND(LPC_SC->SSP_PCLKREG, SSP_PCLKBIT) = 1; + BITBAND(*(ssp->SSP_PCLKREG), ssp->SSP_PCLKBIT) = 1; +} + +void spi_init(spi_speed_t speed, int device) { + + /* select interface */ + ssp_props *ssp = &(SSP_SEL[device]); /* configure data format - 8 bits, SPI, CPOL=0, CPHA=0, 1 clock per bit */ - SSP_REGS->CR0 = (8-1); + ssp->SSP_REGS->CR0 = (8-1); /* set clock prescaler */ if (speed == SPI_SPEED_FAST) { - SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST; + ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST; + } else if (speed == SPI_SPEED_SLOW) { + ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW; + } else if (speed == SPI_SPEED_FPGA_FAST) { + ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_FAST; } else { - SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW; + ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_SLOW; } /* Enable SSP */ - SSP_REGS->CR1 = BV(1); + ssp->SSP_REGS->CR1 = BV(1); /* Enable DMA controller, little-endian mode */ BITBAND(LPC_SC->PCONP, 29) = 1; LPC_GPDMA->DMACConfig = 1; } -void spi_tx_byte(uint8_t data) { +void spi_tx_sync(int device) { + /* select interface */ + ssp_props *ssp = &(SSP_SEL[device]); + + /* Wait until TX fifo is flushed */ + while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ; +} + +void spi_tx_byte(uint8_t data, int device) { + + /* select interface */ + ssp_props *ssp = &(SSP_SEL[device]); + /* Wait until TX fifo can accept data */ - while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ; + while (!BITBAND(ssp->SSP_REGS->SR, SSP_TNF)) ; /* Send byte */ - SSP_REGS->DR = data; + ssp->SSP_REGS->DR = data; } -uint8_t spi_rx_byte(void) { +uint8_t spi_txrx_byte(uint8_t data, int device) { + /* select interface */ + ssp_props *ssp = &(SSP_SEL[device]); + /* Wait until SSP is not busy */ - while (BITBAND(SSP_REGS->SR, SSP_BSY)) ; + while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ; /* Clear RX fifo */ - while (BITBAND(SSP_REGS->SR, SSP_RNE)) - (void) SSP_REGS->DR; + while (BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) + (void) ssp->SSP_REGS->DR; /* Transmit a single dummy byte */ - SSP_REGS->DR = 0xff; + ssp->SSP_REGS->DR = data; /* Wait until answer has been received */ - while (!BITBAND(SSP_REGS->SR, SSP_RNE)) ; + while (!BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) ; - return SSP_REGS->DR; + return ssp->SSP_REGS->DR; } -void spi_tx_block(const void *ptr, unsigned int length) { +uint8_t spi_rx_byte(int device) { + + /* select interface */ + ssp_props *ssp = &(SSP_SEL[device]); + + /* Wait until SSP is not busy */ + while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ; + + /* Clear RX fifo */ + while (BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) + (void) ssp->SSP_REGS->DR; + + /* Transmit a single dummy byte */ + ssp->SSP_REGS->DR = 0xff; + + /* Wait until answer has been received */ + while (!BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) ; + + return ssp->SSP_REGS->DR; +} + +void spi_tx_block(const void *ptr, unsigned int length, int device) { const uint8_t *data = (const uint8_t *)ptr; + /* select interface */ + ssp_props *ssp = &(SSP_SEL[device]); + while (length--) { /* Wait until TX fifo can accept data */ - while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ; + while (!BITBAND(ssp->SSP_REGS->SR, SSP_TNF)) ; - SSP_REGS->DR = *data++; + ssp->SSP_REGS->DR = *data++; } } -void spi_rx_block(void *ptr, unsigned int length) { +void spi_rx_block(void *ptr, unsigned int length, int device) { uint8_t *data = (uint8_t *)ptr; unsigned int txlen = length; + /* select interface */ + ssp_props *ssp = &(SSP_SEL[device]); + /* Wait until SSP is not busy */ - while (BITBAND(SSP_REGS->SR, SSP_BSY)) ; + while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ; /* Clear RX fifo */ - while (BITBAND(SSP_REGS->SR, SSP_RNE)) - (void) SSP_REGS->DR; + while (BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) + (void) ssp->SSP_REGS->DR; if ((length & 3) != 0 || ((uint32_t)ptr & 3) != 0) { /* Odd length or unaligned buffer */ while (length > 0) { /* Wait until TX or RX FIFO are ready */ - while (txlen > 0 && !BITBAND(SSP_REGS->SR, SSP_TNF) && - !BITBAND(SSP_REGS->SR, SSP_RNE)) ; + while (txlen > 0 && !BITBAND(ssp->SSP_REGS->SR, SSP_TNF) && + !BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) ; /* Try to receive data */ - while (length > 0 && BITBAND(SSP_REGS->SR, SSP_RNE)) { - *data++ = SSP_REGS->DR; + while (length > 0 && BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) { + *data++ = ssp->SSP_REGS->DR; length--; } /* Send dummy data until TX full or RX ready */ - while (txlen > 0 && BITBAND(SSP_REGS->SR, SSP_TNF) && !BITBAND(SSP_REGS->SR, SSP_RNE)) { + while (txlen > 0 && BITBAND(ssp->SSP_REGS->SR, SSP_TNF) && !BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) { txlen--; - SSP_REGS->DR = 0xff; + ssp->SSP_REGS->DR = 0xff; } } } else { /* Clear interrupt flags of DMA channels 0 */ - LPC_GPDMA->DMACIntTCClear = BV(0); - LPC_GPDMA->DMACIntErrClr = BV(0); + LPC_GPDMA->DMACIntTCClear = BV(device); + LPC_GPDMA->DMACIntErrClr = BV(device); /* Set up RX DMA channel */ - LPC_GPDMACH0->DMACCSrcAddr = (uint32_t)&SSP_REGS->DR; - LPC_GPDMACH0->DMACCDestAddr = (uint32_t)ptr; - LPC_GPDMACH0->DMACCLLI = 0; // no linked list - LPC_GPDMACH0->DMACCControl = length + ssp->SSP_DMACH->DMACCSrcAddr = (uint32_t)&ssp->SSP_REGS->DR; + ssp->SSP_DMACH->DMACCDestAddr = (uint32_t)ptr; + ssp->SSP_DMACH->DMACCLLI = 0; // no linked list + ssp->SSP_DMACH->DMACCControl = length | (0 << 12) // source burst size 1 (FIXME: Check if larger possible/useful) | (0 << 15) // destination burst size 1 | (0 << 18) // source transfer width 1 byte @@ -173,45 +225,52 @@ void spi_rx_block(void *ptr, unsigned int length) { | (0 << 26) // source address not incremented | (1 << 27) // destination address incremented ; - LPC_GPDMACH0->DMACCConfig = 1 // enable channel - | (SSP_DMAID_RX << 1) // data source SSP RX + ssp->SSP_DMACH->DMACCConfig = 1 // enable channel + | (ssp->SSP_DMAID_RX << 1) // data source SSP RX | (2 << 11) // transfer from peripheral to memory ; /* Enable RX FIFO DMA */ - SSP_REGS->DMACR = 1; + ssp->SSP_REGS->DMACR = 1; /* Write bytes into TX FIFO */ // FIXME: Any value in doing this using DMA too? while (txlen > 0) { - while (txlen > 0 && BITBAND(SSP_REGS->SR, SSP_TNF)) { + while (txlen > 0 && BITBAND(ssp->SSP_REGS->SR, SSP_TNF)) { txlen--; - SSP_REGS->DR = 0xff; + ssp->SSP_REGS->DR = 0xff; } } /* Wait until DMA channel disables itself */ - while (LPC_GPDMACH0->DMACCConfig & 1) ; + while (ssp->SSP_DMACH->DMACCConfig & 1) ; /* Disable RX FIFO DMA */ - SSP_REGS->DMACR = 0; + ssp->SSP_REGS->DMACR = 0; } } -void spi_set_speed(spi_speed_t speed) { +void spi_set_speed(spi_speed_t speed, int device) { + /* select interface */ + ssp_props *ssp = &(SSP_SEL[device]); + /* Wait until TX fifo is empty */ - while (!BITBAND(SSP_REGS->SR, 0)) ; + while (!BITBAND(ssp->SSP_REGS->SR, 0)) ; /* Disable SSP (FIXME: Is this required?) */ - SSP_REGS->CR1 = 0; + ssp->SSP_REGS->CR1 = 0; /* Change clock divisor */ if (speed == SPI_SPEED_FAST) { - SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST; + ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST; + } else if (speed == SPI_SPEED_SLOW) { + ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW; + } else if (speed == SPI_SPEED_FPGA_FAST) { + ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_FAST; } else { - SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW; + ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_SLOW; } /* Enable SSP */ - SSP_REGS->CR1 = BV(1); + ssp->SSP_REGS->CR1 = BV(1); } diff --git a/src/spi.h b/src/spi.h index 493edd2..7bebac8 100644 --- a/src/spi.h +++ b/src/spi.h @@ -30,24 +30,33 @@ #define SPI_H /* Low speed 400kHz for init, fast speed <=20MHz (MMC limit) */ -typedef enum { SPI_SPEED_FAST, SPI_SPEED_SLOW } spi_speed_t; +typedef enum { SPI_SPEED_FAST, SPI_SPEED_SLOW, SPI_SPEED_FPGA_FAST, SPI_SPEED_FPGA_SLOW } spi_speed_t; + +/* Pre-Initialize SPI interface (PCLK divider before PLL setup) */ +void spi_preinit(int device); /* Initialize SPI interface */ -void spi_init(spi_speed_t speed); +void spi_init(spi_speed_t speed, int device); /* Transmit a single byte */ -void spi_tx_byte(uint8_t data); +void spi_tx_byte(uint8_t data, int device); + +/* Transmit a single byte and return received data */ +uint8_t spi_txrx_byte(uint8_t data, int device); /* Transmit a data block */ -void spi_tx_block(const void *data, unsigned int length); +void spi_tx_block(const void *data, unsigned int length, int device); /* Receive a single byte */ -uint8_t spi_rx_byte(void); +uint8_t spi_rx_byte(int device); /* Receive a data block */ -void spi_rx_block(void *data, unsigned int length); +void spi_rx_block(void *data, unsigned int length, int device); /* Switch speed of SPI interface */ -void spi_set_speed(spi_speed_t speed); +void spi_set_speed(spi_speed_t speed, int device); + +/* wait for SPI TX FIFO to become empty */ +void spi_tx_sync(int device); #endif