diff --git a/docs/01_memory_map.md b/docs/01_memory_map.md index 0d1e0d6..ea56f85 100644 --- a/docs/01_memory_map.md +++ b/docs/01_memory_map.md @@ -7,7 +7,10 @@ - [`0x1FFF_0004`: **DATA0** and `0x1FFF_0008`: **DATA1**](#0x1fff_0004-data0-and-0x1fff_0008-data1) - [`0x1FFF_000C`: **IDENTIFIER**](#0x1fff_000c-identifier) - [`0x1FFF_0010`: **KEY**](#0x1fff_0010-key) + - [`0x1FFF_0014`: **IRQ**](#0x1fff_0014-irq) - [Command execution flow](#command-execution-flow) + - [Without interrupt](#without-interrupt) + - [With interrupt](#with-interrupt) --- @@ -51,7 +54,7 @@ This mapping is used when accessing flashcart from N64 side. | EEPROM | `0x1FFE_2000` | 2 kiB | RW | `0x0500_2000` | Block RAM | mem bus | SC64 register access is enabled | | 64DD buffer [8] | `0x1FFE_2800` | 256 bytes | RW | `0x0500_2800` | Block RAM | mem bus | SC64 register access is enabled | | FlashRAM buffer [8] | `0x1FFE_2900` | 128 bytes | R | `0x0500_2900` | Block RAM | mem bus | SC64 register access is enabled | -| SC64 registers | `0x1FFF_0000` | 20 bytes | RW | N/A | Flashcart Interface | reg bus | SC64 register access is enabled | +| SC64 registers | `0x1FFF_0000` | 24 bytes | RW | N/A | Flashcart Interface | reg bus | SC64 register access is enabled | - Note [1]: 64DD IPL share SDRAM memory space with ROM (last 4 MiB minus 128 kiB for saves). Write access is always disabled for this section. - Note [2]: SRAM and FlashRAM save types share SDRAM memory space with ROM (last 128 kiB). @@ -88,25 +91,28 @@ SC64 contains small register region used for communication between N64 and contr Protocol is command based with support for up to 256 diferrent commands and two 32-bit argument/result values per operation. Support for interrupts is provided but currently no command relies on it, 64DD IRQ is handled separately. -| name | address | size | access | usage | -| ------------------ | ------------- | ------- | ------ | ---------------------------------- | -| **STATUS/COMMAND** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status | -| **DATA0** | `0x1FFF_0004` | 4 bytes | RW | Command argument/result 0 | -| **DATA1** | `0x1FFF_0008` | 4 bytes | RW | Command argument/result 1 | -| **IDENTIFIER** | `0x1FFF_000C` | 4 bytes | RW | Flashcart identifier and IRQ clear | -| **KEY** | `0x1FFF_0010` | 4 bytes | W | SC64 register access lock/unlock | +| name | address | size | access | usage | +| ------------------ | ------------- | ------- | ------ | -------------------------------- | +| **STATUS/COMMAND** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status | +| **DATA0** | `0x1FFF_0004` | 4 bytes | RW | Command argument/result 0 | +| **DATA1** | `0x1FFF_0008` | 4 bytes | RW | Command argument/result 1 | +| **IDENTIFIER** | `0x1FFF_000C` | 4 bytes | RW | Flashcart identifier | +| **KEY** | `0x1FFF_0010` | 4 bytes | W | SC64 register access lock/unlock | +| **IRQ** | `0x1FFF_0014` | 4 bytes | W | Pending IRQ clear | --- #### `0x1FFF_0000`: **STATUS/COMMAND** -| name | bits | access | meaning | -| ------------- | ------ | ------ | ----------------------------------------------------- | -| `CMD_BUSY` | [31] | R | `1` if dispatched command is pending/executing | -| `CMD_ERROR` | [30] | R | `1` if last executed command returned with error code | -| `IRQ_PENDING` | [29] | R | `1` if flashcart has raised an interrupt | -| N/A | [28:8] | N/A | Unused, write `0` for future compatibility | -| `CMD_ID` | [7:0] | RW | Command ID to be executed | +| name | bits | access | meaning | +| ----------------- | ------ | ------ | ----------------------------------------------------------- | +| `CMD_BUSY` | [31] | R | `1` if dispatched command is pending/executing | +| `CMD_ERROR` | [30] | R | `1` if last executed command returned with error code | +| `MCU_IRQ_PENDING` | [29] | R | `1` if flashcart has raised a MCU interrupt | +| `CMD_IRQ_PENDING` | [28] | R | `1` if flashcart has raised a command finish interrupt | +| N/A | [27:9] | N/A | Unused, write `0` for future compatibility | +| `CMD_IRQ_REQUEST` | [8] | RW | Raise cart interrupt signal when command finishes execution | +| `CMD_ID` | [7:0] | RW | Command ID to be executed | Note: Write to this register raises `CMD_BUSY` bit and clears `CMD_ERROR` bit. Flashcart then will start executing provided command. @@ -124,11 +130,10 @@ Note: Result is valid only when command has executed and `CMD_BUSY` bit is reset #### `0x1FFF_000C`: **IDENTIFIER** -| name | bits | access | meaning | -| ------------ | ------ | ------ | ------------------------------------------------- | -| `IDENTIFIER` | [31:0] | RW | Flashcart identifier (ASCII `SCv2`) and IRQ clear | +| name | bits | access | meaning | +| ------------ | ------ | ------ | ----------------------------------- | +| `IDENTIFIER` | [31:0] | R | Flashcart identifier (ASCII `SCv2`) | -Note: Writing any value to this register will clear pending flashcart interrupt. --- @@ -147,8 +152,20 @@ Value `0xFFFFFFFF` will lock all SC64 registers if flashcart is in unlock state. --- +#### `0x1FFF_0014`: **IRQ** + +| name | bits | access | meaning | +| ----------- | ------ | ------ | --------------------------------------------- | +| `MCU_CLEAR` | [31] | W | Write `1` to clear a MCU interrupt | +| `CMD_CLEAR` | [30] | W | Write `1` to clear a command finish interrupt | +| N/A | [29:0] | N/A | Unused, write `0` for future compatibility | + +--- + ## Command execution flow +### Without interrupt + 1. Check if command is already executing by reading `CMD_BUSY` bit in **STATUS/COMMAND** register (optional). 2. Write command argument values to **DATA0** and **DATA1** registers, can be skipped if command doesn't require it. 3. Write command ID to **STATUS/COMMAND** register. @@ -156,3 +173,15 @@ Value `0xFFFFFFFF` will lock all SC64 registers if flashcart is in unlock state. 5. Check if `CMD_ERROR` bit in **STATUS/COMMAND** is set: - If error is set then read **DATA0** register containing error code. - If error is not set then read **DATA0** and **DATA1** registers containing command result values, can be skipped if command doesn't return any values. + +### With interrupt + +1. Check if command is already executing by reading `CMD_BUSY` bit in **STATUS/COMMAND** register (optional). +2. Write command argument values to **DATA0** and **DATA1** registers, can be skipped if command doesn't require it. +3. Write command ID to **STATUS/COMMAND** register and set `CMD_IRQ_REQUEST` bit high. +4. Wait for cart interrupt. +5. Check (in cart interrupt handler) if `CMD_IRQ_PENDING` bit in **STATUS/COMMAND** register is set high. +6. Clear interrupt by setting `CMD_CLEAR` bit high in the **IRQ** register. +7. Check if `CMD_ERROR` bit in **STATUS/COMMAND** is set: + - If error is set then read **DATA0** register containing error code. + - If error is not set then read **DATA0** and **DATA1** registers containing command result values, can be skipped if command doesn't return any values. diff --git a/fw/rtl/n64/n64_cfg.sv b/fw/rtl/n64/n64_cfg.sv index 0301250..eaa01d8 100644 --- a/fw/rtl/n64/n64_cfg.sv +++ b/fw/rtl/n64/n64_cfg.sv @@ -9,7 +9,7 @@ module n64_cfg ( output logic irq ); - typedef enum bit [3:0] { + typedef enum bit [3:0] { REG_STATUS, REG_COMMAND, REG_DATA_0_H, @@ -19,10 +19,19 @@ module n64_cfg ( REG_IDENTIFIER_H, REG_IDENTIFIER_L, REG_KEY_H, - REG_KEY_L + REG_KEY_L, + REG_IRQ_H, + REG_IRQ_L } e_reg; - logic cfg_error; + logic cmd_error; + logic cmd_irq_request; + logic cmd_irq; + logic mcu_irq; + + always_ff @(posedge clk) begin + irq <= (cmd_irq || mcu_irq); + end always_comb begin reg_bus.rdata = 16'd0; @@ -30,11 +39,12 @@ module n64_cfg ( case (reg_bus.address[4:1]) REG_STATUS: reg_bus.rdata = { n64_scb.cfg_pending, - cfg_error, - irq, - 13'd0 + cmd_error, + mcu_irq, + cmd_irq, + 12'd0 }; - REG_COMMAND: reg_bus.rdata = {8'd0, n64_scb.cfg_cmd}; + REG_COMMAND: reg_bus.rdata = {7'd0, cmd_irq_request, n64_scb.cfg_cmd}; REG_DATA_0_H: reg_bus.rdata = n64_scb.cfg_wdata[0][31:16]; REG_DATA_0_L: reg_bus.rdata = n64_scb.cfg_wdata[0][15:0]; REG_DATA_1_H: reg_bus.rdata = n64_scb.cfg_wdata[1][31:16]; @@ -43,6 +53,8 @@ module n64_cfg ( REG_IDENTIFIER_L: reg_bus.rdata = n64_scb.cfg_identifier[15:0]; REG_KEY_H: reg_bus.rdata = 16'd0; REG_KEY_L: reg_bus.rdata = 16'd0; + REG_IRQ_H: reg_bus.rdata = 16'd0; + REG_IRQ_L: reg_bus.rdata = 16'd0; endcase end end @@ -51,39 +63,71 @@ module n64_cfg ( logic lock_sequence_counter; always_ff @(posedge clk) begin - if (n64_scb.cfg_done) begin + if (n64_scb.cfg_pending && n64_scb.cfg_done) begin n64_scb.cfg_pending <= 1'b0; - cfg_error <= n64_scb.cfg_error; + cmd_irq <= cmd_irq_request; + cmd_error <= n64_scb.cfg_error; end if (n64_scb.cfg_irq) begin - irq <= 1'b1; + mcu_irq <= 1'b1; end if (unlock_flag) begin n64_scb.cfg_unlock <= 1'b1; end - if (reset || n64_scb.n64_reset || n64_scb.n64_nmi) begin + if (reset) begin n64_scb.cfg_unlock <= 1'b0; n64_scb.cfg_pending <= 1'b0; n64_scb.cfg_cmd <= 8'h00; - irq <= 1'b0; - cfg_error <= 1'b0; + cmd_error <= 1'b0; + cmd_irq_request <= 1'b0; + cmd_irq <= 1'b0; + mcu_irq <= 1'b0; + lock_sequence_counter <= 1'd0; + end else if (n64_scb.n64_reset || n64_scb.n64_nmi) begin + n64_scb.cfg_unlock <= 1'b0; + cmd_irq_request <= 1'b0; + cmd_irq <= 1'b0; + mcu_irq <= 1'b0; lock_sequence_counter <= 1'd0; end else if (n64_scb.cfg_unlock) begin if (reg_bus.write && reg_bus.address[16] && (reg_bus.address[15:5] == 11'd0)) begin case (reg_bus.address[4:1]) REG_COMMAND: begin - n64_scb.cfg_pending <= 1'b1; - n64_scb.cfg_cmd <= reg_bus.wdata[7:0]; - cfg_error <= 1'b0; + if (!n64_scb.cfg_pending) begin + n64_scb.cfg_pending <= 1'b1; + cmd_error <= 1'b0; + cmd_irq_request <= reg_bus.wdata[8]; + n64_scb.cfg_cmd <= reg_bus.wdata[7:0]; + end end - REG_DATA_0_H: n64_scb.cfg_rdata[0][31:16] <= reg_bus.wdata; - REG_DATA_0_L: n64_scb.cfg_rdata[0][15:0] <= reg_bus.wdata; - REG_DATA_1_H: n64_scb.cfg_rdata[1][31:16] <= reg_bus.wdata; - REG_DATA_1_L: n64_scb.cfg_rdata[1][15:0] <= reg_bus.wdata; - REG_IDENTIFIER_H: irq <= 1'b0; + + REG_DATA_0_H: begin + if (!n64_scb.cfg_pending) begin + n64_scb.cfg_rdata[0][31:16] <= reg_bus.wdata; + end + end + + REG_DATA_0_L: begin + if (!n64_scb.cfg_pending) begin + n64_scb.cfg_rdata[0][15:0] <= reg_bus.wdata; + end + end + + REG_DATA_1_H: begin + if (!n64_scb.cfg_pending) begin + n64_scb.cfg_rdata[1][31:16] <= reg_bus.wdata; + end + end + + REG_DATA_1_L: begin + if (!n64_scb.cfg_pending) begin + n64_scb.cfg_rdata[1][15:0] <= reg_bus.wdata; + end + end + REG_KEY_H, REG_KEY_L: begin lock_sequence_counter <= lock_sequence_counter + 1'd1; if (reg_bus.wdata != 16'hFFFF) begin @@ -93,6 +137,11 @@ module n64_cfg ( n64_scb.cfg_unlock <= (reg_bus.wdata != 16'hFFFF); end end + + REG_IRQ_H: begin + mcu_irq <= (reg_bus.wdata[15] ? 1'b0 : mcu_irq); + cmd_irq <= (reg_bus.wdata[14] ? 1'b0 : cmd_irq); + end endcase end end diff --git a/sw/controller/src/cfg.c b/sw/controller/src/cfg.c index 7dad6a5..87b72e7 100644 --- a/sw/controller/src/cfg.c +++ b/sw/controller/src/cfg.c @@ -18,6 +18,32 @@ #define DATA_BUFFER_SIZE (8192) +typedef enum { + CMD_ID_IDENTIFIER_GET = 'v', + CMD_ID_VERSION_GET = 'V', + CMD_ID_CONFIG_GET = 'c', + CMD_ID_CONFIG_SET = 'C', + CMD_ID_SETTING_GET = 'a', + CMD_ID_SETTING_SET = 'A', + CMD_ID_TIME_GET = 't', + CMD_ID_TIME_SET = 'T', + CMD_ID_USB_READ = 'm', + CMD_ID_USB_WRITE = 'M', + CMD_ID_USB_READ_STATUS = 'u', + CMD_ID_USB_WRITE_STATUS = 'U', + CMD_ID_SD_CARD_OP = 'i', + CMD_ID_SD_SECTOR_SET = 'I', + CMD_ID_SD_READ = 's', + CMD_ID_SD_WRITE = 'S', + CMD_ID_DISK_MAPPING_SET = 'D', + CMD_ID_WRITEBACK_PENDING = 'w', + CMD_ID_WRITEBACK_SD_INFO = 'W', + CMD_ID_FLASH_PROGRAM = 'K', + CMD_ID_FLASH_WAIT_BUSY = 'p', + CMD_ID_FLASH_ERASE_BLOCK = 'P', + CMD_ID_DIAGNOSTIC_GET = '%', +} cmd_id_t; + typedef enum { CFG_ID_BOOTLOADER_SWITCH = 0, CFG_ID_ROM_WRITE_ENABLE = 1, @@ -97,6 +123,9 @@ typedef enum { struct process { + bool cmd_queued; + cmd_id_t cmd; + uint32_t data[2]; boot_mode_t boot_mode; save_type_t save_type; cic_seed_t cic_seed; @@ -109,8 +138,43 @@ struct process { static struct process p; -static void cfg_set_usb_output_ready (void) { - p.usb_output_ready = true; +static bool cfg_cmd_check (void) { + if (!p.cmd_queued) { + uint32_t reg = fpga_reg_get(REG_CFG_CMD); + + if (!(reg & CFG_CMD_PENDING)) { + return true; + } + + p.cmd_queued = true; + p.cmd = (cmd_id_t) ((reg & CFG_CMD_MASK) >> CFG_CMD_BIT); + p.data[0] = fpga_reg_get(REG_CFG_DATA_0); + p.data[1] = fpga_reg_get(REG_CFG_DATA_1); + } + + return false; +} + +static void cfg_cmd_reply_success (void) { + p.cmd_queued = false; + fpga_reg_set(REG_CFG_DATA_0, p.data[0]); + fpga_reg_set(REG_CFG_DATA_1, p.data[1]); + fpga_reg_set(REG_CFG_CMD, CFG_CMD_DONE); +} + +static void cfg_cmd_reply_error (cfg_error_t error) { + p.cmd_queued = false; + fpga_reg_set(REG_CFG_DATA_0, error); + fpga_reg_set(REG_CFG_DATA_1, 0); + fpga_reg_set(REG_CFG_CMD, CFG_CMD_ERROR | CFG_CMD_DONE); +} + +static void cfg_change_scr_bits (uint32_t mask, bool value) { + if (value) { + fpga_reg_set(REG_CFG_SCR, fpga_reg_get(REG_CFG_SCR) | mask); + } else { + fpga_reg_set(REG_CFG_SCR, fpga_reg_get(REG_CFG_SCR) & (~mask)); + } } static bool cfg_translate_address (uint32_t *address, uint32_t length, translate_type_t type) { @@ -169,20 +233,6 @@ static bool cfg_translate_address (uint32_t *address, uint32_t length, translate return true; } -static void cfg_set_error (cfg_error_t error) { - fpga_reg_set(REG_CFG_DATA_0, error); - fpga_reg_set(REG_CFG_DATA_1, 0); - fpga_reg_set(REG_CFG_CMD, CFG_CMD_ERROR | CFG_CMD_DONE); -} - -static void cfg_change_scr_bits (uint32_t mask, bool value) { - if (value) { - fpga_reg_set(REG_CFG_SCR, fpga_reg_get(REG_CFG_SCR) | mask); - } else { - fpga_reg_set(REG_CFG_SCR, fpga_reg_get(REG_CFG_SCR) & (~mask)); - } -} - static bool cfg_set_save_type (save_type_t save_type) { if (save_type >= __SAVE_TYPE_COUNT) { return true; @@ -247,6 +297,10 @@ static bool cfg_read_diagnostic_data (uint32_t *args) { return false; } +static void cfg_set_usb_output_ready (void) { + p.usb_output_ready = true; +} + uint32_t cfg_get_identifier (void) { return fpga_reg_get(REG_CFG_IDENTIFIER); @@ -476,265 +530,237 @@ void cfg_reset_state (void) { void cfg_init (void) { fpga_reg_set(REG_CFG_SCR, CFG_SCR_BOOTLOADER_ENABLED); cfg_reset_state(); + p.cmd_queued = false; p.usb_output_ready = true; } void cfg_process (void) { - uint32_t reg; - uint32_t args[2]; - uint32_t prev_cfg[2]; - usb_tx_info_t packet_info; + if (cfg_cmd_check()) { + return; + } - reg = fpga_reg_get(REG_CFG_CMD); + switch (p.cmd) { + case CMD_ID_IDENTIFIER_GET: + p.data[0] = cfg_get_identifier(); + break; - if (reg & CFG_CMD_PENDING) { - args[0] = fpga_reg_get(REG_CFG_DATA_0); - args[1] = fpga_reg_get(REG_CFG_DATA_1); - char cmd = (char) ((reg & CFG_CMD_MASK) >> CFG_CMD_BIT); + case CMD_ID_VERSION_GET: + version_firmware(&p.data[0], &p.data[1]); + break; - switch (cmd) { - case 'v': - args[0] = cfg_get_identifier(); - break; - - case 'V': - version_firmware(&args[0], &args[1]); - break; - - case 'c': - if (cfg_query(args)) { - cfg_set_error(CFG_ERROR_BAD_CONFIG_ID); - return; - } - break; - - case 'C': - prev_cfg[0] = args[0]; - cfg_query(prev_cfg); - if (cfg_update(args)) { - cfg_set_error(CFG_ERROR_BAD_CONFIG_ID); - return; - } - args[1] = prev_cfg[1]; - break; - - case 'a': - if (cfg_query_setting(args)) { - cfg_set_error(CFG_ERROR_BAD_CONFIG_ID); - return; - } - break; - - case 'A': - if (cfg_update_setting(args)) { - cfg_set_error(CFG_ERROR_BAD_CONFIG_ID); - return; - } - break; - - case 't': - cfg_get_time(args); - break; - - case 'T': - cfg_set_time(args); - break; - - case 'm': - if (cfg_translate_address(&args[0], args[1], (SDRAM | BRAM))) { - cfg_set_error(CFG_ERROR_BAD_ADDRESS); - return; - } - if (!usb_prepare_read(args)) { - return; - } - break; - - case 'M': - if (cfg_translate_address(&args[0], args[1] & 0xFFFFFF, (SDRAM | BRAM))) { - cfg_set_error(CFG_ERROR_BAD_ADDRESS); - return; - } - usb_create_packet(&packet_info, PACKET_CMD_DEBUG_OUTPUT); - packet_info.data_length = 4; - packet_info.data[0] = args[1]; - packet_info.dma_length = (args[1] & 0xFFFFFF); - packet_info.dma_address = args[0]; - packet_info.done_callback = cfg_set_usb_output_ready; - if (usb_enqueue_packet(&packet_info)) { - p.usb_output_ready = false; - } else { - return; - } - break; - - case 'u': - usb_get_read_info(args); - break; - - case 'U': - args[0] = p.usb_output_ready ? 0 : (1 << 31); - break; - - case 'i': - switch (args[1]) { - case SD_CARD_OP_DEINIT: - sd_card_deinit(); - break; - case SD_CARD_OP_INIT: { - led_activity_on(); - bool error = sd_card_init(); - led_activity_off(); - if (error) { - cfg_set_error(CFG_ERROR_SD_CARD); - return; - } - break; - } - case SD_CARD_OP_GET_STATUS: - args[1] = sd_card_get_status(); - break; - case SD_CARD_OP_GET_INFO: - if (cfg_translate_address(&args[0], SD_CARD_INFO_SIZE, (SDRAM | BRAM))) { - cfg_set_error(CFG_ERROR_BAD_ADDRESS); - return; - } - if (sd_card_get_info(args[0])) { - cfg_set_error(CFG_ERROR_SD_CARD); - return; - } - break; - case SD_CARD_OP_BYTE_SWAP_ON: - if (sd_set_byte_swap(true)) { - cfg_set_error(CFG_ERROR_SD_CARD); - return; - } - break; - case SD_CARD_OP_BYTE_SWAP_OFF: - if (sd_set_byte_swap(false)) { - cfg_set_error(CFG_ERROR_SD_CARD); - return; - } - break; - default: - cfg_set_error(CFG_ERROR_BAD_ARGUMENT); - return; - } - break; - - case 'I': - p.sd_card_sector = args[0]; - break; - - case 's': { - if (args[1] >= 0x800000) { - cfg_set_error(CFG_ERROR_BAD_ARGUMENT); - return; - } - if (cfg_translate_address(&args[0], args[1] * SD_SECTOR_SIZE, (SDRAM | FLASH | BRAM))) { - cfg_set_error(CFG_ERROR_BAD_ADDRESS); - return; - } - led_activity_on(); - bool error = sd_read_sectors(args[0], p.sd_card_sector, args[1]); - led_activity_off(); - if (error) { - cfg_set_error(CFG_ERROR_SD_CARD); - return; - } - p.sd_card_sector += args[1]; - break; + case CMD_ID_CONFIG_GET: + if (cfg_query(p.data)) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_CONFIG_ID); } + break; - case 'S': { - if (args[1] >= 0x800000) { - cfg_set_error(CFG_ERROR_BAD_ARGUMENT); - return; - } - if (cfg_translate_address(&args[0], args[1] * SD_SECTOR_SIZE, (SDRAM | FLASH | BRAM))) { - cfg_set_error(CFG_ERROR_BAD_ADDRESS); - return; - } - led_activity_on(); - bool error = sd_write_sectors(args[0], p.sd_card_sector, args[1]); - led_activity_off(); - if (error) { - cfg_set_error(CFG_ERROR_SD_CARD); - return; - } - p.sd_card_sector += args[1]; - break; + case CMD_ID_CONFIG_SET: { + uint32_t prev[2] = { p.data[0], 0 }; + cfg_query(prev); + if (cfg_update(p.data)) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_CONFIG_ID); } - - case 'D': - if (cfg_translate_address(&args[0], args[1], (SDRAM | BRAM))) { - cfg_set_error(CFG_ERROR_BAD_ADDRESS); - return; - } - dd_set_disk_mapping(args[0], args[1]); - break; - - case 'w': - args[0] = writeback_pending(); - break; - - case 'W': - if (cfg_translate_address(&args[0], WRITEBACK_SECTOR_TABLE_SIZE, (SDRAM | BRAM))) { - cfg_set_error(CFG_ERROR_BAD_ADDRESS); - return; - } - writeback_load_sector_table(args[0]); - writeback_enable(WRITEBACK_SD); - break; - - case 'K': - if (args[1] >= DATA_BUFFER_SIZE) { - cfg_set_error(CFG_ERROR_BAD_ARGUMENT); - return; - } - if (cfg_translate_address(&args[0], args[1], FLASH)) { - cfg_set_error(CFG_ERROR_BAD_ADDRESS); - return; - } - if (flash_program(DATA_BUFFER_ADDRESS, args[0], args[1])) { - cfg_set_error(CFG_ERROR_BAD_ARGUMENT); - return; - } - break; - - case 'p': - if (args[0]) { - flash_wait_busy(); - } - args[0] = FLASH_ERASE_BLOCK_SIZE; - break; - - case 'P': - if (cfg_translate_address(&args[0], FLASH_ERASE_BLOCK_SIZE, FLASH)) { - cfg_set_error(CFG_ERROR_BAD_ADDRESS); - return; - } - if (flash_erase_block(args[0])) { - cfg_set_error(CFG_ERROR_BAD_ARGUMENT); - return; - } - break; - - case '%': - if (cfg_read_diagnostic_data(args)) { - cfg_set_error(CFG_ERROR_BAD_CONFIG_ID); - return; - } - break; - - default: - cfg_set_error(CFG_ERROR_UNKNOWN_CMD); - return; + p.data[1] = prev[1]; + break; } - fpga_reg_set(REG_CFG_DATA_0, args[0]); - fpga_reg_set(REG_CFG_DATA_1, args[1]); - fpga_reg_set(REG_CFG_CMD, CFG_CMD_DONE); + case CMD_ID_SETTING_GET: + if (cfg_query_setting(p.data)) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_CONFIG_ID); + } + break; + + case CMD_ID_SETTING_SET: + if (cfg_update_setting(p.data)) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_CONFIG_ID); + } + break; + + case CMD_ID_TIME_GET: + cfg_get_time(p.data); + break; + + case CMD_ID_TIME_SET: + cfg_set_time(p.data); + break; + + case CMD_ID_USB_READ: + if (cfg_translate_address(&p.data[0], p.data[1], (SDRAM | BRAM))) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ADDRESS); + } + if (!usb_prepare_read(p.data)) { + return; + } + break; + + case CMD_ID_USB_WRITE: { + if (cfg_translate_address(&p.data[0], p.data[1] & 0xFFFFFF, (SDRAM | BRAM))) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ADDRESS); + } + usb_tx_info_t packet_info; + usb_create_packet(&packet_info, PACKET_CMD_DEBUG_OUTPUT); + packet_info.data_length = 4; + packet_info.data[0] = p.data[1]; + packet_info.dma_length = (p.data[1] & 0xFFFFFF); + packet_info.dma_address = p.data[0]; + packet_info.done_callback = cfg_set_usb_output_ready; + if (usb_enqueue_packet(&packet_info)) { + p.usb_output_ready = false; + } else { + return; + } + break; + } + + case CMD_ID_USB_READ_STATUS: + usb_get_read_info(p.data); + break; + + case CMD_ID_USB_WRITE_STATUS: + p.data[0] = p.usb_output_ready ? 0 : (1 << 31); + break; + + case CMD_ID_SD_CARD_OP: + switch (p.data[1]) { + case SD_CARD_OP_DEINIT: + sd_card_deinit(); + break; + + case SD_CARD_OP_INIT: { + led_activity_on(); + bool error = sd_card_init(); + led_activity_off(); + if (error) { + return cfg_cmd_reply_error(CFG_ERROR_SD_CARD); + } + break; + } + + case SD_CARD_OP_GET_STATUS: + p.data[1] = sd_card_get_status(); + break; + + case SD_CARD_OP_GET_INFO: + if (cfg_translate_address(&p.data[0], SD_CARD_INFO_SIZE, (SDRAM | BRAM))) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ADDRESS); + } + if (sd_card_get_info(p.data[0])) { + return cfg_cmd_reply_error(CFG_ERROR_SD_CARD); + } + break; + + case SD_CARD_OP_BYTE_SWAP_ON: + if (sd_set_byte_swap(true)) { + return cfg_cmd_reply_error(CFG_ERROR_SD_CARD); + } + break; + + case SD_CARD_OP_BYTE_SWAP_OFF: + if (sd_set_byte_swap(false)) { + return cfg_cmd_reply_error(CFG_ERROR_SD_CARD); + } + break; + + default: + return cfg_cmd_reply_error(CFG_ERROR_BAD_ARGUMENT); + } + break; + + case CMD_ID_SD_SECTOR_SET: + p.sd_card_sector = p.data[0]; + break; + + case CMD_ID_SD_READ: { + if (p.data[1] >= 0x800000) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ARGUMENT); + } + if (cfg_translate_address(&p.data[0], p.data[1] * SD_SECTOR_SIZE, (SDRAM | FLASH | BRAM))) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ADDRESS); + } + led_activity_on(); + bool error = sd_read_sectors(p.data[0], p.sd_card_sector, p.data[1]); + led_activity_off(); + if (error) { + return cfg_cmd_reply_error(CFG_ERROR_SD_CARD); + } + p.sd_card_sector += p.data[1]; + break; + } + + case CMD_ID_SD_WRITE: { + if (p.data[1] >= 0x800000) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ARGUMENT); + } + if (cfg_translate_address(&p.data[0], p.data[1] * SD_SECTOR_SIZE, (SDRAM | FLASH | BRAM))) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ADDRESS); + } + led_activity_on(); + bool error = sd_write_sectors(p.data[0], p.sd_card_sector, p.data[1]); + led_activity_off(); + if (error) { + return cfg_cmd_reply_error(CFG_ERROR_SD_CARD); + } + p.sd_card_sector += p.data[1]; + break; + } + + case CMD_ID_DISK_MAPPING_SET: + if (cfg_translate_address(&p.data[0], p.data[1], (SDRAM | BRAM))) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ADDRESS); + } + dd_set_disk_mapping(p.data[0], p.data[1]); + break; + + case CMD_ID_WRITEBACK_PENDING: + p.data[0] = writeback_pending(); + break; + + case CMD_ID_WRITEBACK_SD_INFO: + if (cfg_translate_address(&p.data[0], WRITEBACK_SECTOR_TABLE_SIZE, (SDRAM | BRAM))) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ADDRESS); + } + writeback_load_sector_table(p.data[0]); + writeback_enable(WRITEBACK_SD); + break; + + case CMD_ID_FLASH_PROGRAM: + if (p.data[1] >= DATA_BUFFER_SIZE) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ARGUMENT); + } + if (cfg_translate_address(&p.data[0], p.data[1], FLASH)) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ADDRESS); + } + if (flash_program(DATA_BUFFER_ADDRESS, p.data[0], p.data[1])) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ARGUMENT); + } + break; + + case CMD_ID_FLASH_WAIT_BUSY: + if (p.data[0]) { + flash_wait_busy(); + } + p.data[0] = FLASH_ERASE_BLOCK_SIZE; + break; + + case CMD_ID_FLASH_ERASE_BLOCK: + if (cfg_translate_address(&p.data[0], FLASH_ERASE_BLOCK_SIZE, FLASH)) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ADDRESS); + } + if (flash_erase_block(p.data[0])) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_ARGUMENT); + } + break; + + case CMD_ID_DIAGNOSTIC_GET: + if (cfg_read_diagnostic_data(p.data)) { + return cfg_cmd_reply_error(CFG_ERROR_BAD_CONFIG_ID); + } + break; + + default: + return cfg_cmd_reply_error(CFG_ERROR_UNKNOWN_CMD); } + + cfg_cmd_reply_success(); }