From 12b260e200ef1a0e44545d6c26ba8b2473b48bab Mon Sep 17 00:00:00 2001 From: holub Date: Wed, 1 Dec 2021 18:49:48 -0500 Subject: [PATCH] spi_sdcard: add CMD18 - CMD_READ_MULTIPLE_BLOCK (#8913) * spi_sdcard: add CMD18 - CMD_READ_MULTIPLE_BLOCK and clean up state changes --- .gitignore | 1 + src/devices/machine/spi_sdcard.cpp | 436 +++++++++++++++++------------ src/devices/machine/spi_sdcard.h | 29 +- 3 files changed, 280 insertions(+), 186 deletions(-) diff --git a/.gitignore b/.gitignore index 4cd57d9f2bf..f5231d17f98 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ !/*.bdf !/LICENSE /.idea +.vscode docs/build docs/source/_build regtests/chdman/temp diff --git a/src/devices/machine/spi_sdcard.cpp b/src/devices/machine/spi_sdcard.cpp index 3ace0591742..588727d83d4 100644 --- a/src/devices/machine/spi_sdcard.cpp +++ b/src/devices/machine/spi_sdcard.cpp @@ -18,6 +18,8 @@ Refrences: https://www.sdcard.org/downloads/pls/ (Physical Layer Simplified Specification) + REF: tags are refering to the spec form above. 'Physical Layer Simplified Specification v8.00' + http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf https://embdev.net/attachment/39390/TOSHIBA_SD_Card_Specification.pdf http://elm-chan.org/docs/mmc/mmc_e.html @@ -46,9 +48,12 @@ spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type device_t(mconfig, type, tag, owner, clock), write_miso(*this), m_image(*this, "image"), + m_state(SD_STATE_IDLE), m_harddisk(nullptr), - m_in_latch(0), m_out_latch(0), m_cmd_ptr(0), m_state(0), m_out_ptr(0), m_out_count(0), m_ss(0), m_in_bit(0), - m_cur_bit(0), m_write_ptr(0), m_blksize(512), m_bACMD(false) + m_ss(0), m_in_bit(0), + m_in_latch(0), m_out_latch(0xff), m_cur_bit(0), + m_out_count(0), m_out_ptr(0), m_write_ptr(0), m_blksize(512), m_blknext(0), + m_bACMD(false) { } @@ -64,13 +69,15 @@ spi_sdcard_sdhc_device::spi_sdcard_sdhc_device(const machine_config &mconfig, co m_type = SD_TYPE_HC; } +ALLOW_SAVE_TYPE(spi_sdcard_device::sd_state); +ALLOW_SAVE_TYPE(spi_sdcard_device::sd_type); + void spi_sdcard_device::device_start() { write_miso.resolve_safe(); + save_item(NAME(m_state)); save_item(NAME(m_in_latch)); save_item(NAME(m_out_latch)); - save_item(NAME(m_cmd_ptr)); - save_item(NAME(m_state)); save_item(NAME(m_out_ptr)); save_item(NAME(m_out_count)); save_item(NAME(m_ss)); @@ -78,6 +85,7 @@ void spi_sdcard_device::device_start() save_item(NAME(m_cur_bit)); save_item(NAME(m_write_ptr)); save_item(NAME(m_blksize)); + save_item(NAME(m_blknext)); save_item(NAME(m_type)); save_item(NAME(m_cmd)); save_item(NAME(m_data)); @@ -94,10 +102,11 @@ void spi_sdcard_device::device_add_mconfig(machine_config &config) HARDDISK(config, m_image).set_interface("spi_sdcard"); } -void spi_sdcard_device::send_data(int count) +void spi_sdcard_device::send_data(u16 count, sd_state new_state) { m_out_ptr = 0; m_out_count = count; + change_state(new_state); } void spi_sdcard_device::spi_clock_w(int state) @@ -121,56 +130,73 @@ void spi_sdcard_device::spi_clock_w(int state) if (m_cur_bit == 8) { LOGMASKED(LOG_SPI, "SDCARD: got %02x\n", m_in_latch); + for (u8 i = 0; i < 5; i++) + { + m_cmd[i] = m_cmd[i + 1]; + } + m_cmd[5] = m_in_latch; switch (m_state) { - case SD_STATE_IDLE: - for (int i = 0; i < 5; i++) + case SD_STATE_IDLE: + do_command(); + break; + + case SD_STATE_WRITE_WAITFE: + if (m_in_latch == 0xfe) + { + m_state = SD_STATE_WRITE_DATA; + m_out_latch = 0xff; + m_write_ptr = 0; + change_state(SD_STATE_RCV); + } + break; + + case SD_STATE_WRITE_DATA: + m_data[m_write_ptr++] = m_in_latch; + if (m_write_ptr == (m_blksize + 2)) + { + u32 blk = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]); + if (m_type == SD_TYPE_V2) { - m_cmd[i] = m_cmd[i + 1]; + blk /= m_blksize; } - m_cmd[5] = m_in_latch; - if ((((m_cmd[0] & 0xc0) == 0x40) && (m_cmd[5] & 1)) && (m_out_count == 0)) + LOGMASKED(LOG_GENERAL, "writing LBA %x, data %02x %02x %02x %02x\n", blk, m_data[0], m_data[1], m_data[2], m_data[3]); + if (hard_disk_write(m_harddisk, blk, &m_data[0])) { - do_command(); + m_data[0] = DATA_RESPONSE_OK; } - break; - - case SD_STATE_WRITE_WAITFE: - if (m_in_latch == 0xfe) + else { - m_state = SD_STATE_WRITE_DATA; - m_out_latch = 0xff; - m_write_ptr = 0; + m_data[0] = DATA_RESPONSE_IO_ERROR; } - break; + m_data[1] = 0x01; - case SD_STATE_WRITE_DATA: - m_data[m_write_ptr++] = m_in_latch; - if (m_write_ptr == (m_blksize + 2)) - { - u32 blk = (m_cmd[1] << 24) | (m_cmd[2] << 16) | (m_cmd[3] << 8) | m_cmd[4]; - if (m_type == SD_TYPE_V2) - { - blk /= m_blksize; - } + send_data(2, SD_STATE_IDLE); + } + break; - LOGMASKED(LOG_GENERAL, "writing LBA %x, data %02x %02x %02x %02x\n", blk, m_data[0], m_data[1], m_data[2], m_data[3]); - if (hard_disk_write(m_harddisk, blk, &m_data[0])) - { - m_data[0] = DATA_RESPONSE_OK; - } - else - { - m_data[0] = DATA_RESPONSE_IO_ERROR; - } - m_data[1] = 0x01; + case SD_STATE_DATA_MULTI: + do_command(); + if (m_state == SD_STATE_DATA_MULTI && m_out_count == 0) + { + m_data[0] = 0xfe; // data token + hard_disk_read(m_harddisk, m_blknext++, &m_data[1]); + util::crc16_t crc16 = util::crc16_creator::simple( + &m_data[1], m_blksize); + m_data[m_blksize + 1] = (crc16 >> 8) & 0xff; + m_data[m_blksize + 2] = (crc16 & 0xff); + send_data(1 + m_blksize + 2, SD_STATE_DATA_MULTI); + } + break; - m_state = SD_STATE_IDLE; - send_data(2); - } - break; + default: + if (((m_cmd[0] & 0x70) == 0x40) || (m_out_count == 0)) // CMD0 - GO_IDLE_STATE + { + do_command(); + } + break; } } } @@ -178,12 +204,11 @@ void spi_sdcard_device::spi_clock_w(int state) { m_in_latch <<= 1; m_out_latch <<= 1; - LOGMASKED(LOG_SPI, "\tsdcard: S %02x %02x (%d)\n", m_in_latch, m_out_latch, m_cur_bit); - if (m_cur_bit == 8) - { - m_cur_bit = 0; - } + m_out_latch |= 1; + LOGMASKED(LOG_SPI, "\tsdcard: S %02x %02x (%d)\n", m_in_latch, + m_out_latch, m_cur_bit); + m_cur_bit &= 0x07; if (m_cur_bit == 0) { if (m_out_count > 0) @@ -193,163 +218,214 @@ void spi_sdcard_device::spi_clock_w(int state) m_out_count--; } } - - write_miso(BIT(m_out_latch, 7) ? ASSERT_LINE : CLEAR_LINE); + write_miso(BIT(m_out_latch, 7)); } } } void spi_sdcard_device::do_command() { - LOGMASKED(LOG_COMMAND, "SDCARD: cmd %02d %02x %02x %02x %02x %02x\n", m_cmd[0] & 0x3f, m_cmd[1], m_cmd[2], m_cmd[3], m_cmd[4], m_cmd[5]); - switch (m_cmd[0] & 0x3f) + if (((m_cmd[0] & 0xc0) == 0x40) && (m_cmd[5] & 1)) { - case 0: // CMD0 - GO_IDLE_STATE - if (m_harddisk) + LOGMASKED(LOG_COMMAND, "SDCARD: cmd %02d %02x %02x %02x %02x %02x\n", m_cmd[0] & 0x3f, m_cmd[1], m_cmd[2], m_cmd[3], m_cmd[4], m_cmd[5]); + bool clean_cmd = true; + switch (m_cmd[0] & 0x3f) { - m_data[0] = 0x01; - } - else - { - m_data[0] = 0x00; - } - send_data(1); - break; + case 0: // CMD0 - GO_IDLE_STATE + if (m_harddisk) + { + m_data[0] = 0x01; + send_data(1, SD_STATE_IDLE); + } + else + { + m_data[0] = 0x00; + send_data(1, SD_STATE_INA); + } + break; - case 8: // CMD8 - SEND_IF_COND (SD v2 only) - m_data[0] = 0x01; - m_data[1] = 0; - m_data[2] = 0; - m_data[3] = 0; - m_data[4] = 0xaa; - send_data(5); - break; + case 8: // CMD8 - SEND_IF_COND (SD v2 only) + m_data[0] = 0; // 0x01; + m_data[1] = m_cmd[1]; // 0; + m_data[2] = m_cmd[2]; // 0; + m_data[3] = m_cmd[3]; // 0x01; + m_data[4] = m_cmd[4]; // 0xaa; + send_data(5, SD_STATE_IDLE); + break; - case 10: // CMD10 - SEND_CID - m_data[0] = 0x01; // initial R1 response - m_data[1] = 0x00; // throwaway byte before data transfer - m_data[2] = 0xfe; // data token - m_data[3] = 'M'; // Manufacturer ID - we'll use M for MAME - m_data[4] = 'M'; // OEM ID - MD for MAMEdev - m_data[5] = 'D'; - m_data[6] = 'M'; // Product Name - "MCARD" - m_data[7] = 'C'; - m_data[8] = 'A'; - m_data[9] = 'R'; - m_data[10] = 'D'; - m_data[11] = 0x10; // Product Revision in BCD (1.0) - { - u32 uSerial = 0x12345678; - m_data[12] = (uSerial>>24) & 0xff; // PSN - Product Serial Number - m_data[13] = (uSerial>>16) & 0xff; - m_data[14] = (uSerial>>8) & 0xff; - m_data[15] = (uSerial & 0xff); - } - m_data[16] = 0x01; // MDT - Manufacturing Date - m_data[17] = 0x59; // 0x15 9 = 2021, September - m_data[18] = 0x00; // CRC7, bit 0 is always 0 - { - util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], 16); - m_data[19] = (crc16 >> 8) & 0xff; - m_data[20] = (crc16 & 0xff); - } - send_data(3 + 16 + 2); - break; - - case 16: // CMD16 - SET_BLOCKLEN - m_blksize = (m_cmd[3] << 8) | m_cmd[4]; - if (hard_disk_set_block_size(m_harddisk, m_blksize)) - { - m_data[0] = 0; - } - else - { - m_data[0] = 0xff; // indicate an error - // if false was returned, it means the hard disk is a CHD file, and we can't resize the - // blocks on CHD files. - logerror("spi_sdcard: Couldn't change block size to %d, wrong CHD file?", m_blksize); - } - send_data(1); - break; - - case 17: // CMD17 - READ_SINGLE_BLOCK - if (m_harddisk) - { - m_data[0] = 0x00; // initial R1 response - // data token occurs some time after the R1 response. A2SD expects at least 1 - // byte of space between R1 and the data packet. + case 10: // CMD10 - SEND_CID + m_data[0] = 0x01; // initial R1 response + m_data[1] = 0x00; // throwaway byte before data transfer m_data[2] = 0xfe; // data token - u32 blk = (m_cmd[1] << 24) | (m_cmd[2] << 16) | (m_cmd[3] << 8) | m_cmd[4]; - if (m_type == SD_TYPE_V2) + m_data[3] = 'M'; // Manufacturer ID - we'll use M for MAME + m_data[4] = 'M'; // OEM ID - MD for MAMEdev + m_data[5] = 'D'; + m_data[6] = 'M'; // Product Name - "MCARD" + m_data[7] = 'C'; + m_data[8] = 'A'; + m_data[9] = 'R'; + m_data[10] = 'D'; + m_data[11] = 0x10; // Product Revision in BCD (1.0) { - blk /= m_blksize; + u32 uSerial = 0x12345678; + m_data[12] = (uSerial >> 24) & 0xff; // PSN - Product Serial Number + m_data[13] = (uSerial >> 16) & 0xff; + m_data[14] = (uSerial >> 8) & 0xff; + m_data[15] = (uSerial & 0xff); } - LOGMASKED(LOG_GENERAL, "reading LBA %x\n", blk); - hard_disk_read(m_harddisk, blk, &m_data[3]); + m_data[16] = 0x01; // MDT - Manufacturing Date + m_data[17] = 0x59; // 0x15 9 = 2021, September + m_data[18] = 0x00; // CRC7, bit 0 is always 0 { - util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_blksize); - m_data[m_blksize + 3] = (crc16 >> 8) & 0xff; - m_data[m_blksize + 4] = (crc16 & 0xff); + util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], 16); + m_data[19] = (crc16 >> 8) & 0xff; + m_data[20] = (crc16 & 0xff); } - send_data(3 + m_blksize + 2); - } - else - { - m_data[0] = 0xff; // show an error - send_data(1); - } - break; + send_data(3 + 16 + 2, SD_STATE_STBY); + break; - case 24: // CMD24 - WRITE_BLOCK - m_data[0] = 0; - send_data(1); - m_state = SD_STATE_WRITE_WAITFE; - break; - - case 41: - if (m_bACMD) // ACMD41 - SD_SEND_OP_COND - { + case 12: // CMD12 - STOP_TRANSMISSION m_data[0] = 0; - } - else // CMD41 - illegal - { - m_data[0] = 0xff; - } - send_data(1); - break; + send_data(1, + m_state == SD_STATE_RCV ? SD_STATE_PRG : SD_STATE_TRAN); + break; - case 55: // CMD55 - APP_CMD - m_data[0] = 0x01; - send_data(1); - break; + case 16: // CMD16 - SET_BLOCKLEN + m_blksize = (u16(m_cmd[3]) << 8) | u16(m_cmd[4]); + if (hard_disk_set_block_size(m_harddisk, m_blksize)) + { + m_data[0] = 0; + } + else + { + m_data[0] = 0xff; // indicate an error + // if false was returned, it means the hard disk is a CHD file, and we can't resize the + // blocks on CHD files. + logerror("spi_sdcard: Couldn't change block size to %d, wrong " + "CHD file?", + m_blksize); + } + send_data(1, SD_STATE_TRAN); + break; - case 58: // CMD58 - READ_OCR - m_data[0] = 0; - if (m_type == SD_TYPE_HC) + case 17: // CMD17 - READ_SINGLE_BLOCK + if (m_harddisk) + { + m_data[0] = 0x00; // initial R1 response + // data token occurs some time after the R1 response. A2SD expects at least 1 + // byte of space between R1 and the data packet. + m_data[1] = 0xff; + m_data[2] = 0xfe; // data token + u32 blk = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]); + if (m_type == SD_TYPE_V2) + { + blk /= m_blksize; + } + LOGMASKED(LOG_GENERAL, "reading LBA %x\n", blk); + hard_disk_read(m_harddisk, blk, &m_data[3]); + { + util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_blksize); + m_data[m_blksize + 3] = (crc16 >> 8) & 0xff; + m_data[m_blksize + 4] = (crc16 & 0xff); + } + send_data(3 + m_blksize + 2, SD_STATE_DATA); + } + else + { + m_data[0] = 0xff; // show an error + send_data(1, SD_STATE_DATA); + } + break; + + case 18: // CMD18 - CMD_READ_MULTIPLE_BLOCK + if (m_harddisk) + { + m_data[0] = 0x00; // initial R1 response + // data token occurs some time after the R1 response. A2SD + // expects at least 1 byte of space between R1 and the data + // packet. + m_blknext = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]); + if (m_type == SD_TYPE_V2) + { + m_blknext /= m_blksize; + } + } + else + { + m_data[0] = 0xff; // show an error + } + send_data(1, SD_STATE_DATA_MULTI); + break; + + case 24: // CMD24 - WRITE_BLOCK + m_data[0] = 0; + send_data(1, SD_STATE_RCV); + break; + + case 41: + if (m_bACMD) // ACMD41 - SD_SEND_OP_COND + { + m_data[0] = 0; + send_data(1, SD_STATE_READY); // + SD_STATE_IDLE + } + else // CMD41 - illegal + { + m_data[0] = 0xff; + send_data(1, SD_STATE_INA); + } + break; + + case 55: // CMD55 - APP_CMD + m_data[0] = 0x01; + send_data(1, SD_STATE_IDLE); + break; + + case 58: // CMD58 - READ_OCR + m_data[0] = 0; + if (m_type == SD_TYPE_HC) + { + m_data[1] = 0x40; // indicate SDHC support + } + else + { + m_data[1] = 0; + } + m_data[2] = 0; + m_data[3] = 0; + m_data[4] = 0; + send_data(5, SD_STATE_DATA); + break; + + default: + LOGMASKED(LOG_COMMAND, "SDCARD: Unsupported %02x\n", m_cmd[0] & 0x3f); + clean_cmd = false; + break; + } + + // if this is command 55, that's a prefix indicating the next command is an "app command" or "ACMD" + if ((m_cmd[0] & 0x3f) == 55) { - m_data[1] = 0x40; // indicate SDHC support + m_bACMD = true; } else { - m_data[1] = 0; + m_bACMD = false; } - m_data[2] = 0; - m_data[3] = 0; - m_data[4] = 0; - send_data(5); - break; - default: - break; - } - - // if this is command 55, that's a prefix indicating the next command is an "app command" or "ACMD" - if ((m_cmd[0] & 0x3f) == 55) - { - m_bACMD = true; - } - else - { - m_bACMD = false; + if (clean_cmd) + { + for (u8 i = 0; i < 6; i++) + { + m_cmd[i] = 0xff; + } + } } } + +void spi_sdcard_device::change_state(sd_state new_state) +{ + // TODO validate if transition is valid using refs below. + // REF Figure 4-13:SD Memory Card State Diagram (Transition Mode) + // REF Table 4-35:Card State Transition Table + m_state = new_state; +} diff --git a/src/devices/machine/spi_sdcard.h b/src/devices/machine/spi_sdcard.h index 23a292664d2..544c884510f 100644 --- a/src/devices/machine/spi_sdcard.h +++ b/src/devices/machine/spi_sdcard.h @@ -21,7 +21,7 @@ public: devcb_write_line write_miso; protected: - enum + enum sd_type : u8 { SD_TYPE_V2 = 0, SD_TYPE_HC @@ -35,24 +35,41 @@ protected: required_device m_image; - int m_type; + sd_type m_type; private: - enum + enum sd_state : u8 { + //REF Table 4-1:Overview of Card States vs. Operation Mode SD_STATE_IDLE = 0, + SD_STATE_READY, + SD_STATE_IDENT, + SD_STATE_STBY, + SD_STATE_TRAN, + SD_STATE_DATA, + SD_STATE_DATA_MULTI, // synthetical state for this implementation + SD_STATE_RCV, + SD_STATE_PRG, + SD_STATE_DIS, + SD_STATE_INA, + + //FIXME Existing states wich must be revisited SD_STATE_WRITE_WAITFE, SD_STATE_WRITE_DATA }; + sd_state m_state; - void send_data(int count); + void send_data(u16 count, sd_state new_state); void do_command(); + void change_state(sd_state new_state); u8 m_data[520], m_cmd[6]; hard_disk_file *m_harddisk; - u8 m_in_latch, m_out_latch; - int m_cmd_ptr, m_state, m_out_ptr, m_out_count, m_ss, m_in_bit, m_cur_bit, m_write_ptr, m_blksize; + int m_ss, m_in_bit; + u8 m_in_latch, m_out_latch, m_cur_bit; + u16 m_out_count, m_out_ptr, m_write_ptr, m_blksize; + u32 m_blknext; bool m_bACMD; };