1
0
mirror of synced 2026-01-29 04:41:07 +00:00
Files
rdolbeau.SBusFPGA/sbus-to-ztex-gateware/SimpleSDHC_wrapper.vhd

397 lines
16 KiB
VHDL

library ieee;
USE ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity SimpleSDHC_wrapper is
port (
SimpleSDHC_wrapper_rst : in std_logic;
SimpleSDHC_wrapper_clk : in std_logic;
output_fifo_in : out std_logic_vector(160 downto 0); --1+ 32 +128
output_fifo_full : in std_logic;
output_fifo_wr_en : out std_logic;
input_fifo_out : in std_logic_vector(127 downto 0);
input_fifo_empty : in std_logic;
input_fifo_rd_en : out std_logic;
out_sd_rd : in std_logic;
out_sd_addr : in std_logic_vector(31 downto 0);
out_sd_rd_addr_req : in std_logic;
out_sd_rd_addr_ack : out std_logic;
-- pins
cs_bo : out std_logic;
sclk_o : out std_logic;
mosi_o : out std_logic;
miso_i : in std_logic;
-- LEDs
leds : out std_logic_vector(7 downto 0)
);
end SimpleSDHC_wrapper;
architecture RTL of SimpleSDHC_wrapper is
component sd_controller is
generic (
clockRate : integer := 50000000; -- Incoming clock is 25MHz (can change this to 2000 to test Write Timeout)
slowClockDivider : integer := 128; -- For a 50MHz clock, slow clock for startup is 50/128 = 390kHz
R1_TIMEOUT : integer := 64; -- Number of bytes to wait before giving up on receiving R1 response
WRITE_TIMEOUT : integer range 0 to 999 := 500; -- Number of ms to wait before giving up on write completing
RESET_TICKS : integer := 64; -- Number of half clock cycles being pulsed before lowing sd_busy in IDLE2
ACTION_RETRIES : integer := 200; -- Number of retries when SEND_CMD_5 fails
READ_TOKEN_TIMEOUT : integer := 1000 -- Number of retries to receive the read start token "FE"
);
port (
cs : out std_logic; -- To SD card
mosi : out std_logic; -- To SD card
miso : in std_logic; -- From SD card
sclk : out std_logic; -- To SD card
card_present : in std_logic; -- From socket - can be fixed to '1' if no switch is present
card_write_prot : in std_logic; -- From socket - can be fixed to '0' if no switch is present, or '1' to make a Read-Only interface
rd : in std_logic; -- Trigger single block read
rd_multiple : in std_logic; -- Trigger multiple block read
dout : out std_logic_vector(7 downto 0); -- Data from SD card
dout_avail : out std_logic; -- Set when dout is valid
dout_taken : in std_logic; -- Acknowledgement for dout
wr : in std_logic; -- Trigger single block write
wr_multiple : in std_logic; -- Trigger multiple block write
din : in std_logic_vector(7 downto 0); -- Data to SD card
din_valid : in std_logic; -- Set when din is valid
din_taken : out std_logic; -- Ackowledgement for din
addr : in std_logic_vector(31 downto 0); -- Block address
erase_count : in std_logic_vector(7 downto 0); -- For wr_multiple only
sd_error : out std_logic; -- '1' if an error occurs, reset on next RD or WR
sd_busy : out std_logic; -- '0' if a RD or WR can be accepted
sd_error_code : out std_logic_vector(7 downto 0); -- See above, 000=No error
reset : in std_logic; -- System reset
clk : in std_logic; -- twice the SPI clk (max 50MHz)
-- Optional debug outputs
sd_type : out std_logic_vector(1 downto 0); -- Card status (see above)
sd_fsm : out std_logic_vector(7 downto 0) := "11111111" -- FSM state (see block at end of file)
);
end component;
signal sd_reset : std_logic;
signal sd_rd : std_logic := '0';
signal sd_wr : std_logic := '0';
signal sd_continue : std_logic := '0';
signal sd_addr : std_logic_vector(31 downto 0);
signal sd_data_i : std_logic_vector(7 downto 0);
signal sd_data_o : std_logic_vector(7 downto 0);
signal sd_busy : std_logic;
signal sd_dout_avail : std_logic;
signal sd_dout_taken : std_logic := '0';
signal sd_din_valid : std_logic := '0';
signal sd_din_taken : std_logic;
signal sd_error : std_logic;
signal sd_error_code : std_logic_vector(7 downto 0);
signal sd_type : std_logic_vector(1 downto 0);
constant BLOCK_SIZE_G : natural := 512;
TYPE SIMPLESDHC_States IS (
SIMPLESDHC_IDLE,
SIMPLESDHC_INIT,
SIMPLESDHC_READ_WAIT_BUSY,
SIMPLESDHC_READ_WAIT_READ,
SIMPLESDHC_READ_WAIT_READ2,
SIMPLESDHC_WAIT_NOTBUSY,
SIMPLESDHC_WRITE_WAIT_BUSY,
SIMPLESDHC_WRITE_WAIT_WRITE,
SIMPLESDHC_WRITE_WAIT_WRITE2);
SIGNAL SIMPLESDHC_State : SIMPLESDHC_States := SIMPLESDHC_INIT;
begin
label_sd_controller: sd_controller
generic map (
clockRate => 50000000,
slowClockDivider => 128,
R1_TIMEOUT => 64,
WRITE_TIMEOUT => 500
)
port map (
-- pins
cs => cs_bo,
mosi => mosi_o,
miso => miso_i,
sclk => sclk_o,
card_present => '1',
card_write_prot => '0',
-- internal
rd => sd_rd,
rd_multiple => '0',
dout => sd_data_o,
dout_avail => sd_dout_avail,
dout_taken => sd_dout_taken,
wr => sd_wr,
wr_multiple => '0',
din => sd_data_i,
din_valid => sd_din_valid,
din_taken => sd_din_taken,
addr => sd_addr,
erase_count => (others => '0'),
sd_error => sd_error,
sd_busy => sd_busy,
sd_error_code => sd_error_code,
reset => sd_reset,
clk => SimpleSDHC_wrapper_clk,
-- Optional debug outputs
sd_type => sd_type,
sd_fsm => leds
);
SimpleSDHC_wrapper: process (SimpleSDHC_wrapper_rst, SimpleSDHC_wrapper_clk)
variable init_done : boolean := false;
constant TIMEOUT_MAX : integer := 5000000;
variable timeout_counter : natural range 0 to TIMEOUT_MAX := TIMEOUT_MAX;
variable timedout : std_logic_vector(15 downto 0) := x"0000";
variable byte_counter : natural range 0 to BLOCK_SIZE_G := 0; -- fixme, wasteful
variable databuf : std_logic_vector(127 downto 0);
variable buf_counter : natural range 0 to 65535 := 0;
variable last_addr : std_logic_vector(31 downto 0);
begin -- process SimpleSDHC_wrapper
IF (SimpleSDHC_wrapper_rst = '0') THEN
-- if (RISING_EDGE(SimpleSDHC_wrapper_clk)) THEN
sd_reset <= '1';
SIMPLESDHC_State <= SIMPLESDHC_INIT;
timedout := x"0000";
byte_counter := 0;
timeout_counter := TIMEOUT_MAX;
init_done := false;
buf_counter := 0;
-- end if;
ELSIF RISING_EDGE(SimpleSDHC_wrapper_clk) then
sd_reset <= '0';
output_fifo_wr_en <= '0';
input_fifo_rd_en <= '0';
if (out_sd_rd_addr_req = '0') THEN
out_sd_rd_addr_ack <= '0';
END IF;
-- out_sd_rd_addr_ack <= '0';
case SIMPLESDHC_State IS
when SIMPLESDHC_IDLE =>
sd_rd <= '0';
sd_wr <= '0';
sd_continue <= '0';
sd_dout_taken <= '0';
sd_din_valid <= '0';
if ((sd_busy = '0') and (out_sd_rd_addr_req ='1')) THEN -- handshake
--output_fifo_in <= '1' & x"7000" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
out_sd_rd_addr_ack <= '1';
sd_addr <= out_sd_addr;
last_addr := out_sd_addr;
byte_counter := 0;
buf_counter := 0;
timeout_counter := TIMEOUT_MAX;
IF (out_sd_rd = '1') THEN
--output_fifo_in <= '1' & x"6000" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
sd_rd <= '1';
SIMPLESDHC_State <= SIMPLESDHC_READ_WAIT_BUSY;
ELSE
sd_wr <= '1';
SIMPLESDHC_State <= SIMPLESDHC_WRITE_WAIT_BUSY;
END IF;
END IF;
-- if (timeout_counter = 0) then
-- output_fifo_in <= (NOT timedout) & sd_error;
-- output_fifo_wr_en <= '1';
-- timedout := conv_std_logic_vector(conv_integer(timedout)+1,16);
-- timeout_counter := TIMEOUT_MAX;
-- else
-- timeout_counter := timeout_counter - 1;
-- end if;
when SIMPLESDHC_INIT =>
sd_rd <= '0';
sd_wr <= '0';
sd_continue <= '0';
sd_addr <= (others => '0');
sd_data_i <= (others => '0');
sd_din_valid <= '0';
sd_dout_taken <= '0';
out_sd_rd_addr_ack <= '0';
IF (sd_busy = '0') THEN
SIMPLESDHC_State <= SIMPLESDHC_IDLE;
output_fifo_in <= '1' & x"8000" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
output_fifo_wr_en <= '1';
elsif (init_done = false) THEN
output_fifo_in <= '1' & x"0F0F0F0F" & x"00000000000000000000000000000000";
output_fifo_wr_en <= '1';
init_done := true;
elsif (timeout_counter = 0) then
output_fifo_in <= '1' & x"F" & timedout(11 downto 0) & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
output_fifo_wr_en <= '1';
timedout := conv_std_logic_vector(conv_integer(timedout)+1,16);
timeout_counter := TIMEOUT_MAX;
else
timeout_counter := timeout_counter - 1;
end IF;
when SIMPLESDHC_READ_WAIT_BUSY =>
IF (sd_busy = '1') THEN
--output_fifo_in <= '1' & x"5000" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
--sd_addr <= (others => '0');
SIMPLESDHC_State <= SIMPLESDHC_READ_WAIT_READ;
timeout_counter := TIMEOUT_MAX;
elsif (timeout_counter = 0) then
--output_fifo_in <= '1' & x"E" & timedout(11 downto 0) & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
timedout := conv_std_logic_vector(conv_integer(timedout)+1,16);
timeout_counter := TIMEOUT_MAX;
else
timeout_counter := timeout_counter - 1;
END IF;
when SIMPLESDHC_READ_WAIT_READ =>
IF (sd_error = '1') THEN
--output_fifo_in <= '1' & x"1100" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
sd_rd <= '0';
SIMPLESDHC_State <= SIMPLESDHC_IDLE;
--only read byte if we'll have some space to output the buffer
ELSIF ((output_fifo_full = '0') AND (sd_dout_avail = '1')) THEN
--output_fifo_in <= '1' & x"40" & sd_data_o & conv_std_logic_vector(byte_counter,16) & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
databuf(((15 - (byte_counter mod 16))*8 + 7) downto ((15 - (byte_counter mod 16))*8)) := sd_data_o;
sd_dout_taken <= '1';
byte_counter := byte_counter + 1;
SIMPLESDHC_State <= SIMPLESDHC_READ_WAIT_READ2;
-- ELSIF ((output_fifo_full = '0') AND (timeout_counter = 0)) THEN
--output_fifo_in <= '1' & x"1100" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
-- SIMPLESDHC_State <= SIMPLESDHC_IDLE;
-- ELSIF (output_fifo_full = '0') THEN
-- timeout_counter := timeout_counter - 1;
ELSIF (sd_busy = '0') THEN
--output_fifo_in <= '1' & x"1000" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
SIMPLESDHC_State <= SIMPLESDHC_IDLE;
END IF;
when SIMPLESDHC_READ_WAIT_READ2 =>
IF (sd_error = '1') THEN
--output_fifo_in <= '1' & x"3100" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
sd_rd <= '0';
SIMPLESDHC_State <= SIMPLESDHC_IDLE;
ELSIF (sd_dout_avail = '0') THEN
--output_fifo_in <= '1' & x"3000" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
sd_dout_taken <= '0';
timeout_counter := TIMEOUT_MAX;
IF ((byte_counter mod 16) = 0) THEN
output_fifo_in <= '0' & last_addr(15 downto 0) & conv_std_logic_vector(buf_counter,16) & databuf;
output_fifo_wr_en <= '1';
buf_counter := buf_counter + 1;
END IF;
IF (byte_counter = BLOCK_SIZE_G) THEN
sd_rd <= '0';
SIMPLESDHC_State <= SIMPLESDHC_WAIT_NOTBUSY;
ELSE
SIMPLESDHC_State <= SIMPLESDHC_READ_WAIT_READ;
END IF;
END IF;
when SIMPLESDHC_WAIT_NOTBUSY =>
IF (sd_busy = '0') THEN
SIMPLESDHC_State <= SIMPLESDHC_IDLE;
elsif (timeout_counter = 0) then
output_fifo_in <= '1' & x"D" & timedout(11 downto 0) & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
output_fifo_wr_en <= '1';
timedout := conv_std_logic_vector(conv_integer(timedout)+1,16);
timeout_counter := TIMEOUT_MAX;
else
timeout_counter := timeout_counter - 1;
END IF;
when SIMPLESDHC_WRITE_WAIT_BUSY =>
IF (sd_busy = '1') THEN
--output_fifo_in <= '1' & x"5001" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
--sd_addr <= (others => '0');
SIMPLESDHC_State <= SIMPLESDHC_WRITE_WAIT_WRITE;
timeout_counter := TIMEOUT_MAX;
elsif (timeout_counter = 0) then
output_fifo_in <= '1' & x"C" & timedout(11 downto 0) & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
output_fifo_wr_en <= '1';
timedout := conv_std_logic_vector(conv_integer(timedout)+1,16);
timeout_counter := TIMEOUT_MAX;
else
timeout_counter := timeout_counter - 1;
END IF;
when SIMPLESDHC_WRITE_WAIT_WRITE =>
IF (sd_error = '1') THEN
output_fifo_in <= '1' & x"1101" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
output_fifo_wr_en <= '1';
sd_wr <= '0';
SIMPLESDHC_State <= SIMPLESDHC_IDLE;
--only write byte if we have some space to output the buffer
ELSIF ((input_fifo_empty = '0') OR ((byte_counter mod 16) /= 0)) THEN
--output_fifo_in <= '1' & x"40" & sd_data_o & conv_std_logic_vector(byte_counter,16) & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
IF ((byte_counter mod 16) = 0) THEN
databuf := input_fifo_out;
input_fifo_rd_en <= '1';
END IF;
sd_data_i <= databuf(((15 - (byte_counter mod 16))*8 + 7) downto ((15 - (byte_counter mod 16))*8));
sd_din_valid <= '1';
byte_counter := byte_counter + 1;
SIMPLESDHC_State <= SIMPLESDHC_WRITE_WAIT_WRITE2;
-- ELSIF ((input_fifo_empty = '0') AND (timeout_counter = 0)) THEN
--output_fifo_in <= '1' & x"1101" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
-- SIMPLESDHC_State <= SIMPLESDHC_IDLE;
-- ELSIF (input_fifo_empty = '0') THEN
-- timeout_counter := timeout_counter - 1;
ELSIF (sd_busy = '0') THEN
output_fifo_in <= '1' & x"1001" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
output_fifo_wr_en <= '1';
SIMPLESDHC_State <= SIMPLESDHC_IDLE;
END IF;
when SIMPLESDHC_WRITE_WAIT_WRITE2 =>
IF (sd_error = '1') THEN
output_fifo_in <= '1' & x"3101" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
output_fifo_wr_en <= '1';
sd_wr <= '0';
SIMPLESDHC_State <= SIMPLESDHC_IDLE;
ELSIF (sd_din_taken = '1') THEN
--output_fifo_in <= '1' & x"3001" & x"0" & sd_type & '0' & sd_error & sd_error_code & x"00000000000000000000000000000000";
--output_fifo_wr_en <= '1';
sd_din_valid <= '0';
timeout_counter := TIMEOUT_MAX;
IF (byte_counter = BLOCK_SIZE_G) THEN
sd_wr <= '0';
SIMPLESDHC_State <= SIMPLESDHC_WAIT_NOTBUSY;
ELSE
SIMPLESDHC_State <= SIMPLESDHC_WRITE_WAIT_WRITE;
END IF;
END IF;
end case;
end IF;
end process SimpleSDHC_wrapper;
end RTL;