Hey I really need help writing an I2C Controller that can perform a single byte write and a single byte read. Compensation may be given.
Here is what I have so far:
LIBRARY ieee;USE ieee.std_logic_1164.all;USE ieee.std_logic_unsigned.all;ENTITY i2c_master IS GENERIC( input_clk : INTEGER := 50_000_000; --input clock speed from user logic in Hz bus_clk : INTEGER := 400_000); --speed the i2c bus (scl) will run at in Hz PORT( clk : IN STD_LOGIC; --system clock reset_n : IN STD_LOGIC; --active low reset ena : IN STD_LOGIC; --latch in command addr : IN STD_LOGIC_VECTOR(6 DOWNTO 0); --address of target slave rw : IN STD_LOGIC; --'0' is write, '1' is read data_wr : IN STD_LOGIC_VECTOR(7 DOWNTO 0); --data to write to slave busy : OUT STD_LOGIC; --indicates transaction in progress data_rd : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); --data read from slave ack_error : BUFFER STD_LOGIC; --flag if improper acknowledge from slave sda : INOUT STD_LOGIC; --serial data output of i2c bus scl : INOUT STD_LOGIC); --serial clock output of i2c busEND i2c_master;ARCHITECTURE logic OF i2c_master IS CONSTANT divider : INTEGER := (input_clk/bus_clk)/4; --number of clocks in 1/4 cycle of scl TYPE machine IS(ready, start, command, slv_ack1, wr, rd, slv_ack2, mstr_ack, stop); --needed states SIGNAL state : machine; --state machine SIGNAL data_clk : STD_LOGIC; --clock edges for sda SIGNAL scl_clk : STD_LOGIC; --constantly running internal scl SIGNAL scl_ena : STD_LOGIC := '0'; --enables internal scl to output SIGNAL sda_int : STD_LOGIC := '1'; --internal sda SIGNAL sda_ena_n : STD_LOGIC; --enables internal sda to output SIGNAL addr_rw : STD_LOGIC_VECTOR(7 DOWNTO 0); --latched in address and read/write SIGNAL data_tx : STD_LOGIC_VECTOR(7 DOWNTO 0); --latched in data to write to slave SIGNAL data_rx : STD_LOGIC_VECTOR(7 DOWNTO 0); --data received from slave SIGNAL bit_cnt : INTEGER RANGE 0 TO 7 := 7; --tracks bit number in transaction SIGNAL stretch : STD_LOGIC := '0'; --identifies if slave is stretching sclBEGIN --generate the timing for the bus clock (scl_clk) and the data clock (data_clk) PROCESS(clk, reset_n) VARIABLE count : INTEGER RANGE 0 TO divider*4; --timing for clock generation BEGIN IF(reset_n = '0') THEN --reset asserted stretch <= '0'; count := 0; ELSIF(clk'EVENT AND clk = '1') THEN IF(count = divider*4-1) THEN --end of timing cycle count := 0; --reset timer ELSIF(stretch = '0') THEN --clock stretching from slave not detected count := count + 1; --continue clock generation timing END IF; CASE count IS WHEN 0 TO divider-1 => --first 1/4 cycle of clocking scl_clk <= '0'; data_clk <= '0'; WHEN divider TO divider*2-1 => --second 1/4 cycle of clocking scl_clk <= '0'; data_clk <= '1'; WHEN divider*2 TO divider*3-1 => --third 1/4 cycle of clocking scl_clk <= 'Z'; --release scl IF(scl = '0') THEN --detect if slave is stretching clock stretch <= '1'; ELSE stretch <= '0'; END IF; data_clk <= '1'; WHEN OTHERS => --last 1/4 cycle of clocking scl_clk <= 'Z'; data_clk <= '0'; END CASE; END IF; END PROCESS;Thanks
Here is what I have so far:
LIBRARY ieee;USE ieee.std_logic_1164.all;USE ieee.std_logic_unsigned.all;ENTITY i2c_master IS GENERIC( input_clk : INTEGER := 50_000_000; --input clock speed from user logic in Hz bus_clk : INTEGER := 400_000); --speed the i2c bus (scl) will run at in Hz PORT( clk : IN STD_LOGIC; --system clock reset_n : IN STD_LOGIC; --active low reset ena : IN STD_LOGIC; --latch in command addr : IN STD_LOGIC_VECTOR(6 DOWNTO 0); --address of target slave rw : IN STD_LOGIC; --'0' is write, '1' is read data_wr : IN STD_LOGIC_VECTOR(7 DOWNTO 0); --data to write to slave busy : OUT STD_LOGIC; --indicates transaction in progress data_rd : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); --data read from slave ack_error : BUFFER STD_LOGIC; --flag if improper acknowledge from slave sda : INOUT STD_LOGIC; --serial data output of i2c bus scl : INOUT STD_LOGIC); --serial clock output of i2c busEND i2c_master;ARCHITECTURE logic OF i2c_master IS CONSTANT divider : INTEGER := (input_clk/bus_clk)/4; --number of clocks in 1/4 cycle of scl TYPE machine IS(ready, start, command, slv_ack1, wr, rd, slv_ack2, mstr_ack, stop); --needed states SIGNAL state : machine; --state machine SIGNAL data_clk : STD_LOGIC; --clock edges for sda SIGNAL scl_clk : STD_LOGIC; --constantly running internal scl SIGNAL scl_ena : STD_LOGIC := '0'; --enables internal scl to output SIGNAL sda_int : STD_LOGIC := '1'; --internal sda SIGNAL sda_ena_n : STD_LOGIC; --enables internal sda to output SIGNAL addr_rw : STD_LOGIC_VECTOR(7 DOWNTO 0); --latched in address and read/write SIGNAL data_tx : STD_LOGIC_VECTOR(7 DOWNTO 0); --latched in data to write to slave SIGNAL data_rx : STD_LOGIC_VECTOR(7 DOWNTO 0); --data received from slave SIGNAL bit_cnt : INTEGER RANGE 0 TO 7 := 7; --tracks bit number in transaction SIGNAL stretch : STD_LOGIC := '0'; --identifies if slave is stretching sclBEGIN --generate the timing for the bus clock (scl_clk) and the data clock (data_clk) PROCESS(clk, reset_n) VARIABLE count : INTEGER RANGE 0 TO divider*4; --timing for clock generation BEGIN IF(reset_n = '0') THEN --reset asserted stretch <= '0'; count := 0; ELSIF(clk'EVENT AND clk = '1') THEN IF(count = divider*4-1) THEN --end of timing cycle count := 0; --reset timer ELSIF(stretch = '0') THEN --clock stretching from slave not detected count := count + 1; --continue clock generation timing END IF; CASE count IS WHEN 0 TO divider-1 => --first 1/4 cycle of clocking scl_clk <= '0'; data_clk <= '0'; WHEN divider TO divider*2-1 => --second 1/4 cycle of clocking scl_clk <= '0'; data_clk <= '1'; WHEN divider*2 TO divider*3-1 => --third 1/4 cycle of clocking scl_clk <= 'Z'; --release scl IF(scl = '0') THEN --detect if slave is stretching clock stretch <= '1'; ELSE stretch <= '0'; END IF; data_clk <= '1'; WHEN OTHERS => --last 1/4 cycle of clocking scl_clk <= 'Z'; data_clk <= '0'; END CASE; END IF; END PROCESS;Thanks