From 8e49e9b08708f1a5a9711e89a9424b4295773225 Mon Sep 17 00:00:00 2001 From: the great greene arkleseizure Date: Sat, 12 Oct 2013 08:17:43 +0100 Subject: fish --- i2c_loader.vhd | 304 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 i2c_loader.vhd (limited to 'i2c_loader.vhd') diff --git a/i2c_loader.vhd b/i2c_loader.vhd new file mode 100644 index 0000000..35da643 --- /dev/null +++ b/i2c_loader.vhd @@ -0,0 +1,304 @@ +-- ZX Spectrum for Altera DE1 +-- +-- Copyright (c) 2009-2011 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 agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- 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) + 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; + -- cgit v1.2.3