diff --git a/README.md b/README.md index 9a834fb..6daa88e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ - 8 kB on-chip buffer for general use - 25 MB/s peak transfer rate USB interface for data upload/download and debug functionality - 25 MB/s peak transfer rate SD card interface - - EEPROM, SRAM and FlashRAM save types ~~with automatic writeback to SD card~~ (_not implemented yet_) + - EEPROM, SRAM and FlashRAM save types with automatic writeback to SD card - Battery backed real time clock (RTC) - Status LED and button for general use - 64DD add-on emulation diff --git a/sw/bootloader/src/sc64.c b/sw/bootloader/src/sc64.c index b452878..aaf7683 100644 --- a/sw/bootloader/src/sc64.c +++ b/sw/bootloader/src/sc64.c @@ -19,7 +19,8 @@ typedef enum { SC64_CMD_SD_SECTOR_SET = 'I', SC64_CMD_SD_WRITE = 'S', SC64_CMD_SD_READ = 's', - SC64_CMD_DD_SD_DISK_INFO = 'D', + SC64_CMD_DD_SD_INFO = 'D', + SC64_CMD_WRITEBACK_SD_INFO = 'W', SC64_CMD_FLASH_ERASE_BLOCK = 'P', SC64_CMD_FLASH_WAIT_BUSY = 'p', } cmd_id_t; @@ -203,9 +204,17 @@ bool sc64_sd_read_sectors (uint32_t *address, uint32_t sector, uint32_t count) { return sc64_execute_cmd(SC64_CMD_SD_READ, read_args, NULL); } -bool sc64_dd_set_sd_disk_info (uint32_t *address, uint32_t length) { +bool sc64_dd_set_sd_info (uint32_t *address, uint32_t length) { uint32_t args[2] = { (uint32_t) (address), length }; - if (sc64_execute_cmd(SC64_CMD_DD_SD_DISK_INFO, args, NULL)) { + if (sc64_execute_cmd(SC64_CMD_DD_SD_INFO, args, NULL)) { + return true; + } + return false; +} + +bool sc64_writeback_set_sd_info (uint32_t *address, bool enabled) { + uint32_t args[2] = { (uint32_t) (address), (uint32_t) (enabled) }; + if (sc64_execute_cmd(SC64_CMD_WRITEBACK_SD_INFO, args, NULL)) { return true; } return false; diff --git a/sw/bootloader/src/sc64.h b/sw/bootloader/src/sc64.h index 70e5e9e..4feb948 100644 --- a/sw/bootloader/src/sc64.h +++ b/sw/bootloader/src/sc64.h @@ -117,6 +117,7 @@ bool sc64_sd_card_get_info (uint32_t *address); bool sc64_sd_write_sectors (uint32_t *address, uint32_t sector, uint32_t count); bool sc64_sd_read_sectors (uint32_t *address, uint32_t sector, uint32_t count); bool sc64_dd_set_sd_disk_info (uint32_t *address, uint32_t length); +bool sc64_writeback_set_sd_info (uint32_t *address, bool enabled); #endif diff --git a/sw/controller/app.mk b/sw/controller/app.mk index c43df45..bf5870b 100644 --- a/sw/controller/app.mk +++ b/sw/controller/app.mk @@ -20,8 +20,10 @@ SRC_FILES = \ rtc.c \ sd.c \ task.c \ + timer.c \ update.c \ - usb.c + usb.c \ + writeback.c PAD_TO = 0x08008000 include common.mk diff --git a/sw/controller/src/cfg.c b/sw/controller/src/cfg.c index 8481e84..1044bfc 100644 --- a/sw/controller/src/cfg.c +++ b/sw/controller/src/cfg.c @@ -7,6 +7,7 @@ #include "rtc.h" #include "sd.h" #include "usb.h" +#include "writeback.h" typedef enum { @@ -552,7 +553,15 @@ void cfg_process (void) { cfg_set_error(CFG_ERROR_BAD_ADDRESS); return; } - dd_set_sd_disk_info(args[0], args[1]); + dd_set_sd_info(args[0], args[1]); + break; + + case 'W': + if (cfg_translate_address(&args[0], 1024, (SDRAM | BRAM))) { + cfg_set_error(CFG_ERROR_BAD_ADDRESS); + return; + } + writeback_set_sd_info(args[0], args[1]); break; case 'p': diff --git a/sw/controller/src/dd.c b/sw/controller/src/dd.c index 43f2ef0..b3dee08 100644 --- a/sw/controller/src/dd.c +++ b/sw/controller/src/dd.c @@ -268,7 +268,7 @@ void dd_set_sd_mode (bool value) { p.sd_mode = value; } -void dd_set_sd_disk_info (uint32_t address, uint32_t length) { +void dd_set_sd_info (uint32_t address, uint32_t length) { sd_disk_info_t info; length /= sizeof(info); p.sd_current_disk = 0; @@ -313,7 +313,7 @@ void dd_init (void) { p.drive_type = DD_DRIVE_TYPE_RETAIL; p.sd_mode = false; p.sd_current_disk = 0; - dd_set_sd_disk_info(0, 0); + dd_set_sd_info(0, 0); } void dd_process (void) { diff --git a/sw/controller/src/dd.h b/sw/controller/src/dd.h index 3bddaff..df5114d 100644 --- a/sw/controller/src/dd.h +++ b/sw/controller/src/dd.h @@ -25,7 +25,7 @@ dd_disk_state_t dd_get_disk_state (void); bool dd_set_disk_state (dd_disk_state_t state); bool dd_get_sd_mode (void); void dd_set_sd_mode (bool value); -void dd_set_sd_disk_info (uint32_t address, uint32_t length); +void dd_set_sd_info (uint32_t address, uint32_t length); void dd_handle_button (void); void dd_init (void); void dd_process (void); diff --git a/sw/controller/src/gvr.c b/sw/controller/src/gvr.c index bdc836a..0dc6cf7 100644 --- a/sw/controller/src/gvr.c +++ b/sw/controller/src/gvr.c @@ -7,6 +7,7 @@ #include "rtc.h" #include "sd.h" #include "usb.h" +#include "writeback.h" void gvr_task (void) { @@ -19,6 +20,7 @@ void gvr_task (void) { isv_init(); sd_init(); usb_init(); + writeback_init(); while (1) { button_process(); @@ -29,5 +31,6 @@ void gvr_task (void) { rtc_process(); sd_process(); usb_process(); + writeback_process(); } } diff --git a/sw/controller/src/led.c b/sw/controller/src/led.c index 73178d3..ba5cd69 100644 --- a/sw/controller/src/led.c +++ b/sw/controller/src/led.c @@ -2,6 +2,7 @@ #include "hw.h" #include "led.h" #include "task.h" +#include "timer.h" #define LED_MS_PER_TICK (10) @@ -113,6 +114,8 @@ void led_blink_act (void) { } void led_task (void) { + timer_init(); + while (1) { hw_tim_setup(TIM_ID_LED, LED_MS_PER_TICK, led_task_resume); @@ -124,6 +127,8 @@ void led_task (void) { led_process_act(); } + timer_update(); + task_yield(); } } diff --git a/sw/controller/src/timer.c b/sw/controller/src/timer.c new file mode 100644 index 0000000..03d44a5 --- /dev/null +++ b/sw/controller/src/timer.c @@ -0,0 +1,30 @@ +#include "hw.h" +#include "timer.h" + + +static volatile uint32_t timer[__TIMER_ID_COUNT]; + + +uint32_t timer_get (timer_id_t id) { + return (uint32_t) (timer[id]); +} + +void timer_set (timer_id_t id, uint32_t ticks) { + hw_tim_disable_irq(TIM_ID_LED); + timer[id] = ticks; + hw_tim_enable_irq(TIM_ID_LED); +} + +void timer_init (void) { + for (timer_id_t id = 0; id < __TIMER_ID_COUNT; id++) { + timer[id] = 0; + } +} + +void timer_update (void) { + for (timer_id_t id = 0; id < __TIMER_ID_COUNT; id++) { + if (timer[id] > 0) { + timer[id] -= 1; + } + } +} diff --git a/sw/controller/src/timer.h b/sw/controller/src/timer.h new file mode 100644 index 0000000..e9f480d --- /dev/null +++ b/sw/controller/src/timer.h @@ -0,0 +1,20 @@ +#ifndef TIMER_H__ +#define TIMER_H__ + + +#include + + +typedef enum { + TIMER_ID_WRITEBACK, + __TIMER_ID_COUNT +} timer_id_t; + + +uint32_t timer_get (timer_id_t id); +void timer_set (timer_id_t id, uint32_t ticks); +void timer_init (void); +void timer_update (void); + + +#endif diff --git a/sw/controller/src/writeback.c b/sw/controller/src/writeback.c new file mode 100644 index 0000000..c21cf84 --- /dev/null +++ b/sw/controller/src/writeback.c @@ -0,0 +1,82 @@ +#include "fpga.h" +#include "sd.h" +#include "timer.h" +#include "writeback.h" +#include "led.h" + + +#define SAVE_MAX_SECTOR_COUNT (256) +#define SRAM_FLASHRAM_ADDRESS (0x03FE0000) +#define EEPROM_ADDRESS (0x05002000) +#define WRITEBACK_DELAY_TICKS (100) + + +struct process { + bool enabled; + bool pending; + uint16_t last_save_count; + uint32_t sectors[SAVE_MAX_SECTOR_COUNT]; +}; + + +static struct process p; + + +static void writeback_save_to_sd (void) { + uint32_t save_address = SRAM_FLASHRAM_ADDRESS; + if (fpga_reg_get(REG_CFG_SCR) & CFG_SCR_EEPROM_ENABLED) { + save_address = EEPROM_ADDRESS; + } + for (int i = 0; i < SAVE_MAX_SECTOR_COUNT; i++) { + uint32_t sector = p.sectors[i]; + if (sector == 0) { + break; + } + if (sd_write_sectors(save_address, sector, 1)) { + break; + } + save_address += SD_SECTOR_SIZE; + } +} + + +void writeback_set_sd_info (uint32_t address, bool enabled) { + p.enabled = enabled; + p.pending = false; + p.last_save_count = fpga_reg_get(REG_SAVE_COUNT); + if (p.enabled) { + fpga_mem_read(address, sizeof(p.sectors), (uint8_t *) (p.sectors)); + for (int i = 0; i < SAVE_MAX_SECTOR_COUNT; i++) { + p.sectors[i] = SWAP32(p.sectors[i]); + } + } else { + for (int i = 0; i < SAVE_MAX_SECTOR_COUNT; i++) { + p.sectors[i] = 0; + } + } +} + +void writeback_init (void) { + p.enabled = false; + p.pending = false; + for (int i = 0; i < SAVE_MAX_SECTOR_COUNT; i++) { + p.sectors[i] = 0; + } +} + +void writeback_process (void) { + if (p.enabled) { + uint16_t save_count = fpga_reg_get(REG_SAVE_COUNT); + if (save_count != p.last_save_count) { + p.pending = true; + timer_set(TIMER_ID_WRITEBACK, WRITEBACK_DELAY_TICKS); + p.last_save_count = save_count; + } + } + if (p.pending) { + if (timer_get(TIMER_ID_WRITEBACK) == 0) { + p.pending = false; + writeback_save_to_sd(); + } + } +} diff --git a/sw/controller/src/writeback.h b/sw/controller/src/writeback.h new file mode 100644 index 0000000..3895121 --- /dev/null +++ b/sw/controller/src/writeback.h @@ -0,0 +1,14 @@ +#ifndef WRITEBACK_H__ +#define WRITEBACK_H__ + + +#include +#include + + +void writeback_set_sd_info (uint32_t address, bool enabled); +void writeback_init (void); +void writeback_process (void); + + +#endif