1
0
Fork 0
mirror of https://github.com/RobotechLille/cdf2018-principal synced 2024-11-21 15:46:06 +01:00
cdf2018-principal/fpga/i2c.vhd

284 lines
6.8 KiB
VHDL

--###############################
--# Project Name : I2C slave
--# File : i2cslave.vhd
--# Project : i2c slave for FPGA
--# Engineer : Philippe THIRION
--# Modification History
--###############################
-- copyright Philippe Thirion
-- github.com/tirfil
--
-- Copyright 2016 Philippe THIRION
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity I2CSLAVE is
generic(
DEVICE : std_logic_vector(7 downto 0) := x"38"
);
port(
MCLK : in std_logic;
nRST : in std_logic;
SDA_IN : in std_logic;
SCL_IN : in std_logic;
SDA_OUT : out std_logic;
SCL_OUT : out std_logic;
ADDRESS : out std_logic_vector(7 downto 0);
DATA_OUT : out std_logic_vector(7 downto 0);
DATA_IN : in std_logic_vector(7 downto 0);
WR : out std_logic;
RD : out std_logic
);
end I2CSLAVE;
architecture rtl of I2CSLAVE is
type tstate is ( S_IDLE, S_START, S_SHIFTIN, S_RW, S_SENDACK, S_SENDACK2, S_SENDNACK,
S_ADDRESS, S_WRITE, S_SHIFTOUT, S_READ, S_WAITACK
);
type toperation is (OP_READ, OP_WRITE);
signal state : tstate;
signal next_state : tstate;
signal operation : toperation;
signal rising_scl, falling_scl : std_logic;
signal address_i : std_logic_vector(7 downto 0);
signal next_address : std_logic_vector(7 downto 0);
signal counter : integer range 0 to 7;
signal start_cond : std_logic;
signal stop_cond : std_logic;
signal sda_q, sda_qq, sda_qqq : std_logic;
signal scl_q, scl_qq, scl_qqq : std_logic;
signal shiftreg : std_logic_vector(7 downto 0);
signal sda: std_logic;
signal address_incr : std_logic;
signal rd_d : std_logic;
begin
ADDRESS <= address_i;
next_address <= (others=>'0') when (address_i = x"FF") else
std_logic_vector(to_unsigned(to_integer(unsigned( address_i )) + 1, 8));
S_RSY: process(MCLK,nRST)
begin
if (nRST = '0') then
sda_q <= '1';
sda_qq <= '1';
sda_qqq <= '1';
scl_q <= '1';
scl_qq <= '1';
scl_qqq <= '1';
elsif (MCLK'event and MCLK='1') then
sda_q <= SDA_IN;
sda_qq <= sda_q;
sda_qqq <= sda_qq;
scl_q <= SCL_IN;
scl_qq <= scl_q;
scl_qqq <= scl_qq;
end if;
end process S_RSY;
rising_scl <= scl_qq and not scl_qqq;
falling_scl <= not scl_qq and scl_qqq;
START_BIT: process(MCLK,nRST)
begin
if (nRST = '0') then
start_cond <= '0';
elsif (MCLK'event and MCLK='1') then
if (sda_qqq = '1' and sda_qq = '0' and scl_qq = '1') then
start_cond <= '1';
else
start_cond <= '0';
end if;
end if;
end process START_BIT;
STOP_BIT: process(MCLK,nRST)
begin
if (nRST = '0') then
stop_cond <= '0';
elsif (MCLK'event and MCLK='1') then
if (sda_qqq = '0' and sda_qq = '1' and scl_qq = '1') then
stop_cond <= '1';
else
stop_cond <= '0';
end if;
end if;
end process STOP_BIT;
sda <= sda_qq;
RD_DELAY: process(MCLK, nRST)
begin
if (nRST = '0') then
RD <= '0';
elsif (MCLK'event and MCLK='1') then
RD <= rd_d;
end if;
end process RD_DELAY;
OTO: process(MCLK, nRST)
begin
if (nRST = '0') then
state <= S_IDLE;
SDA_OUT <= '1';
SCL_OUT <= '1';
WR <= '0';
rd_d <= '0';
address_i <= (others=>'0');
DATA_OUT <= (others=>'0');
shiftreg <= (others=>'0');
elsif (MCLK'event and MCLK='1') then
if (stop_cond = '1') then
state <= S_IDLE;
SDA_OUT <= '1';
SCL_OUT <= '1';
operation <= OP_READ;
WR <= '0';
rd_d <= '0';
address_incr <= '0';
elsif(start_cond = '1') then
state <= S_START;
SDA_OUT <= '1';
SCL_OUT <= '1';
operation <= OP_READ;
WR <= '0';
rd_d <= '0';
address_incr <= '0';
elsif(state = S_IDLE) then
state <= S_IDLE;
SDA_OUT <= '1';
SCL_OUT <= '1';
operation <= OP_READ;
WR <= '0';
rd_d <= '0';
address_incr <= '0';
elsif(state = S_START) then
shiftreg <= (others=>'0');
state <= S_SHIFTIN;
next_state <= S_RW;
counter <= 6;
elsif(state = S_SHIFTIN) then
if (rising_scl = '1') then
shiftreg(7 downto 1) <= shiftreg(6 downto 0);
shiftreg(0) <= sda;
if (counter = 0) then
state <= next_state;
counter <= 7;
else
counter <= counter - 1;
end if;
end if;
elsif(state = S_RW) then
if (rising_scl = '1') then
if (shiftreg = DEVICE) then
state <= S_SENDACK;
if (sda = '1') then
operation <= OP_READ;
-- next_state <= S_READ; -- no needed
rd_d <= '1';
else
operation <= OP_WRITE;
next_state <= S_ADDRESS;
address_incr <= '0';
end if;
else
state <= S_SENDNACK;
end if;
end if;
elsif(state = S_SENDACK) then
WR <= '0';
rd_d <= '0';
if (falling_scl = '1') then
SDA_OUT <= '0';
counter <= 7;
if (operation= OP_WRITE) then
state <= S_SENDACK2;
else -- OP_READ
state <= S_SHIFTOUT;
shiftreg <= DATA_IN;
end if;
end if;
elsif(state = S_SENDACK2) then
if (falling_scl = '1') then
SDA_OUT <= '1';
state <= S_SHIFTIN;
shiftreg <= (others=>'0');
if (address_incr = '1') then
address_i <= next_address;
end if;
end if;
elsif(state = S_SENDNACK) then
if (falling_scl = '1') then
SDA_OUT <= '1';
state <= S_IDLE;
end if;
elsif(state = S_ADDRESS) then
address_i <= shiftreg;
next_state <= S_WRITE;
state <= S_SENDACK;
address_incr <= '0';
elsif(state = S_WRITE) then
DATA_OUT <= shiftreg;
next_state <= S_WRITE;
state <= S_SENDACK;
WR <= '1';
address_incr <= '1';
elsif(state = S_SHIFTOUT) then
if (falling_scl = '1') then
SDA_OUT <= shiftreg(7);
shiftreg(7 downto 1) <= shiftreg(6 downto 0);
shiftreg(0) <= '1';
if (counter = 0) then
state <= S_READ;
address_i <= next_address;
rd_d <= '1';
else
counter <= counter - 1;
end if;
end if;
elsif(state = S_READ) then
rd_d <= '0';
if (falling_scl = '1') then
SDA_OUT <= '1';
state <= S_WAITACK;
end if;
elsif(state = S_WAITACK) then
if (rising_scl = '1') then
if (sda = '0') then
state <= S_SHIFTOUT;
counter <= 7;
shiftreg <= DATA_IN;
else
state <= S_IDLE;
end if;
end if;
end if;
end if;
end process OTO;
end rtl;