Line 140... |
Line 140... |
-- Tbuf 4.7us 1.3us Bus free time between a stop and start condition
|
-- Tbuf 4.7us 1.3us Bus free time between a stop and start condition
|
--
|
--
|
|
|
library ieee;
|
library ieee;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_arith.all;
|
use ieee.numeric_std.all;
|
|
|
entity i2c_master_bit_ctrl is
|
entity i2c_master_bit_ctrl is
|
port (
|
port (
|
clk : in std_logic;
|
clk : in std_logic;
|
rst : in std_logic;
|
rst : in std_logic;
|
Line 188... |
Line 188... |
signal sSCL, sSDA : std_logic; -- synchronized SCL and SDA inputs
|
signal sSCL, sSDA : std_logic; -- synchronized SCL and SDA inputs
|
signal dSCL, dSDA : std_logic; -- delayed versions ofsSCL and sSDA
|
signal dSCL, dSDA : std_logic; -- delayed versions ofsSCL and sSDA
|
signal clk_en : std_logic; -- statemachine clock enable
|
signal clk_en : std_logic; -- statemachine clock enable
|
signal scl_sync, slave_wait : std_logic; -- clock generation signals
|
signal scl_sync, slave_wait : std_logic; -- clock generation signals
|
signal ial : std_logic; -- internal arbitration lost signal
|
signal ial : std_logic; -- internal arbitration lost signal
|
-- signal cnt : unsigned(15 downto 0) := clk_cnt; -- clock divider counter (simulation)
|
|
signal cnt : unsigned(15 downto 0); -- clock divider counter (synthesis)
|
signal cnt : unsigned(15 downto 0); -- clock divider counter (synthesis)
|
|
|
begin
|
begin
|
-- whenever the slave is not ready it can delay the cycle by pulling SCL low
|
-- whenever the slave is not ready it can delay the cycle by pulling SCL low
|
-- delay scl_oen
|
-- delay scl_oen
|
process (clk)
|
process (clk, nReset)
|
begin
|
begin
|
if (clk'event and clk = '1') then
|
if (nReset = '0') then
|
|
dscl_oen <= '0';
|
|
elsif (clk'event and clk = '1') then
|
dscl_oen <= iscl_oen;
|
dscl_oen <= iscl_oen;
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
-- slave_wait is asserted when master wants to drive SCL high, but the slave (another master) pulls it low
|
-- slave_wait is asserted when master wants to drive SCL high, but the slave pulls it low
|
-- slave_wait remains asserted until the slave (other master) releases SCL
|
-- slave_wait remains asserted until the slave releases SCL
|
process (clk, nReset)
|
process (clk, nReset)
|
begin
|
begin
|
if (nReset = '0') then
|
if (nReset = '0') then
|
slave_wait <= '0';
|
slave_wait <= '0';
|
elsif (clk'event and clk = '1') then
|
elsif (clk'event and clk = '1') then
|
Line 223... |
Line 224... |
begin
|
begin
|
if (nReset = '0') then
|
if (nReset = '0') then
|
cnt <= (others => '0');
|
cnt <= (others => '0');
|
clk_en <= '1';
|
clk_en <= '1';
|
elsif (clk'event and clk = '1') then
|
elsif (clk'event and clk = '1') then
|
if (rst = '1') then
|
if ((rst = '1') or (cnt = 0) or (ena = '0') or (scl_sync = '1')) then
|
cnt <= (others => '0');
|
|
clk_en <= '1';
|
|
elsif ( (cnt = 0) or (ena = '0') or (scl_sync = '1') ) then
|
|
cnt <= clk_cnt;
|
cnt <= clk_cnt;
|
clk_en <= '1';
|
clk_en <= '1';
|
elsif (slave_wait = '1') then
|
elsif (slave_wait = '1') then
|
cnt <= cnt;
|
cnt <= cnt;
|
clk_en <= '0';
|
clk_en <= '0';
|
Line 242... |
Line 240... |
end process gen_clken;
|
end process gen_clken;
|
|
|
|
|
-- generate bus status controller
|
-- generate bus status controller
|
bus_status_ctrl: block
|
bus_status_ctrl: block
|
|
signal cSCL, cSDA : std_logic_vector( 1 downto 0); -- capture SDA and SCL
|
|
signal fSCL, fSDA : std_logic_vector( 2 downto 0); -- filter inputs for SCL and SDA
|
|
signal filter_cnt : unsigned(13 downto 0); -- clock divider for filter
|
signal sta_condition : std_logic; -- start detected
|
signal sta_condition : std_logic; -- start detected
|
signal sto_condition : std_logic; -- stop detected
|
signal sto_condition : std_logic; -- stop detected
|
signal cmd_stop : std_logic; -- STOP command
|
signal cmd_stop : std_logic; -- STOP command
|
signal ibusy : std_logic; -- internal busy signal
|
signal ibusy : std_logic; -- internal busy signal
|
begin
|
begin
|
-- synchronize SCL and SDA inputs
|
-- capture SCL and SDA
|
synch_scl_sda: process(clk, nReset)
|
capture_scl_sda: process(clk, nReset)
|
|
begin
|
|
if (nReset = '0') then
|
|
cSCL <= "00";
|
|
cSDA <= "00";
|
|
elsif (clk'event and clk = '1') then
|
|
if (rst = '1') then
|
|
cSCL <= "00";
|
|
cSDA <= "00";
|
|
else
|
|
cSCL <= (cSCL(0) & scl_i);
|
|
cSDA <= (cSDA(0) & sda_i);
|
|
end if;
|
|
end if;
|
|
end process capture_scl_sda;
|
|
|
|
-- filter SCL and SDA; (attempt to) remove glitches
|
|
filter_divider: process(clk, nReset)
|
|
begin
|
|
if (nReset = '0') then
|
|
filter_cnt <= (others => '0');
|
|
elsif (clk'event and clk = '1') then
|
|
if ((rst = '1') or (filter_cnt = 0)) then
|
|
filter_cnt <= clk_cnt(15 downto 2);
|
|
else
|
|
filter_cnt <= filter_cnt -1;
|
|
end if;
|
|
end if;
|
|
end process filter_divider;
|
|
|
|
filter_scl_sda: process(clk, nReset)
|
|
begin
|
|
if (nReset = '0') then
|
|
fSCL <= (others => '1');
|
|
fSDA <= (others => '1');
|
|
elsif (clk'event and clk = '1') then
|
|
if (rst = '1') then
|
|
fSCL <= (others => '1');
|
|
fSDA <= (others => '1');
|
|
elsif (filter_cnt = 0) then
|
|
fSCL <= (fSCL(1 downto 0) & cSCL(1));
|
|
fSDA <= (fSDA(1 downto 0) & cSDA(1));
|
|
end if;
|
|
end if;
|
|
end process filter_scl_sda;
|
|
|
|
-- generate filtered SCL and SDA signals
|
|
scl_sda: process(clk, nReset)
|
begin
|
begin
|
if (nReset = '0') then
|
if (nReset = '0') then
|
sSCL <= '1';
|
sSCL <= '1';
|
sSDA <= '1';
|
sSDA <= '1';
|
|
|
Line 264... |
Line 312... |
sSDA <= '1';
|
sSDA <= '1';
|
|
|
dSCL <= '1';
|
dSCL <= '1';
|
dSDA <= '1';
|
dSDA <= '1';
|
else
|
else
|
sSCL <= scl_i;
|
sSCL <= (fSCL(2) and fSCL(1)) or
|
sSDA <= sda_i;
|
(fSCL(2) and fSCL(0)) or
|
|
(fSCL(1) and fSCL(0));
|
|
sSDA <= (fSDA(2) and fSDA(1)) or
|
|
(fSDA(2) and fSDA(0)) or
|
|
(fSCL(1) and fSCL(0));
|
|
|
dSCL <= sSCL;
|
dSCL <= sSCL;
|
dSDA <= sSDA;
|
dSDA <= sSDA;
|
end if;
|
end if;
|
end if;
|
end if;
|
end process synch_SCL_SDA;
|
end process scl_sda;
|
|
|
|
|
-- detect start condition => detect falling edge on SDA while SCL is high
|
-- detect start condition => detect falling edge on SDA while SCL is high
|
-- detect stop condition => detect rising edge on SDA while SCL is high
|
-- detect stop condition => detect rising edge on SDA while SCL is high
|
detect_sta_sto: process(clk, nReset)
|
detect_sta_sto: process(clk, nReset)
|
begin
|
begin
|
Line 291... |
Line 344... |
sto_condition <= (sSDA and not dSDA) and sSCL;
|
sto_condition <= (sSDA and not dSDA) and sSCL;
|
end if;
|
end if;
|
end if;
|
end if;
|
end process detect_sta_sto;
|
end process detect_sta_sto;
|
|
|
|
|
-- generate i2c-bus busy signal
|
-- generate i2c-bus busy signal
|
gen_busy: process(clk, nReset)
|
gen_busy: process(clk, nReset)
|
begin
|
begin
|
if (nReset = '0') then
|
if (nReset = '0') then
|
ibusy <= '0';
|
ibusy <= '0';
|
Line 330... |
Line 384... |
cmd_stop <= '0';
|
cmd_stop <= '0';
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
if (c_state = idle) then
|
if (c_state = idle) then
|
ial <= (sda_chk and not sSDA and isda_oen);
|
|
else
|
|
ial <= (sda_chk and not sSDA and isda_oen) or (sto_condition and not cmd_stop);
|
ial <= (sda_chk and not sSDA and isda_oen) or (sto_condition and not cmd_stop);
|
|
else
|
|
ial <= (sda_chk and not sSDA and isda_oen);
|
end if;
|
end if;
|
|
|
end if;
|
end if;
|
end if;
|
end if;
|
end process gen_al;
|
end process gen_al;
|
al <= ial;
|
al <= ial;
|
|
|
|
|
-- generate dout signal, store dout on rising edge of SCL
|
-- generate dout signal, store dout on rising edge of SCL
|
gen_dout: process(clk)
|
gen_dout: process(clk, nReset)
|
begin
|
begin
|
if (clk'event and clk = '1') then
|
if (nReset = '0') then
|
|
dout <= '0';
|
|
elsif (clk'event and clk = '1') then
|
if (sSCL = '1' and dSCL = '0') then
|
if (sSCL = '1' and dSCL = '0') then
|
dout <= sSDA;
|
dout <= sSDA;
|
end if;
|
end if;
|
end if;
|
end if;
|
end process gen_dout;
|
end process gen_dout;
|
end block bus_status_ctrl;
|
end block bus_status_ctrl;
|
|
|
|
|
-- generate statemachine
|
-- generate statemachine
|
nxt_state_decoder : process (clk, nReset, c_state, cmd)
|
nxt_state_decoder : process (clk, nReset)
|
begin
|
begin
|
if (nReset = '0') then
|
if (nReset = '0') then
|
c_state <= idle;
|
c_state <= idle;
|
cmd_ack <= '0';
|
cmd_ack <= '0';
|
iscl_oen <= '1';
|
iscl_oen <= '1';
|
Line 482... |
Line 538... |
|
|
when wr_b =>
|
when wr_b =>
|
c_state <= wr_c;
|
c_state <= wr_c;
|
iscl_oen <= '1'; -- set SCL high
|
iscl_oen <= '1'; -- set SCL high
|
isda_oen <= din; -- keep SDA
|
isda_oen <= din; -- keep SDA
|
sda_chk <= '1'; -- check SDA
|
sda_chk <= '0'; -- don't check SDA yet
|
|
-- Allow some more time for SDA and SCL to settle
|
|
|
when wr_c =>
|
when wr_c =>
|
c_state <= wr_d;
|
c_state <= wr_d;
|
iscl_oen <= '1'; -- keep SCL high
|
iscl_oen <= '1'; -- keep SCL high
|
isda_oen <= din; -- keep SDA
|
isda_oen <= din; -- keep SDA
|