Select Git revision
i2c_slim2.vhd
Jan Michel authored
i2c_slim2.vhd 19.28 KiB
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity i2c_slim2 is
port(
CLOCK : in std_logic;
RESET : in std_logic;
-- I2C command / setup
I2C_GO_IN : in std_logic; -- startbit to trigger I2C actions
ACTION_IN : in std_logic; -- '0' -> write, '1' -> read
WORD_IN : in std_logic; -- '0' -> byte, '1' -> word
DIRECT_IN : in std_logic; -- '0' -> normal access, '1' -> direct read
I2C_SPEED_IN : in std_logic_vector(5 downto 0); -- speed adjustment (to be defined)
I2C_ADDR_IN : in std_logic_vector(7 downto 0); -- I2C address byte (R/W bit is ignored)
I2C_CMD_IN : in std_logic_vector(7 downto 0); -- I2C command byte (sent after address byte)
I2C_DW_IN : in std_logic_vector(15 downto 0); -- data word for write command
I2C_DR_OUT : out std_logic_vector(15 downto 0); -- data word from read command
STATUS_OUT : out std_logic_vector(7 downto 0); -- status and error bits
VALID_OUT : out std_logic;
I2C_BUSY_OUT : out std_logic;
I2C_DONE_OUT : out std_logic;
-- I2C connections
SDA_IN : in std_logic;
SDA_OUT : out std_logic;
SCL_IN : in std_logic;
SCL_OUT : out std_logic;
-- Debug
BSM_OUT : out std_logic_vector(3 downto 0)
);
end entity i2c_slim2;
architecture Behavioral of i2c_slim2 is
-- Signals
type STATES is (SLEEP, LOADA, GSTART, SENDA, LOADC, SENDC, LOADD, SENDD,
GSTOP, INC, DONE, FAILED, LOADD2, SENDD2);
signal CURRENT_STATE, NEXT_STATE: STATES;
signal bsm : std_logic_vector(3 downto 0);
signal phase : std_logic; -- '0' => first phase, '1' => second phase of read cycle
signal start_x : std_logic;
signal start : std_logic; -- '0' => generate STOP, '1' => generate START
signal dostart_x : std_logic;
signal dostart : std_logic; -- trigger the GenStart module
signal dobyte_x : std_logic;
signal dobyte : std_logic; -- trigger the ByteSend module
signal i2c_done_x : std_logic;
signal i2c_done : std_logic; -- acknowledge signal to the outside world
signal running_x : std_logic;
signal running : std_logic; -- legacy
signal valid_x : std_logic;
signal valid : std_logic;
signal load_dh_x : std_logic;
signal load_dh : std_logic;
signal load_dl_x : std_logic;
signal load_dl : std_logic;
signal clr_data_x : std_logic;
signal clr_data : std_logic;
signal sdone : std_logic; -- acknowledge signal from GenStart module
signal sok : std_logic; -- status signal from GenStart module
signal bdone : std_logic; -- acknowledge signal from SendByte module
signal bok : std_logic; -- status signal from SendByte module
signal i2c_byte : std_logic_vector(8 downto 0);
signal i2c_dr : std_logic_vector(8 downto 0);
signal i2c_drw : std_logic_vector(15 downto 0);
signal s_scl : std_logic;
signal r_scl : std_logic;
signal s_sda : std_logic;
signal r_sda : std_logic;
signal r_scl_gs : std_logic;
signal s_scl_gs : std_logic;
signal r_sda_gs : std_logic;
signal s_sda_gs : std_logic;
signal r_scl_sb : std_logic;
signal s_scl_sb : std_logic;
signal r_sda_sb : std_logic;
signal s_sda_sb : std_logic;
signal scl_q : std_logic_vector(2 downto 0);
signal sda_q : std_logic_vector(2 downto 0);
signal i2c_speed : std_logic_vector(8 downto 0);
signal errors_x : std_logic_vector(7 downto 0) := x"00";
signal save_x : std_logic;
signal error_bits : std_logic_vector(7 downto 0) := x"00";
-- Components
component i2c_gstart2 is
port(
CLOCK : in std_logic;
RESET : in std_logic;
START_IN : in std_logic;
DOSTART_IN : in std_logic;
I2C_SPEED_IN : in std_logic_vector(8 downto 0);
SDONE_OUT : out std_logic;
SOK_OUT : out std_logic;
SDA_IN : in std_logic;
SCL_IN : in std_logic;
R_SCL_OUT : out std_logic;
S_SCL_OUT : out std_logic;
R_SDA_OUT : out std_logic;
S_SDA_OUT : out std_logic;
BSM_OUT : out std_logic_vector(3 downto 0)
);
end component i2c_gstart2;
component i2c_sendb2 is
port(
CLOCK : in std_logic;
RESET : in std_logic;
DOBYTE_IN : in std_logic;
I2C_SPEED_IN : in std_logic_vector(8 downto 0);
I2C_BYTE_IN : in std_logic_vector(8 downto 0);
I2C_BACK_OUT : out std_logic_vector(8 downto 0);
SDA_IN : in std_logic;
R_SDA_OUT : out std_logic;
S_SDA_OUT : out std_logic;
SCL_IN : in std_logic;
R_SCL_OUT : out std_logic;
S_SCL_OUT : out std_logic;
BDONE_OUT : out std_logic;
BOK_OUT : out std_logic;
BSM_OUT : out std_logic_vector(3 downto 0)
);
end component i2c_sendb2;
begin
THE_SYNC_PROC: process( CLOCK )
begin
if( rising_edge(CLOCK) ) then
sda_q(2 downto 0) <= sda_q(1 downto 0) & SDA_IN; scl_q(2 downto 0) <= scl_q(1 downto 0) & SCL_IN;
end if;
end process THE_SYNC_PROC;
-- lower limit of speed
i2c_speed <= i2c_speed_in & b"010";
-- Read phase indicator
THE_PHASE_PROC: process( CLOCK )
begin
if( rising_edge(CLOCK) ) then
if( RESET = '1' ) then
phase <= '0';
elsif( (CURRENT_STATE = INC) ) then
phase <= '1';
elsif( (CURRENT_STATE = DONE) or (CURRENT_STATE = SLEEP) ) then
phase <= '0';
end if;
end if;
end process THE_PHASE_PROC;
-- The main state machine
-- State memory process
STATE_MEM: process( CLOCK )
begin
if( rising_edge(CLOCK) ) then
if( RESET = '1' ) then
CURRENT_STATE <= SLEEP;
start <= '0';
dostart <= '0';
dobyte <= '0';
running <= '0';
load_dh <= '0';
load_dl <= '0';
clr_data <= '1';
valid <= '0';
i2c_done <= '0';
else
CURRENT_STATE <= NEXT_STATE;
start <= start_x;
dostart <= dostart_x;
dobyte <= dobyte_x;
running <= running_x;
load_dh <= load_dh_x;
load_dl <= load_dl_x;
clr_data <= clr_data_x;
valid <= valid_x;
i2c_done <= i2c_done_x;
end if;
end if;
end process STATE_MEM;
-- Transition matrix
TRANSFORM: process(CURRENT_STATE, I2C_GO_IN, sdone, sok, phase, bdone, bok, ACTION_IN, WORD_IN)
begin
NEXT_STATE <= SLEEP;
start_x <= '0';
dostart_x <= '0';
dobyte_x <= '0';
running_x <= '1';
load_dh_x <= '0';
load_dl_x <= '0';
clr_data_x <= '0';
valid_x <= '0';
i2c_done_x <= '0';
errors_x <= x"00";
save_x <= '0';
case CURRENT_STATE is
when SLEEP =>
if( I2C_GO_IN = '1' ) then NEXT_STATE <= LOADA;
save_x <= '1';
clr_data_x <= '1';
else
NEXT_STATE <= SLEEP;
running_x <= '0';
end if;
when LOADA =>
NEXT_STATE <= GSTART;
start_x <= '1';
dostart_x <= '1';
when GSTART =>
if ( (sdone = '1') and (sok = '1') ) then
NEXT_STATE <= SENDA; -- generating START did succeed
dobyte_x <= '1';
elsif( (sdone = '1') and (sok = '0') and (phase = '0') ) then
NEXT_STATE <= FAILED; -- first START condition failed
errors_x <= x"80";
save_x <= '1';
dostart_x <= '1';
elsif( (sdone = '1') and (sok = '0') and (phase = '1') ) then
NEXT_STATE <= FAILED; -- second START condition failed
errors_x <= x"40";
save_x <= '1';
dostart_x <= '1';
else
NEXT_STATE <= GSTART; -- wait for START generation ending
start_x <= '1';
dostart_x <= '1';
end if;
when SENDA =>
if ( (bdone = '1') and (bok = '1') and (ACTION_IN = '0') and (DIRECT_IN = '0') ) then
NEXT_STATE <= LOADC; -- I2C normal write
elsif( (bdone = '1') and (bok = '1') and (ACTION_IN = '1') and (DIRECT_IN = '0') and (phase = '0') ) then
NEXT_STATE <= LOADC; -- I2C normal read, address stage (same for byte and word)
elsif( (bdone = '1') and (bok = '1') and (ACTION_IN = '1') and (phase = '1') and (WORD_IN = '0') ) then
NEXT_STATE <= LOADD2; -- I2C normal read (byte), data stage
elsif( (bdone = '1') and (bok = '1') and (ACTION_IN = '1') and (phase = '1') and (WORD_IN = '1') ) then
NEXT_STATE <= LOADD; -- I2C normal read (word), data stage
elsif( (bdone = '1') and (bok = '1') and (ACTION_IN = '0') and (DIRECT_IN = '1') and (WORD_IN = '0') ) then
NEXT_STATE <= LOADD2; -- I2C direct write (byte)
elsif( (bdone = '1') and (bok = '1') and (ACTION_IN = '0') and (DIRECT_IN = '1') and (WORD_IN = '1') ) then
NEXT_STATE <= LOADD; -- I2C direct write (word)
elsif( (bdone = '1') and (bok = '1') and (ACTION_IN = '1') and (DIRECT_IN = '1') and (WORD_IN = '0') ) then
NEXT_STATE <= LOADD2; -- I2C direct read (byte)
elsif( (bdone = '1') and (bok = '1') and (ACTION_IN = '1') and (DIRECT_IN = '1') and (WORD_IN = '1') ) then
NEXT_STATE <= LOADD; -- I2C direct read (word)
elsif( (bdone = '1') and (bok = '0') and (phase = '0') ) then
NEXT_STATE <= FAILED; -- first address phase failed
errors_x <= x"20";
save_x <= '1';
dostart_x <= '1';
elsif( (bdone = '1') and (bok = '0') and (phase = '1') ) then
NEXT_STATE <= FAILED; -- second address phase failed
errors_x <= x"10";
save_x <= '1';
dostart_x <= '1';
else
NEXT_STATE <= SENDA; -- wait for send address ending
dobyte_x <= '1';
end if;
when LOADC =>
NEXT_STATE <= SENDC;
when SENDC =>
if ( (bdone = '1') and (bok = '1') and (ACTION_IN = '0') and (WORD_IN = '0') ) then
NEXT_STATE <= LOADD2; -- I2C byte write, prepare data
elsif( (bdone = '1') and (bok = '1') and (ACTION_IN = '0') and (WORD_IN = '1') ) then
NEXT_STATE <= LOADD; -- I2C word write, prepare data
elsif( (bdone = '1') and (bok = '1') and (ACTION_IN = '1') ) then
NEXT_STATE <= GSTOP; -- I2C read, first phase ends dostart_x <= '1';
elsif( (bdone = '1') and (bok = '0') ) then
NEXT_STATE <= FAILED; -- command phase failed
errors_x <= x"08";
save_x <= '1';
dostart_x <= '1';
else
NEXT_STATE <= SENDC; -- wait for send command ending
dobyte_x <= '1';
end if;
when LOADD =>
NEXT_STATE <= SENDD;
when SENDD =>
if ( (bdone = '1') and (bok = '1') and (ACTION_IN = '0') ) then
NEXT_STATE <= LOADD2; -- I2C write, word access, last byte to send
elsif( (bdone = '1') and (ACTION_IN = '1') ) then
NEXT_STATE <= LOADD2; -- I2C read, word access, last byte to receive
load_dh_x <= '1';
elsif( (bdone = '1') and (bok = '0') and (ACTION_IN = '0') ) then
NEXT_STATE <= FAILED; -- I2C write, first data phase failed
errors_x <= x"04";
save_x <= '1';
dostart_x <= '1';
else
NEXT_STATE <= SENDD; -- wait for send data ending
dobyte_x <= '1';
end if;
when LOADD2 =>
NEXT_STATE <= SENDD2;
when SENDD2 =>
if ( (bdone = '1') and (bok = '1') and (ACTION_IN = '0') ) then
NEXT_STATE <= GSTOP; -- I2C write, done
dostart_x <= '1';
elsif( (bdone = '1') and (ACTION_IN = '1') ) then
NEXT_STATE <= GSTOP; -- I2C read, data phase
dostart_x <= '1';
load_dl_x <= '1';
elsif( (bdone = '1') and (bok = '0') and (ACTION_IN = '0') ) then
NEXT_STATE <= FAILED; -- I2C write, data phase failed
errors_x <= x"02";
save_x <= '1';
dostart_x <= '1';
else
NEXT_STATE <= SENDD2;
dobyte_x <= '1';
end if;
when GSTOP =>
if ( (sdone = '1') and (ACTION_IN = '0') ) then
NEXT_STATE <= DONE;
i2c_done_x <= '1';
valid_x <= '1';
elsif( (sdone = '1') and (ACTION_IN = '1') and (phase = '1') ) then
NEXT_STATE <= DONE;
i2c_done_x <= '1';
valid_x <= '1';
elsif( (sdone = '1') and (ACTION_IN = '1') and (phase = '0') and (DIRECT_IN = '0') ) then
NEXT_STATE <= INC;
elsif( (sdone = '1') and (ACTION_IN = '1') and (DIRECT_IN = '1') ) then
NEXT_STATE <= DONE;
i2c_done_x <= '1';
valid_x <= '1';
else
NEXT_STATE <= GSTOP;
dostart_x <= '1';
end if;
when INC =>
NEXT_STATE <= LOADA;
when FAILED =>
-- emergency STOP condition to correctly free the bus
if( sdone = '1' ) then NEXT_STATE <= DONE;
i2c_done_x <= '1';
running_x <= '0';
else
NEXT_STATE <= FAILED;
dostart_x <= '1';
end if;
when DONE =>
if( i2c_go_in = '1' ) then
NEXT_STATE <= DONE;
i2c_done_x <= '1';
running_x <= '0';
else
NEXT_STATE <= SLEEP;
running_x <= '0';
end if;
-- Just in case...
when others =>
NEXT_STATE <= SLEEP;
end case;
end process TRANSFORM;
-- Error bits
THE_ERROR_PROC: process( CLOCK )
begin
if( rising_edge(CLOCK) ) then
if( save_x = '1' ) then
error_bits <= errors_x;
end if;
end if;
end process THE_ERROR_PROC;
---- DONE bit: set by FSM completion, cleared by start of FSM
--THE_DONE_PROC: process( CLOCK )
--begin
-- if( rising_edge(CLOCK) ) then
-- if ( RESET = '1' ) then
-- i2c_done <= '0';
-- elsif( CURRENT_STATE = LOADA ) then
-- i2c_done <= '0';
-- elsif( CURRENT_STATE = DONE ) then
-- i2c_done <= '1';
-- end if;
-- end if;
--end process THE_DONE_PROC;
-- Output decoding
DECODE: process(CURRENT_STATE)
begin
case CURRENT_STATE is
when SLEEP => bsm <= b"0000"; -- 0
when LOADA => bsm <= b"0001"; -- 1
when GSTART => bsm <= b"0010"; -- 2
when SENDA => bsm <= b"0011"; -- 3
when LOADC => bsm <= b"0100"; -- 4
when SENDC => bsm <= b"0101"; -- 5
when LOADD => bsm <= b"0110"; -- 6
when SENDD => bsm <= b"0111"; -- 7
when GSTOP => bsm <= b"1000"; -- 8
when INC => bsm <= b"1001"; -- 9
when FAILED => bsm <= b"1010"; -- a
when DONE => bsm <= b"1011"; -- b
when LOADD2 => bsm <= b"1100"; -- c
when SENDD2 => bsm <= b"1101"; -- d
when others => bsm <= b"1111"; -- f
end case;
end process DECODE;
-- We need to load different data sets
LOAD_DATA_PROC: process( CLOCK )begin
if( rising_edge(CLOCK) ) then
if ( RESET = '1' ) then
i2c_byte <= (others => '1');
elsif( (CURRENT_STATE = LOADA) and (phase = '0') and (DIRECT_IN = '0') ) then
i2c_byte <= i2c_addr_in(7 downto 1) & '0' & '1'; -- send write address, receive ACK
elsif( (CURRENT_STATE = LOADA) and (phase = '0') and (DIRECT_IN = '1') and (ACTION_IN = '0') ) then
i2c_byte <= i2c_addr_in(7 downto 1) & '0' & '1'; -- send write address, receive ACK
elsif( (CURRENT_STATE = LOADA) and (phase = '0') and (DIRECT_IN = '1') and (ACTION_IN = '1') ) then
i2c_byte <= i2c_addr_in(7 downto 1) & '1' & '1'; -- send read address, receive ACK
elsif( (CURRENT_STATE = LOADA) and (phase = '1') ) then
i2c_byte <= i2c_addr_in(7 downto 1) & '1' & '1'; -- send read address, receive ACK
elsif( (CURRENT_STATE = LOADC) ) then
i2c_byte <= i2c_cmd_in(7 downto 0) & '1'; -- send command byte (read/write), receive ACK
elsif( (CURRENT_STATE = LOADD) and (ACTION_IN = '0') ) then
i2c_byte <= i2c_dw_in(15 downto 8) & '1'; -- send data byte, receive ACK
elsif( (CURRENT_STATE = LOADD2) and (ACTION_IN = '0') ) then
i2c_byte <= i2c_dw_in(7 downto 0) & '1'; -- send data byte, receive ACK
elsif( (CURRENT_STATE = LOADD) and (ACTION_IN = '1') ) then
i2c_byte <= x"ff" & '0'; -- send 0xff byte, send ACK
elsif( (CURRENT_STATE = LOADD2) and (ACTION_IN = '1') ) then
i2c_byte <= x"ff" & '1'; -- send 0xff byte, send NACK
end if;
end if;
end process LOAD_DATA_PROC;
-- The SendByte module
THE_I2C_SENDB: i2c_sendb2
port map(
CLOCK => CLOCK,
RESET => RESET,
DOBYTE_IN => dobyte,
I2C_SPEED_IN => i2c_speed,
I2C_BYTE_IN => i2c_byte,
I2C_BACK_OUT => i2c_dr,
SDA_IN => sda_q(2),
R_SDA_OUT => r_sda_sb,
S_SDA_OUT => s_sda_sb,
SCL_IN => scl_q(2),
R_SCL_OUT => r_scl_sb,
S_SCL_OUT => s_scl_sb,
BDONE_OUT => bdone,
BOK_OUT => bok,
BSM_OUT => open
);
-- The GenStart module
THE_I2C_GSTART: i2c_gstart2
port map(
CLOCK => CLOCK,
RESET => RESET,
START_IN => start,
DOSTART_IN => dostart,
I2C_SPEED_IN => i2c_speed,
SDONE_OUT => sdone,
SOK_OUT => sok,
SDA_IN => sda_q(2),
SCL_IN => scl_q(2),
R_SCL_OUT => r_scl_gs,
S_SCL_OUT => s_scl_gs,
R_SDA_OUT => r_sda_gs,
S_SDA_OUT => s_sda_gs,
BSM_OUT => open
);
r_scl <= r_scl_gs or r_scl_sb;
s_scl <= s_scl_gs or s_scl_sb;
r_sda <= r_sda_gs or r_sda_sb;
s_sda <= s_sda_gs or s_sda_sb;
-- Output flipflop for SCL line
THE_SCL_PROC: process( CLOCK )
begin
if( rising_edge(CLOCK) ) then
if( RESET = '1' ) then
SCL_OUT <= '1';
elsif( (r_scl = '1') and (s_scl = '0') ) then
SCL_OUT <= '0';
elsif( (r_scl = '0') and (s_scl = '1') ) then
SCL_OUT <= '1';
end if;
end if;
end process THE_SCL_PROC;
-- Output flipflop for SDA line
THE_SDA_PROC: process( CLOCK )
begin
if( rising_edge(CLOCK) ) then
if( RESET = '1' ) then
SDA_OUT <= '1';
elsif( (r_sda = '1') and (s_sda = '0') ) then
SDA_OUT <= '0';
elsif( (r_sda = '0') and (s_sda = '1') ) then
SDA_OUT <= '1';
end if;
end if;
end process THE_SDA_PROC;
-- store data read from I2C (high byte)
THE_STORE_READ_H_PROC: process( CLOCK )
begin
if( rising_edge(CLOCK) ) then
if ( clr_data = '1' ) then
i2c_drw(15 downto 8) <= (others => '1');
elsif( rising_edge(CLOCK) ) then
if( load_dh = '1' ) then
i2c_drw(15 downto 8) <= i2c_dr(8 downto 1);
end if;
end if;
end if;
end process THE_STORE_READ_H_PROC;
-- store data read from I2C (low byte)
THE_STORE_READ_L_PROC: process( CLOCK )
begin
if( rising_edge(CLOCK) ) then
if ( clr_data = '1' ) then
i2c_drw(7 downto 0) <= (others => '1');
elsif( rising_edge(CLOCK) ) then
if( load_dl = '1' ) then
i2c_drw(7 downto 0) <= i2c_dr(8 downto 1);
end if;
end if;
end if;
end process THE_STORE_READ_L_PROC;
-- Status output
status_out <= error_bits;
-- Outputs
I2C_DR_OUT <= i2c_drw;
I2C_BUSY_OUT <= running;
I2C_DONE_OUT <= i2c_done;
VALID_OUT <= valid;
BSM_OUT <= bsm;
end Behavioral;