URL
https://opencores.org/ocsvn/i2c/i2c/trunk
Subversion Repositories i2c
[/] [i2c/] [trunk/] [rtl/] [vhdl/] [i2c_master_bit_ctrl.vhd] - Rev 22
Go to most recent revision | Compare with Previous | Blame | View Log
--------------------------------------------------------------------- ---- ---- ---- WISHBONE revB2 compl. I2C Master Core; bit-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_bit_ctrl.vhd,v 1.2 2002-06-15 07:37:04 rherveille Exp $ -- -- $Date: 2002-06-15 07:37:04 $ -- $Revision: 1.2 $ -- $Author: rherveille $ -- $Locker: $ -- $State: Exp $ -- -- Change History: -- $Log: not supported by cvs2svn $ -- 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. -- -- ------------------------------------- -- Bit controller section ------------------------------------ -- -- Translate simple commands into SCL/SDA transitions -- Each command has 5 states, A/B/C/D/idle -- -- start: SCL ~~~~~~~~~~\____ -- SDA ~~~~~~~~\______ -- x | A | B | C | D | i -- -- repstart SCL ____/~~~~\___ -- SDA __/~~~\______ -- x | A | B | C | D | i -- -- stop SCL ____/~~~~~~~~ -- SDA ==\____/~~~~~ -- x | A | B | C | D | i -- --- write SCL ____/~~~~\____ -- SDA ==X=========X= -- x | A | B | C | D | i -- --- read SCL ____/~~~~\____ -- SDA XXXX=====XXXX -- x | A | B | C | D | i -- -- Timing: Normal mode Fast mode ----------------------------------------------------------------- -- Fscl 100KHz 400KHz -- Th_scl 4.0us 0.6us High period of SCL -- Tl_scl 4.7us 1.3us Low period of SCL -- Tsu:sta 4.7us 0.6us setup time for a repeated start condition -- Tsu:sto 4.0us 0.6us setup time for a stop conditon -- Tbuf 4.7us 1.3us Bus free time between a stop and start condition -- library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; entity i2c_master_bit_ctrl is generic( Tcq : time := 1 ns ); 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; busy : out std_logic; 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 entity i2c_master_bit_ctrl; architecture structural of i2c_master_bit_ctrl is 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"; type states is (idle, start_a, start_b, start_c, start_d, stop_a, stop_b, stop_c, rd_a, rd_b, rd_c, rd_d, wr_a, wr_b, wr_c, wr_d); signal c_state : states; signal iscl_oen, isda_oen : std_logic; -- internal I2C lines signal sSCL, sSDA : std_logic; -- synchronized SCL and SDA inputs signal dscl_oen : std_logic; -- delayed scl_oen signals signal clk_en, slave_wait :std_logic; -- clock generation signals -- signal cnt : unsigned(15 downto 0) := clk_cnt; -- clock divider counter (simulation) signal cnt : unsigned(15 downto 0); -- clock divider counter (synthesis) begin -- synchronize SCL and SDA inputs synch_scl_sda: process(clk) begin if (clk'event and clk = '1') then sSCL <= scl_i after Tcq; sSDA <= sda_i after Tcq; end if; end process synch_SCL_SDA; -- delay scl_oen process (clk) begin if (clk'event and clk = '1') then dscl_oen <= iscl_oen after Tcq; end if; end process; -- whenever the slave is not ready it can delay the cycle by pulling SCL low slave_wait <= dscl_oen and not sSCL; -- generate clk enable signal gen_clken: process(clk, nReset) begin if (nReset = '0') then cnt <= (others => '0') after Tcq; clk_en <= '1' after Tcq; elsif (clk'event and clk = '1') then if (rst = '1') then cnt <= (others => '0') after Tcq; clk_en <= '1' after Tcq; else if ( (cnt = 0) or (ena = '0') ) then clk_en <= '1' after Tcq; cnt <= clk_cnt after Tcq; else if (slave_wait = '0') then cnt <= cnt -1 after Tcq; end if; clk_en <= '0' after Tcq; end if; end if; end if; end process gen_clken; -- generate bus status controller bus_status_ctrl: block signal dSDA : std_logic; signal sta_condition : std_logic; signal sto_condition : std_logic; signal ibusy : std_logic; begin -- 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_sta_sto: process(clk) begin if (clk'event and clk = '1') then dSDA <= sSDA; -- generate a delayed version of sSDA sta_condition <= (not sSDA and dSDA) and sSCL; sto_condition <= (sSDA and not dSDA) and sSCL; end if; end process detect_sta_sto; -- generate bus busy signal gen_busy: process(clk, nReset) begin if (nReset = '0') then ibusy <= '0' after Tcq; elsif (clk'event and clk = '1') then if (rst = '1') then ibusy <= '0' after Tcq; else ibusy <= (sta_condition or ibusy) and not sto_condition after Tcq; end if; end if; end process gen_busy; -- assign output busy <= ibusy; end block bus_status_ctrl; -- generate statemachine nxt_state_decoder : process (clk, nReset, c_state, cmd) variable nxt_state : states; variable icmd_ack, store_sda : std_logic; begin nxt_state := c_state; icmd_ack := '0'; -- default no acknowledge store_sda := '0'; case (c_state) is -- idle when idle => case cmd is when I2C_CMD_START => nxt_state := start_a; when I2C_CMD_STOP => nxt_state := stop_a; when I2C_CMD_WRITE => nxt_state := wr_a; when I2C_CMD_READ => nxt_state := rd_a; when others => -- NOP command nxt_state := idle; end case; -- start when start_a => nxt_state := start_b; when start_b => nxt_state := start_c; when start_c => nxt_state := start_d; when start_d => nxt_state := idle; icmd_ack := '1'; -- command completed -- stop when stop_a => nxt_state := stop_b; when stop_b => nxt_state := stop_c; when stop_c => nxt_state := idle; icmd_ack := '1'; -- command completed -- read when rd_a => nxt_state := rd_b; when rd_b => nxt_state := rd_c; when rd_c => nxt_state := rd_d; store_sda := '1'; when rd_d => nxt_state := idle; icmd_ack := '1'; -- command completed -- write when wr_a => nxt_state := wr_b; when wr_b => nxt_state := wr_c; when wr_c => nxt_state := wr_d; when wr_d => nxt_state := idle; icmd_ack := '1'; -- command completed end case; -- generate regs if (nReset = '0') then c_state <= idle after Tcq; cmd_ack <= '0' after Tcq; Dout <= '0' after Tcq; elsif (clk'event and clk = '1') then if (rst = '1') then c_state <= idle after Tcq; cmd_ack <= '0' after Tcq; Dout <= '0' after Tcq; else if (clk_en = '1') then c_state <= nxt_state after Tcq; if (store_sda = '1') then dout <= sSDA after Tcq; end if; end if; cmd_ack <= icmd_ack and clk_en; end if; end if; end process nxt_state_decoder; -- -- convert states to SCL and SDA signals -- output_decoder: process (clk, nReset, c_state, iscl_oen, isda_oen, din) variable iscl, isda : std_logic; begin case (c_state) is when idle => iscl := iscl_oen; -- keep SCL in same state isda := isda_oen; -- keep SDA in same state -- start when start_a => iscl := iscl_oen; -- keep SCL in same state (for repeated start) isda := '1'; -- set SDA high when start_b => iscl := '1'; -- set SCL high isda := '1'; -- keep SDA high when start_c => iscl := '1'; -- keep SCL high isda := '0'; -- sel SDA low when start_d => iscl := '0'; -- set SCL low isda := '0'; -- keep SDA low -- stop when stop_a => iscl := '0'; -- keep SCL disabled isda := '0'; -- set SDA low when stop_b => iscl := '1'; -- set SCL high isda := '0'; -- keep SDA low when stop_c => iscl := '1'; -- keep SCL high isda := '1'; -- set SDA high -- write when wr_a => iscl := '0'; -- keep SCL low isda := din; -- set SDA when wr_b => iscl := '1'; -- set SCL high isda := din; -- keep SDA when wr_c => iscl := '1'; -- keep SCL high isda := din; -- keep SDA when wr_d => iscl := '0'; -- set SCL low isda := din; -- keep SDA -- read when rd_a => iscl := '0'; -- keep SCL low isda := '1'; -- tri-state SDA when rd_b => iscl := '1'; -- set SCL high isda := '1'; -- tri-state SDA when rd_c => iscl := '1'; -- keep SCL high isda := '1'; -- tri-state SDA when rd_d => iscl := '0'; -- set SCL low isda := '1'; -- tri-state SDA end case; -- generate registers if (nReset = '0') then iscl_oen <= '1' after Tcq; isda_oen <= '1' after Tcq; elsif (clk'event and clk = '1') then if (rst = '1') then iscl_oen <= '1' after Tcq; isda_oen <= '1' after Tcq; else if (clk_en = '1') then iscl_oen <= iscl after Tcq; isda_oen <= isda after Tcq; end if; end if; end if; end process output_decoder; -- assign outputs scl_o <= '0'; scl_oen <= iscl_oen; sda_o <= '0'; sda_oen <= isda_oen; end architecture structural;
Go to most recent revision | Compare with Previous | Blame | View Log