hdet/fpga/src/i2c/master/i2c_master_byte_ctrl.vhd

372 lines
13 KiB
VHDL

---------------------------------------------------------------------
---- ----
---- WISHBONE revB2 compl. I2C Master Core; byte-controller ----
---- ----
---- ----
---- Author: Richard Herveille ----
---- richard@asics.ws ----
---- www.asics.ws ----
---- ----
---- Downloaded from: http://www.opencores.org/projects/i2c/ ----
---- ----
---------------------------------------------------------------------
---- ----
---- Copyright (C) 2000 Richard Herveille ----
---- richard@asics.ws ----
---- ----
---- This source file may be used and distributed without ----
---- restriction provided that this copyright statement is not ----
---- removed from the file and that any derivative work contains ----
---- the original copyright notice and the associated disclaimer.----
---- ----
---- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ----
---- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ----
---- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ----
---- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ----
---- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ----
---- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ----
---- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ----
---- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ----
---- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ----
---- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ----
---- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ----
---- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ----
---- POSSIBILITY OF SUCH DAMAGE. ----
---- ----
---------------------------------------------------------------------
-- CVS Log
--
-- $Id: i2c_master_byte_ctrl.vhd,v 1.5 2004/02/18 11:41:48 rherveille Exp $
--
-- $Date: 2004/02/18 11:41:48 $
-- $Revision: 1.5 $
-- $Author: rherveille $
-- $Locker: $
-- $State: Exp $
--
-- Change History:
-- $Log: i2c_master_byte_ctrl.vhd,v $
-- Revision 1.5 2004/02/18 11:41:48 rherveille
-- Fixed a potential bug in the statemachine. During a 'stop' 2 cmd_ack signals were generated. Possibly canceling a new start command.
--
-- Revision 1.4 2003/08/09 07:01:13 rherveille
-- Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line.
-- Fixed a potential bug in the byte controller's host-acknowledge generation.
--
-- Revision 1.3 2002/12/26 16:05:47 rherveille
-- Core is now a Multimaster I2C controller.
--
-- Revision 1.2 2002/11/30 22:24:37 rherveille
-- Cleaned up code
--
-- Revision 1.1 2001/11/05 12:02:33 rherveille
-- Split i2c_master_core.vhd into separate files for each entity; same layout as verilog version.
-- Code updated, is now up-to-date to doc. rev.0.4.
-- Added headers.
--
--
------------------------------------------
-- Byte controller section
------------------------------------------
--
library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_Logic_unsigned.all;
use IEEE.numeric_std.all;
entity i2c_master_byte_ctrl is
port (
clk : in std_logic;
rst : in std_logic; -- synchronous active high reset (WISHBONE compatible)
nReset : in std_logic; -- asynchornous active low reset (FPGA compatible)
ena : in std_logic; -- core enable signal
clk_cnt : in unsigned(15 downto 0); -- 4x SCL
-- input signals
start,
stop,
read,
write,
ack_in : std_logic;
din : in std_logic_vector(7 downto 0);
-- output signals
cmd_ack : out std_logic; -- command done
ack_out : out std_logic;
i2c_busy : out std_logic; -- arbitration lost
i2c_al : out std_logic; -- i2c bus busy
dout : out std_logic_vector(7 downto 0);
-- i2c lines
scl_i : in std_logic; -- i2c clock line input
scl_o : out std_logic; -- i2c clock line output
scl_oen : out std_logic; -- i2c clock line output enable, active low
sda_i : in std_logic; -- i2c data line input
sda_o : out std_logic; -- i2c data line output
sda_oen : out std_logic -- i2c data line output enable, active low
);
end entity i2c_master_byte_ctrl;
architecture structural of i2c_master_byte_ctrl is
component i2c_master_bit_ctrl is
port (
clk : in std_logic;
rst : in std_logic;
nReset : in std_logic;
ena : in std_logic; -- core enable signal
clk_cnt : in unsigned(15 downto 0); -- clock prescale value
cmd : in std_logic_vector(3 downto 0);
cmd_ack : out std_logic; -- command done
busy : out std_logic; -- i2c bus busy
al : out std_logic; -- arbitration lost
din : in std_logic;
dout : out std_logic;
-- i2c lines
scl_i : in std_logic; -- i2c clock line input
scl_o : out std_logic; -- i2c clock line output
scl_oen : out std_logic; -- i2c clock line output enable, active low
sda_i : in std_logic; -- i2c data line input
sda_o : out std_logic; -- i2c data line output
sda_oen : out std_logic -- i2c data line output enable, active low
);
end component i2c_master_bit_ctrl;
-- commands for bit_controller block
constant I2C_CMD_NOP : std_logic_vector(3 downto 0) := "0000";
constant I2C_CMD_START : std_logic_vector(3 downto 0) := "0001";
constant I2C_CMD_STOP : std_logic_vector(3 downto 0) := "0010";
constant I2C_CMD_READ : std_logic_vector(3 downto 0) := "0100";
constant I2C_CMD_WRITE : std_logic_vector(3 downto 0) := "1000";
-- signals for bit_controller
signal core_cmd : std_logic_vector(3 downto 0);
signal core_ack, core_txd, core_rxd : std_logic;
signal al : std_logic;
-- signals for shift register
signal sr : std_logic_vector(7 downto 0); -- 8bit shift register
signal shift, ld : std_logic;
-- signals for state machine
signal go, host_ack : std_logic;
signal dcnt : unsigned(2 downto 0); -- data counter
signal cnt_done : std_logic;
begin
-- hookup bit_controller
bit_ctrl: i2c_master_bit_ctrl port map(
clk => clk,
rst => rst,
nReset => nReset,
ena => ena,
clk_cnt => clk_cnt,
cmd => core_cmd,
cmd_ack => core_ack,
busy => i2c_busy,
al => al,
din => core_txd,
dout => core_rxd,
scl_i => scl_i,
scl_o => scl_o,
scl_oen => scl_oen,
sda_i => sda_i,
sda_o => sda_o,
sda_oen => sda_oen
);
i2c_al <= al;
-- generate host-command-acknowledge
cmd_ack <= host_ack;
-- generate go-signal
go <= (read or write or stop) and not host_ack;
-- assign Dout output to shift-register
dout <= sr;
-- generate shift register
shift_register: process(clk, nReset)
begin
if (nReset = '0') then
sr <= (others => '0');
elsif (clk'event and clk = '1') then
if (rst = '1') then
sr <= (others => '0');
elsif (ld = '1') then
sr <= din;
elsif (shift = '1') then
sr <= (sr(6 downto 0) & core_rxd);
end if;
end if;
end process shift_register;
-- generate data-counter
data_cnt: process(clk, nReset)
begin
if (nReset = '0') then
dcnt <= (others => '0');
elsif (clk'event and clk = '1') then
if (rst = '1') then
dcnt <= (others => '0');
elsif (ld = '1') then
dcnt <= (others => '1'); -- load counter with 7
elsif (shift = '1') then
dcnt <= dcnt -1;
end if;
end if;
end process data_cnt;
cnt_done <= '1' when (dcnt = 0) else '0';
--
-- state machine
--
statemachine : block
type states is (st_idle, st_start, st_read, st_write, st_ack, st_stop);
signal c_state : states;
begin
--
-- command interpreter, translate complex commands into simpler I2C commands
--
nxt_state_decoder: process(clk, nReset)
begin
if (nReset = '0') then
core_cmd <= I2C_CMD_NOP;
core_txd <= '0';
shift <= '0';
ld <= '0';
host_ack <= '0';
c_state <= st_idle;
ack_out <= '0';
elsif (clk'event and clk = '1') then
if (rst = '1' or al = '1') then
core_cmd <= I2C_CMD_NOP;
core_txd <= '0';
shift <= '0';
ld <= '0';
host_ack <= '0';
c_state <= st_idle;
ack_out <= '0';
else
-- initialy reset all signal
core_txd <= sr(7);
shift <= '0';
ld <= '0';
host_ack <= '0';
case c_state is
when st_idle =>
if (go = '1') then
if (start = '1') then
c_state <= st_start;
core_cmd <= I2C_CMD_START;
elsif (read = '1') then
c_state <= st_read;
core_cmd <= I2C_CMD_READ;
elsif (write = '1') then
c_state <= st_write;
core_cmd <= I2C_CMD_WRITE;
else -- stop
c_state <= st_stop;
core_cmd <= I2C_CMD_STOP;
end if;
ld <= '1';
end if;
when st_start =>
if (core_ack = '1') then
if (read = '1') then
c_state <= st_read;
core_cmd <= I2C_CMD_READ;
else
c_state <= st_write;
core_cmd <= I2C_CMD_WRITE;
end if;
ld <= '1';
end if;
when st_write =>
if (core_ack = '1') then
if (cnt_done = '1') then
c_state <= st_ack;
core_cmd <= I2C_CMD_READ;
else
c_state <= st_write; -- stay in same state
core_cmd <= I2C_CMD_WRITE; -- write next bit
shift <= '1';
end if;
end if;
when st_read =>
if (core_ack = '1') then
if (cnt_done = '1') then
c_state <= st_ack;
core_cmd <= I2C_CMD_WRITE;
else
c_state <= st_read; -- stay in same state
core_cmd <= I2C_CMD_READ; -- read next bit
end if;
shift <= '1';
core_txd <= ack_in;
end if;
when st_ack =>
if (core_ack = '1') then
-- check for stop; Should a STOP command be generated ?
if (stop = '1') then
c_state <= st_stop;
core_cmd <= I2C_CMD_STOP;
else
c_state <= st_idle;
core_cmd <= I2C_CMD_NOP;
-- generate command acknowledge signal
host_ack <= '1';
end if;
-- assign ack_out output to core_rxd (contains last received bit)
ack_out <= core_rxd;
core_txd <= '1';
else
core_txd <= ack_in;
end if;
when st_stop =>
if (core_ack = '1') then
c_state <= st_idle;
core_cmd <= I2C_CMD_NOP;
-- generate command acknowledge signal
host_ack <= '1';
end if;
when others => -- illegal states
c_state <= st_idle;
core_cmd <= I2C_CMD_NOP;
report ("Byte controller entered illegal state.");
end case;
end if;
end if;
end process nxt_state_decoder;
end block statemachine;
end architecture structural;