Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)
16556 Discussions

i2c master/slave interface, can you implement both on a board? [SOLVED]

CMcga
Beginner
909 Views

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!

0 Kudos
2 Replies
CMcga
Beginner
542 Views
--synthesis VHDL_INPUT_VERSION VHDL_2008 library ieee;   use ieee.std_logic_1164.all;   entity 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) := "001100101" ); port( clk50, -- slvAddress, WR, RD, reset : in std_logic := '0'; SCL : out std_logic := '0'; SDA : inout std_logic := '0' -- output : out std_logic_vector(7 downto 0) := (others => '0') ); end i2c_master;   architecture FSM of i2c_master is   type masterState is ( Idle, Start, Slave_Address, Ack1, Stop, Hold, WR_Address, Ack2, Write_Data, Ack3, RD_Address, Ack4, restartL, restartR, Initiate_Read, Ack5, Read_Data, noAck );   signal prev_State, next_State : masterState;   signal i2c_clk : std_logic := '0';   signal i, iReg : natural range 0 to 8; signal j, jReg : natural range 0 to 7;   signal storeData : std_logic_vector(7 downto 0) := (others => '0'); signal wr_Reg : std_logic := '0';   --constant slaveAddress0 : std_logic_vector(7 downto 0) := slaveWR0; begin   process(i2c_clk, reset) begin if (reset = '1') then prev_State <= Idle; iReg <= 0; jReg <= 0; elsif(falling_edge(i2c_clk)) then prev_State <= next_State; iReg <= i; jReg <= j; end if;   end process;   process(all) variable clk_counter : natural range 0 to clk_freq/(2*i2c_freq); begin if(rising_edge(clk50)) then clk_counter := clk_counter + 1; if(clk_counter = clk_freq/(2*i2c_freq)) then i2c_clk <= not i2c_clk; clk_counter := 0; end if; end if; end process; process(all) begin SCL <= i2c_clk; i <= 0; j <= 0; case (prev_State) is when Idle => SCL <= '1'; SDA <= '1'; if(WR = '1') then next_State <= Start; else next_State <= Idle; end if; when Start => SCL <= '1'; SDA <= '0'; next_State <= Slave_Address; when Slave_Address =>   SDA <= slaveWR0(8-i); i <= iReg + 1; if(i = 8) then next_State <= Ack1; else next_State <= Slave_Address; end if; when Ack1 => SDA <= 'Z'; if(wr_Reg = '1') then next_State <= WR_Address; else next_State <= RD_Address; end if; when Stop => SDA <= '0'; next_State <= Hold; when Hold => SCL <= '1'; SDA <= '1'; if(wr = '0' and rd = '0') then next_State <= Idle; else next_State <= Hold; end if; when WR_Address => SDA <= reg0WR0(8-i); i <= iReg + 1; if(i = 8) then next_State <= Ack2; else next_State <= WR_Address; end if; when Ack2 => SDA <= 'Z'; next_State <= Write_Data; when Write_Data => SDA <= slvData0(8-i); i <= iReg + 1; if(i = 8) then next_State <= Ack3; else next_State <= Write_Data; end if; when Ack3 => SDA <= 'Z'; next_State <= Stop; when RD_Address => SDA <= reg0RD0(8-i); i <= iReg + 1; if(i = 8) then next_State <= Ack4; else next_State <= RD_Address; end if; when Ack4 => SDA <= 'Z'; next_State <= restartL; when restartL => SCL <= '0'; SDA <= '1'; next_State <= restartR; when restartR => SCL <= '1'; SDA <= not i2c_clk; next_State <= Initiate_Read; when Initiate_Read => SDA <= slaveRD0(8-i); i <= iReg + 1; if(i = 8) then next_State <= Ack5; else next_State <= Initiate_Read; end if; when Ack5 =>   SDA <= 'Z'; next_State <= Read_Data; when Read_Data => SDA <= 'Z'; i <= iReg + 1; if(i = 8) then next_State <= noAck; else next_State <= Read_Data; end if; when noAck => SDA <= '1'; next_State <= Stop; end case; end process; process(clk50) begin if(rising_edge(clk50)) then if(prev_State = Start) then wr_Reg <= WR; end if; end if; end process; process(i2c_clk) begin if(rising_edge(i2c_clk)) then if(prev_State = Read_Data) then storeData(8-i) <= SDA; end if; end if; -- -- if(falling_edge(i2c_clk)) then -- if(prev_State = Read_Data and i = 8) then output <= storeData; -- end if; -- end if; end process;   end FSM;

Here is the master just in case.

0 Kudos
RRomano001
New Contributor I
542 Views

Hi, Your design is accessing SDA from different (unnamed) processes.

 

So write SDA just in one process, in case signal from one process to other what to do on SDA.

 

Curing this, can work anyway but you are not guaranteed external devices are high state. To avoid contention, never drive SDA nor SCK High, drive 'Z' or '0'.

 

FPGA work better on syncronous design, avoid ripple carry or clock crossing domain, generate enable instead.

 

it is optional but is better style name every process with a name. This can help identify logic and help read code:

example U1_SCK_edge_detect : process(clk, reset)

 

On my old books where written to avoid elsif rising_edge, goggling around also college site I seen a lot of example using

if reset then   elsif rising_edge(clk) then

 

This was discouraged on old school with nested if then else instead of if then elsif:

 

[processname:] process(clk,reset) begin if reset then   {reset action} else if risng_edge(clk) then [if clk_enable then else end if]   end if; end if; end process;

Aout this no idea if exhibit side effect.

 

If this cannot solve your issue ask again.

Regards

 

0 Kudos
Reply