--
-- Opcode Block TWOWIRE
-- 
-- To 2-wire serial interface for hopefully all 2-wire serial devices (I2C, SHT71, SPI etc.)
--
-- The first word contains controls applicable to the whole packet(let). These are
-- things like destination port (there are 16 serial busses possible) or clock
-- invert (if this ever needs to be implemented)
--
-- 
-- Each word sent (except the first) is in the form (8b command; 8b data). 
-- Each does a single operation and has a corrosponding response word in the reply packet
-- (even if it didn't expect any data)
-- If there is a timeout waiting for an operation to respond, the data
-- returned, and all subsequent data, will be 0xF00B.
-- As (so far) we have no sensor that returns 16b data, a 1 in the returned msb
-- used to indicate and error.
--
-- A new packetlet can be started by inserting a word of command type 7 as a separtor
--
--
-- Command Protocol:
-- 7:5: protocol: 0=i2c, 2=sht71, 4=spi, 7=new packeltlet, rest=reserved
-- 4: stop (append)
-- 3: start (prepend)
-- 2: send byte
-- 1:0 get n bytes (but 3 will be treated like 2!)

-- The command protocal allows a start + send byte + get 2 bytes + stop in a
-- single word. The must be considered amazing only because it took me 2 weeks
-- of iterating to come up with this most obvious protocol. There is no
-- accounting for Super Caravel induced stupidy.
--
--


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library utils;
use utils.pkg_types.all;

library hsio;
use hsio.pkg_core_globals.all;

library locallink;


entity ocb_twowire is
   port(
      --tw_exec_i : in std_logic;
      -- oc rx interface
      oc_valid_i    : in     std_logic;
      oc_data_i     : in     slv16;
      oc_dack_no    : out    std_logic;
      -- locallink tx interface
      lls_o         : out    t_llsrc;
      lld_i         : in     std_logic;
      -- serialiser
      tick_400khz_i : in     std_logic;   --100kHz
      tick_40khz_i  : in     std_logic;   -- 10kHz
      tick_4khz_i   : in     std_logic;   -- 1kHz
      wdog_tick_i   : in     std_logic;
      sck_dbg_o     : out    std_logic;
      sda_dbg_o     : out    std_logic;
      sda_in_dbg_o  : out    std_logic;
      sda_t_dbg_o   : out    std_logic;
      sck_o         : out    slv16;
      sck_to        : out    slv16;
      sda_o         : out    slv16;
      sda_to        : out    slv16;
      sda_i         : in     slv16;
      -- infrastructure
      clk           : in     std_logic;
      rst           : in     std_logic
   );

-- Declarations

end ocb_twowire ;


architecture rtl of ocb_twowire is

  component ll_fifo_ack_gen
    port(
      data_i   : in  slv16;
      wren_i   : in  std_logic;
      eof_i    : in  std_logic;
      sof_i    : in  std_logic;
      send_i   : in  std_logic;
      opcode_i : in  slv16;
      ocseq_i  : in  slv16;
      size_i   : in  slv16;
      busy_o   : out std_logic;
      -- locallink tx interface
      lls_o    : out t_llsrc;
      lld_i    : in  std_logic;
      clk      : in  std_logic;
      rst      : in  std_logic
      );
  end component;


  component cg_spram1kx18
    port (
      clka  : in  std_logic;
      wea   : in  std_logic_vector(0 downto 0);
      addra : in  std_logic_vector(9 downto 0);
      dina  : in  std_logic_vector(17 downto 0);
      douta : out std_logic_vector(17 downto 0)
      );
  end component;


  signal ram_we       : std_logic;
  signal ram_wea00    : std_logic_vector(0 downto 0);
  signal ram_din      : slv18;
  signal ram_dout     : slv18;
  signal ram_ocdata   : slv16;
  signal ram_ocvalid  : std_logic;
  signal ram_addr     : slv10;
  signal ram_addr_inc : std_logic;
  signal ram_addr_clr : std_logic;


  signal fifo_we   : std_logic;
  signal fifo_sof  : std_logic;
  signal fifo_eof  : std_logic;
  signal fifo_data : slv16;

  signal tx_opcode : slv16;
  signal tx_size   : slv16;

  signal rx_opcode    : slv16;
  signal rx_opcode_we : std_logic;
  signal oc_opcodepl    : slv16;

  signal rx_ocseq    : slv16;
  signal rx_ocseq_we : std_logic;

  signal rx_size     : slv16;
  signal rx_size_we  : std_logic;
  signal rx_size_int : integer range 0 to 127;

  signal rx_data    : slv16;
  signal rx_data_we : std_logic;

  signal rx_word0    : slv16;
  signal rx_word0_we : std_logic;

  signal dest_port : integer range 0 to 15;

  signal serial_on : std_logic;

  signal tick_count : slv2;

  signal sck_select : slv4;
  signal sck_clk    : std_logic;
  signal sck_tick   : std_logic;
  signal sck_2tick  : std_logic;
  signal sck_4tick  : std_logic;
  signal sck_rising : std_logic;
  signal sck_hi     : std_logic;
-- signal sck_lo : std_logic;
  signal sck_en     : std_logic;
  signal sck        : std_logic;
  signal sda        : std_logic;
  signal sda_t      : slv16;
  signal sda_in     : std_logic;

  signal ecount     : integer range 0 to 15;
  signal ecount_clr : std_logic;
  signal ecount16   : std_logic;
  signal ccount     : integer range 0 to 15;
  signal ccount_clr : std_logic;
  signal ccount_inc : std_logic;
  signal wcount     : integer range 0 to 255;
  signal wcount_clr : std_logic;
  signal wcount_inc : std_logic;

  constant WDOG_MAX : integer := 7;
  signal   wdog     : integer range 0 to WDOG_MAX;
  signal   wdog_clr : std_logic;
  signal   bark     : std_logic;

  signal reply_busy : std_logic;
  signal reply_send : std_logic;

  signal sdcmd      : slv8;
  signal sdcmd_mode : slv3;

  signal sdout    : std_logic_vector(0 to 7);  -- this is to reverse bit order
  signal sdin     : slv16;
  signal sdin_clr : std_logic;

  signal shift_en : std_logic;

  signal io_done : std_logic;


  constant DEST_TOP_DCS0 : integer := 0;
  constant DEST_TOP_DCS1 : integer := 1;
  constant DEST_TOP_DCS2 : integer := 2;
  constant DEST_BOT_DCS0 : integer := 4;
  constant DEST_BOT_DCS1 : integer := 5;
  constant DEST_BOT_DCS2 : integer := 6;
  constant DEST_SPI0     : integer := 8;
  constant DEST_AUX0     : integer := 12;

  constant B_GET1  : integer := 0;
  constant B_GET2  : integer := 1;
  constant B_SEND1 : integer := 2;
  constant B_START : integer := 3;
  constant B_STOP  : integer := 4;
  constant B_MODE0 : integer := 5;
  constant B_MODE1 : integer := 6;
  constant B_MODE2 : integer := 7;

  constant MODE_I2C : slv3 := "000";
  constant MODE_SHT : slv3 := "010";
  constant MODE_SPI : slv3 := "100";
  constant MODE_END : slv3 := "111";



  -- Bit Allocations
  -- 
  -- 7:5  Protocol  0=I2C, 2=SHT, 4=SPI, 7=new packetlet, rest=reserved
  --   4  Append Stop
  --   3  Prepend Start
  --   2  Send Byte
  -- 1:0  Get N Bytes (where N is 0,1,2)

  constant SHT_CMD_SWC  : slv4 := x"c";
  constant SHT_CMD_SB   : slv4 := x"4";
  constant SHT_CMD_GS   : slv4 := x"d";
  constant SHT_CMD_GTH  : slv4 := x"e";
  constant SHT_CMD_GTH2 : slv4 := x"f";  -- same as above, optional interp of settings

  -- SHT71
  ------------------------------------------------------
  -- 0x4c06 - Send Write command only (issue before WriteByte)
  -- 0x44nn - Send byte (issue after WriteCmdOnly)
  -- 0x4d07 - Get/Read Stats (returns 1 byte in LSB)
  -- 0x4f03 - Get/Read Temp (returns 2 bytes)
  -- 0x4f05 - Get/Read Humi (returns 2 bytes)


  -- I2C 
  -------------------------------------------------------
  -- 0x04nn - Send byte
  -- 0x01.. - Get  byte
  -- 0x02.. - Get word (2 bytes)
  -- 0x0Cnn - Start + Send byte (for addressing chips)
  -- 0x0Dnn - Start + Send byte + Get Byte
  -- 0x0Enn - Start + Send byte + Get 2 Bytes
  -- 0x1N.. - Do above + append stop



  type states is (
    FillRAM, ExecuteRAM, ExecuteRAM1,
    RXHeader0, RXHeader1, RXHeader2,
    ControlWord, DoWord, DoWord1, DoCommand,
    NextWord, NextBlock, NextBlock1,
    InvalidCmdNextWord, WaitOCEnd,
    SendReply, SendReply1, SendBark, SendBark1,
    DoI2C, DoI2C_Start, DoI2C_Send0, DoI2C_Send1,
    DoI2C_Get0, DoI2C_Get1, DoI2C_Get2, DoI2C_StopAck, DoI2C_Stop,
    DoSHT, DoSHT_SendByte, DoSHT_SendCmdOnly, DoSHT_GetStat, DoSHT_GetHT, DoneSHT,
    Idle, WaitOCReady
    );

  signal state, nstate : states;

  type iostates is (
    twStart,
    i2cStart, i2cStart1, i2cStart2,
    i2cStop, i2cStop1, i2cStop2, i2cStop3,
    WaitAck, WaitAck1,
    SendAck,                            --SendAck1,
    SendNAck,
    WaitSDALo, WaitSDAHi,
    GetByte, SendByte,
    ioIdle_SDA0, Done_SDA0,
    ioIdle, Done
    );


  signal ios, nios, ioCmd : iostates;

begin


oc_opcodepl <= oc_get_opcodepl(oc_data_i);


-- State Machine
--------------------------------------------------------
  prc_controller_sync : process (clk)
  begin
    if rising_edge (clk) then
      if (rst = '1') then
        state   <= WaitOCReady;
      else
        if (wdog = WDOG_MAX) then
          state <= SendBark;
        else
          state <= nstate;
        end if;
      end if;
    end if;
  end process;


  prc_controller_async : process (
    oc_valid_i, oc_data_i,
    reply_busy, sda_in, ram_ocvalid, ram_ocdata,
    sck_tick,                           --sck_2tick,
    ecount, ecount16, sdin, sdcmd, sdcmd_mode,
    wcount, rx_size_int,
    rx_data, ccount, io_done,
    state
    )
  begin

    -- defaults
    nstate       <= WaitOCReady;
    ioCmd        <= ioIdle;
    oc_dack_no   <= 'Z';
    wcount_inc   <= '0';
    wcount_clr   <= '0';
    ccount_inc   <= '0';
    ccount_clr   <= '0';
    reply_send   <= '0';
    rx_opcode_we <= '0';
    rx_ocseq_we  <= '0';
    rx_size_we   <= '0';
    rx_word0_we  <= '0';
    rx_data_we   <= '0';
    wdog_clr     <= '1';
    bark         <= '0';
    fifo_eof     <= '0';
    fifo_sof     <= '0';
    fifo_we      <= '0';
    fifo_data    <= sdin;
    sdin_clr     <= '0';
    serial_on    <= '1';
    ram_we       <= '0';
    ram_addr_clr <= '0';
    ram_addr_inc <= '0';


    case state is
      -------------------------------------------------------------

      when WaitOCReady =>
        nstate     <= WaitOCReady;
        --oc_dack_no <= 'Z';
        --wdog_clr   <= '1';
        serial_on  <= '0';
        if (oc_valid_i = '0') then
          nstate   <= Idle;
        end if;


      when Idle =>
        nstate       <= Idle;
        --oc_dack_no   <= 'Z';
        --wdog_clr     <= '1';
        wcount_clr   <= '1';
        sdin_clr     <= '1';
        ram_addr_clr <= '1';
        serial_on    <= '0';

        --if (tw_exec_i = '1') then
        --  nstate <= ExecuteRAM;
        --els
        if (oc_valid_i = '1') then
          case oc_opcodepl is
            when OC_TWOWIRE => nstate <= FillRAM;
            when others     => nstate <= WaitOCReady;
          end case;
        end if;


      when FillRAM =>
        nstate       <= FillRAM;
        ram_we       <= '1';
        ram_addr_inc <= '1';
        oc_dack_no   <= '0';
        if (oc_valid_i = '0') then
          nstate     <= ExecuteRAM;
        end if;


        -- Execute RAM contents  
        --------------------------------------------------------

      when ExecuteRAM =>
        ram_addr_clr <= '1';
        nstate       <= ExecuteRAM1;


      when ExecuteRAM1 =>               -- the RAM data is one clock behind ...
        ram_addr_inc <= '1';
        nstate       <= RXHeader0;


      when RXHeader0 =>
        ram_addr_inc <= '1';
        wcount_inc   <= '1';
        serial_on    <= '0';
        rx_opcode_we <= '1';
        nstate       <= RXHeader1;


      when RXHeader1 =>
        ram_addr_inc <= '1';
        wcount_inc   <= '1';
        serial_on    <= '0';
        rx_ocseq_we  <= '1';
        nstate       <= RXHeader2;


      when RXHeader2 =>
        --ram_addr_inc <= '1';          -- keep behind ..
        serial_on  <= '0';
        rx_size_we <= '1';
        wcount_clr <= '1';
        nstate     <= ControlWord;


      when ControlWord =>
        ram_addr_inc <= '1';
        rx_word0_we  <= '1';
        fifo_data    <= ram_ocdata;
        fifo_we      <= '1';
        fifo_sof     <= '1';
        wcount_inc   <= '1';
        serial_on    <= '0';
        nstate       <= DoWord;


      when DoWord =>                    -- wait for data to appear on ram_dout
        nstate <= DoWord1;

      when DoWord1 =>
        nstate       <= DoWord1;
        if (sck_tick = '1') then
          rx_data_we <= '1';
          nstate     <= DoCommand;
        end if;


        ------------------------------------------------------
      when DoCommand    =>
        ccount_clr                <= '1';
        case sdcmd_mode is
          when MODE_END => nstate <= NextBlock;
          when MODE_I2C => nstate <= DoI2C;
          when MODE_SHT => nstate <= DoSHT;
          when others   => nstate <= InvalidCmdNextWord;
        end case;


      when InvalidCmdNextWord =>
        fifo_data      <= x"F00B";
        fifo_we        <= '1';
        wcount_inc     <= '1';
        if (wcount < (rx_size_int/2)) and (ram_ocvalid = '1') then
          ram_addr_inc <= '1';
          nstate       <= DoWord;
        else
          fifo_eof     <= '1';
          nstate       <= WaitOCEnd;
        end if;


      when NextWord =>
        fifo_we        <= '1';          -- write result of just finished transaction
        wcount_inc     <= '1';
        wdog_clr       <= '1';
        if (wcount < (rx_size_int/2)) and (ram_ocvalid = '1') then
          ram_addr_inc <= '1';
          nstate       <= DoWord;
        else
          fifo_eof     <= '1';
          nstate       <= WaitOCEnd;
        end if;


      when NextBlock =>
        fifo_data      <= ram_ocdata;
        fifo_we        <= '1';
        wcount_inc     <= '1';
        wdog_clr       <= '1';
        if (wcount < (rx_size_int/2)) and (ram_ocvalid = '1') then
          ram_addr_inc <= '1';
          nstate       <= NextBlock1;   -- wait for ram to update
        else
          fifo_eof     <= '1';
          nstate       <= WaitOCEnd;
        end if;

      when NextBlock1 =>
        nstate <= ControlWord;


      when WaitOCEnd =>
        nstate       <= WaitOCEnd;
        ram_addr_inc <= '1';
        if (ram_ocvalid = '0') then
          nstate     <= SendReply;
        end if;


        -- Send Reply
        --------------------------------------------------------------
      when SendReply =>
        nstate     <= SendReply;
        reply_send <= '1';
        if (reply_busy = '0') then
          nstate   <= Idle;
        end if;


        -- Send Bark
        ---------------------------------------
      when SendBark =>
        nstate       <= SendBark;
        --wdog_clr     <= '1';
        fifo_data    <= x"F00B";
        fifo_we      <= '1';
        wcount_inc   <= '1';
        ram_addr_inc <= '1';
        if (ram_ocvalid = '0') then
          fifo_eof   <= '1';
          nstate     <= SendBark1;
        end if;


      when SendBark1 =>
        nstate     <= SendBark1;
        bark       <= '1';
        reply_send <= '1';
        if (reply_busy = '0') then
          nstate   <= Idle;
        end if;

        -----------------------------------------------------------------
        -----------------------------------------------------------------


      when DoI2C =>
        ccount_clr <= '1';
        sdin_clr   <= '1';
        if (sdcmd(B_START) = '1') then
          nstate   <= DoI2C_Start;
        else
          nstate   <= DoI2C_Send0;
        end if;



      when DoI2C_Start =>
        nstate   <= DoI2C_Start;
        ioCmd    <= i2cStart;
        wdog_clr <= '0';
        if (io_done = '1') then
          nstate <= DoI2C_Send0;
        end if;


      when DoI2C_Send0 =>
        ccount_clr <= '1';
        if (sdcmd(B_SEND1) = '1') then
          nstate   <= DoI2C_Send1;
        elsif (sdcmd(B_GET2) = '1') then
          nstate   <= DoI2C_Get2;
        elsif (sdcmd(B_GET1) = '1') then
          nstate   <= DoI2C_Get1;
        else
          nstate   <= DoI2C_Stop;
        end if;


      when DoI2C_Send1 =>
        nstate                   <= DoI2C_Send1;
        wdog_clr <= '0';
        ccount_inc               <= io_done;
        case ccount is
          when 0       => ioCmd  <= SendByte;
          when 1       => ioCmd  <= WaitAck;
          when 2       => nstate <= DoI2C_Get0;
          when others  => null;
        end case;


      when DoI2C_Get0 =>
        ccount_clr <= '1';
        if (sdcmd(B_GET2) = '1') then
          nstate   <= DoI2C_Get2;
        elsif (sdcmd(B_GET1) = '1') then
          nstate   <= DoI2C_Get1;
        else
          nstate   <= DoI2C_Stop;
        end if;


      when DoI2C_Get1 =>
        nstate                  <= DoI2C_Get1;
        wdog_clr <= '0';
        ccount_inc              <= io_done;
        case ccount is
          when 0      => ioCmd  <= GetByte;
                         --when 1      => ioCmd  <= SendAck;
          when 1      => nstate <= DoI2C_StopAck;
          when others => null;
        end case;


      when DoI2C_Get2 =>
        nstate                  <= DoI2C_Get2;
        wdog_clr <= '0';
        ccount_inc              <= io_done;
        case ccount is
          when 0      => ioCmd  <= GetByte;
          when 1      => ioCmd  <= SendAck;
          when 2      => ioCmd  <= GetByte;
                         --when 3      => ioCmd  <= SendAck;
          when 3      => nstate <= DoI2C_StopAck;
          when others => null;
        end case;


      when DoI2C_StopAck =>
        nstate   <= DoI2C_StopAck;
        wdog_clr <= '0';
        if (sdcmd(B_STOP) = '1') then
          ioCmd  <= SendNAck;
        else
          ioCmd  <= SendAck;
        end if;
        if (io_done = '1') then
          nstate <= DoI2C_Stop;
        end if;


      when DoI2C_Stop =>
        nstate     <= DoI2C_Stop;
        wdog_clr <= '0';
        if (sdcmd(B_STOP) = '1') then
          ioCmd    <= i2cStop;
          if (io_done = '1') then
            nstate <= NextWord;
          end if;
        else
          nstate   <= NextWord;
        end if;


        -----------------------------------------------------------------
        -----------------------------------------------------------------

        -- command byte format
        -- tp1 tp0 res stop start send1 get2 get1

      when DoSHT =>
        ccount_clr <= '1';
        sdin_clr   <= '1';

        case sdcmd(3 downto 0) is
          when SHT_CMD_SB   => nstate <= DoSHT_SendByte;
          when SHT_CMD_SWC  => nstate <= DoSHT_SendCmdOnly;
          when SHT_CMD_GS   => nstate <= DoSHT_GetStat;
          when SHT_CMD_GTH  => nstate <= DoSHT_GetHT;
          when SHT_CMD_GTH2 => nstate <= DoSHT_GetHT;
          when others       => nstate <= InvalidCmdNextWord;
        end case;



      when DoSHT_SendCmdOnly =>
        nstate     <= DoSHT_SendCmdOnly;
        wdog_clr <= '0';
        ccount_inc <= io_done;

        case ccount is
          when 0 => ioCmd  <= twStart;
          when 1 => ioCmd  <= SendByte;
          when 2 => ioCmd  <= SendNAck;
          when 3 => nstate <= DoneSHT;

          when others => null;
        end case;


      when DoSHT_SendByte =>
        nstate     <= DoSHT_SendByte;
        wdog_clr <= '0';
        ccount_inc <= io_done;

        case ccount is
          when 0      => ioCmd  <= SendByte;
          when 1      => ioCmd  <= SendNAck;
          when others => nstate <= DoneSHT;
        end case;


      when DoSHT_GetStat =>
        nstate                     <= DoSHT_GetStat;
        wdog_clr <= '0';
        ccount_inc                 <= io_done;
        case ccount is
          -- start + command
          when 0         => ioCmd  <= twStart;
          when 1         => ioCmd  <= SendByte;
          when 2         => ioCmd  <= SendNAck;  -- $%^& sht - not like the datasheet
                            -- get byte
          when 3         => ioCmd  <= GetByte;
          when 4         => ioCmd  <= SendNAck;
                            -- done                   
          when others    => nstate <= DoneSHT;
        end case;



      when DoSHT_GetHT =>
        nstate     <= DoSHT_GetHT;
        wdog_clr <= '0';
        ccount_inc <= io_done;

        case ccount is
          -- start + command
          when 0      => ioCmd  <= twStart;
          when 1      => ioCmd  <= SendByte;
          when 2      => ioCmd  <= WaitAck;
                         -- wait conv done
          when 3      => ioCmd  <= WaitSDAHi;
          when 4      => ioCmd  <= WaitSDALo;
                         -- get result
          when 5      => ioCmd  <= GetByte;
          when 6      => ioCmd  <= SendAck;
          when 7      => ioCmd  <= GetByte;
          when 8      => ioCmd  <= SendNAck;
                         -- done
          when others => nstate <= DoneSHT;
        end case;


      when DoneSHT =>
        nstate <= NextWord;






        ------ TW Mem
        ---------------------------------------------------------------

-- when TWMemLoad =>
-- ram_addr_clr <= '1';







      when others => null;
    end case;
  end process;


-----------------------------------------------------------

  prc_word_counter : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        wcount   <= 0;
      else
        if (wcount_clr = '1') then
          wcount <= 0;

        elsif (wcount_inc = '1') then
          wcount <= wcount + 1;

        end if;
      end if;
    end if;
  end process;


-----------------------------------------------------------

  prc_cmd_counter : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        ccount   <= 0;
      else
        if (ccount_clr = '1') then
          ccount <= 0;

        elsif (ccount_inc = '1') then
          ccount <= ccount + 1;

        end if;
      end if;
    end if;
  end process;




-- IO Worker Machine - does all IO
--------------------------------------------------------

  prc_smio_sync : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        ios   <= ioIdle;
      else
        if (wdog = WDOG_MAX) then
          ios <= ioIdle;
        else
          ios <= nios;
        end if;
      end if;
    end if;
  end process;


  prc_smio_async : process (sck_tick,   --sck_2tick,
                            reply_busy, sda_in,
                            ecount, ecount16,
                            rx_data, ioCmd, sdout,
                            ios
                            )
  begin

    -- defaults
    shift_en   <= '0';
    io_done    <= '0';
    sck_en     <= '0';
    sda        <= '1';
    ecount_clr <= '1';
    sck_hi     <= '0';
-- sck_lo <= '0';


    case ios is
      -------------------------------------------------------------

      when Done =>                      -- this releases SDA and SCK
        io_done <= '1';
        nios    <= ioIdle;


      when ioIdle =>
        nios   <= ioIdle;
        if (sck_tick = '1') then        -- always start with sck=0
          nios <= ioCmd;                -- command from control state machine
        end if;

      when Done_SDA0 =>                 -- versions of idle for keeping SDA low after e.g. a Start
        sda     <= '0';
        io_done <= '1';
        nios    <= ioIdle_SDA0;


      when ioIdle_SDA0 =>
        nios   <= ioIdle_SDA0;
        sda    <= '0';
        if (sck_tick = '1') then        -- always start with sck=0
          nios <= ioCmd;                -- command from control state machine
        end if;



        ----------------------------------------------------------
        -- Two-wire (SHT71) Stuff
        ----------------------------------------------------------

        -- tw Start                     ------------------


      when twStart =>
        nios       <= twStart;
        ecount_clr <= '0';
        sck_en     <= '1';

        case ecount is
          when 0   => sda  <= '1';      -- sck=0
          when 1   => sda  <= '1'; sck_hi <= '1';
          when 2   => sda  <= '0'; sck_hi <= '1';
          when 3   => sda  <= '0';      -- sck=1
          when 4   => sda  <= '0';      -- sck=0
          when 5   => sda  <= '0'; sck_hi <= '1';
          when 6   => sda  <= '1'; sck_hi <= '1';
          when 7   => sda  <= '1';      -- sck=1
          when 8   => sda  <= '1';      -- sck=0
                      nios <= Done;
          when
            others => null;
        end case;



        --------------------------------------------------------------
        -- I2C
        ----------------------------------------------------------

        -- I2C Start                    ----------------

      when i2cStart =>
        nios   <= i2cStart;
        sck_hi <= '1';
        if (sck_tick = '1') then
          nios <= i2cStart1;
        end if;

      when i2cStart1 =>
        nios   <= i2cStart1;
        sck_hi <= '1';
        sda    <= '0';
        if (sck_tick = '1') then
          nios <= i2cStart2;
        end if;

      when i2cStart2 =>
        nios   <= i2cStart2;
        sda    <= '0';
        if (sck_tick = '1') then
          nios <= Done_SDA0;
        end if;

        -- I2C Stop                     ----------------

      when i2cStop =>
        nios   <= i2cStop;
        sda    <= '0';
        if (sck_tick = '1') then
          nios <= i2cStop1;
        end if;

      when i2cStop1 =>
        nios   <= i2cStop1;
        sda    <= '0';
        sck_hi <= '1';
        if (sck_tick = '1') then
          nios <= i2cStop2;
        end if;

      when i2cStop2 =>
        nios   <= i2cStop2;
        sck_hi <= '1';
        if (sck_tick = '1') then
          nios <= i2cStop3;
        end if;

      when i2cStop3 =>
        nios   <= i2cStop3;
        if (sck_tick = '1') then
          nios <= Done;
        end if;


        --------------------------------------------------------------
        -- Generic Stuff
        ----------------------------------------------------------

        --  Wait Ack
        ---------------
      when WaitAck =>
        nios   <= WaitAck;
        if (sda_in = '0') and (sck_tick = '1') then
          nios <= WaitAck1;
        end if;

      when WaitAck1 =>
        nios   <= WaitAck1;
        sck_en <= '1';
        if (sck_tick = '1') then
          nios <= Done;
        end if;

        --  Send Ack
        ----------------           
      when SendAck =>
        nios   <= SendAck;
-- if ((sda_in = '1') or (sda_in = 'H')) and (sck_tick = '1') then
-- nios <= SendAck1;
-- end if;
-- when SendAck1 =>
-- nios <= SendAck1;
        sda    <= '0';
        sck_en <= '1';
        if (sck_tick = '1') then
          nios <= Done;
        end if;


        --  Send NACK
        ----------------           
      when SendNAck =>
        nios   <= SendNAck;
        sck_en <= '1';
        if (sck_tick = '1') then
          nios <= Done;
        end if;


        -- Wait SDA hi/lo
        -----------------
      when WaitSDALo =>
        nios   <= WaitSDALo;
        if (sda_in = '0') and (sck_tick = '1') then
          nios <= Done;
        end if;


      when WaitSDAHi =>
        nios   <= WaitSDAHi;
        if ((sda_in = '1') or (sda_in = 'H')) and (sck_tick = '1') then
          nios <= Done;
        end if;


        -- Get/Send Bytes
        ------------------
      when GetByte =>
        nios       <= GetByte;
        ecount_clr <= '0';
        sck_en     <= '1';
        shift_en   <= '1';
        if (ecount16 = '1') then
          nios     <= Done;
        end if;

      when SendByte =>
        nios       <= SendByte;
        ecount_clr <= '0';
        sck_en     <= '1';
        sda        <= sdout(ecount/2);
        if (ecount16 = '1') then
          nios     <= Done;
        end if;

    end case;

  end process;


-----------------------------------------------------------

  with sck_select select
    sck_4tick <= tick_4khz_i when x"2",
    tick_40khz_i             when x"1",
    tick_400khz_i            when others;


  prc_sck_gen : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        tick_count <= "00";
      else

        -- default
        sck_tick   <= '0';
        sck_2tick  <= '0';
        sck_rising <= '0';

        if (sck_4tick = '1') then
          if (tick_count = "11") then
            tick_count <= "00";
          else
            tick_count <= tick_count + '1';
          end if;

          case tick_count is
            when "00"   => sck_clk <= '1'; sck_rising <= '1';
            when "01"   => sck_clk <= '1'; sck_2tick <= '1';
            when "10"   => sck_clk <= '0';
            when "11"   => sck_clk <= '0'; sck_2tick <= '1'; sck_tick <= '1';
            when others => null;
          end case;

        end if;


      end if;
    end if;

  end process;


  prc_sck_control : process (clk)
  begin
    if rising_edge(clk) then

      if (sck_hi = '1') then
        sck <= '1';

      elsif (sck_en = '1') then
        sck <= sck_clk;

      else
        sck <= '0';

      end if;
    end if;
  end process;



---------------------------------

  prc_clockout : process (clk)
  begin
    if rising_edge(clk) then

      -- defaults
      sck_o  <= (others => '1');
      sck_to <= (others => '1');

      if (serial_on = '1') then
        if (sck = '0') then
          sck_o(dest_port)  <= '0';
          sck_to(dest_port) <= '0';

          -- sck = '1'
        elsif (sdcmd_mode = MODE_SHT) then
          sck_o(dest_port)  <= '1';
          sck_to(dest_port) <= '0';
        end if;
      end if;
    end if;
  end process;


  prc_sdaout : process (clk)
  begin
    if rising_edge(clk) then

      -- defaults
      sda_o <= (others => '1');
      sda_t <= (others => '1');

      if (sda = '0') and (serial_on = '1') then
        sda_o(dest_port) <= '0';
        sda_t(dest_port) <= '0';
      end if;
    end if;
  end process;

  sda_in <= sda_i(dest_port);

  sda_to <= sda_t;

  sck_dbg_o    <= sck;
  sda_dbg_o    <= sda;
  sda_t_dbg_o  <= sda_t(dest_port);
  sda_in_dbg_o <= sda_in;

-----------------------------------------------------------

  prc_clk_edge_counter : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        ecount <= 0;
      else

        -- default
        ecount16 <= '0';

        if (ecount_clr = '1') then
          ecount <= 0;

        elsif (sck_2tick = '1') then

          if (ecount = 15) then
            ecount16 <= '1';
          else
            ecount   <= ecount + 1;
          end if;

        end if;
      end if;
    end if;
  end process;


-------------------------------------------------------------------

  prc_sdin : process (clk)
  begin
    if rising_edge(clk) then
      if (sdin_clr = '1') then
        sdin <= (others => '0');

      else
        if (shift_en = '1') and (sck_rising = '1') then
          sdin <= sdin(14 downto 0) & sda_in;
        end if;

      end if;
    end if;
  end process;

-------------------------------------------------------------------


-- RX Words Store



  prc_words_store : process (clk)
  begin
    if rising_edge(clk) then

      if (rx_opcode_we = '1') then
        rx_opcode <= ram_ocdata;
      end if;

      if (rx_ocseq_we = '1') then
        rx_ocseq <= ram_ocdata;
      end if;

      if (rx_size_we = '1') then
        rx_size <= ram_ocdata;
      end if;

      if (rx_word0_we = '1') then
        rx_word0 <= ram_ocdata;
      end if;

      if (rx_data_we = '1') then
        rx_data <= ram_ocdata;
      end if;

    end if;
  end process;


  rx_size_int <= conv_integer(rx_size(6 downto 0));

  sdout      <= rx_data(7 downto 0);
  sdcmd      <= rx_data(15 downto 8);
  sdcmd_mode <= sdcmd(7 downto 5);

  dest_port  <= conv_integer(rx_word0(3 downto 0));
  sck_select <= rx_word0(7 downto 4);


------------------------------------------------------------
-- RAM Interface

  prc_ram_stores : process (clk)
  begin
    if rising_edge(clk) then

      if (ram_addr_clr = '1') then
        ram_addr <= (others => '0');
      elsif (ram_addr_inc = '1') then
        ram_addr <= ram_addr + '1';
      end if;

    end if;
  end process;

  opcode_ram : cg_spram1kx18
    port map (
      clka  => clk,
      wea   => ram_wea00,
      addra => ram_addr,
      dina  => ram_din,
      douta => ram_dout
      );

  ram_din   <= '0' & oc_valid_i & oc_data_i;
  ram_wea00 <= "1" when (ram_we = '1') else "0";

  ram_ocvalid <= ram_dout(16);
  ram_ocdata  <= ram_dout(15 downto 0);


------------------------------------------------------------
-- Reply Interface

  --tx_opcode <= x"F" & rx_opcode(11 downto 0) when (bark = '1') else rx_opcode;
  tx_opcode <= rx_opcode;
  tx_size   <= x"00" & conv_std_logic_vector(wcount*2, 8);


  Ureply_fifo : entity locallink.ll_fifo_ack_gen
    port map(
      data_i   => fifo_data,
      wren_i   => fifo_we,
      eof_i    => fifo_eof,
      sof_i    => fifo_sof,
      send_i   => reply_send,
      opcode_i => tx_opcode,
      ocseq_i  => rx_ocseq,
      size_i   => tx_size,
      busy_o   => reply_busy,
      lls_o    => lls_o,
      lld_i    => lld_i,
      clk      => clk,
      rst      => rst
      );



--================================================================================
  prc_watchdog : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        wdog   <= 0;
      else
        if (wdog_clr = '1') then
          wdog <= 0;

        elsif (wdog_tick_i = '1') then
          wdog <= wdog + 1;

        end if;
      end if;
    end if;
  end process;


end architecture;