URL
https://opencores.org/ocsvn/a-z80/a-z80/trunk
Subversion Repositories a-z80
[/] [a-z80/] [trunk/] [host/] [zxspectrum_de1/] [ula/] [i2c_loader.vhd] - Rev 8
Compare with Previous | Blame | View Log
-- ZX Spectrum for Altera DE1 -- -- Copyright (c) 2009-2010 Mike Stirling -- -- All rights reserved -- -- Redistribution and use in source and synthezised forms, with or without -- modification, are permitted provided that the following conditions are met: -- -- * Redistributions of source code must retain the above copyright notice, -- this list of conditions and the following disclaimer. -- -- * Redistributions in synthesized form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- * Neither the name of the author nor the names of other contributors may -- be used to endorse or promote products derived from this software without -- specific prior written permission. -- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -- PURPOSE ARE DISCLAIMED. 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. -- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; use IEEE.STD_LOGIC_MISC.ALL; -- for AND_REDUCE use IEEE.NUMERIC_STD.ALL; entity i2c_loader is generic ( -- Address of slave to be loaded device_address : integer := 16#1a#; -- Number of retries to allow before stopping num_retries : integer := 0; -- Length of clock divider in bits. Resulting bus frequency is -- CLK/2^(log2_divider + 2) log2_divider : integer := 6 ); port ( CLK : in std_logic; nRESET : in std_logic; I2C_SCL : inout std_logic; I2C_SDA : inout std_logic; IS_DONE : out std_logic; IS_ERROR : out std_logic ); end i2c_loader; architecture i2c_loader_arch of i2c_loader is type regs is array(0 to 19) of std_logic_vector(7 downto 0); constant init_regs : regs := ( -- Left line in, 0dB, unmute X"00", X"17", -- Right line in, 0dB, unmute X"02", X"17", -- Left headphone out, 0dB X"04", X"79", -- Right headphone out, 0dB X"06", X"79", -- Audio path, DAC enabled, Line in, Bypass off, mic unmuted X"08", X"10", -- Digital path, Unmute, HP filter enabled X"0A", X"00", -- Power down mic, clkout and xtal osc X"0C", X"62", -- Format 16-bit I2S, no bit inversion or phase changes X"0E", X"02", -- Sampling control, 8 kHz USB mode (MCLK = 250fs * 6) X"10", X"0D", -- Activate X"12", X"01" ); -- Number of bursts (i.e. total number of registers) constant burst_length : positive := 2; -- Number of bytes to transfer per burst constant num_bursts : positive := (init_regs'length / burst_length); type state_t is (Idle, Start, Data, Ack, Stop, Pause, Done); signal state : state_t; signal phase : std_logic_vector(1 downto 0); subtype nbit_t is integer range 0 to 7; signal nbit : nbit_t; subtype nbyte_t is integer range 0 to burst_length; -- +1 for address byte signal nbyte : nbyte_t; subtype thisbyte_t is integer range 0 to init_regs'length; -- +1 for "done" signal thisbyte : thisbyte_t; subtype retries_t is integer range 0 to num_retries; signal retries : retries_t; signal clken : std_logic; signal divider : std_logic_vector(log2_divider-1 downto 0); signal shiftreg : std_logic_vector(7 downto 0); signal scl_out : std_logic; signal sda_out : std_logic; signal nak : std_logic; begin -- Create open-drain outputs for I2C bus I2C_SCL <= '0' when scl_out = '0' else 'Z'; I2C_SDA <= '0' when sda_out = '0' else 'Z'; -- Status outputs are driven both ways IS_DONE <= '1' when state = Done else '0'; IS_ERROR <= nak; -- Generate clock enable for desired bus speed clken <= AND_REDUCE(divider); process(nRESET,CLK) begin if nRESET = '0' then divider <= (others => '0'); elsif falling_edge(CLK) then divider <= divider + '1'; end if; end process; -- The I2C loader process process(nRESET,CLK,clken) begin if nRESET = '0' then scl_out <= '1'; sda_out <= '1'; state <= Idle; phase <= "00"; nbit <= 0; nbyte <= 0; thisbyte <= 0; shiftreg <= (others => '0'); nak <= '0'; -- No error retries <= num_retries; elsif rising_edge(CLK) and clken = '1' then -- Next phase by default phase <= phase + 1; -- STATE: IDLE if state = Idle then -- Start loading the device registers straight away -- A 'GO' bit could be polled here if required state <= Start; phase <= "00"; scl_out <= '1'; sda_out <= '1'; -- STATE: START elsif state = Start then -- Generate START condition case phase is when "00" => -- Drop SDA first sda_out <= '0'; when "10" => -- Then drop SCL scl_out <= '0'; when "11" => -- Advance to next state -- Shift register loaded with device slave address state <= Data; nbit <= 7; shiftreg <= std_logic_vector(to_unsigned(device_address,7)) & '0'; -- writing nbyte <= burst_length; when others => null; end case; -- STATE: DATA elsif state = Data then -- Generate data case phase is when "00" => -- Drop SCL scl_out <= '0'; when "01" => -- Output data and shift (MSb first) sda_out <= shiftreg(7); shiftreg <= shiftreg(6 downto 0) & '0'; when "10" => -- Raise SCL scl_out <= '1'; when "11" => -- Next bit or advance to next state when done if nbit = 0 then state <= Ack; else nbit <= nbit - 1; end if; when others => null; end case; -- STATE: ACK elsif state = Ack then -- Generate ACK clock and check for error condition case phase is when "00" => -- Drop SCL scl_out <= '0'; when "01" => -- Float data sda_out <= '1'; when "10" => -- Sample ack bit nak <= I2C_SDA; if I2C_SDA = '1' then -- Error nbyte <= 0; -- Close this burst and skip remaining registers thisbyte <= init_regs'length; else -- Hold ACK to avoid spurious stops - this seems to fix a -- problem with the Wolfson codec which releases the ACK -- right on the falling edge of the clock pulse. It looks like -- the device interprets this is a STOP condition and then fails -- to acknowledge the next byte. We can avoid this by holding the -- ACK condition for a little longer. sda_out <= '0'; end if; -- Raise SCL scl_out <= '1'; when "11" => -- Advance to next state if nbyte = 0 then -- No more bytes in this burst - generate a STOP state <= Stop; else -- Generate next byte state <= Data; nbit <= 7; shiftreg <= init_regs(thisbyte); nbyte <= nbyte - 1; thisbyte <= thisbyte + 1; end if; when others => null; end case; -- STATE: STOP elsif state = Stop then -- Generate STOP condition case phase is when "00" => -- Drop SCL first scl_out <= '0'; when "01" => -- Drop SDA sda_out <= '0'; when "10" => -- Raise SCL scl_out <= '1'; when "11" => if thisbyte = init_regs'length then -- All registers done, advance to finished state. This will -- bring SDA high while SCL is still high, completing the STOP -- condition state <= Done; else -- Load the next register after a short delay state <= Pause; end if; when others => null; end case; -- STATE: PAUSE elsif state = Pause then -- Delay for one cycle of 'phase' then start the next burst scl_out <= '1'; sda_out <= '1'; if phase = "11" then state <= Start; end if; -- STATE: DONE else -- Finished scl_out <= '1'; sda_out <= '1'; if nak = '1' and retries > 0 then -- We can retry in the event of a NAK in case the -- slave got out of sync for some reason retries <= retries - 1; state <= Idle; end if; end if; end if; end process; end i2c_loader_arch;