diff --git a/sim/README.md b/sim/README.md index 1eea382..14fe409 100644 --- a/sim/README.md +++ b/sim/README.md @@ -31,6 +31,19 @@ card are probably sufficient: $ dd if=/dev/sdb of=sd16g.img bs=1024 count=10240 ``` +## sdc_tb + +The [SD card testbench](sdc_tb) is a low level testbench that was +created to implement a 4 bit SD card driver with read and write +capabilities. It is based in the +[FPGA-SDcard-Reader](https://github.com/WangXuan95/FPGA-SDcard-Reader) +project. This testbench includes a verilator/c++ implementation of an +SD card. + +This testbench comes with an [Arduino sketch](sdc_tb/sdtest) that was +used on a ESP8266 to test and learn about the 4 bit SD card mode with +a real SD card connected to the ESP8266 microcontroller. + ## flash_tb [Flash_tb](flash_tb) simulates interfacing to the SPI flash of the Tang Nano diff --git a/sim/floppy_tb/Makefile b/sim/floppy_tb/Makefile index f87befa..7f6d86a 100644 --- a/sim/floppy_tb/Makefile +++ b/sim/floppy_tb/Makefile @@ -10,7 +10,7 @@ OBJ_DIR=obj_dir VERILATOR_DIR=/usr/local/share/verilator/include VERILATOR_FILES=verilated.cpp verilated_vcd_c.cpp verilated_threads.cpp -HDL_FILES = floppy_tb.v sd_fake.v ../../src/misc/sd_card.v ../../src/misc/sdcmd_ctrl.v ../../src/misc/sd_reader.v ../../src/fdc1772/fdc1772.v ../../src/fdc1772/floppy.v +HDL_FILES = floppy_tb.v ../../src/misc/sd_card.v ../../src/misc/sdcmd_ctrl.v ../../src/misc/sd_rw.v ../../src/fdc1772/fdc1772.v ../../src/fdc1772/floppy.v FATFS=../../../../firmware/bouffalo_sdk/components/fs/fatfs diff --git a/sim/floppy_tb/floppy_tb.cpp b/sim/floppy_tb/floppy_tb.cpp index 5b22a8f..fd7790b 100644 --- a/sim/floppy_tb/floppy_tb.cpp +++ b/sim/floppy_tb/floppy_tb.cpp @@ -18,6 +18,90 @@ static double simulation_time; #define TICKLEN (1.0/64000000) +// Calculate CRC7 +// It's a 7 bit CRC with polynomial x^7 + x^3 + 1 +// input: +// crcIn - the CRC before (0 for first step) +// data - byte for CRC calculation +// return: the new CRC7 +uint8_t CRC7_one(uint8_t crcIn, uint8_t data) { + const uint8_t g = 0x89; + uint8_t i; + + crcIn ^= data; + for (i = 0; i < 8; i++) { + if (crcIn & 0x80) crcIn ^= g; + crcIn <<= 1; + } + + return crcIn; +} + +// Calculate CRC16 CCITT +// It's a 16 bit CRC with polynomial x^16 + x^12 + x^5 + 1 +// input: +// crcIn - the CRC before (0 for rist step) +// data - byte for CRC calculation +// return: the CRC16 value +uint16_t CRC16_one(uint16_t crcIn, uint8_t data) { + crcIn = (uint8_t)(crcIn >> 8)|(crcIn << 8); + crcIn ^= data; + crcIn ^= (uint8_t)(crcIn & 0xff) >> 4; + crcIn ^= (crcIn << 8) << 4; + crcIn ^= ((crcIn & 0xff) << 4) << 1; + + return crcIn; +} + +uint8_t getCRC(unsigned char cmd, unsigned long arg) { + uint8_t CRC = CRC7_one(0, cmd); + for (int i=0; i<4; i++) CRC = CRC7_one(CRC, ((unsigned char*)(&arg))[3-i]); + return CRC; +} + +uint8_t getCRC_bytes(unsigned char *data, int len) { + uint8_t CRC = 0; + while(len--) CRC = CRC7_one(CRC, *data++); + return CRC; +} + +void hexdump(void *data, int size) { + int i, b2c; + int n=0; + char *ptr = (char*)data; + + if(!size) return; + + while(size>0) { + printf("%04x: ", n); + + b2c = (size>16)?16:size; + for(i=0;iclk = c; tb->eval(); - - if(simulation_time == 0) - ticks = GetTickCountMs(); - // check for floppy data request - if(!fd) { - fd = fopen("sd.img", "rb"); - if(!fd) { perror("OPEN ERROR"); exit(-1); } - fseek(fd, 0, SEEK_END); - flen = ftello(fd); - printf("Image size is %lld\n", flen); - fseek(fd, 0, SEEK_SET); + static int last_sdclk = -1; + + if(tb->sdclk != last_sdclk) { + // rising sd card clock edge + if(tb->sdclk) { + + // write postamble (ack + busy) + int ack_start = 580; + int busy_end = 700; + int ack_token = 0b00101000; // start bit 0, ack ok 010, stopbit 1, busy 000 + if(write_count < 512) { + static unsigned char rbuf[520]; + if(write_expect < 0) { + // wait for first data != 15 + if(!(tb->sddat & 1)) + write_expect = 1024+16; // 1024 * 4 bit + 4 * 16 bit crc + + } else if(write_expect > 0) { + if((write_expect-1)&1) + rbuf[519-((write_expect-1)/2)] = tb->sddat << 4; + else + rbuf[519-((write_expect-1)/2)] |= tb->sddat; + + //printf("Write %d %s = %x\n", (write_expect-1)/2, + // ((write_expect-1)&1)?"hi":"lo", + // rbuf[519-((write_expect-1)/2)]); + + write_expect--; + if(write_expect == 0) { + + printf("Data written to card:\n"); + hexdump(rbuf, 512); + + // done with data. verify checksum + unsigned short crc[4] = { 0,0,0,0 }; + unsigned char dbits[4]; + for(int i=0;i<512;i++) { + // calculate the crc for each data line seperately + for(int c=0;c<4;c++) { + if((i & 3) == 0) dbits[c] = 0; + dbits[c] = (dbits[c] << 2) | + ((rbuf[i]&(0x10<> ((i&1)?0:4)) & 15; + s_crc[0] = (s_crc[0] << 1)|((nibble&1)?1:0); + s_crc[1] = (s_crc[1] << 1)|((nibble&2)?1:0); + s_crc[2] = (s_crc[2] << 1)|((nibble&4)?1:0); + s_crc[3] = (s_crc[3] << 1)|((nibble&8)?1:0); + } + + printf("WR SENT CRC = %04x/%04x/%04x/%04x\n", + s_crc[0], s_crc[1], s_crc[2], s_crc[3]); + + write_count = 512; + } + } + + // printf("WRITE %d = %d\n", write_count, tb->sddat); + } else if(write_count < busy_end) { + if(write_count < ack_start) tb->sddat_in = 15; + else if(write_count < ack_start+8) + tb->sddat_in = (ack_token & (0x80>>(write_count-ack_start)))?1:0; + else if(write_count < busy_end-1) tb->sddat_in = 0; + else tb->sddat_in = 15; + + write_count++; + } + + cmd_in = ((cmd_in << 1) | tb->sdcmd) & 0xffffffffffffll; + + // sending 4 bits + if(dat_ptr && dat_bits) { + if(dat_bits == 128*8 + 16 + 1 + 1) { + // card sends start bit + tb->sddat_in = 0; + printf("READ-4 START\n"); + } else if(dat_bits > 1) { + if(dat_bits == 128*8 + 16 + 1) printf("READ DATA START\n"); + if(dat_bits == 1) printf("READ DATA END\n"); + int nibble = dat_bits&1; // 1: high nibble, 0: low nibble + if(nibble) tb->sddat_in = (*dat_ptr >> 4)&15; + else tb->sddat_in = *dat_ptr++ & 15; + } else + tb->sddat_in = 15; + + dat_bits--; + } + + if(cmd_ptr && cmd_bits) { + int bit = 7-((cmd_bits-1) & 7); + tb->sdcmd_in = (*cmd_ptr & (0x80>>bit))?1:0; + if(bit == 7) cmd_ptr++; + cmd_bits--; + } else { + tb->sdcmd_in = (cmd_out & (1ll<<47))?1:0; + cmd_out = (cmd_out << 1)|1; + } + + // check if bit 47 is 0, 46 is 1 and 0 is 1 + if( !(cmd_in & (1ll<<47)) && (cmd_in & (1ll<<46)) && (cmd_in & (1ll<<0))) { + unsigned char cmd = (cmd_in >> 40) & 0x7f; + unsigned long arg = (cmd_in >> 8) & 0xffffffff; + unsigned char crc7 = cmd_in & 0xfe; + + // r1 reply: + // bit 7 - 0 + // bit 6 - parameter error + // bit 5 - address error + // bit 4 - erase sequence error + // bit 3 - com crc error + // bit 2 - illegal command + // bit 1 - erase reset + // bit 0 - in idle state + + if(crc7 == getCRC(cmd, arg)) { + printf("%cCMD %2d, ARG %08lx\n", last_was_acmd?'A':' ', cmd & 0x3f, arg); + switch(cmd & 0x3f) { + case 0: // Go Idle State + break; + case 8: // Send Interface Condition Command + cmd_out = reply(8, arg); + break; + case 55: // Application Specific Command + cmd_out = reply(55, 0); + break; + case 41: // Send Host Capacity Support + cmd_out = reply(63, OCR); + break; + case 2: // Send CID + cid[16] = getCRC_bytes(cid, 16) | 1; // Adjust CRC + cmd_ptr = cid; + cmd_bits = 136; + break; + case 3: // Send Relative Address + cmd_out = reply(3, (RCA<<16) | 0); // status = 0 + break; + case 7: // select card + cmd_out = reply(7, 0); // may indicate busy + break; + case 6: // set bus width + printf("Set bus width to %ld\n", arg); + cmd_out = reply(6, 0); + break; + case 16: // set block len (should be 512) + printf("Set block len to %ld\n", arg); + cmd_out = reply(16, 0); // ok + break; + case 17: // read block + printf("Request to read single block %ld\n", arg); + cmd_out = reply(17, 0); // ok + + // load sector + { + // check for floppy data request + if(!fd) { + fd = fopen("sd.img", "rb"); + if(!fd) { perror("OPEN ERROR"); exit(-1); } + fseek(fd, 0, SEEK_END); + flen = ftello(fd); + printf("Image size is %lld\n", flen); + fseek(fd, 0, SEEK_SET); + } + + fseek(fd, 512 * arg, SEEK_SET); + int items = fread(sector_data, 2, 256, fd); + if(items != 256) perror("fread()"); + + // for(int i=0;i<16;i++) printf("%02x ", sector_data[i]); + // printf("\n"); + } + { + unsigned short crc[4] = { 0,0,0,0 }; + unsigned char dbits[4]; + for(int i=0;i<512;i++) { + // calculate the crc for each data line seperately + for(int c=0;c<4;c++) { + if((i & 3) == 0) dbits[c] = 0; + dbits[c] = (dbits[c] << 2) | ((sector_data[i]&(0x10<> i))?1:0) + + ((crc[1] & (0x8000 >> i))?2:0) + + ((crc[2] & (0x8000 >> i))?4:0) + + ((crc[3] & (0x8000 >> i))?8:0); + + sector_data[512+i/2] |= (i&1)?(crc_nibble):(crc_nibble<<4); + } + } + dat_ptr = sector_data; + dat_bits = 128*8 + 16 + 1 + 1; + break; + + case 24: // write block + printf("Request to write single block %ld\n", arg); + write_count = 0; + write_expect = -1; + cmd_out = reply(24, 0); // ok + break; + + default: + printf("unexpected command\n"); + } + + last_was_acmd = (cmd & 0x3f) == 55; + + cmd_in = -1; + } else + printf("CMD %02x, ARG %08lx, CRC7 %02x != %02x!!\n", cmd, arg, crc7, getCRC(cmd, arg)); + } + } + last_sdclk = tb->sdclk; } + + + if(simulation_time == 0) + ticks = GetTickCountMs(); + +#if 0 // now obsolete sd_fake interface static int last_rdreq = 0; if(c && tb->rdreq) { int s = tb->rdaddr / 256; @@ -64,6 +380,7 @@ void tick(int c) { } last_rdreq = tb->rdreq; +#endif trace->dump(1000000000000 * simulation_time); simulation_time += TICKLEN; @@ -86,6 +403,13 @@ void wait_ms(int ms) { } } +void wait_us(int us) { + for(int i=0;i<32*us;i++) { + tick(1); + tick(0); + } +} + void wait_ns(int ns) { // printf("WAIT %dns\n", ns); for(int i=0;i<(32*ns)/1000;i++) { @@ -126,28 +450,6 @@ void run(int ticks) { } } -void hexdump(void *data, int size) { - int i, b2c; - int n=0; - char *ptr = (char*)data; - - if(!size) return; - - while(size>0) { - printf("%04x: ", n); - - b2c = (size>16)?16:size; - for(i=0;i> 8*(3-i))&0xff, 0); // wait for data to arrive + printf("Waiting for data ...\n"); while(mcu_read_byte()); // further reads will return the sector data for(int i=0;i<512;i++) buffer[i] = mcu_read_byte(); + + // printf("MCU data for sector %ld\n", sector); + // hexdump(buffer, 512); + + wait_ms(1); +} + +void mcu_write_sector(unsigned long sector, unsigned char *buffer) { + printf("Write sector %lu\n", sector); + + mcu_write_byte(0x05, 1); // command byte 5, start byte + for(int i=0;i<4;i++) mcu_write_byte((sector >> 8*(3-i))&0xff, 0); + + // write payload + for(int i=0;i<512;i++) + mcu_write_byte(buffer[i],0); - // hexdump(buffer, 512); + // read busy flag + while(mcu_read_byte()); + + wait_ms(1); } -void mcu_poll(void) { +void mcu_poll(int quiet) { // MCU requests sd card status unsigned char status = mcu_write_byte(0x01, 1); - unsigned char request = mcu_write_byte(0, 0); - // push in one more byte - // unsigned char status = mcu_read_byte(); - printf("STATUS = %02x\n", status); - char *type[] = { "UNKNOWN", "SDv1", "SDv2", "SDHCv2" }; + unsigned char request = mcu_write_byte(0, 0); - printf(" card status: %d\n", (status >> 4)&15); - printf(" card type: %s\n", type[(status >> 2)&3]); + if(!quiet) { + printf("Request is %d\n", request); + // push in one more byte + // unsigned char status = mcu_read_byte(); + printf("STATUS = %02x\n", status); + char *type[] = { (char*)"UNKNOWN", (char*)"SDv1", + (char*)"SDv2", (char*)"SDHCv2" }; + + printf(" card status: %d\n", (status >> 4)&15); + printf(" card type: %s\n", type[(status >> 2)&3]); + } + unsigned long sector = 0; for(int i=0;i<4;i++) sector = (sector << 8) | mcu_read_byte(); @@ -291,7 +621,7 @@ void mcu_poll(void) { void read_sector(int track, int sec) { // this poll should return no request to load a sector - mcu_poll(); + mcu_poll(0); // fdc read a sector printf("FDC: READ_SECTOR %d/%d\n", track, sec); @@ -303,9 +633,8 @@ void read_sector(int track, int sec) { wait_ns(1000); // this poll should see a request and handle it - mcu_poll(); + mcu_poll(0); -#if 1 // reading data should generate 512 drq's until a irq is generated int i = 0; unsigned char buffer[1024]; @@ -321,8 +650,44 @@ void read_sector(int track, int sec) { printf("READ_SECTOR done, read %d bytes, status = %x\n", i, cpu_read(0)); hexdump(buffer, i); +} + +void write_sector(int track, int sec) { + // this poll should return no request to load a sector + mcu_poll(0); + + // fdc write a sector + printf("FDC: WRITE_SECTOR %d/%d\n", track, sec); + cpu_write(1, track); // track + cpu_write(2, sec); // sector + cpu_write(3, 0x00); // data 0 ? + cpu_write(0, 0xa8); // write sector, spinup + + wait_ns(1000); + +#if 1 + // writing data should generate 512 drq's until a irq is generated + int i = 0; + unsigned char buffer[1024]; + while(!tb->irq) { + // while(i < 512) { + wait_ns(100); + if(tb->drq) { + cpu_write(3, i); + i++; + } + + // this poll should see the sector translation request after 512 bytes + mcu_poll(1); + wait_ns(100); + } + + // read status to clear interrupt + printf("WRITE_SECTOR done, wrote %d bytes, status = %x\n", i, cpu_read(0)); + + wait_ms(10); #else - run(100); + wait_ms(10); #endif } @@ -421,17 +786,33 @@ int main(int argc, char **argv) { tb->mcu_start = 0; tb->mcu_strobe = 0; + tb->sdcmd_in = 1; tb->sddat_in = 15; // inputs of sd card + run(10); tb->reset = 1; - wait_ms(10); + printf("Giving card 10ms to initialize\n"); + wait_ms(4); + +#if 0 // MCU direct r/w test + // read a sector + unsigned char buffer[512]; + mcu_read_sector(1, buffer); + hexdump(buffer, 512); + + // write sector back + mcu_write_sector(1, buffer); +#endif +#if 1 + printf("Initializing FS ...\n"); fs_init(); // this demo expects a file named "disk_a.st" to be present // inside the sd card image on root level - fs_prepare("/sd/disk_a.st"); - + printf("Opening disk_a.st\n"); + fs_prepare((char*)"/sd/disk_a.st"); + #if 1 printf("RESTORE\n"); cpu_write(0, 0x0b); // Restore, Motor on, 6ms @@ -440,15 +821,15 @@ int main(int argc, char **argv) { wait_ms(40); - read_sector(0, 3); - read_sector(0, 4); + // read_sector(0, 3); + write_sector(0, 3); - wait_ms(20); + wait_ms(10); #else run(10000); #endif - +#endif trace->close(); } diff --git a/sim/floppy_tb/floppy_tb.gtkw b/sim/floppy_tb/floppy_tb.gtkw index eb056aa..1b610b0 100644 --- a/sim/floppy_tb/floppy_tb.gtkw +++ b/sim/floppy_tb/floppy_tb.gtkw @@ -1,15 +1,15 @@ [*] -[*] GTKWave Analyzer v3.3.116 (w)1999-2023 BSI -[*] Wed Nov 15 07:42:54 2023 +[*] GTKWave Analyzer v3.3.114 (w)1999-2023 BSI +[*] Sun Dec 17 11:08:31 2023 [*] -[dumpfile] "/home/tharbaum/tmp/private_stuff/mistlite/atarist/sim/floppy_tb/floppy_tb.vcd" -[dumpfile_mtime] "Wed Nov 15 07:42:39 2023" -[dumpfile_size] 45930258 -[savefile] "/home/tharbaum/tmp/private_stuff/mistlite/atarist/sim/floppy_tb/floppy_tb.gtkw" -[timestart] 0 +[dumpfile] "floppy_tb.vcd" +[dumpfile_mtime] "Sun Dec 17 11:07:49 2023" +[dumpfile_size] 1027205334 +[savefile] "floppy_tb.gtkw" +[timestart] 292676000000 [size] 1280 947 -[pos] -1 -1 -*-32.534229 52083093751 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[pos] -1346 -2 +*-28.634232 293635062463 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] TOP. [treeopen] TOP.floppy_tb. [treeopen] TOP.floppy_tb.fdc1772. @@ -18,7 +18,7 @@ [sst_width] 209 [signals_width] 316 [sst_expanded] 1 -[sst_vpaned_height] 533 +[sst_vpaned_height] 381 @28 TOP.clk TOP.clk8m_en @@ -26,7 +26,6 @@ TOP.clk8m_en TOP.floppy_tb.fdc1772.cmd[7:0] @28 TOP.floppy_tb.fdc1772.sd_rd[0] -TOP.floppy_tb.fdc1772.sd_card_read TOP.floppy_tb.fdc1772.fd_sector_hdr TOP.floppy_tb.fdc1772.fdd[0].floppy.sector_data TOP.floppy_tb.fdc1772.fdd[0].floppy.ready @@ -40,33 +39,55 @@ TOP.floppy_tb.fdc1772.drq TOP.floppy_tb.fdc1772.irq @24 TOP.floppy_tb.fdc1772.unnamedblk5.data_transfer_cnt[10:0] -@28 -TOP.floppy_tb.fdc1772.fdd[0].floppy.index_pulse_start -@24 +@25 TOP.floppy_tb.fdc1772.fdd[0].floppy.byte_cnt[14:0] @28 -TOP.floppy_tb.sd_card.data_strobe -TOP.floppy_tb.sd_card.data_start -@22 -TOP.floppy_tb.sd_card.data_in[7:0] -TOP.floppy_tb.sd_card.data_out[7:0] -@28 TOP.floppy_tb.sd_card.rbusy TOP.floppy_tb.sd_card.rdone +TOP.floppy_tb.sd_card.wstart[1:0] +TOP.floppy_tb.sd_card.rstart[1:0] +TOP.floppy_tb.fdc1772.sd_rd[0] +TOP.floppy_tb.fdc1772.sd_wr[0] +TOP.floppy_tb.fdc1772.sd_ack +TOP.floppy_tb.sd_card.sd_rw.wstart +TOP.floppy_tb.sd_card.wstart_int +TOP.floppy_tb.sd_card.wstart_any +TOP.floppy_tb.sd_card.rstart_int @22 TOP.floppy_tb.sd_card.command[7:0] -@28 -TOP.floppy_tb.sd_card.rstart -TOP.floppy_tb.sd_card.sd_reader.rstart @24 TOP.floppy_tb.sd_card.lsector[31:0] -@29 -TOP.floppy_tb.sd_card.lstart @28 -TOP.floppy_tb.sd_card.outen -TOP.floppy_tb.sd_card.louten +TOP.floppy_tb.sd_card.sd_rw.inen @22 -TOP.floppy_tb.sd_card.outaddr[8:0] +TOP.floppy_tb.sd_card.mcu_tx_cnt[8:0] +TOP.floppy_tb.sd_card.sddat_in[3:0] +TOP.floppy_tb.sd_card.sd_rw.sddat[3:0] TOP.floppy_tb.sd_card.outbyte[7:0] +TOP.floppy_tb.sd_card.buffer[0][7:0] +TOP.mcu_din[7:0] +@28 +TOP.floppy_tb.sd_card.data_strobe +@22 +TOP.floppy_tb.sd_card.data_out[7:0] +[color] 2 +TOP.floppy_tb.sd_card.sd_rw.sddat_stat[3:0] +TOP.floppy_tb.sd_card.sd_rw.sdcmd_stat[3:0] +TOP.floppy_tb.sd_card.mcu_tx_cnt[8:0] +TOP.floppy_tb.cpu_din[7:0] +@28 +TOP.floppy_tb.cpu_sel +@22 +TOP.floppy_tb.fdc1772.fifo_sdptr[9:0] +TOP.floppy_tb.fdc1772.sd_dout[7:0] +@28 +TOP.floppy_tb.fdc1772.sd_dout_strobe +@22 +TOP.floppy_tb.sd_card.outaddr[8:0] +[color] 1 +TOP.floppy_tb.fdc1772.sd_din[7:0] +@28 +TOP.floppy_tb.fdc1772.fifo.wren_a +TOP.floppy_tb.sd_card.outen [pattern_trace] 1 [pattern_trace] 0 diff --git a/sim/floppy_tb/floppy_tb.v b/sim/floppy_tb/floppy_tb.v index c8f3732..e5992f6 100644 --- a/sim/floppy_tb/floppy_tb.v +++ b/sim/floppy_tb/floppy_tb.v @@ -1,27 +1,28 @@ module floppy_tb( - input clk, - input reset, - output clk8m_en, + input clk, + input reset, + output clk8m_en, // fdc interface - input [1:0] cpu_addr, - input cpu_sel, - input cpu_rw, - input [7:0] cpu_din, + input [1:0] cpu_addr, + input cpu_sel, + input cpu_rw, + input [7:0] cpu_din, output reg [7:0] cpu_dout, - output irq, - output drq, + output irq, + output drq, - input mcu_strobe, // byte strobe for sc card target - input mcu_start, - input [7:0] mcu_dout, - output [7:0] mcu_din, - - // interface to read data for fake sd card - output rdreq, - output [39:0] rdaddr, - input [15:0] rddata + input mcu_strobe, // byte strobe for sc card target + input mcu_start, + input [7:0] mcu_dout, + output [7:0] mcu_din, + + output sdclk, + output sdcmd, + input sdcmd_in, + output [3:0] sddat, + input [3:0] sddat_in ); reg [1:0] cnt_8mhz; @@ -31,7 +32,9 @@ always @(posedge clk) assign clk8m_en = cnt_8mhz == 2'd0; wire sd_rd; // fdc requests sector read +wire sd_wr; // fdc requests sector write wire [7:0] sd_rd_data; +wire [7:0] sd_wr_data; wire [31:0] sd_lba; wire [8:0] sd_byte_index; wire sd_rd_byte_strobe; @@ -63,38 +66,20 @@ fdc1772 #( .FD_NUM(1'b1) ) fdc1772 // place any signals that need to be passed up to the top after here. .img_type(3'd1), // atari st .img_mounted(1'b1), // signaling that new image has been mounted - .img_wp(1'b1), // write protect + .img_wp(1'b0), // write protect .img_ds(1'd0), // double-sided image (for BBC Micro only) .img_size(32'd737280), // size of image in bytes .sd_lba(sd_lba), .sd_rd(sd_rd), - .sd_wr(), + .sd_wr(sd_wr), .sd_ack(sd_busy), .sd_buff_addr(sd_byte_index), .sd_dout(sd_rd_data), - .sd_din(), + .sd_din(sd_wr_data), .sd_dout_strobe(sd_rd_byte_strobe) ); -wire sdclk, sdcmd, sdcmd_in, sddat0; -wire [3:0] sddat; - -assign sddat0 = sddat[0]; - -sd_fake sd_fake -( - .rstn_async(reset), - .sdclk(sdclk), - .sdcmd(sdcmd), - .sdcmd_out(sdcmd_in), - .sddat( sddat ), - - .rdreq(rdreq), - .rdaddr(rdaddr), - .rddata(rddata) - ); - sd_card #( .CLK_DIV(3'd1), // for 32 Mhz clock .SIMULATE(1'b1) @@ -106,21 +91,27 @@ sd_card #( .sdclk(sdclk), .sdcmd(sdcmd), .sdcmd_in(sdcmd_in), - .sddat0(sddat0), + .sddat(sddat), + .sddat_in(sddat_in), // MCU interface .data_strobe(mcu_strobe), // byte strobe for sc card target .data_start(mcu_start), .data_in(mcu_dout), .data_out(mcu_din), + + .image_mounted(), + .image_size(), // user read sector command interface (sync with clk) - .rstart(sd_rd), + .rstart({1'b0,sd_rd}), + .wstart({1'b0,sd_wr}), .rsector(sd_lba), .rbusy(sd_busy), .rdone(sd_done), // sector data output interface (sync with clk) + .inbyte(sd_wr_data), .outen(sd_rd_byte_strobe), // when outen=1, a byte of sector content is read out from outbyte .outaddr(sd_byte_index), // outaddr from 0 to 511, because the sector size is 512 .outbyte(sd_rd_data) // a byte of sector content diff --git a/sim/floppy_tb/sd_fake.v b/sim/floppy_tb/sd_fake.v deleted file mode 100644 index 17b00c2..0000000 --- a/sim/floppy_tb/sd_fake.v +++ /dev/null @@ -1,667 +0,0 @@ - -//-------------------------------------------------------------------------------------------------------- -// Module : sd_fake -// Type : synthesizable, IP's top -// Standard: Verilog 2001 (IEEE1364-2001) -// Function: Imitate a SDHCv2 Read-Only SD card -//-------------------------------------------------------------------------------------------------------- - -module sd_fake ( - input wire rstn_async, - // SD-card signals, connect to a SD-host, such as a SDcard Reader - input wire sdclk, -`ifdef VERILATOR - input sdcmd, - output sdcmd_out, -`else - inout sdcmd, -`endif - output wire [ 3:0] sddat, - // data read interface, connect to a RAM which contains SD-card's data. - output reg rdreq, - output reg [39:0] rdaddr, - input wire [15:0] rddata, - // show status (optional) - output wire [ 7:0] show_status_bits, - // show parsed request command on sdcmd (optional) - output reg show_sdcmd_en, - output reg [ 5:0] show_sdcmd_cmd, - output reg [31:0] show_sdcmd_arg -); - - -initial rdreq = 1'b0; -initial rdaddr = 40'd0; - -initial show_sdcmd_en = 1'b0; -initial show_sdcmd_cmd = 6'h0; -initial show_sdcmd_arg = 0; - - -// generate reset sync with posedge of sdclk -reg rstn_sdclk_p = 1'b0; -reg [1:0] rstn_sdclk_p_l = 2'b0; - -always @ (posedge sdclk or negedge rstn_async) - if(~rstn_async) - {rstn_sdclk_p, rstn_sdclk_p_l} <= 3'b0; - else - {rstn_sdclk_p, rstn_sdclk_p_l} <= {rstn_sdclk_p_l, 1'b1}; - - -// generate reset sync with negedge of sdclk -reg rstn_sdclk_n = 1'b0; -reg [1:0] rstn_sdclk_n_l = 2'b0; - -always @ (negedge sdclk or negedge rstn_async) - if(~rstn_async) - {rstn_sdclk_n, rstn_sdclk_n_l} <= 3'b0; - else - {rstn_sdclk_n, rstn_sdclk_n_l} <= {rstn_sdclk_n_l, 1'b1}; - - - -reg sdcmdoe = 1'b0; -reg sdcmdout = 1'b1; -reg sddatoe = 1'b0; -reg [3:0] sddatout = 4'hF; - -`ifdef VERILATOR -assign sdcmd_out = sdcmdout; -`else -assign sdcmd = sdcmdoe ? sdcmdout : 1'bz; -`endif -// assign sddat = sddatoe ? sddatout : 4'bz; -assign sddat = sddatout; - -function [6:0] CalcCrcCMD; - input [6:0] crc; - input [0:0] inbit; -begin - CalcCrcCMD = {crc[5:0],crc[6]^inbit} ^ {3'b0,crc[6]^inbit,3'b0}; -end -endfunction - - -function [15:0] CalcCrcDAT; - input [15:0] crc; - input [ 0:0] inbit; -begin - CalcCrcDAT = {crc[14:0],crc[15]^inbit} ^ {3'b0,crc[15]^inbit,6'b0,crc[15]^inbit,5'b0}; -end -endfunction - - -localparam BLOCK_SIZE = 512; // 512B per block -localparam [ 15:0] RCA_REG = 16'h0013; - -localparam [ 31:0] OCR_REG = {1'b1,1'b1,6'b0,9'h1ff,7'b0,1'b0,7'b0}; // not busy, CCS=1(SDHC card), all voltage, not dual-voltage card - -localparam [119:0] CID_REG = 120'h02544d53_41303847_14394a67_c700e4; - -localparam [119:0] CSD_REG = 120'h400e0032_50590000_39b73f80_000030; // 25MHz, SD-ROM card - // 120'h400e0032_5b590000_39b77f80_0a4000; // 25MHz, Normal card - -localparam [ 64:0] SCR_REG = 64'h0005_0000_00000000; - // 64'h0201_0000_00000000; // SD-ROM card, disable 4-bit bus mode - // 64'h0205_0000_00000000; // SD-ROM card, enable 4-bit bus mode - // 64'h0231_0000_00000000; // Normal card, disable 4-bit bus mode - // 64'h0235_0000_00000000; // Normal card, enable 4-bit bus mode - -reg last_is_acmd = 1'b0; - - -localparam [1:0] WAITINGCMD = 2'd0, - LOADRESP = 2'd1, - RESPING = 2'd2; -reg [1:0] respstate = WAITINGCMD; - - -reg [49:0] request = 50'h3ffffffffffff; -wire [ 3:0] request_pre_st; -wire [ 5:0] request_cmd; -wire [31:0] request_arg; -wire [ 6:0] request_crc; -wire request_stop; -assign {request_pre_st, request_cmd, request_arg, request_crc, request_stop} = request; - - -localparam [3:0] IDLE = 4'd0, - READY = 4'd1, - IDENT = 4'd2, - STBY = 4'd3, - TRAN = 4'd4, - DATA = 4'd5, - RCV = 4'd6, - PRG = 4'd7, - DIS = 4'd8; -//typedef enum logic [3:0] {IDLE, READY, IDENT, STBY, TRAN, DATA, RCV, PRG, DIS} current_state_t; - - -//struct packed{ -reg cardstatus_out_of_range = 0; -reg cardstatus_address_error = 0; -reg cardstatus_block_len_error = 0; -reg cardstatus_erase_seq_error = 0; -reg cardstatus_erase_param = 0; -reg cardstatus_wp_violation = 0; -reg cardstatus_card_is_locked = 0; -reg cardstatus_lock_unlock_failed = 0; -reg cardstatus_com_crc_error = 0; -reg cardstatus_illegal_command = 0; -reg cardstatus_card_ecc_failed = 0; -reg cardstatus_cc_error = 0; -reg cardstatus_error = 0; -reg [1:0] cardstatus_rsvd1 = 0; // reserved -reg cardstatus_csd_overwrite = 0; -reg cardstatus_wp_erase_skip = 0; -reg cardstatus_card_ecc_disabled = 0; -reg cardstatus_erase_reset = 0; -reg [3:0] cardstatus_current_state = 0; -reg cardstatus_ready_for_data = 0; -reg [1:0] cardstatus_rsvd2 = 0; -reg cardstatus_app_cmd = 0; -reg cardstatus_rsvd3 = 0; -reg cardstatus_ake_seq_error = 0; -reg [2:0] cardstatus_rsvd4 = 0; - -wire [31:0] cardstatus = {cardstatus_out_of_range, cardstatus_address_error, cardstatus_block_len_error, cardstatus_erase_seq_error, cardstatus_erase_param, cardstatus_wp_violation, cardstatus_card_is_locked, cardstatus_lock_unlock_failed, cardstatus_com_crc_error, cardstatus_illegal_command, cardstatus_card_ecc_failed, cardstatus_cc_error, cardstatus_error, cardstatus_rsvd1, cardstatus_csd_overwrite, cardstatus_wp_erase_skip, cardstatus_card_ecc_disabled, cardstatus_erase_reset, cardstatus_current_state, cardstatus_ready_for_data, cardstatus_rsvd2, cardstatus_app_cmd, cardstatus_rsvd3, cardstatus_ake_seq_error, cardstatus_rsvd4}; -wire [15:0] cardstatus_short = {cardstatus[23:22], cardstatus[19], cardstatus[12:0]}; // for R6 (CMD3) - - -localparam HIGHZLEN = 1; -localparam WAITLEN = HIGHZLEN + 3; - -reg [ 5:0] cmd = 6'd0; -reg [119:0] arg = 120'd0; -reg [ 6:0] crc = 7'd0; - -reg response_end = 1'b0; -reg valid=1'b0, dummycrc=1'b0; -reg [31:0] idx = 0; -reg [ 7:0] arglen = 0; - - -task response_init; - input _valid; - input _dummycrc; - input [5:0] _cmd; - input [7:0] _arglen; - input [119:0] _arg; -begin - cmd = _cmd; - arg = _arg; - crc = 0; - valid = _valid; - dummycrc = _dummycrc; - idx = 0; - arglen = _arglen; - response_end = 1'b0; -end -endtask - - -task response_yield; -begin - response_end = 1'b0; - if ( ~valid) begin - sdcmdoe = 0; - sdcmdout = 1; - response_end = 1'b1; - end else if(idx0; i=i-1) cmdcrcval = CalcCrcCMD(cmdcrcval, request[i]); -end - - -always @ (posedge sdclk or negedge rstn_sdclk_p) - if(~rstn_sdclk_p) begin - respstate <= WAITINGCMD; - request <= 50'h3ffffffffffff; - end else begin - case(respstate) - WAITINGCMD: - if (request_pre_st==4'b1101 && request_stop) begin - if(cmdcrcval==7'd0) - respstate <= LOADRESP; - else - request <= 50'h3ffffffffffff; - end else begin - request <= {request[48:0], sdcmd}; - end - LOADRESP : - respstate <= RESPING; - default : //RESPING : - if (response_end) begin - respstate <= WAITINGCMD; - request <= 50'h3ffffffffffff; - end - endcase - end - - - -always @ (negedge sdclk or negedge rstn_sdclk_n) - if(~rstn_sdclk_n) begin - response_init( 0, 0, 0, 0, 0 ); - data_response_stop; - response_yield; - data_response_yield; - last_is_acmd <= 1'b0; - {cardstatus_out_of_range, cardstatus_address_error, cardstatus_block_len_error, cardstatus_erase_seq_error, cardstatus_erase_param, cardstatus_wp_violation, cardstatus_card_is_locked, cardstatus_lock_unlock_failed, cardstatus_com_crc_error, cardstatus_illegal_command, cardstatus_card_ecc_failed, cardstatus_cc_error, cardstatus_error, cardstatus_rsvd1, cardstatus_csd_overwrite, cardstatus_wp_erase_skip, cardstatus_card_ecc_disabled, cardstatus_erase_reset, cardstatus_current_state, cardstatus_ready_for_data, cardstatus_rsvd2, cardstatus_app_cmd, cardstatus_rsvd3, cardstatus_ake_seq_error, cardstatus_rsvd4} = 0; - widebus = 0; - cmd6_invalid <= 6'h0; - end else begin - if(respstate==LOADRESP) begin - last_is_acmd <= 1'b0; - cardstatus_app_cmd = 1'b0; - cardstatus_block_len_error = 1'b0; - case (request_cmd) - 0 : begin // GO_IDLE_STATE - response_init( 0, 0 , 0 , 0 , 0 ); // there is NO RESPONSE for CMD0 - data_response_stop; - response_yield; - data_response_yield; - last_is_acmd <= 1'b0; - {cardstatus_out_of_range, cardstatus_address_error, cardstatus_block_len_error, cardstatus_erase_seq_error, cardstatus_erase_param, cardstatus_wp_violation, cardstatus_card_is_locked, cardstatus_lock_unlock_failed, cardstatus_com_crc_error, cardstatus_illegal_command, cardstatus_card_ecc_failed, cardstatus_cc_error, cardstatus_error, cardstatus_rsvd1, cardstatus_csd_overwrite, cardstatus_wp_erase_skip, cardstatus_card_ecc_disabled, cardstatus_erase_reset, cardstatus_current_state, cardstatus_ready_for_data, cardstatus_rsvd2, cardstatus_app_cmd, cardstatus_rsvd3, cardstatus_ake_seq_error, cardstatus_rsvd4} = 0; - cardstatus_ready_for_data = 1'b1; - widebus = 0; - cmd6_invalid <= 6'h0; - end - 2 : begin // ALL_SEND_CID - response_init( 1, 0 , 6'b000000 , 120 , CID_REG ); // R2 TODO: why cmd=000000 instead of 111111 ??? - cardstatus_current_state = IDENT; - cardstatus_illegal_command = 1'b0; - end - 3 : begin // SEND_RELATIVE_ADDR(send RCA) - response_init( 1, 0 , request_cmd , 32 , {RCA_REG,cardstatus_short} ); // R6 - cardstatus_current_state = STBY; - cardstatus_illegal_command = 1'b0; - end - 4 : if(request_arg[15:0] == 16'h0) begin // SET_DSR - response_init( 0, 0 , 0 , 0 , 0 ); // there is NO RESPONSE for CMD4 - cardstatus_illegal_command = 1'b0; - end - 6 : if(last_is_acmd && cardstatus_current_state==TRAN) begin // SET_BUS_WIDTH - cardstatus_app_cmd = 1'b1; - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - widebus = request_arg[1]; - cardstatus_illegal_command = 1'b0; - end else if(cardstatus_current_state==TRAN) begin // SWITCH_FUNC - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - cmd6_invalid[0] <= ( request_arg[0*4+:4]!=4'h0 && request_arg[0*4+:4]!=4'hf ); - cmd6_invalid[1] <= ( request_arg[1*4+:4]!=4'h0 && request_arg[1*4+:4]!=4'hf ); - cmd6_invalid[2] <= ( request_arg[2*4+:4]!=4'h0 && request_arg[2*4+:4]!=4'hf ); - cmd6_invalid[3] <= ( request_arg[3*4+:4]!=4'h0 && request_arg[3*4+:4]!=4'hf ); - cmd6_invalid[4] <= ( request_arg[4*4+:4]!=4'h0 && request_arg[4*4+:4]!=4'hf ); - cmd6_invalid[5] <= ( request_arg[5*4+:4]!=4'h0 && request_arg[5*4+:4]!=4'hf ); - data_response_cmd6stat_init; - cardstatus_illegal_command = 1'b0; - end - 7 : if(request_arg[31:16] == RCA_REG) begin // SELECT_CARD - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - cardstatus_current_state = TRAN; - cardstatus_illegal_command = 1'b0; - end else begin // DESELECT_CARD - cardstatus_current_state = STBY; - cardstatus_illegal_command = 1'b0; - end - 8 : begin // SEND_IF_COND - response_init( 1, 0 , request_cmd , 32 , {24'd1,request_arg[7:0]} ); - cardstatus_illegal_command = 1'b0; - end - 9 : if(request_arg[31:16]==RCA_REG) begin // SEND_CSD - response_init( 1, 0 , 6'b000000 , 120 , CSD_REG ); - cardstatus_illegal_command = 1'b0; - end - 10 : if(request_arg[31:16]==RCA_REG) begin // SEND_CID - response_init( 1, 0 , 6'b000000 , 120 , CID_REG ); - cardstatus_illegal_command = 1'b0; - end - 12 : if(cardstatus_current_state==DATA) begin // STOP_TRANSMISSION - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - data_response_stop; - cardstatus_illegal_command = 1'b0; - end - 13 : if(last_is_acmd) begin // SEND_SD_STATUS - if(cardstatus_current_state==TRAN) begin - cardstatus_app_cmd = 1'b1; - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - data_response_sdstat_init; - cardstatus_illegal_command = 1'b0; - end - end else if(request_arg[31:16]==RCA_REG) begin // SEND_STATUS - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - cardstatus_illegal_command = 1'b0; - end - 15 : if(request_arg[31:16]==RCA_REG) begin // GO_INACTIVE_STATE - response_init( 0, 0 , 0 , 0 , 0 ); - cardstatus_current_state = IDLE; - cardstatus_illegal_command = 1'b0; - end - 16 : if(cardstatus_current_state==TRAN) begin // SET_BLOCKLEN - if(request_arg > 512) cardstatus_block_len_error = 1'b1; - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - cardstatus_illegal_command = 1'b0; - end - 17 : if(cardstatus_current_state==TRAN) begin // READ_SINGLE_BLOCK - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - data_response_init(request_arg, 0); - cardstatus_illegal_command = 1'b0; - end - 18 : if(cardstatus_current_state==TRAN) begin // READ_MULTIPLE_BLOCK - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - data_response_init(request_arg, 1); - cardstatus_illegal_command = 1'b0; - end - 55 : if(request_arg[31:16]==16'd0 || request_arg[31:16]==RCA_REG) begin // APP_CMD - last_is_acmd <= 1'b1; - cardstatus_app_cmd = 1'b1; - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - cardstatus_illegal_command = 1'b0; - end - 41 : if(last_is_acmd) begin // SD_SEND_OP_COND - cardstatus_app_cmd = 1'b1; - response_init( 1, 1 , 6'b111111 , 32 , OCR_REG ); - cardstatus_illegal_command = 1'b0; - end - 42 : if(last_is_acmd) begin // SET_CLR_CARD_DETECT - cardstatus_app_cmd = 1'b1; - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - cardstatus_illegal_command = 1'b0; - end - 51 : if(last_is_acmd && cardstatus_current_state==TRAN) begin // SEND_SCR - cardstatus_app_cmd = 1'b1; - response_init( 1, 0 , request_cmd , 32 , cardstatus ); - data_response_scr_init; - cardstatus_illegal_command = 1'b0; - end - default : begin // undefined CMD - response_init( 0, 0 , 0 , 0 , 0 ); - cardstatus_illegal_command = 1'b1; - end - endcase - end - response_yield; - data_response_yield; - end - - -always @ (posedge sdclk or negedge rstn_sdclk_p) - if(~rstn_sdclk_p) begin - show_sdcmd_en <= 1'b0; - show_sdcmd_cmd <= 0; - show_sdcmd_arg <= 0; - end else begin - show_sdcmd_en <= 1'b0; - if (respstate == LOADRESP) begin - show_sdcmd_en <= 1'b1; - show_sdcmd_cmd <= request_cmd; - show_sdcmd_arg <= request_arg; - end - end - -endmodule diff --git a/sim/sdc_tb/Makefile b/sim/sdc_tb/Makefile new file mode 100644 index 0000000..21741ad --- /dev/null +++ b/sim/sdc_tb/Makefile @@ -0,0 +1,30 @@ +# +# Makefile +# + +PRJ=sdc_tb +TOP=sd_rw + +OBJ_DIR=obj_dir + +VERILATOR_DIR=/usr/local/share/verilator/include +VERILATOR_FILES=verilated.cpp verilated_vcd_c.cpp verilated_threads.cpp + +HDL_FILES = ../../src/misc/$(TOP).v ../../src/misc/sdcmd_ctrl.v + +VFLAGS=-GSIMULATE=1\'b1 -CFLAGS "-I.. -fpermissive" -Wno-fatal --trace --trace-max-array 512 --trace-max-width 512 + +all: $(PRJ) + +$(PRJ): $(PRJ).cpp ${HDL_FILES} Makefile + verilator -cc $(VFLAGS) --top-module $(TOP) ${HDL_FILES} $(C_FILES) --exe $(PRJ).cpp -o ../$(PRJ) + make -j -C ${OBJ_DIR} -f V$(TOP).mk + +$(PRJ).vcd: $(PRJ) + ./$(PRJ) + +wave: $(PRJ).vcd + gtkwave $(PRJ).gtkw + +clean: + rm -rf *~ obj_dir $(PRJ) $(PRJ).vcd diff --git a/sim/sdc_tb/sdc_tb.cpp b/sim/sdc_tb/sdc_tb.cpp new file mode 100644 index 0000000..eec5373 --- /dev/null +++ b/sim/sdc_tb/sdc_tb.cpp @@ -0,0 +1,473 @@ +#include +#include +#include +#include + +#include "Vsd_rw.h" +#include "verilated.h" +#include "verilated_vcd_c.h" + +#define BIT4 // send/receive 4bits data + +static Vsd_rw *tb; +static VerilatedVcdC *trace; +static double simulation_time; + +#define TICKLEN (1.0/64000000) + +#ifndef BIT4 +uint8_t sector_data[514]; // 512 bytes + one 16 bit crc +#else +uint8_t sector_data[520]; // 512 bytes + four 16 bit crcs +#endif + +// https://github.com/LonelyWolf/stm32/blob/master/stm32l-dosfs/sdcard.c + +// Calculate CRC7 +// It's a 7 bit CRC with polynomial x^7 + x^3 + 1 +// input: +// crcIn - the CRC before (0 for first step) +// data - byte for CRC calculation +// return: the new CRC7 +uint8_t CRC7_one(uint8_t crcIn, uint8_t data) { + const uint8_t g = 0x89; + uint8_t i; + + crcIn ^= data; + for (i = 0; i < 8; i++) { + if (crcIn & 0x80) crcIn ^= g; + crcIn <<= 1; + } + + return crcIn; +} + +// Calculate CRC16 CCITT +// It's a 16 bit CRC with polynomial x^16 + x^12 + x^5 + 1 +// input: +// crcIn - the CRC before (0 for rist step) +// data - byte for CRC calculation +// return: the CRC16 value +uint16_t CRC16_one(uint16_t crcIn, uint8_t data) { + crcIn = (uint8_t)(crcIn >> 8)|(crcIn << 8); + crcIn ^= data; + crcIn ^= (uint8_t)(crcIn & 0xff) >> 4; + crcIn ^= (crcIn << 8) << 4; + crcIn ^= ((crcIn & 0xff) << 4) << 1; + + return crcIn; +} + +uint8_t getCRC(unsigned char cmd, unsigned long arg) { + uint8_t CRC = CRC7_one(0, cmd); + for (int i=0; i<4; i++) CRC = CRC7_one(CRC, ((unsigned char*)(&arg))[3-i]); + return CRC; +} + +uint8_t getCRC_bytes(unsigned char *data, int len) { + uint8_t CRC = 0; + while(len--) CRC = CRC7_one(CRC, *data++); + return CRC; +} + +unsigned long long reply(unsigned char cmd, unsigned long arg) { + unsigned long r = 0; + r |= ((unsigned long long)cmd) << 40; + r |= ((unsigned long long)arg) << 8; + r |= getCRC(cmd, arg); + r |= 1; + return r; +} + +#define OCR 0xc0ff8000 // not busy, CCS=1(SDHC card), all voltage, not dual-voltage card +#define RCA 0x0013 + +// total cid respose is 136 bits / 17 bytes +unsigned char cid[17] = "\x3f" "\x02TMS" "A08G" "\x14\x39\x4a\x67" "\xc7\x00\xe4"; + +int rw_state = 0; + +void tick(int c) { + static long long cmd_in = -1; + static long long cmd_out = -1; + static unsigned char *cmd_ptr = 0; + static int cmd_bits = 0; + static unsigned char *dat_ptr = 0; + static int dat_bits = 0; + static int last_was_acmd = 0; + static int write_count = 0; + static int write_expect = 0; + + tb->clk = c; + tb->eval(); + + if(c) { + // data byte to be written to sd card + if(tb->inen) + tb->inbyte = 0xff ^ tb->outaddr; + } + + static int last_sdclk = -1; + if(tb->sdclk != last_sdclk) { + // rising sd card clock edge + if(tb->sdclk) { + static unsigned char rbuf[520]; + + // write postamble (ack + busy) + int ack_start = 580; + int busy_end = 700; + int ack_token = 0b00101000; // start bit 0, ack ok 010, stopbit 1, busy 000 + if(write_count < 512) { + if(write_expect < 0) { + // wait for first data != 15 + if(!(tb->sddat & 1)) + write_expect = 1024+16; // 1024 * 4 bit + 4 * 16 bit crc + + } else if(write_expect > 0) { + if((write_expect-1)&1) + rbuf[519-((write_expect-1)/2)] = tb->sddat << 4; + else + rbuf[519-((write_expect-1)/2)] |= tb->sddat; + + // printf("Write %d %s = %x\n", (write_expect-1)/2, + // ((write_expect-1)&1)?"hi":"lo", + // rbuf[519-((write_expect-1)/2)]); + + write_expect--; + if(write_expect == 0) { + // done with data. verify checksum + unsigned short crc[4] = { 0,0,0,0 }; + unsigned char dbits[4]; + for(int i=0;i<512;i++) { + // calculate the crc for each data line seperately + for(int c=0;c<4;c++) { + if((i & 3) == 0) dbits[c] = 0; + dbits[c] = (dbits[c] << 2) | + ((rbuf[i]&(0x10<> ((i&1)?0:4)) & 15; + s_crc[0] = (s_crc[0] << 1)|((nibble&1)?1:0); + s_crc[1] = (s_crc[1] << 1)|((nibble&2)?1:0); + s_crc[2] = (s_crc[2] << 1)|((nibble&4)?1:0); + s_crc[3] = (s_crc[3] << 1)|((nibble&8)?1:0); + } + + printf("WR SENT CRC = %04x/%04x/%04x/%04x\n", + s_crc[0], s_crc[1], s_crc[2], s_crc[3]); + + write_count = 512; + } + } + + // printf("WRITE %d = %d\n", write_count, tb->sddat); + } else if(write_count < busy_end) { + if(write_count < ack_start) tb->sddat_in = 15; + else if(write_count < ack_start+8) + tb->sddat_in = (ack_token & (0x80>>(write_count-ack_start)))?1:0; + else if(write_count < busy_end-1) tb->sddat_in = 0; + else tb->sddat_in = 15; + + write_count++; + } + + cmd_in = ((cmd_in << 1) | tb->sdcmd) & 0xffffffffffffll; + +#ifndef BIT4 + if(dat_ptr && dat_bits) { + if(dat_bits == 512*8 + 16 + 1 + 1) { + // card sends start bit + tb->sddat_in = 0; + printf("READ START\n"); + } else if(dat_bits > 1) { + int bit = 7-((dat_bits-2) & 7); + tb->sddat_in = (*dat_ptr & (0x80>>bit))?1:0; + if(bit == 7) dat_ptr++; + } else + tb->sddat_in = 15; + + dat_bits--; + } +#else + // sending 4 bits + if(dat_ptr && dat_bits) { + if(dat_bits == 128*8 + 16 + 1 + 1) { + // card sends start bit + tb->sddat_in = 0; + printf("READ-4 START\n"); + } else if(dat_bits > 1) { + int nibble = dat_bits&1; // 1: high nibble, 0: low nibble + if(nibble) tb->sddat_in = (*dat_ptr >> 4)&15; + else tb->sddat_in = *dat_ptr++ & 15; + } else + tb->sddat_in = 15; + dat_bits--; + } +#endif + + if(cmd_ptr && cmd_bits) { + int bit = 7-((cmd_bits-1) & 7); + tb->sdcmd_in = (*cmd_ptr & (0x80>>bit))?1:0; + if(bit == 7) cmd_ptr++; + cmd_bits--; + } else { + tb->sdcmd_in = (cmd_out & (1ll<<47))?1:0; + cmd_out = (cmd_out << 1)|1; + } + +#if 0 + { static int bcnt = 0; + if(cmd_in) printf("cmd_in %d %llx\r\n", bcnt++, cmd_in); + else bcnt = 0; + } +#endif + + // check if bit 47 is 0, 46 is 1 and 0 is 1 + if( !(cmd_in & (1ll<<47)) && (cmd_in & (1ll<<46)) && (cmd_in & (1ll<<0))) { + unsigned char cmd = (cmd_in >> 40) & 0x7f; + unsigned long arg = (cmd_in >> 8) & 0xffffffff; + unsigned char crc7 = cmd_in & 0xfe; + + // r1 reply: + // bit 7 - 0 + // bit 6 - parameter error + // bit 5 - address error + // bit 4 - erase sequence error + // bit 3 - com crc error + // bit 2 - illegal command + // bit 1 - erase reset + // bit 0 - in idle state + + if(crc7 == getCRC(cmd, arg)) { + printf("%cCMD %2d, ARG %08lx\n", last_was_acmd?'A':' ', cmd & 0x3f, arg); + switch(cmd & 0x3f) { + case 0: // Go Idle State + break; + case 8: // Send Interface Condition Command + cmd_out = reply(8, arg); + break; + case 55: // Application Specific Command + cmd_out = reply(55, 0); + break; + case 41: // Send Host Capacity Support + cmd_out = reply(63, OCR); + break; + case 2: // Send CID + cid[16] = getCRC_bytes(cid, 16) | 1; // Adjust CRC + cmd_ptr = cid; + cmd_bits = 136; + break; + case 3: // Send Relative Address + cmd_out = reply(3, (RCA<<16) | 0); // status = 0 + break; + case 7: // select card + cmd_out = reply(7, 0); // may indicate busy + break; + case 6: // set bus width + printf("Set bus width to %ld\n", arg); + cmd_out = reply(6, 0); + break; + case 16: // set block len (should be 512) + printf("Set block len to %ld\n", arg); + cmd_out = reply(16, 0); // ok + break; + case 17: // read block + printf("Request to read single block %ld\n", arg); + tb->rstart = 0; + cmd_out = reply(17, 0); // ok + if(rw_state != 1) { + printf("unexpected read state\n"); + // exit(-1); + } + rw_state = 2; + + // load sector + { + FILE *fd = fopen("disk_a.st", "rb"); + if(!fd) { perror("OPEN ERROR"); exit(-1); } + fseek(fd, 512 * arg, SEEK_SET); + int items = fread(sector_data, 2, 256, fd); + if(items != 256) perror("fread()"); + + for(int i=0;i<16;i++) printf("%02x ", sector_data[i]); + printf("\n"); + + fclose(fd); + } + +#ifndef BIT4 + { + unsigned short crc = 0; + for(int i=0;i<512;i++) + crc = CRC16_one(crc, sector_data[i]); + + printf("CRC = %04x\n", crc); + sector_data[512] = crc >> 8; + sector_data[513] = crc & 0xff; + } + dat_ptr = sector_data; + dat_bits = 512*8 + 16 + 1 + 1; +#else + { + unsigned short crc[4] = { 0,0,0,0 }; + unsigned char dbits[4]; + for(int i=0;i<512;i++) { + // calculate the crc for each data line seperately + for(int c=0;c<4;c++) { + if((i & 3) == 0) dbits[c] = 0; + dbits[c] = (dbits[c] << 2) | ((sector_data[i]&(0x10<> i))?1:0) + + ((crc[1] & (0x8000 >> i))?2:0) + + ((crc[2] & (0x8000 >> i))?4:0) + + ((crc[3] & (0x8000 >> i))?8:0); + + sector_data[512+i/2] |= (i&1)?(crc_nibble):(crc_nibble<<4); + } + } + dat_ptr = sector_data; + dat_bits = 128*8 + 16 + 1 + 1; +#endif + break; + + case 24: // write block + printf("Request to write single block %ld\n", arg); + tb->wstart = 0; + write_expect = -1; + write_count = 0; + cmd_out = reply(24, 0); // ok + break; + + default: + printf("unexpected command\n"); + } + + last_was_acmd = (cmd & 0x3f) == 55; + + cmd_in = -1; + } else + printf("CMD %02x, ARG %08lx, CRC7 %02x != %02x!!\n", cmd, arg, crc7, getCRC(cmd, arg)); + } + } + last_sdclk = tb->sdclk; + } + + trace->dump(1000000000000 * simulation_time); + simulation_time += TICKLEN; +} + +void wait_ms(int ms) { + for(int i=0;i<32000*ms;i++) { + tick(1); + tick(0); + } +} + +void wait_ns(int ns) { + for(int i=0;i<(32*ns)/1000;i++) { + tick(1); + tick(0); + } +} + +void run(int ticks) { + for(int i=0;i0) { + printf("%04x: ", n); + + b2c = (size>16)?16:size; + for(i=0;ispTrace()->set_time_unit("1ns"); + trace->spTrace()->set_time_resolution("1ps"); + simulation_time = 0; + + // Create an instance of our module under test + tb = new Vsd_rw; + + tb->trace(trace, 99); + trace->open("sdc_tb.vcd"); + + // reset + tb->rstn = 0; run(10); tb->rstn = 1; run(10); + tb->sdcmd_in = 1; tb->sddat_in = 15; // inputs of sd card + + // wait for ready + while(tb->card_stat != 8) run(1); + + char *type[] = { (char*)"UNKNOWN", (char*)"SDv1", + (char*)"SDv2", (char*)"SDHCv2" }; + printf("SD card \"%s\" is ready\n", type[tb->card_type]); + + wait_ms(1); + + printf("Requesting read ...\n"); + tb->rstart = 1; + tb->sector = 100; + rw_state = 1; + // wait for busy + while(!tb->rbusy) tick(1); + tb->rstart = 0; + while(tb->card_stat != 8) run(1); + printf("read done\n"); + + wait_ms(1); + + printf("Requesting write ...\n"); + tb->wstart = 1; + tb->sector = 100; + rw_state = 1; + // wait for busy + while(!tb->rbusy) tick(1); + tb->wstart = 0; + + // while(tb->card_stat != 8) run(1); + // printf("write done\n"); + + wait_ms(5); + + trace->close(); +} diff --git a/sim/sdc_tb/sdc_tb.gtkw b/sim/sdc_tb/sdc_tb.gtkw new file mode 100644 index 0000000..25ff47a --- /dev/null +++ b/sim/sdc_tb/sdc_tb.gtkw @@ -0,0 +1,73 @@ +[*] +[*] GTKWave Analyzer v3.3.114 (w)1999-2023 BSI +[*] Tue Dec 12 17:27:43 2023 +[*] +[dumpfile] "sdc_tb.vcd" +[dumpfile_mtime] "Tue Dec 12 17:26:58 2023" +[dumpfile_size] 23912372 +[savefile] "sdc_tb.gtkw" +[timestart] 8616880000 +[size] 1280 947 +[pos] -1 -1 +*-21.434227 8620468749 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] TOP. +[treeopen] TOP.sd_rw. +[sst_width] 209 +[signals_width] 316 +[sst_expanded] 1 +[sst_vpaned_height] 533 +@28 +TOP.clk +TOP.sd_rw.rstn +TOP.sd_rw.sdclk +TOP.sd_rw.sdcmd +@22 +TOP.sd_rw.sddat_in[3:0] +TOP.sd_rw.sddat[3:0] +TOP.sd_rw.u_sdcmd_ctrl.req_crc[6:0] +@28 +TOP.sd_rw.sdcmd_in +TOP.sd_rw.sddatoe +@c00024 +TOP.sd_rw.ridx[31:0] +@28 +(28)TOP.sd_rw.ridx[31:0] +(29)TOP.sd_rw.ridx[31:0] +(30)TOP.sd_rw.ridx[31:0] +(31)TOP.sd_rw.ridx[31:0] +@1401200 +-group_end +@22 +TOP.sd_rw.wack[3:0] +@28 +TOP.sd_rw.wstart +TOP.sd_rw.rstart +TOP.sd_rw.inen +@22 +TOP.sd_rw.inbyte[7:0] +@28 +TOP.sd_rw.outen +@24 +TOP.sd_rw.outaddr[8:0] +@22 +TOP.sd_rw.outbyte[7:0] +@24 +[color] 1 +TOP.sd_rw.sddat_stat[3:0] +TOP.sd_rw.sdcmd_stat[3:0] +TOP.sd_rw.u_sdcmd_ctrl.resp_cmd[5:0] +@22 +TOP.sd_rw.u_sdcmd_ctrl.resp_arg[31:0] +TOP.sd_rw.outbyte[7:0] +TOP.sd_rw.wdata[3:0] +TOP.sd_rw.data_crc[0][15:0] +TOP.sd_rw.data_crc[1][15:0] +TOP.sd_rw.data_crc[2][15:0] +TOP.sd_rw.data_crc[3][15:0] +TOP.sd_rw.read_crc[0][15:0] +TOP.sd_rw.read_crc[1][15:0] +TOP.sd_rw.read_crc[2][15:0] +@23 +TOP.sd_rw.read_crc[3][15:0] +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/sim/sdc_tb/sdtest/sdtest.ino b/sim/sdc_tb/sdtest/sdtest.ino new file mode 100644 index 0000000..9b55535 --- /dev/null +++ b/sim/sdc_tb/sdtest/sdtest.ino @@ -0,0 +1,400 @@ +/* + * sdtest.ino + * + * SD card communication test. Adresses the SD card in + * 4 bit SD mode via bit banging. + * +*/ + +#define PIN_LED LED_BUILTIN +#define PIN_CLK D0 +#define PIN_CMD D1 +#define PIN_DAT0 D2 +#define PIN_DAT1 D3 +#define PIN_DAT2 D4 +#define PIN_DAT3 D5 + +void clk(int n) { + while(n--) { + digitalWrite(PIN_CLK, LOW); + digitalWrite(PIN_CLK, HIGH); + } +} + +uint8_t CRC7_one(uint8_t crcIn, uint8_t data) { + const uint8_t g = 0x89; + uint8_t i; + + crcIn ^= data; + for (i = 0; i < 8; i++) { + if (crcIn & 0x80) crcIn ^= g; + crcIn <<= 1; + } + + return crcIn; +} + +// Calculate CRC16 CCITT +// It's a 16 bit CRC with polynomial x^16 + x^12 + x^5 + 1 +// input: +// crcIn - the CRC before (0 for rist step) +// data - byte for CRC calculation +// return: the CRC16 value +uint16_t CRC16_one(uint16_t crcIn, uint8_t data) { + crcIn = (uint8_t)(crcIn >> 8)|(crcIn << 8); + crcIn ^= data; + crcIn ^= (uint8_t)(crcIn & 0xff) >> 4; + crcIn ^= (crcIn << 8) << 4; + crcIn ^= ((crcIn & 0xff) << 4) << 1; + + return crcIn; +} + +void cmd_bit(bool b) { + digitalWrite(PIN_CMD, b?HIGH:LOW); + clk(1); +} + +void send_cmd(unsigned char cmd, unsigned long arg) { + pinMode(PIN_CMD, OUTPUT); + + Serial.print("CMD: "); + Serial.print(cmd, DEC); + + // first byte always starts with '01' + cmd = (cmd & 0x3f) | 0x40; + + // calculate crc + uint8_t crc = CRC7_one(0, cmd); + for (int i=0; i<4; i++) + crc = CRC7_one(crc, ((unsigned char*)(&arg))[3-i]); + + // send command bits + for(int i=0;i<8;i++) { + cmd_bit((cmd & 0x80)?1:0); + cmd <<= 1; + } + + Serial.print(" ARG: "); + Serial.print(arg, HEX); + + // send arg + for(int i=0;i<32;i++) { + cmd_bit((arg & 0x80000000)?1:0); + arg <<= 1; + } + + Serial.print(" CRC: "); + Serial.println(crc>>1, HEX); + + // send crc + crc |= 1; + for(int i=0;i<8;i++) { + cmd_bit((crc & 0x80)?1:0); + crc <<= 1; + } +} + +char cmd_bit_get(void) { + char bit = digitalRead(PIN_CMD)?1:0; + clk(1); + return bit; +} + +char data_get(void) { + char nib = (digitalRead(PIN_DAT0)?1:0) + + (digitalRead(PIN_DAT1)?2:0) + + (digitalRead(PIN_DAT2)?4:0) + + (digitalRead(PIN_DAT3)?8:0); + clk(1); + return nib; +} + +unsigned long reply(int len) { + unsigned char buffer[(len-16)/8]; + + pinMode(PIN_CMD, INPUT_PULLUP); + + // wait for startbit + int i; + for(i=0;i<32 && cmd_bit_get();i++); + + if(i == 32) { + Serial.println("Response timeout!"); + pinMode(PIN_CMD, OUTPUT); + return 0; + } + + Serial.print("@"); + Serial.print(i, DEC); + Serial.print(" "); + + // response bit must also be 0 + if(cmd_bit_get()) { + pinMode(PIN_CMD, OUTPUT); + Serial.println("Response bit is not 0!"); + return 0; + } + + uint8_t cmd = 0; + for(i=0;i<6;i++) + cmd = (cmd << 1)|cmd_bit_get(); + + Serial.print("-> CMD: "); + Serial.print(cmd, DEC); + + Serial.print(" ARG: "); + + // get data bits + uint32_t arg = 0; + for(i=0;i= 32 && buffer[i] < 127) { + Serial.print("("); + Serial.print((char)buffer[i]); + Serial.print(")"); + } + + Serial.print(" "); + } + + uint8_t crc = 0; + for(i=0;i<8;i++) + crc = (crc << 1)|cmd_bit_get(); + + uint8_t crc_calc = CRC7_one(0, cmd); + if(cmd == 63) crc_calc = 0; + for (int i=0; i<(len-16)/8; i++) + crc_calc = CRC7_one(crc_calc, buffer[i]); + + if(cmd != 63 || crc != 0xff) { + if((crc&0xfe) != crc_calc) { + Serial.print(" CRC error: "); + Serial.print(crc&0xfe, HEX); + Serial.print("/"); + Serial.println(crc_calc, HEX); + pinMode(PIN_CMD, OUTPUT); + return arg; + } + } + + Serial.println(" OK"); + + pinMode(PIN_CMD, OUTPUT); + + return arg; +} + +void dump(unsigned char *data) { + // hex dump + for(int i=0;i<32;i++) { + Serial.printf("%04x:", i*16); + for(int j=0;j<16;j++) Serial.printf(" %02x", data[16*i+j]); + Serial.print(" "); + for(int j=0;j<16;j++) Serial.printf("%c", isprint(data[16*i+j])?data[16*i+j]:'.'); + Serial.println(""); + } +} + +int readData(void) { + // wait for dat0 to go low + int i; + for(i=0;i<1024 && (data_get()&1);i++); + + if(i == 1024) { + Serial.println("Data timeout!"); + return 0; + } + + // read 512 bytes + unsigned short crc_calc[4] = { 0,0,0,0 }; + unsigned char buffer[512]; + unsigned char dbits[4]; + for(int i=0;i<512;i++) { + unsigned char dat = data_get(); + dat = (dat << 4) | data_get(); + + buffer[i] = dat; + + // calculate the crc for each data line seperately + for(int c=0;c<4;c++) { + if((i & 3) == 0) dbits[c] = 0; + dbits[c] = (dbits[c] << 2) | ((dat&(0x10<>4) & 0x0f); + data_set(buffer[i] & 0x0f); + } + + // send crc's + for(int i=0;i<16;i++) { + unsigned char b = 0; + for(int c=0;c<4;c++) + if(crc[c] & (0x8000 >> i)) + b |= (1<> 16; + printf("RCA = %04x\n", rca); + + clk(256); send_cmd( 9, rca << 16); reply(136); // get CSD + clk(256); send_cmd( 7, rca << 16); reply(48); // select card + + clk(512); send_cmd(55, rca << 16); reply(48); // prepare ACMD + clk(256); send_cmd(6, 2); r=reply(48); // switch to 4 bit mode + + clk(256); send_cmd(16, 512); reply(48); // select block len 512 + + // on the demo cards, the first partition starts at sector 2048. So we can + // mess with the sectors 2 to 2047 without damaging the file systems + + clk(96); send_cmd(17, 2); reply(48); readData(); // read sector 2 + +// clk(96); send_cmd(24, 2); reply(48); writeData(); // write sector // 2 +} + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.printf("\n\nSD card test\n------------\n"); + delay(10); + + pinMode(PIN_LED, OUTPUT); + pinMode(PIN_CLK, OUTPUT); + pinMode(PIN_CMD, OUTPUT); + pinMode(PIN_DAT0, INPUT_PULLUP); + pinMode(PIN_DAT1, INPUT_PULLUP); + pinMode(PIN_DAT2, INPUT_PULLUP); + pinMode(PIN_DAT3, INPUT_PULLUP); + + digitalWrite(PIN_CLK, HIGH); + digitalWrite(PIN_CMD, HIGH); + + sd_init(); +} + +// the loop function runs over and over again forever +void loop() { + digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) + delay(1000); // wait for a second + digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW + delay(1000); // wait for a second +}