Created
June 20, 2012 14:45
-
-
Save pinski1/2960235 to your computer and use it in GitHub Desktop.
An I2C Master peripheral written in VHDL.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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