*

i2c_manager.vhd


--------------------------------------------------------------------------

-- FILENAME : i2c_manager.vhd
--
-- Processes the commands coming from i2c_slave.
--
-- AUTHOR : Craig Dunn
-- DATE STARTED : 16 April 2004
-- TAB SETTING : 4
-- RESET : Async (active low)
-- KNOWN BUGS : None
-- VERSION : 1.0
--
-- All of the design and code in this module is my own work. No design or
-- code has been borrowed or copied from any source.
--------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity i2c_manager is
port ( RST : in std_logic;
MCLK : in std_logic;
RAM_DO : in std_logic_vector(7 downto 0);
RDY : in std_logic;
BSY : in std_logic;
CRC_ERROR : in std_logic;
SEEK_ERROR : in std_logic;
WP_ERROR : in std_logic;
FAT_VALUE_IN : in std_logic_vector(11 downto 0);
DIR_OFFSET : in std_logic_vector(8 downto 0);
I2C_ADDR : in std_logic_vector(2 downto 0);
TRACK : out std_logic_vector(7 downto 0);
SIDE : out std_logic;
WR_SECT : out std_logic;
RD_SECT : out std_logic;
SECTOR : out std_logic_vector(4 downto 0);
FAT_VALUE_OUT : out std_logic_vector(11 downto 0);
FAT_ENTRY : out std_logic_vector(11 downto 0);
DIR_ENTRY : out std_logic_vector(7 downto 0);
MEM_WE : out std_logic;
RAM_CLK : out std_logic;
RD_FAT : out std_logic;
WR_FAT : out std_logic;
SET_DIR : out std_logic;
WR_DIR : out std_logic;
RAM_DI : out std_logic_vector(7 downto 0);
MEM_ADDR : out std_logic_vector(8 downto 0);
SCL : inout std_logic;
SDA : inout std_logic
);
end i2c_manager;

architecture i2c_manager_arch of i2c_manager is

component i2c_slave
port(
RST : in std_logic;
MCLK : in std_logic;
DATA_IN : in std_logic_vector(7 downto 0);
ACK_IN : in std_logic;
RECV : in std_logic;
SEND : in std_logic;
SCL : inout std_logic;
SDA : inout std_logic;
DATA_OUT : out std_logic_vector(7 downto 0);
HOLDING : out std_logic;
BYTE_COUNT : out std_logic_vector(9 downto 0);
START : out std_logic
);
end component;

type state_type is ( CLOCK_RAM, READ_RAM, INC_ADDR, WAIT_FOR_NEXT );

signal i2c_cmd : std_logic_vector(7 downto 0);
signal state : state_type;
signal i2c_ack_in : std_logic;
signal i2c_holding : std_logic;
signal i2c_recv : std_logic;
signal i2c_send : std_logic;
signal i2c_send_data : std_logic;
signal i2c_start : std_logic;
signal i2c_recv_control : std_logic;
signal i2c_recv_command : std_logic;
signal i2c_recv_fat : std_logic;
signal cmd_valid : std_logic;
signal i2c_recv_sector : std_logic;
signal i2c_recv_fat_v : std_logic;
signal i2c_active : std_logic;
signal i2c_rw : std_logic;
signal i2c_send_command : std_logic;
signal i2c_recv_data : std_logic;
signal i2c_recv_set_dir : std_logic;
signal idc_address : std_logic_vector(6 downto 0);
signal i2c_data_in : std_logic_vector(7 downto 0);
signal i2c_data_out : std_logic_vector(7 downto 0);
signal data : std_logic_vector(7 downto 0);
signal status : std_logic_vector(7 downto 0);
signal d_entry : std_logic_vector(7 downto 0);
signal address : std_logic_vector(8 downto 0);
signal byte_cnt : std_logic_vector(9 downto 0);
signal f_entry : std_logic_vector(11 downto 0);
signal f_value : std_logic_vector(11 downto 0);

constant WRITE : std_logic := '0';
constant READ : std_logic := '1';
constant CMD_FAT_ENTRY : std_logic_vector(7 downto 0) := x"46";
constant CMD_FAT_VALUE : std_logic_vector(7 downto 0) := x"47";
constant CMD_SECTOR : std_logic_vector(7 downto 0) := x"4F";
constant CMD_DATA : std_logic_vector(7 downto 0) := x"F0";
constant CMD_STATUS : std_logic_vector(7 downto 0) := x"53";
constant CMD_DIR_ENTRY : std_logic_vector(7 downto 0) := x"88";
constant CMD_DIR_VALUE : std_logic_vector(7 downto 0) := x"99";

begin

idc_address(6 downto 4) <= (others => '0');
idc_address(3) <= I2C_ADDR(2);
idc_address(2) <= I2C_ADDR(1);
idc_address(1) <= I2C_ADDR(0);
idc_address(0) <= '1';


c0 : i2c_slave PORT MAP(
RST => RST,
MCLK => MCLK,
DATA_IN => i2c_data_in,
DATA_OUT => i2c_data_out,
ACK_IN => i2c_ack_in,
SCL => SCL,
SDA => SDA,
HOLDING => i2c_holding,
RECV => i2c_recv,
SEND => i2c_send,
BYTE_COUNT => byte_cnt,
START => i2c_start
);


