Skip to content

Instantly share code, notes, and snippets.

@pinski1
Created June 20, 2012 14:45
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pinski1/2960235 to your computer and use it in GitHub Desktop.
Save pinski1/2960235 to your computer and use it in GitHub Desktop.
An I2C Master peripheral written in VHDL.
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
ENTITY i2c IS
PORT(
clock_L : IN STD_LOGIC; -- 400kHz
scl_H : INOUT STD_LOGIC;
sda_H : INOUT STD_LOGIC;
int_i2c_L : OUT STD_LOGIC;
recieve : OUT STD_LOGIC_VECTOR (7 DOWNTO 0);
transmit : IN STD_LOGIC_VECTOR (7 DOWNTO 0);
config : INOUT STD_LOGIC_VECTOR (7 DOWNTO 0)
);
-- Declarations
END i2c ;
ARCHITECTURE behaviour OF i2c IS
SIGNAL i_scl_H : STD_LOGIC := 'Z';
SIGNAL i_sda_H : STD_LOGIC := 'Z';
SIGNAL i_recieve : STD_LOGIC_VECTOR(7 DOWNTO 0) := X"00";
SIGNAL i_transmit : STD_LOGIC_VECTOR(7 DOWNTO 0) := X"00";
SIGNAL i_config : STD_LOGIC_VECTOR(7 DOWNTO 0) := "00100000";
SIGNAL i_interrupt_L : STD_LOGIC := '1';
SIGNAL i_data_buffer : STD_LOGIC_VECTOR(7 DOWNTO 0) := X"00";
ALIAS busy_H : STD_LOGIC is i_config(7);
ALIAS ack_status_L : STD_LOGIC is i_config(6); -- 1 = nack, 0 = ack
ALIAS address_H : STD_LOGIC is i_config(5);
ALIAS direction_H : STD_LOGIC is i_config(4); -- Read_H, Write_L
ALIAS enable_H : STD_LOGIC is i_config(3);
ALIAS command : STD_LOGIC_VECTOR(1 DOWNTO 0) is i_config(2 DOWNTO 1);
-- 00 = start
-- 01 = data
-- 10 = repeat
-- 11 = stop
ALIAS go_H : STD_LOGIC is i_config(0);
TYPE i2c_master_states IS (i2c_idle, i2c_start, i2c_data, i2c_repeat, i2c_ack, i2c_stop);
SIGNAL fsm_master : i2c_master_states := i2c_idle; -- initalise in idle
SIGNAL i_bit_counter : natural range 0 to 7 := 0; -- bits transmitted
SIGNAL i_tick_counter : natural range 0 to 3 := 0; -- clock ticks elapsed
BEGIN
i2c_stuff: PROCESS(clock_L)
BEGIN
if(falling_edge(clock_L)) then
-- could do this block a bit better, non?
if(enable_H = '0' and (not (fsm_master = i2c_idle or fsm_master = i2c_stop))) then
fsm_master <= i2c_stop;
--i_tick_counter <= 0;
end if;
case fsm_master is
-- #############
-- ### START ###
-- #############
when i2c_start =>
if(i_tick_counter = 0) then
i_tick_counter <= 1;
direction_H <= transmit(0);
address_H <= '1';
busy_H <= '1';
elsif(i_tick_counter = 1) then
i_tick_counter <= 2;
elsif(i_tick_counter = 2) then
i_tick_counter <= 3;
i_sda_H <= '0';
elsif(i_tick_counter = 3) then
i_tick_counter <= 0;
fsm_master <= i2c_data;
end if;
-- ############
-- ### DATA ###
-- ############
when i2c_data =>
if(i_tick_counter = 0) then
i_tick_counter <= 1;
if(i_bit_counter = 0) then i_data_buffer <= i_transmit;
end if;
i_scl_H <= '0'; -- scl low
busy_H <= '1';
elsif(i_tick_counter = 1) then
i_tick_counter <= 2;
if(direction_H = '0' or address_H = '1') then -- if dir = W or address = t then sda <= data
if(i_data_buffer(7) = '0') then i_sda_H <= '0';
else i_sda_H <= 'Z';
end if;
end if;
i_data_buffer <= i_data_buffer(6 downto 0) & '0';
elsif(i_tick_counter = 2) then
i_tick_counter <= 3;
i_scl_H <= 'Z'; -- scl high
elsif(i_tick_counter = 3) then
i_tick_counter <= 0;
if(direction_H = '1' and address_H = '0') then -- if dir = R & address = f then data <= sda
if(sda_H = 'Z' or sda_H = 'H' or sda_H = '1') then i_data_buffer(0) <= '1';
else i_data_buffer(0) <= '0';
end if;
end if;
if(i_bit_counter < 7) then i_bit_counter <= i_bit_counter + 1;
else
i_bit_counter <= 0;
fsm_master <= i2c_ack;
end if;
end if;
-- ###################
-- ### ACKNOWLEDGE ###
-- ###################
when i2c_ack =>
if(i_tick_counter = 0) then
i_scl_H <= '0'; -- scl low
i_sda_H <= 'Z';
i_recieve <= i_data_buffer; -- seems to take an extra clock tick
if(i_interrupt_L = '1') then i_tick_counter <= 1;
elsif(go_H = '0') then null; -- spool
elsif(command = "01") then
fsm_master <= i2c_data; -- continue
i_interrupt_L <= '1';
elsif(command = "10") then
fsm_master <= i2c_repeat;-- repeat
i_interrupt_L <= '1';
elsif(command = "11") then
fsm_master <= i2c_stop; -- stop
i_interrupt_L <= '1';
end if;
elsif(i_tick_counter = 1) then
i_tick_counter <= 2;
if(direction_H = '1' and address_H = '0') then
i_sda_H <= '0'; -- acknowledge data
else i_sda_H <= 'Z'; -- release sda_H
end if;
elsif(i_tick_counter = 2) then
i_tick_counter <= 3;
i_scl_H <= 'Z'; -- scl high
elsif(i_tick_counter = 3) then
i_tick_counter <= 0;
if(direction_H = '0' or address_H = '1') then
address_H <= '0';
if(sda_H = '0') then ack_status_L <= '0'; -- ACKed
else ack_status_L <= '1'; -- NACKed
end if;
end if;
busy_H <= '0'; -- waiting for next instruction
i_interrupt_L <= '0'; -- trigger interrupt
end if;
-- ##############
-- ### REPEAT ###
-- ##############
when i2c_repeat =>
if(i_tick_counter = 0) then
i_tick_counter <= 1;
i_scl_H <= '0';
i_sda_H <= '0';
busy_H <= '1';
elsif(i_tick_counter = 1) then
i_tick_counter <= 2;
i_sda_H <= 'Z';
elsif(i_tick_counter = 2) then
i_tick_counter <= 3;
i_scl_H <= 'Z';
elsif(i_tick_counter = 3) then
i_tick_counter <= 0;
fsm_master <= i2c_start;
end if;
-- ############
-- ### STOP ###
-- ############
when i2c_stop =>
if(i_tick_counter = 0) then
i_tick_counter <= 1;
i_scl_H <= '0';
i_sda_H <= '0';
busy_H <= '1';
elsif(i_tick_counter = 1) then
i_tick_counter <= 2;
elsif(i_tick_counter = 2) then
i_tick_counter <= 3;
i_scl_H <= 'Z';
elsif(i_tick_counter = 3) then
i_tick_counter <= 0;
busy_H <= '0';
fsm_master <= i2c_idle;
end if;
-- ############
-- ### IDLE ###
-- ############
when i2c_idle =>
i_scl_H <= 'Z'; -- set everything to Z
i_sda_H <= 'Z'; -- set everything to Z
i_data_buffer <= X"00"; -- clear out buffer
i_interrupt_L <= '1';
i_tick_counter <= 0;
i_bit_counter <= 0;
ack_status_L <= '1';
direction_H <= '0';
if((command = "00") and (go_H = '1')) then fsm_master <= i2c_start;
end if;
when others =>
fsm_master <= i2c_idle;
end case;
end if;
END PROCESS;
scl_buffer: scl_H <= i_scl_H;
sda_buffer: sda_H <= i_sda_H;
int_buffer: int_i2c_L <= i_interrupt_L;
rx_buffer: recieve <= i_recieve;
tx_buffer: i_transmit <= transmit;
config_buffer_high: config(7 downto 4) <= i_config(7 downto 4);
config_buffer_low: i_config(3 downto 0) <= config(3 downto 0);
END behaviour;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment