mirror of
https://github.com/MiSTer-devel/MultiComp_MiSTer.git
synced 2026-04-19 03:04:38 +00:00
571 lines
17 KiB
VHDL
571 lines
17 KiB
VHDL
-- VHDL SD card interface
|
|
-- Reads and writes a single block of data as a data stream
|
|
|
|
-- Adapted from design by Steven J. Merrifield, June 2008
|
|
-- Read states are derived from the Apple II emulator by Stephen Edwards
|
|
|
|
-- This version of the code contains modifications copyright by Grant Searle 2013
|
|
-- You are free to use this file in your own projects but must never charge for it nor use it without
|
|
-- acknowledgement.
|
|
-- Please ask permission from Grant Searle before republishing elsewhere.
|
|
-- If you use this file or any part of it, please add an acknowledgement to myself and
|
|
-- a link back to my main web site http://searle.hostei.com/grant/
|
|
-- and to the "multicomp" page at http://searle.hostei.com/grant/Multicomp/index.html
|
|
--
|
|
-- Please check on the above web pages to see if there are any updates before using this file.
|
|
-- If for some reason the page is no longer available, please search for "Grant Searle"
|
|
-- on the internet to see if I have moved to another web hosting service.
|
|
--
|
|
-- Grant Searle
|
|
-- eMail address available on my main web page link above.
|
|
-- Minor changes by foofoobedoo@gmail.com
|
|
-- Additional functionality to provide SDHC support and 25 MHz SPI-clock by Rienk Koolstra.
|
|
--
|
|
-- This design uses the SPI interface and supports "standard capacity" (SDSC) and
|
|
-- "high capacity" (SDHC) cards.
|
|
|
|
-- Address Register
|
|
-- 0 SDDATA read/write data
|
|
-- 1 SDSTATUS read
|
|
-- 1 SDCONTROL write
|
|
-- 2 SDLBA0 write-only
|
|
-- 3 SDLBA1 write-only
|
|
-- 4 SDLBA2 write-only (only bits 6:0 are valid)
|
|
--
|
|
-- For both SDSC and SDHC (high capacity) cards, the block size is 512bytes (9-bit value) and the
|
|
-- SDLBA registers select the block number. SDLBA2 is most significant, SDLBA0 is least significant.
|
|
--
|
|
-- For SDSC, the read/write address parameter is a 512-byte aligned byte address. ie, it has 9 low
|
|
-- address bits explicitly set to 0. 23 of the 24 programmable address bits select the 512-byte block.
|
|
-- This gives an address capacity of 2^23 * 512 = 4GB .. BUT maximum SDSC capacity is 2GByte.
|
|
--
|
|
-- The SDLBA registers are used like this:
|
|
--
|
|
-- 31 30 29 28.27 26 25 24.23 22 21 20.19 18 17 16.15 14 13 12.11 10 09 08.07 06 05 04.03 02 01 00
|
|
--+------- SDLBA2 -----+------- SDLBA1 --------+------- SDLBA0 --------+ 0 0 0 0 0 0 0 0 0
|
|
--
|
|
-- For SDHC cards, the read/write address parameter is the ordinal number of 512-byte block ie, the
|
|
-- 9 low address bits are implicity 0. The 24 programmable address bits select the 512-byte block.
|
|
-- This gives an address capacity of 2^24 * 512 = 8GByte. SDHC can be upto 32GByte but this design
|
|
-- can only access the low 8GByte (could add SDLBA3 to get the extra address lines if required).
|
|
--
|
|
-- The SDLBA registers are used like this:
|
|
--
|
|
-- 31 30 29 28.27 26 25 24.23 22 21 20.19 18 17 16.15 14 13 12.11 10 09 08.07 06 05 04.03 02 01 00
|
|
-- 0 0 0 0 0 0 0 0+---------- SDLBA2 -----+------- SDLBA1 --------+------- SDLBA0 --------+
|
|
--
|
|
-- The end result of all this is that the addressing looks the same for SDSC and SDHC cards.
|
|
--
|
|
-- SDSTATUS (RO)
|
|
-- b7 Write Data Byte can be accepted
|
|
-- b6 Read Data Byte available
|
|
-- b5 Block Busy
|
|
-- b4 Init Busy
|
|
-- b3 Unused. Read 0
|
|
-- b2 Unused. Read 0
|
|
-- b1 Unused. Read 0
|
|
-- b0 Unused. Read 0
|
|
--
|
|
-- SDCONTROL (WO)
|
|
-- b7:0 0x00 Read block
|
|
-- 0x01 Write block
|
|
--
|
|
--
|
|
-- To read a 512-byte block from the SDCARD:
|
|
-- Wait until SDSTATUS=0x80 (ensures previous cmd has completed)
|
|
-- Write SDLBA0, SDLBA1 SDLBA2 to select block index to read from
|
|
-- Write 0 to SDCONTROL to issue read command
|
|
-- Loop 512 times:
|
|
-- Wait until SDSTATUS=0xE0 (read byte ready, block busy)
|
|
-- Read byte from SDDATA
|
|
--
|
|
-- To write a 512-byte block to the SDCARD:
|
|
-- Wait until SDSTATUS=0x80 (ensures previous cmd has completed)
|
|
-- Write SDLBA0, SDLBA1 SDLBA2 to select block index to write to
|
|
-- Write 1 to SDCONTROL to issue write command
|
|
-- Loop 512 times:
|
|
-- Wait until SDSTATUS=0xA0 (block busy)
|
|
-- Write byte to SDDATA
|
|
--
|
|
-- At HW level each data transfer is 515 bytes: a start byte, 512 data bytes,
|
|
-- 2 CRC bytes. CRC need not be valid in SPI mode, *except* for CMD0.
|
|
--
|
|
-- SDCARD specification can be downloaded from
|
|
-- https://www.sdcard.org/downloads/pls/
|
|
-- All you need is the "Part 1 Physical Layer Simplified Specification"
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
use ieee.std_logic_unsigned.all;
|
|
|
|
entity sd_controller is
|
|
generic (
|
|
constant CLKEDGE_DIVIDER : integer := 100 -- 50MHz / 100 gives edges at 500kHz ie output
|
|
-- or sdSCLK of 250kHz to be used during init phase
|
|
);
|
|
port (
|
|
sdCS : out std_logic;
|
|
sdMOSI : out std_logic;
|
|
sdMISO : in std_logic;
|
|
sdSCLK : out std_logic;
|
|
n_reset : in std_logic;
|
|
n_rd : in std_logic;
|
|
n_wr : in std_logic;
|
|
dataIn : in std_logic_vector(7 downto 0);
|
|
dataOut : out std_logic_vector(7 downto 0);
|
|
regAddr : in std_logic_vector(2 downto 0);
|
|
clk : in std_logic;
|
|
driveLED : out std_logic := '1'
|
|
);
|
|
|
|
end sd_controller;
|
|
|
|
architecture rtl of sd_controller is
|
|
type states is (
|
|
rst,
|
|
init,
|
|
cmd0,
|
|
cmd8,
|
|
cmd55,
|
|
acmd41,
|
|
poll_cmd,
|
|
cmd58,
|
|
cardsel,
|
|
idle, -- wait for read or write pulse
|
|
read_block_cmd,
|
|
read_block_wait,
|
|
read_block_data,
|
|
send_cmd,
|
|
send_regreq,
|
|
receive_ocr_wait,
|
|
receive_byte_wait,
|
|
receive_byte,
|
|
write_block_cmd,
|
|
write_block_init, -- initialise write command
|
|
write_block_data, -- loop through all data bytes
|
|
write_block_byte, -- send one byte
|
|
write_block_wait -- wait until not busy
|
|
);
|
|
|
|
|
|
-- one start byte, plus 512 bytes of data, plus two ff end bytes (crc)
|
|
constant write_data_size : integer := 515;
|
|
|
|
|
|
signal state, return_state : states;
|
|
signal sclk_sig : std_logic := '0';
|
|
signal cmd_out : std_logic_vector(55 downto 0);
|
|
-- at different times holds 8-bit data, 8-bit R1 response or 40-bit R7 response
|
|
signal recv_data : std_logic_vector(39 downto 0);
|
|
|
|
signal clkCount : std_logic_vector(5 downto 0);
|
|
signal clkEn : std_logic;
|
|
signal HighSpeed : std_logic := '0'; -- flag to switch to 25 MHz operation and back
|
|
signal status : std_logic_vector(7 downto 0) := x"00";
|
|
|
|
signal block_read : std_logic := '0';
|
|
signal block_write : std_logic := '0';
|
|
signal block_start_ack : std_logic := '0';
|
|
|
|
signal cmd_mode : std_logic := '1';
|
|
signal response_mode : std_logic := '1';
|
|
signal data_sig : std_logic_vector(7 downto 0) := x"00";
|
|
signal din_latched : std_logic_vector(7 downto 0) := x"00";
|
|
signal dout : std_logic_vector(7 downto 0) := x"00";
|
|
|
|
signal sdhc : std_logic := '0';
|
|
|
|
signal sd_read_flag : std_logic := '0';
|
|
signal host_read_flag : std_logic := '0';
|
|
|
|
signal sd_write_flag : std_logic := '0';
|
|
signal host_write_flag : std_logic := '0';
|
|
|
|
signal init_busy : std_logic := '1';
|
|
signal block_busy : std_logic := '0';
|
|
|
|
signal address: std_logic_vector(31 downto 0) :=x"00000000";
|
|
|
|
signal led_on_count : integer range 0 to 200;
|
|
|
|
begin
|
|
clock_enable: process(clk)
|
|
begin
|
|
if rising_edge(clk) then
|
|
if clkCount < (CLKEDGE_DIVIDER - 1) then
|
|
clkCount <= clkCount + 1;
|
|
else
|
|
clkCount <= (others=>'0');
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
clkEn <= '1' when ( clkCount = 0 ) or ( HighSpeed = '1' ) else '0';
|
|
|
|
wr_adrs_reg: process(n_wr)
|
|
begin
|
|
-- sdsc address 0..8 (first 9 bits) always zero because each sector is 512 bytes
|
|
if rising_edge(n_wr) then
|
|
if sdhc = '0' then -- SDSC card
|
|
if regAddr = "010" then
|
|
address(16 downto 9) <= dataIn;
|
|
elsif regAddr = "011" then
|
|
address(24 downto 17) <= dataIn;
|
|
elsif regAddr = "100" then
|
|
address(31 downto 25) <= dataIn(6 downto 0);
|
|
end if;
|
|
else -- SDHC card
|
|
-- SDHC address is the 512 bytes block address. starts at bit 0
|
|
if regAddr = "010" then
|
|
address(7 downto 0) <= dataIn; -- 128 k
|
|
elsif regAddr = "011" then
|
|
address(15 downto 8) <= dataIn; -- 32 M
|
|
elsif regAddr = "100" then
|
|
address(23 downto 16) <= dataIn; -- addresses upto 8 G
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- output data is MUXed externally based on CS so only need to
|
|
-- drive 0 by default if dataOut is being ORed externally
|
|
dataOut <= dout when regAddr = "000" else
|
|
status when regAddr = "001" else
|
|
x"00";
|
|
|
|
wr_dat_reg: process(n_wr)
|
|
begin
|
|
if rising_edge(n_wr) then
|
|
if (regAddr = "000") and (sd_write_flag = host_write_flag) then
|
|
din_latched <= dataIn;
|
|
host_write_flag <= not host_write_flag;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
rd_dat_reg: process(n_rd)
|
|
begin
|
|
if rising_edge(n_rd) then
|
|
if (regAddr = "000") and (sd_read_flag /= host_read_flag) then
|
|
host_read_flag <= not host_read_flag;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
wr_cmd_reg: process(n_wr, block_start_ack,init_busy)
|
|
begin
|
|
if init_busy='1' or block_start_ack='1' then
|
|
block_read <= '0';
|
|
block_write <= '0';
|
|
elsif rising_edge(n_wr) then
|
|
if regAddr = "001" and dataIn = "00000000" then
|
|
block_read <= '1';
|
|
end if;
|
|
if regAddr = "001" and dataIn = "00000001" then
|
|
block_write <= '1';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
fsm: process(clk, clkEn, n_reset)
|
|
variable byte_counter : integer range 0 to write_data_size;
|
|
variable bit_counter : integer range 0 to 160;
|
|
begin
|
|
if (n_reset='0') then
|
|
state <= rst;
|
|
sclk_sig <= '0';
|
|
sdCS <= '1';
|
|
HighSpeed <= '0';
|
|
elsif rising_edge(clk) and clkEn = '1' then
|
|
|
|
case state is
|
|
|
|
when rst =>
|
|
-- HighSpeed <= '0';
|
|
sd_read_flag <= host_read_flag;
|
|
sd_write_flag <= host_write_flag;
|
|
sclk_sig <= '0';
|
|
cmd_out <= (others => '1');
|
|
byte_counter := 0;
|
|
cmd_mode <= '1'; -- 0=data, 1=command
|
|
response_mode <= '1'; -- 0=data, 1=command
|
|
bit_counter := 160;
|
|
sdCS <= '1';
|
|
state <= init;
|
|
init_busy <= '1';
|
|
block_start_ack <= '0';
|
|
|
|
when init => -- cs=1, send 80 clocks, cs=0
|
|
if (bit_counter = 0) then
|
|
sdCS <= '0';
|
|
state <= cmd0;
|
|
else
|
|
bit_counter := bit_counter - 1;
|
|
sclk_sig <= not sclk_sig;
|
|
end if;
|
|
|
|
when cmd0 =>
|
|
cmd_out <= x"ff400000000095"; -- GO_IDLE_STATE here, Select SPI
|
|
bit_counter := 55;
|
|
return_state <= cmd8;
|
|
state <= send_cmd;
|
|
|
|
when cmd8 =>
|
|
cmd_out <= x"ff48000001aa87"; -- SEND_IF_COND
|
|
bit_counter := 55;
|
|
return_state <= cmd55;
|
|
state <= send_regreq;
|
|
|
|
-- cmd55 is the "prefix" command for ACMDs
|
|
when cmd55 =>
|
|
cmd_out <= x"ff770000000001"; -- APP_CMD
|
|
bit_counter := 55;
|
|
return_state <= acmd41;
|
|
state <= send_cmd;
|
|
|
|
when acmd41 =>
|
|
cmd_out <= x"ff694000000077"; -- SD_SEND_OP_COND
|
|
bit_counter := 55;
|
|
return_state <= poll_cmd;
|
|
state <= send_cmd;
|
|
|
|
when poll_cmd =>
|
|
if (recv_data(0) = '0') then
|
|
state <= cmd58;
|
|
else
|
|
-- still busy; go round and do it again
|
|
state <= cmd55;
|
|
end if;
|
|
|
|
when cmd58 =>
|
|
cmd_out <= x"ff7a00000000fd"; -- READ_OCR
|
|
bit_counter := 55;
|
|
return_state <= cardsel;
|
|
state <= send_regreq;
|
|
|
|
when cardsel =>
|
|
if (recv_data(31) = '0' ) then -- power up not completed
|
|
state <= cmd58;
|
|
else
|
|
sdhc <= recv_data(30); -- CCS bit
|
|
state <= idle;
|
|
end if;
|
|
|
|
when idle =>
|
|
HighSpeed <= '1';
|
|
sd_read_flag <= host_read_flag;
|
|
sd_write_flag <= host_write_flag;
|
|
sclk_sig <= '0';
|
|
cmd_out <= (others => '1');
|
|
data_sig <= (others => '1');
|
|
byte_counter := 0;
|
|
cmd_mode <= '1'; -- 0=data, 1=command
|
|
response_mode <= '1'; -- 0=data, 1=command
|
|
|
|
block_busy <= '0';
|
|
init_busy <= '0';
|
|
dout <= (others => '0');
|
|
|
|
if (block_read = '1') then
|
|
state <= read_block_cmd;
|
|
block_start_ack <= '1';
|
|
elsif (block_write='1') then
|
|
state <= write_block_cmd;
|
|
block_start_ack <= '1';
|
|
else
|
|
state <= idle;
|
|
end if;
|
|
|
|
when read_block_cmd =>
|
|
block_busy <= '1';
|
|
block_start_ack <= '0';
|
|
cmd_out <= x"ff" & x"51" & address & x"ff"; -- CMD17 read single block
|
|
bit_counter := 55;
|
|
return_state <= read_block_wait;
|
|
state <= send_cmd;
|
|
|
|
-- wait until data token read (= 11111110)
|
|
when read_block_wait =>
|
|
if (sclk_sig='0' and sdMISO='0') then
|
|
state <= receive_byte;
|
|
byte_counter := 513; -- data plus crc
|
|
bit_counter := 8; -- ???????????????????????????????
|
|
return_state <= read_block_data;
|
|
end if;
|
|
sclk_sig <= not sclk_sig;
|
|
|
|
when read_block_data =>
|
|
if (byte_counter = 1) then -- crc byte 1 - ignore
|
|
byte_counter := byte_counter - 1;
|
|
return_state <= read_block_data;
|
|
bit_counter := 7;
|
|
state <= receive_byte;
|
|
elsif (byte_counter = 0) then -- crc byte 2 - ignore
|
|
bit_counter := 7;
|
|
return_state <= idle;
|
|
state <= receive_byte;
|
|
elsif (sd_read_flag /= host_read_flag) then
|
|
state <= read_block_data; -- stay here until previous byte read
|
|
else
|
|
byte_counter := byte_counter - 1;
|
|
return_state <= read_block_data;
|
|
bit_counter := 7;
|
|
state <= receive_byte;
|
|
end if;
|
|
|
|
when send_cmd =>
|
|
if (sclk_sig = '1') then -- sending command
|
|
if (bit_counter = 0) then -- command sent
|
|
state <= receive_byte_wait;
|
|
else
|
|
bit_counter := bit_counter - 1;
|
|
cmd_out <= cmd_out(54 downto 0) & '1';
|
|
end if;
|
|
end if;
|
|
sclk_sig <= not sclk_sig;
|
|
|
|
when send_regreq =>
|
|
if (sclk_sig = '1') then -- sending command
|
|
if (bit_counter = 0) then -- command sent
|
|
state <= receive_ocr_wait;
|
|
else
|
|
bit_counter := bit_counter - 1;
|
|
cmd_out <= cmd_out(54 downto 0) & '1';
|
|
end if;
|
|
end if;
|
|
sclk_sig <= not sclk_sig;
|
|
|
|
when receive_ocr_wait =>
|
|
if (sclk_sig = '0') then
|
|
if (sdMISO = '0') then -- wait for zero bit
|
|
recv_data <= (others => '0');
|
|
bit_counter := 38; -- already read bit 39
|
|
state <= receive_byte;
|
|
end if;
|
|
end if;
|
|
sclk_sig <= not sclk_sig;
|
|
|
|
when receive_byte_wait =>
|
|
if (sclk_sig = '0') then
|
|
if (sdMISO = '0') then -- wait for start bit
|
|
recv_data <= (others => '0');
|
|
if (response_mode='0') then -- data mode
|
|
bit_counter := 3; -- already read bits 7..4
|
|
else -- command mode
|
|
bit_counter := 6; -- already read bit 7 (start bit)
|
|
end if;
|
|
state <= receive_byte;
|
|
end if;
|
|
end if;
|
|
sclk_sig <= not sclk_sig;
|
|
|
|
-- read 8-bit data or 8-bit R1 response or 40-bit R7 response
|
|
when receive_byte =>
|
|
if (sclk_sig = '0') then
|
|
recv_data <= recv_data(38 downto 0) & sdMISO; -- read next bit
|
|
if (bit_counter = 0) then
|
|
state <= return_state;
|
|
-- if real data received then flag it (byte counter = 0 for both crc bytes)
|
|
if return_state= read_block_data and byte_counter > 0 then
|
|
sd_read_flag <= not sd_read_flag;
|
|
dout <= recv_data(7 downto 0);
|
|
end if;
|
|
else
|
|
bit_counter := bit_counter - 1;
|
|
end if;
|
|
end if;
|
|
sclk_sig <= not sclk_sig;
|
|
|
|
when write_block_cmd =>
|
|
block_busy <= '1';
|
|
block_start_ack <= '0';
|
|
cmd_mode <= '1';
|
|
cmd_out <= x"ff" & x"58" & address & x"ff"; -- CMD24 write single block
|
|
bit_counter := 55;
|
|
return_state <= write_block_init;
|
|
state <= send_cmd;
|
|
|
|
when write_block_init =>
|
|
cmd_mode <= '0';
|
|
byte_counter := write_data_size;
|
|
state <= write_block_data;
|
|
|
|
when write_block_data =>
|
|
if byte_counter = 0 then
|
|
state <= receive_byte_wait;
|
|
return_state <= write_block_wait;
|
|
response_mode <= '0';
|
|
else
|
|
if ((byte_counter = 2) or (byte_counter = 1)) then
|
|
data_sig <= x"ff"; -- two crc bytes
|
|
bit_counter := 7;
|
|
state <= write_block_byte;
|
|
byte_counter := byte_counter - 1;
|
|
elsif byte_counter = write_data_size then
|
|
data_sig <= x"fe"; -- start byte, single block
|
|
bit_counter := 7;
|
|
state <= write_block_byte;
|
|
byte_counter := byte_counter - 1;
|
|
elsif host_write_flag /= sd_write_flag then -- only send if flag set
|
|
data_sig <= din_latched;
|
|
bit_counter := 7;
|
|
state <= write_block_byte;
|
|
byte_counter := byte_counter - 1;
|
|
sd_write_flag <= not sd_write_flag;
|
|
end if;
|
|
end if;
|
|
|
|
when write_block_byte =>
|
|
if (sclk_sig = '1') then
|
|
if bit_counter=0 then
|
|
state <= write_block_data;
|
|
else
|
|
data_sig <= data_sig(6 downto 0) & '1';
|
|
bit_counter := bit_counter - 1;
|
|
end if;
|
|
end if;
|
|
sclk_sig <= not sclk_sig;
|
|
|
|
when write_block_wait =>
|
|
cmd_mode <= '1';
|
|
response_mode <= '1';
|
|
if sclk_sig='0' then
|
|
if sdMISO='1' then
|
|
state <= idle;
|
|
end if;
|
|
end if;
|
|
sclk_sig <= not sclk_sig;
|
|
|
|
when others =>
|
|
state <= idle;
|
|
end case;
|
|
end if;
|
|
end process;
|
|
|
|
sdSCLK <= sclk_sig;
|
|
sdMOSI <= cmd_out(55) when cmd_mode='1' else data_sig(7);
|
|
|
|
status(7) <= '1' when host_write_flag=sd_write_flag else '0'; -- tx byte empty when equal
|
|
status(6) <= '0' when host_read_flag=sd_read_flag else '1'; -- rx byte ready when not equal
|
|
status(5) <= block_busy;
|
|
status(4) <= init_busy;
|
|
|
|
-- Make sure the drive LED is on for a visible amount of time
|
|
ctl_led: process (clk, block_busy,init_busy)
|
|
begin
|
|
if block_busy='1' or init_busy = '1' then
|
|
led_on_count <= 200; -- ensure on for at least 200ms (assuming 1MHz clk)
|
|
driveLED <= '0';
|
|
elsif (rising_edge(clk)) then
|
|
if led_on_count>0 then
|
|
led_on_count <= led_on_count-1;
|
|
driveLED <= '0';
|
|
else
|
|
driveLED <= '1';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
end rtl;
|