DIR_ENTRY <= d_entry;
FAT_ENTRY <= f_entry;
RAM_DI <= i2c_data_out;
FAT_VALUE_OUT <= f_value;

status(0) <= RDY;
status(1) <= BSY;
status(2) <= CRC_ERROR;
status(3) <= SEEK_ERROR;
status(4) <= WP_ERROR;
status(7 downto 5) <= (others => '0');

cmd_valid <= '1' when (i2c_cmd = CMD_DIR_ENTRY) or (i2c_cmd = CMD_FAT_ENTRY) or
(i2c_cmd = CMD_FAT_VALUE) or (i2c_cmd = CMD_SECTOR) or
(i2c_cmd = CMD_DATA) or (i2c_cmd = CMD_DIR_VALUE) or
(i2c_cmd = CMD_STATUS and i2c_rw = READ) else '0';

i2c_ack_in <= '0' when (byte_cnt = x"001" and i2c_active = '1') or
(byte_cnt = x"002" and cmd_valid = '1') or
(byte_cnt > x"002" and i2c_cmd /= CMD_STATUS and
(not((i2c_cmd = CMD_FAT_VALUE or i2c_cmd = CMD_DATA or i2c_cmd = CMD_DIR_VALUE) and i2c_rw = READ)))
else '1';

-- One of these, but not both, must be asserted otherwise
-- SCL will be held low forever.
i2c_send <= '1' when (i2c_cmd = CMD_STATUS or i2c_cmd = CMD_FAT_VALUE) and i2c_rw = READ else
(i2c_send_command or i2c_send_data);

i2c_recv <= i2c_recv_control or i2c_recv_command or i2c_recv_fat or i2c_recv_sector or
i2c_recv_data or i2c_recv_fat_v or i2c_recv_set_dir;

i2c_data_in <= status when (i2c_cmd = CMD_STATUS and i2c_rw = READ and byte_cnt = x"3") else
("0000" & FAT_VALUE_IN(11 downto 8)) when (i2c_cmd = CMD_FAT_VALUE and i2c_rw = READ and byte_cnt = x"3") else
FAT_VALUE_IN(7 downto 0) when (i2c_cmd = CMD_FAT_VALUE and i2c_rw = READ and byte_cnt = x"4") else
data when ((i2c_cmd = CMD_DATA or i2c_cmd = CMD_DIR_VALUE) and i2c_rw = READ ) else
(others => '1');

--and byte_cnt > x"2"

MEM_WE <= '1' when (i2c_cmd = CMD_DATA or i2c_cmd = CMD_DIR_VALUE) and i2c_rw = WRITE else '0';
MEM_ADDR <= address when i2c_cmd /= CMD_DIR_VALUE else DIR_OFFSET + address;


--------------------------------------------------------------------------
-- PROCESS : control_byte
--
-- Detect the control byte. When a STOP is detected processes the current
-- command, if any.
--------------------------------------------------------------------------
control_byte : process(MCLK)
begin
if rising_edge(MCLK) then
if i2c_start = '0' then
-- i2c_cmd isn't cleared until i2c_active goes low so we
-- have plenty of time to process it.
case i2c_cmd is
when CMD_FAT_ENTRY =>
if i2c_rw = READ then
RD_FAT <= '1';
else
WR_FAT <= '1';
end if;
when CMD_FAT_VALUE =>
when CMD_DIR_VALUE =>
when CMD_SECTOR =>
if i2c_rw = READ then
RD_SECT <= '1';
else
WR_SECT <= '1';
end if;
when CMD_DATA =>
when CMD_STATUS =>
when CMD_DIR_ENTRY =>
if i2c_rw = READ then
SET_DIR <= '1';
else
WR_DIR <= '1';
end if;
when others =>
WR_DIR <= '0';
SET_DIR <= '0';
RD_FAT <= '0';
WR_FAT <= '0';
RD_SECT <= '0';
WR_SECT <= '0';
end case;

-- this will cause i2c_cmd to be cleared so on the
-- next rising edge of MCLK the i2c_cmd case statement
-- will execute the others option
i2c_active <= '0';
i2c_recv_control <= '1';
else
if i2c_holding = '1' and byte_cnt = x"001" then
if i2c_data_out(7 downto 1) = idc_address then
i2c_active <= '1';
i2c_rw <= i2c_data_out(0);
end if;
i2c_recv_control <= '1';
else
i2c_recv_control <= '0';
end if;
end if;
end if;
end process control_byte;


--------------------------------------------------------------------------
-- PROCESS : command_byte
--
-- Process the command byte.
--------------------------------------------------------------------------
command_byte : process(RST, MCLK)
begin
if RST = '0' then
i2c_cmd <= (others => '0');
i2c_send_command <= '0';
i2c_recv_command <= '0';
elsif rising_edge(MCLK) then
if i2c_active = '0' then
i2c_cmd <= (others => '0');
else
if i2c_holding = '1' and byte_cnt = x"002" then
i2c_cmd <= i2c_data_out;
case i2c_data_out is

