/* SDHC card controller SPI mode 2024/01/13 */ %i "ram_8x512.h" circuit SDHCcard_ctrl_SPImode { output CSn; // SD Card CSn output CLK; // SD Card Clock output CMD; // SD Card Command & Dout input DAT; // SD Card Data reg_ws CSn_reg, CLK_reg, CMD_reg, DAT_reg; instrself base_clk; output ack, err; reg_wr err_reg; instrin read(radrs); input radrs<41>; // 32 + 9 reg_ws radrs_reg<41>; output rdata<8>; reg_ws reset; reg_ws send_data<8>; reg_wr cmd_index<6>, cmd_arg<32>; reg_wr recv_data<8>; ram_8x512 ram; stage_name init { task do(); } stage_name acc { task send(send_data); task recv(); } stage_name send_cmd { task do(cmd_index, cmd_arg); } stage_name read_block { task do(); } stage_name get_block { task do(radrs_reg); } if(reset){ generate init.do(); reset := 0b0; } CSn = CSn_reg; CLK = CLK_reg; CMD = CMD_reg; DAT_reg := DAT; ack = ^(init.do | get_block.do); err = err_reg; rdata = ram.dout; // 初期化中はCLK400kHzとする // SPIで動作中はCLK25MHzとする reg_wr count_800k<6>; count_800k++; // <6>781,250Hz if(init.do){ if(/&count_800k) base_clk(); // 781k -> 390kHz } else{ // CLK 50MHz -> 25MHz base_clk(); } instruct read par{ if(radrs<40:9>==radrs_reg<40:9>){ // すでに取得しているセクタであればデータを返す ram.read(radrs<8:0>); } else generate get_block.do(radrs); } stage init { first_state power_on; state power_on par{ // 電源立ち上げ1msウエイト // CSn_reg := 0b1; reg_wr pon_count<11>; if(base_clk) pon_count++; if(pon_count<10>) goto dummy; // 800kHzで800カウント以上 } state dummy if(^acc.send){ // ダミー74clk以上 // CMD, CSnはH CSn_reg := 0b1; CMD_reg := 0b1; generate acc.send(0xFF); reg_wr dummy_count<4>; // 8x16=128 dummy_count++; if(/&dummy_count) goto cmd0; } state cmd0 if(^acc.send){ // CMD0 ソフトリセット // DOは仕様上10kΩプルアップすること。 // プルアップしていない場合、CMD0を2回繰り返すとうまくいく場合もある。 CSn_reg := 0b0; // SPIモード generate send_cmd.do(0, 0x00000000); goto cmd0w; } state cmd0w if(^send_cmd.do){ if(recv_data==0x01) goto cmd8; else goto dummy; // 二度打ちする } state cmd8 par{ // 下位12ビットが0x1AAならそのカードはSD Ver.2以上 // そのカードはSDHCまたはSDXCで、アドレスの指定はブロック単位(ブロックサイズは512バイト固定) // これを送ることでSDHC/SDXCはCMD58とCMD41を有効化する generate send_cmd.do(8, 0x000001AA); goto cmd55_41; } state cmd55_41 if(^send_cmd.do){ // 最大で1秒(250ms)ほどかかる generate send_cmd.do(55, 0x00000000); goto acmd41; } state acmd41 if(^send_cmd.do){ generate send_cmd.do(41, 0x40000000); goto acmd41w; } state acmd41w if(^send_cmd.do){ if(recv_data==0x00){ finish; } else goto cmd55_41; } } stage get_block { first_state st_cmd17; state st_cmd17 par{ generate send_cmd.do(17, radrs_reg<40:9>); goto st_recv; } state st_recv if(^send_cmd.do){ generate acc.recv(); goto st_resp; } state st_resp if(^acc.recv){ if(recv_data<0>) goto st_recv; // 0xFF=bussy else{ // 0xFEならデータ受信 generate read_block.do(); goto st_read_end; } } state st_read_end if(^read_block.do){ ram.read(radrs_reg<8:0>); goto st_cmd17; finish; } } // 512バイトの読み込み stage read_block { first_state st_init; state st_init if(^(acc.recv | acc.send)){ generate acc.recv(); goto st_read; } state st_read if(^acc.recv){ reg_wr read_count<9>; ram.write(read_count, recv_data); generate acc.recv(); // CRC2つ分recv read_count++; if(/&read_count) goto st_crc; // 512recvした } state st_crc if(^acc.recv){ // CRC2個め generate acc.recv(); goto st_dummy; } state st_dummy if(^acc.recv){ // read_block終了 generate acc.send(0xFF); goto st_dummy_wait; } state st_dummy_wait if(^acc.send){ goto st_init; finish; } } stage send_cmd { first_state st_cmd; state st_cmd if(^(acc.send | acc.recv)){ generate acc.send(0b01 || cmd_index); goto st_arg1; } state st_arg1 if(^acc.send){ generate acc.send(cmd_arg<31:24>); goto st_arg2; } state st_arg2 if(^acc.send){ generate acc.send(cmd_arg<23:16>); goto st_arg3; } state st_arg3 if(^acc.send){ generate acc.send(cmd_arg<15:8>); goto st_arg4; } state st_arg4 if(^acc.send){ generate acc.send(cmd_arg<7:0>); goto st_crc; } state st_crc if(^acc.send){ any{ cmd_index==0 : generate acc.send(0x95); // for CMD0 cmd_index==8 : generate acc.send(0x87); // for CMD8 else : generate acc.send(0xFF); // test } goto st_recv; } state st_recv if(^acc.send){ generate acc.recv(); // コマンドレスポンス goto st_recv_wait; } state st_recv_wait if(^acc.recv){ if(recv_data<7>) goto st_recv; // 0xFF=busy, retry. else goto st_resp; } state st_resp if(^acc.recv){ reg_wr resp_count<3>; if( ((cmd_index!=8) & (resp_count==0)) | // R1 (1Byte) ((cmd_index==8) & (resp_count==4)) // R7 (R1 + 4Byte) ){ resp_count := 0; generate acc.send(0xFF); // dummy goto st_end; } else{ resp_count++; generate acc.recv(); } } state st_end if(^acc.send){ goto st_cmd; finish; } } stage acc { if(base_clk){ if(CLK_reg){ if(acc.send) CMD_reg := send_data<7>; else CMD_reg := 0b1; send_data := send_data<6:0> || 0b1; } else{ if(acc.recv) recv_data := recv_data<6:0> || DAT; // DAT_reg; reg_wr acc_tim<3>; acc_tim++; if(/&acc_tim) finish; } CLK_reg := ^CLK_reg; } } }