i2c master/slave interface, can you implement both on a board? [SOLVED]
Right now, I have a master entity that is sending data to the slave, and the slave is recognizing it and changing the LED according to the bit-stream I send. Hopefully that's not a fluke and is actually working because of the protocol. Here is my problem, when I try to do RD and WR signals, one of them always has to be high or I get that an error saying so and so is getting driven by SDA which has multiple drivers, which makes sense because of the way i2c works. My question is, can I actually implement both so I can practice i2c, or can I only use a master and then use slaves that come with the board already(e.g. the eeprom, accelerometer)?
--synthesis VHDL_INPUT_VERSION VHDL_2008
library ieee;
use ieee.std_logic_1164.all;
entity i2c_slave0 is
generic(
slvAddress : std_logic_vector(6 downto 0) := "0000001";
regAddress : std_logic_vector(7 downto 0) := "10000101"
);
port(
clk50,
SCL,
reset : in std_logic := '0';
SDA : inout std_logic := '1';
output : out std_logic_vector(7 downto 0) := (others => '0')
);
end i2c_slave0;
architecture FSM of i2c_slave0 is
type slaveState is (Idle, Slave_Address, Send_Ack1, Send_Ack2, Register_Address, Read_Data, Write_Data, Wait_Stop);
signal prev_State, next_State : slaveState;
signal i : natural range 0 to 8 := 0;
signal addr : std_logic_vector(7 downto 0) := (others => '0');
signal tempRam : std_logic_vector(7 downto 0) := "00000000";
signal ram : std_logic_vector(7 downto 0) := (others => '0');
signal start, stop : std_logic := '0';
signal syncFF_clk : std_logic_vector(1 downto 0) := "00";
signal syncFF_sda : std_logic_vector(1 downto 0) := "00";
signal edgeDetFF_clk : std_logic := '0';
signal edgeDetFF_SDA : std_logic := '0';
signal rising_edge_clk,
falling_edge_clk : std_logic := '0';
signal regAddr : std_logic_vector(7 downto 0) := (others => '0');
begin
--Synchronize data--
process(clk50)
begin
if(rising_edge(clk50)) then
syncFF_clk(0) <= SCL;
syncFF_clk(1) <= syncFF_clk(0);
syncFF_sda(0) <= SDA; -- <----------------- This is where the error occurs
syncFF_sda(1) <= syncFF_sda(0);
end if;
end process;
--------------------------------
--======Edge detection of SCL=======--
--Pipelining SCL--
process(clk50)
begin
if(rising_edge(clk50)) then edgeDetFF_clk <= syncFF_clk(1);
end if;
end process;
---------------------------------------
--Rising/Falling edge detection--
process(clk50)
begin
if(falling_edge(clk50)) then
rising_edge_clk <= syncFF_clk(1) and not edgeDetFF_clk;
falling_edge_clk <= not syncFF_clk(1) and edgeDetFF_clk;
end if;
end process;
--------------------------------------------------------------
--======Start/Stop detection of SDA=======--
--Pipelining SDA--
process(all)
begin
if(rising_edge(clk50)) then edgeDetFF_SDA <= syncFF_sda(1);
end if;
end process;
--------------------------------------------------------------
--Stop/Start detection--
process(all)
begin
start <= '0';
stop <= '0';
if(syncFF_clk(1) = '1' and edgeDetFF_clk = '1') then
start <= not syncFF_SDA(1) and edgeDetFF_SDA;
stop <= syncFF_SDA(1) and not edgeDetFF_SDA;
end if;
end process;
---------------------------------------------------------------
process(all)
begin
if (reset = '1') then prev_State <= Idle;
i <= 0;
elsif(rising_edge(clk50)) then prev_State <= next_State;
if(prev_State = Idle or start = '1') then i <= 0;
elsif(falling_edge_clk = '1') then
if(i < 8) then i <= i + 1;
else i <= 0;
end if;
end if;
end if;
end process;
process(all)
begin
SDA <= 'Z';
case (prev_State) is
when Idle =>
if(start = '1') then next_State <= Slave_Address;
else next_State <= Idle;
end if;
when Slave_Address =>
if(falling_edge_clk = '1') then
if (i < 8) then next_State <= Slave_Address;
elsif((i = 8) and (addr(7 downto 1) = slvAddress)) then next_State <= Send_Ack1;
else next_State <= Idle;
end if;
else next_State <= Slave_Address;
end if;
when Send_Ack1 =>
SDA <= '0';
if(falling_edge_clk = '1') then
if(addr(0) = '0') then next_State <= Register_Address;
else next_State <= Read_Data;
end if;
else next_State <= Send_Ack1;
end if;
when Register_Address =>
if(falling_edge_clk = '1') then
if (i < 8) then next_State <= Register_Address;
elsif((i = 8) and (regAddr(7 downto 0) = regAddress)) then next_State <= Send_Ack2;
else next_State <= Idle;
end if;
else next_State <= Register_Address;
end if;
when Send_Ack2 =>
SDA <= '0';
if(falling_edge_clk = '1') then next_State <= Write_Data;
else next_State <= Send_Ack2;
end if;
when Write_Data =>
if(falling_edge_clk = '1') then
if (i < 8) then next_State <= Write_Data;
else next_State <= Send_Ack1;
end if;
else next_State <= Write_Data;
end if;
when Read_Data =>
SDA <= ram(8 - i);
if(falling_edge_clk = '1') then
if (i < 8) then next_State <= Read_Data;
else next_State <= Wait_Stop;
end if;
else next_State <= Read_Data;
end if;
when Wait_Stop =>
next_State <= Wait_Stop;
end case;
if (start = '1') then next_State <= Slave_Address;
elsif(stop = '1') then next_State <= Idle;
end if;
end process;
process(all)
begin
if(rising_edge(clk50)) then
if(reset = '1') then addr <= (others => '0');
tempRam <= (others => '0');
regAddr <= (others => '0');
elsif(rising_edge_clk) then
if (prev_State = Slave_Address) then addr(8 - i) <= SDA;
elsif(prev_State = Write_Data) then tempRam(8 - i) <= SDA;
elsif(prev_State = Register_Address) then regAddr(8 - i) <= SDA;
end if;
end if;
if(reset = '1') then
ram <= (others => '0');
elsif(prev_State = Write_Data and i = 8) then ram <= tempRam;
end if;
if(reset = '1') then
output <= (others => '0');
elsif(falling_edge_clk = '1') then
if(prev_State = Write_Data and i = 8) then output <= ram;
end if;
end if;
end if;
end process;
end FSM;--synthesis VHDL_INPUT_VERSION VHDL_2008
library ieee;
use ieee.std_logic_1164.all;
entity black_box is
port(
clk50,
reset : in std_logic := '0';
output : out std_logic_vector(7 downto 0) := (others => '0')
);
end black_box;
architecture structural of black_box is
component i2c_master is
generic(
clk_freq : integer := 50_000_000;
i2c_freq : integer := 300_000;
slaveWR0 : std_logic_vector(8 downto 0) := "000000010";
slaveRD0 : std_logic_vector(8 downto 0) := "000000011";
reg0WR0 : std_logic_vector(8 downto 0) := "010000101";
reg0RD0 : std_logic_vector(8 downto 0) := "010000101";
slvData0 : std_logic_Vector(8 downto 0) := "001110101"
);
port(
clk50,
-- slvAddress,
WR, RD,
reset : in std_logic;
SCL : out std_logic;
SDA : inout std_logic
-- output : out std_logic_vector(7 downto 0) := (others => '0')
);
end component;
component i2c_slave0 is
generic(
slvAddress : std_logic_vector(6 downto 0) := "0000001";
regAddress : std_logic_vector(7 downto 0) := "10000101"
);
port(
clk50,
SCL,
reset : in std_logic;
SDA : inout std_logic;
output : out std_logic_vector(7 downto 0) := (others => '0')
);
end component;
signal SCL, SDA : std_logic := '0';
signal oneSec : std_logic := '0';
signal wr, rd : std_logic := '0';
begin
process(clk50)
variable counter : natural range 0 to 50_000_000;
begin
if(rising_edge(clk50)) then counter := counter + 1;
if(counter = 50_000_000) then counter := 0;
oneSec <= not oneSec;
end if;
end if;
end process;
process(oneSec)
begin
if(rising_edge(oneSec)) then
WR <= '0';
RD <= '0';
end if;
end process;
mstr: i2c_master port map (clk50, WR, RD, reset, SCL, SDA);
slv : i2c_slave0 port map (clk50, SCL, reset, SDA, output);
end structural;EDIT: So you can't do it within the FPGA, which makes sense, so what i ended up doing was using the I/O pins. Worked perfectly after that!