Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
12 years ago

I2C Controller

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

10 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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 bus

    END 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 scl

    BEGIN

    --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;
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I am so stuck on where to go next. I need to perform a single byte read and single byte write, but i have no idea on what is in the next process

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    -------------------------------------------------------------------------------

    -- Port Declarations --

    -------------------------------------------------------------------------------

    entity I2C_Controller 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 (

    -- Inputs

    clk : in std_logic;

    reset_n : in std_logic;

    address : in std_logic_vector (2 downto 0);

    we : in std_logic;

    byte_enable : in std_logic_vector (1 downto 0);

    rd : in std_logic;

    data : in std_logic_vector (31 downto 0);

    -- Bidirectionals

    I2C_SDAT : inout std_logic;

    -- Outputs

    acknowledge : out std_logic;

    I2C_SCLK : out std_logic;

    read_data : out std_logic_vector (31 downto 0);

    --SRAM_ADDR : out std_logic_vector (17 downto 0);

    --SRAM_CE_N : out std_logic;

    --SRAM_WE_N : out std_logic;

    --SRAM_OE_N : out std_logic;

    --SRAM_UB_N : out std_logic;

    --SRAM_LB_N : out std_logic

    );

    end I2C_Controller;

    architecture I2C_Controller_rtl of I2C_Controller is

    CONSTANT Period_Count : INTEGER := "126";

    CONSTANT HALF_PERIOD_COUNT : INTEGER := "63";

    type State_type is (idle,reads,writes);

    signal current_state, next_state: state_type;

    begin

    process(clk,reset_n)

    VARIABLE count : INTEGER RANGE 0 TO divider*4;

    begin

    if (reset_n = '0') then

    current_state <= idle;

    elsif (clk'event and clk = '1') then

    current_state <= next_state;

    end if;

    end process;

    process(bus_enable,rw,current_state,address,I2C_SDAT,write_data)

    begin

    case current_state is

    when idle =>

    acknowledge <= '0';

    SRAM_CE_N <= '1';

    SRAM_OE_N <= '1';

    SRAM_WE_N <= '1';

    read_data <= (others => 'Z');

    SRAM_ADDR <= (others => '0');

    I2C_SDAT <= (others => 'Z');

    if (bus_enable = '1') then

    if (rw = '1') then

    next_state <= reads;

    else

    next_state <= writes;

    end if;

    end if;

    when reads =>

    SRAM_ADDR <= address (17 downto 0);

    SRAM_CE_N <= '0'; --chip enabled

    SRAM_OE_N <= '0'; --read enabled

    SRAM_WE_N <= '1'; --write disabled

    read_data <= I2C_SDAT;

    I2C_SDAT <= (others => 'Z');

    acknowledge <= '1';

    next_state <= idle;

    when writes =>

    SRAM_ADDR <= address (17 downto 0);

    SRAM_CE_N <= '0'; --chip enabled

    SRAM_OE_N <= '1'; --read disabled

    SRAM_WE_N <= '0'; --write enabled

    I2C_SDAT <= write_data;

    read_data <= (others => 'Z');

    acknowledge <= '1';

    next_state <= idle;

    end case;

    end process;

    process(byte_enable)

    begin

    if byte_enable = "00" then

    SRAM_UB_N <= '1';

    SRAM_LB_N <= '1';

    elsif byte_enable = "01" then

    SRAM_UB_N <= '1';

    SRAM_LB_N <= '0';

    elsif byte_enable = "10" then

    SRAM_UB_N <= '0';

    SRAM_LB_N <= '1';

    else

    SRAM_UB_N <= '0';

    SRAM_LB_N <= '0';

    end if;

    end process;

    end I2C_Controller_rtl;
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Above is the code I have for a single byte read/ single byte write I2C controller, but I know its only halfway there. Anyone able to help me fix my errors?

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    You are particularly missing

    - a state machine for I2C frame generation

    - I2C clock generation

    - data serializer/deserializer

    - ACK handling

    For a working I2C controller, there must be also

    - start/stop sequence generation and respective control inputs

    - address generation

    In so far I doubt about "halfway there".
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    You are particularly missing

    - a state machine for I2C frame generation

    - I2C clock generation

    - data serializer/deserializer

    - ACK handling

    For a working I2C controller, there must be also

    - start/stop sequence generation and respective control inputs

    - address generation

    In so far I doubt about "halfway there".

    --- Quote End ---

    So, are you able to assist me?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I thought he already did.

    Now its your job to go an implement the things he suggested.