when CMD_FAT_ENTRY =>
i2c_recv_command <= '1';
when CMD_FAT_VALUE =>
if i2c_rw = WRITE then
i2c_recv_command <= '1';
else
i2c_send_command <= '1';
end if;
when CMD_DIR_VALUE =>
when CMD_SECTOR =>
i2c_recv_command <= '1';
when CMD_DATA =>
-- taken care off in the WHEN clause
when CMD_STATUS =>
when CMD_DIR_ENTRY =>
i2c_recv_command <= '1';
when others =>
null;
end case;

else
i2c_send_command <= '0';
i2c_recv_command <= '0';
end if;
end if;
end if;
end process command_byte;


--------------------------------------------------------------------------
-- PROCESS : dir_entry_command
--
-- Store the directory entry byte of the dir entry command.
--------------------------------------------------------------------------
dir_entry_command : process(MCLK)
begin
if rising_edge(MCLK) then
if i2c_cmd = CMD_DIR_ENTRY then
if i2c_holding = '1' then
if byte_cnt = x"003" then
if i2c_rw = READ then
d_entry <= i2c_data_out;
end if;
end if;
i2c_recv_set_dir <= '1';
else
i2c_recv_set_dir <= '0';
end if;
end if;
end if;
end process dir_entry_command;


--------------------------------------------------------------------------
-- PROCESS : fat_entry_command
--
-- Store the FAT entry bytes of the fat entry command.
--------------------------------------------------------------------------
fat_entry_command : process(MCLK)
begin
if rising_edge(MCLK) then
if i2c_cmd = CMD_FAT_ENTRY then
if i2c_holding = '1' then
if byte_cnt = x"003" then
f_entry(11 downto 8) <= i2c_data_out(3 downto 0);
elsif byte_cnt = x"004" then
f_entry(7 downto 0) <= i2c_data_out;
end if;
i2c_recv_fat <= '1';
else
i2c_recv_fat <= '0';
end if;
end if;
end if;
end process fat_entry_command;


--------------------------------------------------------------------------
-- PROCESS : fat_value_command
--
-- Store the FAT value bytes of the fat value command.
--------------------------------------------------------------------------
fat_value_command : process(MCLK)
begin
if rising_edge(MCLK) then
if i2c_cmd = CMD_FAT_VALUE and i2c_rw = WRITE then
if i2c_holding = '1' then
if byte_cnt = x"003" then
f_value(11 downto 8) <= i2c_data_out(3 downto 0);
elsif byte_cnt = x"004" then
f_value(7 downto 0) <= i2c_data_out;
end if;
i2c_recv_fat_v <= '1';
else
i2c_recv_fat_v <= '0';
end if;
end if;
end if;
end process fat_value_command;


--------------------------------------------------------------------------
-- PROCESS : sector_command
--
-- Store the side, track and sector values when reading or writing a
-- sector.
--------------------------------------------------------------------------
sector_command : process(MCLK)
begin
if rising_edge(MCLK) then
if i2c_cmd = CMD_SECTOR then
if i2c_holding = '1' then
if byte_cnt = x"003" then
TRACK <= i2c_data_out;
elsif byte_cnt = x"004" then
SIDE <= i2c_data_out(7);
SECTOR <= i2c_data_out(4 downto 0);
end if;
i2c_recv_sector <= '1';
else
i2c_recv_sector <= '0';
end if;
end if;
end if;
end process sector_command;


--------------------------------------------------------------------------
-- PROCESS : data_command
--
-- Accesses the disk drive controllers RAM. This is done when reading or
-- writing data or directory entries.
--------------------------------------------------------------------------
data_command : process(MCLK)
begin
if rising_edge(MCLK) then
if i2c_active = '0' then
i2c_send_data <= '0';
i2c_recv_data <= '0';
address <= (others => '0');
else
if i2c_cmd = CMD_DATA or i2c_cmd = CMD_DIR_VALUE then
if i2c_holding = '1' then
case state is
-- Read or write the RAM.
---------------------------------------------------------------
when CLOCK_RAM =>
RAM_CLK <= '1';
state <= READ_RAM;


-- Store the value from RAM.
---------------------------------------------------------------
when READ_RAM =>
data <= RAM_DO;
state <= INC_ADDR;


-- Goto the next RAM adress.
---------------------------------------------------------------
when INC_ADDR =>
RAM_CLK <= '0';

if i2c_rw = READ then
address <= address + '1';
i2c_send_data <= '1';
else
if byte_cnt > x"02" then
address <= address + '1';
end if;
i2c_recv_data <= '1';
end if;

state <= WAIT_FOR_NEXT;


-- Wait for the next I2C holding period (clock stretching).
---------------------------------------------------------------
when WAIT_FOR_NEXT =>
state <= WAIT_FOR_NEXT;

when others =>
null;

end case;
else
RAM_CLK <= '0';
state <= CLOCK_RAM;
i2c_send_data <= '0';
i2c_recv_data <= '0';
end if;
end if;
end if;
end if;
end process data_command;


end i2c_manager_arch;


BackHome