mirror of https://github.com/marqs85/ossc.git
348 lines
13 KiB
VHDL
348 lines
13 KiB
VHDL
-- (C) 2001-2015 Altera Corporation. All rights reserved.
|
|
-- Your use of Altera Corporation's design tools, logic functions and other
|
|
-- software and tools, and its AMPP partner logic functions, and any output
|
|
-- files any of the foregoing (including device programming or simulation
|
|
-- files), and any associated documentation or information are expressly subject
|
|
-- to the terms and conditions of the Altera Program License Subscription
|
|
-- Agreement, Altera MegaCore Function License Agreement, or other applicable
|
|
-- license agreement, including, without limitation, that your use is for the
|
|
-- sole purpose of programming logic devices manufactured by Altera and sold by
|
|
-- Altera or its authorized distributors. Please refer to the applicable
|
|
-- agreement for further details.
|
|
|
|
|
|
----------------------------------------------------------------------------------------------------------------
|
|
-- This is an FSM that controls the SD Card interface circuitry.
|
|
--
|
|
-- On reset, the FSM will initiate a predefined set of commands in an attempt to connect to the SD Card.
|
|
-- When successful, it will allow commands to be issued to the SD Card, otherwise it will return a signal that
|
|
-- no card is present in the SD Card slot.
|
|
--
|
|
-- NOTES/REVISIONS:
|
|
----------------------------------------------------------------------------------------------------------------
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.std_logic_arith.all;
|
|
use ieee.std_logic_unsigned.all;
|
|
|
|
entity Altera_UP_SD_Card_Control_FSM is
|
|
generic (
|
|
PREDEFINED_COMMAND_GET_STATUS : STD_LOGIC_VECTOR(3 downto 0) := "1001"
|
|
);
|
|
port
|
|
(
|
|
-- Clock and Reset signals
|
|
i_clock : in STD_LOGIC;
|
|
i_reset_n : in STD_LOGIC;
|
|
|
|
-- FSM Inputs
|
|
i_user_command_ready : in std_logic;
|
|
i_response_received : in STD_LOGIC;
|
|
i_response_timed_out : in STD_LOGIC;
|
|
i_response_crc_passed : in STD_LOGIC;
|
|
i_command_sent : in STD_LOGIC;
|
|
i_powerup_busy_n : in STD_LOGIC;
|
|
i_clocking_pulse_enable : in std_logic;
|
|
i_current_clock_mode : in std_logic;
|
|
i_user_message_valid : in std_logic;
|
|
i_last_cmd_was_55 : in std_logic;
|
|
i_allow_partial_rw : in std_logic;
|
|
|
|
-- FSM Outputs
|
|
o_generate_command : out STD_LOGIC;
|
|
o_predefined_command_ID : out STD_LOGIC_VECTOR(3 downto 0);
|
|
o_receive_response : out STD_LOGIC;
|
|
o_drive_CMD_line : out STD_LOGIC;
|
|
o_SD_clock_mode : out STD_LOGIC; -- 0 means slow clock for card identification, 1 means fast clock for transfer mode.
|
|
o_resetting : out std_logic;
|
|
o_card_connected : out STD_LOGIC;
|
|
o_command_completed : out std_logic;
|
|
o_clear_response_register : out std_logic;
|
|
o_enable_clock_generator : out std_logic
|
|
);
|
|
|
|
end entity;
|
|
|
|
architecture rtl of Altera_UP_SD_Card_Control_FSM is
|
|
|
|
-- Build an enumerated type for the state machine. On reset always reset the DE2 and read the state
|
|
-- of the switches.
|
|
type state_type is (s_RESET, s_WAIT_74_CYCLES, s_GENERATE_PREDEFINED_COMMAND, s_WAIT_PREDEFINED_COMMAND_TRANSMITTED, s_WAIT_PREDEFINED_COMMAND_RESPONSE,
|
|
s_GO_TO_NEXT_COMMAND, s_TOGGLE_CLOCK_FREQUENCY, s_AWAIT_USER_COMMAND, s_REACTIVATE_CLOCK,
|
|
s_GENERATE_COMMAND, s_SEND_COMMAND, s_WAIT_RESPONSE, s_WAIT_FOR_CLOCK_EDGE_BEFORE_DISABLE, s_WAIT_DEASSERT,
|
|
s_PERIODIC_STATUS_CHECK);
|
|
|
|
-- Register to hold the current state
|
|
signal current_state : state_type;
|
|
signal next_state : state_type;
|
|
|
|
-------------------
|
|
-- Local signals
|
|
-------------------
|
|
-- REGISTERED
|
|
signal SD_clock_mode, waiting_for_vdd_setup : std_logic;
|
|
signal id_sequence_step_index : std_logic_vector(3 downto 0);
|
|
signal delay_counter : std_logic_vector(6 downto 0);
|
|
signal periodic_status_check : std_logic_vector(23 downto 0);
|
|
-- UNREGISTERED
|
|
|
|
begin
|
|
-- Define state transitions.
|
|
state_transitions: process (current_state, i_command_sent, i_response_received, id_sequence_step_index,
|
|
i_response_timed_out, i_response_crc_passed, delay_counter, waiting_for_vdd_setup,
|
|
i_user_command_ready, i_clocking_pulse_enable, i_current_clock_mode,
|
|
i_user_message_valid, i_last_cmd_was_55, periodic_status_check)
|
|
begin
|
|
case current_state is
|
|
when s_RESET =>
|
|
-- Reset local registers and begin identification process.
|
|
next_state <= s_WAIT_74_CYCLES;
|
|
|
|
when s_WAIT_74_CYCLES =>
|
|
-- Wait 74 cycles before the card can be sent commands to.
|
|
if (delay_counter = "1001010") then
|
|
next_state <= s_GENERATE_PREDEFINED_COMMAND;
|
|
else
|
|
next_state <= s_WAIT_74_CYCLES;
|
|
end if;
|
|
|
|
when s_GENERATE_PREDEFINED_COMMAND =>
|
|
-- Generate a predefined command to the SD card. This is the identification process for the SD card.
|
|
next_state <= s_WAIT_PREDEFINED_COMMAND_TRANSMITTED;
|
|
|
|
when s_WAIT_PREDEFINED_COMMAND_TRANSMITTED =>
|
|
-- Send a predefined command to the SD card. This is the identification process for the SD card.
|
|
if (i_command_sent = '1') then
|
|
next_state <= s_WAIT_PREDEFINED_COMMAND_RESPONSE;
|
|
else
|
|
next_state <= s_WAIT_PREDEFINED_COMMAND_TRANSMITTED;
|
|
end if;
|
|
|
|
when s_WAIT_PREDEFINED_COMMAND_RESPONSE =>
|
|
-- Wait for a response from SD card.
|
|
if (i_response_received = '1') then
|
|
if (i_response_timed_out = '1') then
|
|
if (waiting_for_vdd_setup = '1') then
|
|
next_state <= s_GO_TO_NEXT_COMMAND;
|
|
else
|
|
next_state <= s_RESET;
|
|
end if;
|
|
else
|
|
if (i_response_crc_passed = '0') then
|
|
next_state <= s_GENERATE_PREDEFINED_COMMAND;
|
|
else
|
|
next_state <= s_GO_TO_NEXT_COMMAND;
|
|
end if;
|
|
end if;
|
|
else
|
|
next_state <= s_WAIT_PREDEFINED_COMMAND_RESPONSE;
|
|
end if;
|
|
|
|
when s_GO_TO_NEXT_COMMAND =>
|
|
-- Process the next command in the ID sequence.
|
|
if (id_sequence_step_index = PREDEFINED_COMMAND_GET_STATUS) then
|
|
next_state <= s_TOGGLE_CLOCK_FREQUENCY;
|
|
else
|
|
next_state <= s_GENERATE_PREDEFINED_COMMAND;
|
|
end if;
|
|
|
|
when s_TOGGLE_CLOCK_FREQUENCY =>
|
|
-- Now that the card has been initialized, increase the SD card clock frequency to 25MHz.
|
|
-- Wait for the clock generator to switch operating mode before proceeding further.
|
|
if (i_current_clock_mode = '1') then
|
|
next_state <= s_AWAIT_USER_COMMAND;
|
|
else
|
|
next_state <= s_TOGGLE_CLOCK_FREQUENCY;
|
|
end if;
|
|
|
|
when s_AWAIT_USER_COMMAND =>
|
|
-- Wait for the user to send a command to the SD card
|
|
if (i_user_command_ready = '1') then
|
|
next_state <= s_REACTIVATE_CLOCK;
|
|
else
|
|
-- Every 5 million cycles, or 0.1 of a second.
|
|
if (periodic_status_check = "010011000100101101000000") then
|
|
next_state <= s_PERIODIC_STATUS_CHECK;
|
|
else
|
|
next_state <= s_AWAIT_USER_COMMAND;
|
|
end if;
|
|
end if;
|
|
|
|
when s_PERIODIC_STATUS_CHECK =>
|
|
-- Update status every now and then.
|
|
next_state <= s_GENERATE_PREDEFINED_COMMAND;
|
|
|
|
when s_REACTIVATE_CLOCK =>
|
|
-- Activate the clock signal and wait 8 clock cycles.
|
|
if (delay_counter = "0001000") then
|
|
next_state <= s_GENERATE_COMMAND;
|
|
else
|
|
next_state <= s_REACTIVATE_CLOCK;
|
|
end if;
|
|
|
|
when s_GENERATE_COMMAND =>
|
|
-- Generate user command. If valid, proceed further. Otherwise, indicate that the command is invalid.
|
|
if (i_user_message_valid = '0') then
|
|
next_state <= s_WAIT_DEASSERT;
|
|
else
|
|
next_state <= s_SEND_COMMAND;
|
|
end if;
|
|
|
|
when s_SEND_COMMAND =>
|
|
-- Wait for the command to be sent.
|
|
if (i_command_sent = '1') then
|
|
next_state <= s_WAIT_RESPONSE;
|
|
else
|
|
next_state <= s_SEND_COMMAND;
|
|
end if;
|
|
|
|
when s_WAIT_RESPONSE =>
|
|
-- Wait for the SD card to respond.
|
|
if (i_response_received = '1') then
|
|
if (i_response_timed_out = '1') then
|
|
next_state <= s_WAIT_DEASSERT;
|
|
else
|
|
next_state <= s_WAIT_FOR_CLOCK_EDGE_BEFORE_DISABLE;
|
|
end if;
|
|
else
|
|
next_state <= s_WAIT_RESPONSE;
|
|
end if;
|
|
|
|
when s_WAIT_FOR_CLOCK_EDGE_BEFORE_DISABLE =>
|
|
-- Wait for a positive clock edge before you disable the clock.
|
|
if (i_clocking_pulse_enable = '1') then
|
|
next_state <= s_WAIT_DEASSERT;
|
|
else
|
|
next_state <= s_WAIT_FOR_CLOCK_EDGE_BEFORE_DISABLE;
|
|
end if;
|
|
|
|
when s_WAIT_DEASSERT =>
|
|
-- wait for the user to release command generation request.
|
|
if (i_user_command_ready = '1') then
|
|
next_state <= s_WAIT_DEASSERT;
|
|
else
|
|
if (i_last_cmd_was_55 = '1') then
|
|
next_state <= s_AWAIT_USER_COMMAND;
|
|
else
|
|
-- Send a get status command to obtain the result of sending the last command.
|
|
next_state <= s_GENERATE_PREDEFINED_COMMAND;
|
|
end if;
|
|
end if;
|
|
|
|
when others =>
|
|
-- Make sure to start in the reset state if the circuit powers up in an odd state.
|
|
next_state <= s_RESET;
|
|
end case;
|
|
end process;
|
|
|
|
-- State registers.
|
|
state_registers: process (i_clock, i_reset_n)
|
|
begin
|
|
if (i_reset_n = '0') then
|
|
current_state <= s_RESET;
|
|
elsif (rising_edge(i_clock)) then
|
|
current_state <= next_state;
|
|
end if;
|
|
end process;
|
|
|
|
-- Local FFs:
|
|
local_ffs:process ( i_clock, i_reset_n, i_powerup_busy_n, current_state,
|
|
id_sequence_step_index, i_response_received, i_response_timed_out,
|
|
i_allow_partial_rw)
|
|
begin
|
|
if (i_reset_n = '0') then
|
|
SD_clock_mode <= '0';
|
|
id_sequence_step_index <= (OTHERS => '0');
|
|
periodic_status_check <= (OTHERS => '0');
|
|
waiting_for_vdd_setup <= '0';
|
|
elsif (rising_edge(i_clock)) then
|
|
-- Set SD clock mode to 0 initially, thereby using a clock with frequency between 100 kHz and 400 kHz as
|
|
-- per SD card specifications. When the card is initialized change the clock to run at 25 MHz.
|
|
if (current_state = s_WAIT_DEASSERT) then
|
|
periodic_status_check <= (OTHERS => '0');
|
|
elsif (current_state = s_AWAIT_USER_COMMAND) then
|
|
periodic_status_check <= periodic_status_check + '1';
|
|
end if;
|
|
|
|
if (current_state = s_RESET) then
|
|
SD_clock_mode <= '0';
|
|
elsif (current_state = s_TOGGLE_CLOCK_FREQUENCY) then
|
|
SD_clock_mode <= '1';
|
|
end if;
|
|
-- Update the ID sequence step as needed.
|
|
if (current_state = s_RESET) then
|
|
id_sequence_step_index <= (OTHERS => '0');
|
|
elsif (current_state = s_GO_TO_NEXT_COMMAND) then
|
|
if ((i_powerup_busy_n = '0') and (id_sequence_step_index = "0010")) then
|
|
id_sequence_step_index <= "0001";
|
|
else
|
|
if (id_sequence_step_index = "0110") then
|
|
if (i_allow_partial_rw = '0') then
|
|
-- If partial read-write not allowed, then skip SET_BLK_LEN command - it will fail.
|
|
id_sequence_step_index <= "1000";
|
|
else
|
|
id_sequence_step_index <= "0111";
|
|
end if;
|
|
else
|
|
id_sequence_step_index <= id_sequence_step_index + '1';
|
|
end if;
|
|
end if;
|
|
elsif (current_state = s_WAIT_DEASSERT) then
|
|
if (i_last_cmd_was_55 = '0') then
|
|
-- After each command execute a get status command.
|
|
id_sequence_step_index <= PREDEFINED_COMMAND_GET_STATUS;
|
|
end if;
|
|
elsif (current_state = s_PERIODIC_STATUS_CHECK) then
|
|
id_sequence_step_index <= PREDEFINED_COMMAND_GET_STATUS;
|
|
end if;
|
|
|
|
-- Do not reset the card when SD card is having its VDD set up. Wait for it to respond, this may take some time.
|
|
if (id_sequence_step_index = "0010") then
|
|
waiting_for_vdd_setup <= '1';
|
|
elsif ((id_sequence_step_index = "0011") or (current_state = s_RESET)) then
|
|
waiting_for_vdd_setup <= '0';
|
|
end if;
|
|
|
|
end if;
|
|
end process;
|
|
|
|
-- Counter that counts to 74 to delay any commands.
|
|
initial_delay_counter: process(i_clock, i_reset_n, i_clocking_pulse_enable )
|
|
begin
|
|
if (i_reset_n = '0') then
|
|
delay_counter <= (OTHERS => '0');
|
|
elsif (rising_edge(i_clock)) then
|
|
if ((current_state = s_RESET) or (current_state = s_AWAIT_USER_COMMAND))then
|
|
delay_counter <= (OTHERS => '0');
|
|
elsif (((current_state = s_WAIT_74_CYCLES) or (current_state = s_REACTIVATE_CLOCK)) and
|
|
(i_clocking_pulse_enable = '1')) then
|
|
delay_counter <= delay_counter + '1';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- FSM outputs.
|
|
o_SD_clock_mode <= SD_clock_mode;
|
|
o_generate_command <= '1' when ((current_state = s_GENERATE_PREDEFINED_COMMAND) or
|
|
(current_state = s_GENERATE_COMMAND))
|
|
else '0';
|
|
o_receive_response <= '1' when ((current_state = s_WAIT_PREDEFINED_COMMAND_RESPONSE) or
|
|
(current_state = s_WAIT_RESPONSE))
|
|
else '0';
|
|
o_drive_CMD_line <= '1' when ( (current_state = s_WAIT_PREDEFINED_COMMAND_TRANSMITTED) or
|
|
(current_state = s_SEND_COMMAND)) else '0';
|
|
o_predefined_command_ID <= id_sequence_step_index;
|
|
o_card_connected <= '1' when (id_sequence_step_index(3) = '1') and (
|
|
(id_sequence_step_index(2) = '1') or
|
|
(id_sequence_step_index(1) = '1') or
|
|
(id_sequence_step_index(0) = '1'))
|
|
else '0';
|
|
o_resetting <= '1' when (current_state = s_RESET) else '0';
|
|
o_command_completed <= '1' when (current_state = s_WAIT_DEASSERT) else '0';
|
|
o_enable_clock_generator <= '0' when (current_state = s_AWAIT_USER_COMMAND) else '1';
|
|
o_clear_response_register <= '1' when (current_state = s_REACTIVATE_CLOCK) else '0';
|
|
|
|
end rtl;
|
|
|