----------------------------------------------------------------------------------
-- Company:        CERN, BE-CO-HT
-- Engineer:       Carlos Gil Soriano
-- 
-- Create Date:    17:50:12 02/07/2012 
-- Design Name:    control_top
-- Module Name:    control_top - Behavioral 
-- Project Name:   i2c to wishbone access controller
-- Target Devices: Spartan 6
-- Tool versions: 
-- Description:    This is the module that controls the access to the wishbone modules
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity control_top is
  generic(
           g_WISHBONE_MODULES   : INTEGER := 10;
           g_WISHBONE_MEMWIDTH  : INTEGER := 32;
           g_WISHBONE_ADDRWIDTH : INTEGER := 16
         );
  port (
         -- This is a wishbone master
         rst                    : in   STD_LOGIC;
         wb_clk                 : in   STD_LOGIC;
         wb_master_i2c_stb_o    : out  STD_LOGIC;
         wb_master_i2c_cyc_o    : out  STD_LOGIC;
         wb_master_i2c_sel_o    : out  STD_LOGIC_VECTOR( 3  downto 0);
         wb_master_i2c_we_o     : out  STD_LOGIC;
         wb_master_i2c_data_i   : in   STD_LOGIC_VECTOR (31 downto 0);      
         wb_master_i2c_data_o   : out  STD_LOGIC_VECTOR (31 downto 0);
         wb_master_i2c_addr_o   : out  STD_LOGIC_VECTOR (3  downto 0);
         wb_master_i2c_ack_i    : in   STD_LOGIC;
         wb_master_i2c_rty_i    : in   STD_LOGIC;
         wb_master_i2c_err_i    : in   STD_LOGIC;
         -- Interruptions
         ind_wb_addr_i     : in   STD_LOGIC;
         inst_rd_i         : in   STD_LOGIC;
         inst_wr           : in   STD_LOGIC;
         -- Here we add all the wishbone ports needed
         -- According to the HDL specifications the mapping is as follows:
         --
         --   [DEVICE NUMBER]           [Wishbone group]
         --          i          --->          i - 3
         --
         -- where i belongs from 2 to 11
         wb_stb_o          : out  STD_LOGIC_VECTOR (g_WISHBONE_MODULES - 1   downto 0);
         wb_cyc_o          : out  STD_LOGIC_VECTOR (g_WISHBONE_MODULES - 1   downto 0);
         wb_sel_o          : out  STD_LOGIC_VECTOR (g_WISHBONE_MODULES*4 - 1 downto 0);
         wb_we_o           : out  STD_LOGIC_VECTOR (g_WISHBONE_MODULES - 1   downto 0);
         wb_data_i         : in   STD_LOGIC_VECTOR (g_WISHBONE_MEMWIDTH*g_WISHBONE_MODULES  - 1 downto 0);      
         wb_data_o         : out  STD_LOGIC_VECTOR (g_WISHBONE_MEMWIDTH*g_WISHBONE_MODULES  - 1 downto 0);
         wb_addr_o         : out  STD_LOGIC_VECTOR (g_WISHBONE_ADDRWIDTH*g_WISHBONE_MODULES - 1 downto 0);
         wb_ack_i          : in   STD_LOGIC_VECTOR (g_WISHBONE_MODULES - 1 downto 0);
         wb_rty_i          : in   STD_LOGIC_VECTOR (g_WISHBONE_MODULES - 1 downto 0);
         wb_err_i          : in   STD_LOGIC_VECTOR (g_WISHBONE_MODULES - 1 downto 0)
       );
end control_top;

architecture Behavioral of control_top is

  constant OP_addr               : STD_LOGIC_VECTOR(3 downto 0) := X"2";

  constant control_MAP           : STD_LOGIC_VECTOR(7 downto 0) := X"01";
  constant i2c_slave_MAP         : STD_LOGIC_VECTOR(7 downto 0) := X"02";
  constant trigger1_MAP          : STD_LOGIC_VECTOR(7 downto 0) := X"03";
  constant trigger2_MAP          : STD_LOGIC_VECTOR(7 downto 0) := X"04";
  constant trigger3_MAP          : STD_LOGIC_VECTOR(7 downto 0) := X"05";
  constant trigger4_MAP          : STD_LOGIC_VECTOR(7 downto 0) := X"06";
  constant trigger5_MAP          : STD_LOGIC_VECTOR(7 downto 0) := X"07";
  constant trigger6_MAP          : STD_LOGIC_VECTOR(7 downto 0) := X"08";
  constant multiboot_MAP         : STD_LOGIC_VECTOR(7 downto 0) := X"09";
  constant EEPROM_manager_MAP    : STD_LOGIC_VECTOR(7 downto 0) := X"0A";
  constant wr_core_MAP           : STD_LOGIC_VECTOR(7 downto 0) := X"0B";
  constant EEPROM_memory_MAP     : STD_LOGIC_VECTOR(7 downto 0) := X"10";

  constant control_addr          : STD_LOGIC_VECTOR (15 downto 0) := control_MAP   & X"00";
  constant i2c_slave_addr        : STD_LOGIC_VECTOR (15 downto 0) := i2c_slave_MAP & X"00";
  constant trigger1_addr         : STD_LOGIC_VECTOR (15 downto 0) := trigger1_MAP  & X"00";
  constant trigger2_addr         : STD_LOGIC_VECTOR (15 downto 0) := trigger2_MAP  & X"00";
  constant trigger3_addr         : STD_LOGIC_VECTOR (15 downto 0) := trigger3_MAP  & X"00";
  constant trigger4_addr         : STD_LOGIC_VECTOR (15 downto 0) := trigger4_MAP  & X"00";
  constant trigger5_addr         : STD_LOGIC_VECTOR (15 downto 0) := trigger5_MAP  & X"00";
  constant trigger6_addr         : STD_LOGIC_VECTOR (15 downto 0) := trigger6_MAP  & X"00";
  constant multiboot_addr        : STD_LOGIC_VECTOR (15 downto 0) := multiboot_MAP & X"00";
  constant EEPROM_manager_addr   : STD_LOGIC_VECTOR (15 downto 0) := EEPROM_manager_MAP & X"00";
  constant wr_core_addr          : STD_LOGIC_VECTOR (15 downto 0) := wr_core_MAP        & X"00";
  constant EEPROM_memory         : STD_LOGIC_VECTOR (15 downto 0) := EEPROM_memory_MAP  & X"00";

  constant i2c_DRXA              : STD_LOGIC_VECTOR(3 downto 0) := X"4"; 
  constant i2c_DRXB              : STD_LOGIC_VECTOR(3 downto 0) := X"5";
  constant i2c_DTX               : STD_LOGIC_VECTOR(3 downto 0) := X"6";

  type FETCHER_fsm is (S0_IDLE, S1_I2C_DRXA_RQT, S2_I2C_DRXA_READ, S3_I2C_DRXA_FETCHED, S4_WB_DATA_READ, S5_I2C_DRD_ACK, S5_I2C_WRITE_DATA, S6_DONE, S7_WB_ERROR);

  -- With respect to g_WISHBONE_MODULE we are adding here two extra modules:
  -- control:          which is internal and corresponds to device 0.
  -- i2c_slave:        which has a dedicated wishbone interface. It corresponds
  --                   to device 1.

  signal CTRL_FETCHER_fsm        : FETCHER_fsm;
  
  -------------------------------------
  -- OPERATION register
  -------------------------------------
  -- 32 bits
  -- Wishbone access: Read
  ------------------------------------
  -- BIT       NAME           Description
  --  0         RQT            operation ReQuesTed
  --  1         DONE           operation DONE
  --  2         ERROR          operation ERROR
  --  3         WR             WRite into FPGA register
  --  4         RD             ReaD from FPGA register
  --  12-5      MOD            wishbone MODule accessed
  --  13        POK            data Prefetch is OK
  --  15-14     x              Reserved
  --  31-16     WAO            Wishbone Address in Operation
  -------------------------------------

  signal OP: STD_LOGIC_VECTOR(31 downto 0);

  -------------------------------------
  -- Data ReaD from WishBone register
  -------------------------------------
  -- 32 bits
  -- Wishbone access: Read-only
  ------------------------------------
  -- BIT       NAME           Description
  --  31-0     DRD            Data ReaD from WishBone
  -------------------------------------
  signal DRD_WB: STD_LOGIC_VECTOR(31 downto 0);

  -------------------------------------
  -- Data WRite to WishBone
  -------------------------------------
  -- 32 bits
  -- Wishbone access: Read-write
  ------------------------------------
  -- BIT       NAME           Description
  --  31-0     DWR            Data WRite to WishBone
  -------------------------------------
  signal DWR_WB: STD_LOGIC_VECTOR(31 downto 0);

  signal ind_address : STD_LOGIC_VECTOR(15 downto 0);

begin


  reg_proc:  process(wb_clk)

    procedure reset_control is
    begin
      OP                    <= (others => '0');
      wb_master_i2c_stb_o   <= '0'; 
      wb_master_i2c_cyc_o   <= '0'; 
      wb_master_i2c_sel_o   <= (others => '0');
      wb_master_i2c_we_o    <= '0'; 
      wb_master_i2c_data_o  <= (others => '0'); 
      wb_master_i2c_addr_o  <= (others => '0');
      wb_stb_o              <= (others => '0'); 
      wb_cyc_o              <= (others => '0'); 
      wb_sel_o              <= (others => '0'); 
      wb_we_o               <= (others => '0'); 
      wb_data_o             <= (others => '0'); 
      wb_addr_o             <= (others => '0');
      OP                    <= (others => '0'); 
      DRD_WB                <= (others => '0'); 
      DWR_WB                <= (others => '0'); 
    end procedure;

    procedure read_i2c_wb_reg_rqt is
    begin
      CTRL_FETCHER_fsm          <= S4_WB_DATA_READ;
      OP(4 downto 0)            <= "10001";
      OP(8 downto 5)            <= X"1";
      wb_master_i2c_stb_o       <= '1'; 
      wb_master_i2c_cyc_o       <= '1'; 
      wb_master_i2c_we_o        <= '0'; 
      wb_master_i2c_addr_o      <= OP(19 downto 16); 
    end procedure;

    procedure read_wb_reg_rqt(module_number: INTEGER) is
    begin
      CTRL_FETCHER_fsm          <= S4_WB_DATA_READ;
      OP(4 downto 0)            <= "10001";
      OP(8 downto 5)            <= std_logic_vector(to_unsigned(module_number,4));
      wb_stb_o(module_number)       <= '1'; 
      wb_cyc_o(module_number)       <= '1'; 
      wb_we_o(module_number)        <= '0'; 
      wb_addr_o(g_WISHBONE_ADDRWIDTH*(module_number+1)-1 downto g_WISHBONE_ADDRWIDTH*(module_number))      <= OP(31 downto 16); 
    end procedure;


    procedure write_i2c_data(data: IN STD_LOGIC_VECTOR(31 downto 0); addr: IN STD_LOGIC_VECTOR(3 downto 0)) is
    begin
      wb_master_i2c_stb_o   <= '1'; 
      wb_master_i2c_cyc_o   <= '1'; 
      wb_master_i2c_sel_o   <= (others => '1'); 
      wb_master_i2c_we_o    <= '1'; 
      wb_master_i2c_data_o  <= data; 
      wb_master_i2c_addr_o  <= addr; 
    end procedure;


    procedure trans_wb(module_MAP: IN STD_LOGIC_VECTOR(7 downto 0)) is
       variable module : INTEGER;
    begin
       case module_MAP is
          when EEPROM_memory_MAP =>
             module  := 9;
          when others =>
             module  := to_integer(UNSIGNED(module_MAP) - 3);
       end case;

       case OP(4 downto 3) is
          -- RD|WR
          when "01"=>
          -- TODO: here we have to add a passing state
          --             CTRL_FETCHER_fsm <= ;
          when "10"=>
             wb_stb_o(module)  <= '1';
             wb_cyc_o(module)  <= '1';
             wb_sel_o(4*(module + 1) - 1 downto 4*module)  <= X"F";
             wb_we_o(module)   <= '0';
             wb_addr_o(g_WISHBONE_ADDRWIDTH*(module + 1) - 1 downto g_WISHBONE_ADDRWIDTH*module) <= OP(31 downto 16);
             CTRL_FETCHER_fsm  <= S5_I2C_WRITE_DATA; 
          when others  =>
             wb_stb_o(module)  <= '0';
             wb_cyc_o(module)  <= '0';
             wb_sel_o(4*(module + 1) - 1 downto 4*module)  <= X"0";
             wb_we_o(module)   <= '0';
             wb_addr_o(g_WISHBONE_ADDRWIDTH*(module + 1) - 1 downto g_WISHBONE_ADDRWIDTH*module) <= (others => '0');
             CTRL_FETCHER_fsm  <= S7_WB_ERROR;
       end case;
    end procedure;

    procedure i2c_to_wb is
    begin
       case OP(12 downto 5) is

        when control_MAP  =>

        when i2c_slave_MAP  =>

        when trigger1_MAP  =>
           trans_wb(trigger1_MAP);
        when trigger2_MAP  =>
           trans_wb(trigger2_MAP);
        when trigger3_MAP  =>
           trans_wb(trigger3_MAP);
        when trigger4_MAP  =>
           trans_wb(trigger4_MAP);
        when trigger5_MAP  =>
           trans_wb(trigger5_MAP);
        when trigger6_MAP  =>
           trans_wb(trigger6_MAP);
        when EEPROM_manager_MAP  =>
           trans_wb(EEPROM_manager_MAP);
        when wr_core_MAP  =>
           trans_wb(wr_core_MAP);
        when EEPROM_memory_MAP  =>
           trans_wb(EEPROM_memory_MAP);
        when others =>
          DRD_WB            <= (others => '0');
          OP(4 downto 0)    <= "00100"; 
          CTRL_FETCHER_fsm  <= S7_WB_ERROR;
      end case;
    end procedure;

    procedure read_DRD_wb(module_MAP : IN STD_LOGIC_VECTOR(7 downto 0)) is
       variable module     : INTEGER;
    begin
       case module_MAP is
          when EEPROM_memory_MAP =>
             module  := 9;
          when others =>
             module  := to_integer(UNSIGNED(module_MAP) - 3);
       end case;

       DRD_WB                   <= wb_data_i(g_WISHBONE_MEMWIDTH*(module + 1) - 1 downto g_WISHBONE_MEMWIDTH*(module));
       wb_master_i2c_stb_o    <= '1';
       wb_master_i2c_cyc_o    <= '1';
       wb_master_i2c_sel_o    <= X"F";
       wb_master_i2c_we_o     <= '1';

       wb_master_i2c_data_o   <=  wb_data_i(g_WISHBONE_MEMWIDTH*(module + 1) - 1 downto g_WISHBONE_MEMWIDTH*(module));
       wb_master_i2c_addr_o   <= i2c_DTX;

--       write_i2c_data(wb_data_i(g_WISHBONE_MEMWIDTH*(module + 1) -1 downto g_WISHBONE_MEMWIDTH*(module)), X"6");
       CTRL_FETCHER_fsm         <= S5_I2C_DRD_ACK;
    end procedure;

    procedure update_DRB is
    begin
       case OP(12 downto 5) is

        when control_MAP  =>

        when i2c_slave_MAP  =>

        when trigger1_MAP  =>
           read_DRD_wb(trigger1_MAP);
        when trigger2_MAP  =>
           read_DRD_wb(trigger2_MAP);
        when trigger3_MAP  =>
           read_DRD_wb(trigger3_MAP);
        when trigger4_MAP  =>
           read_DRD_wb(trigger4_MAP);
        when trigger5_MAP  =>
           read_DRD_wb(trigger5_MAP);
        when trigger6_MAP  =>
           read_DRD_wb(trigger6_MAP);
        when EEPROM_manager_MAP  =>
           read_DRD_wb(EEPROM_manager_MAP);
        when wr_core_MAP  =>
           read_DRD_wb(wr_core_MAP);
        when EEPROM_memory_MAP  =>
           read_DRD_wb(EEPROM_memory_MAP);
        when others =>
          DRD_WB            <= (others => '0');
          OP(4 downto 0)    <= "00100"; 
          CTRL_FETCHER_fsm  <= S7_WB_ERROR;
      end case;
    end procedure;


  begin

    if rising_edge(wb_clk)then
      if rst = '1' then
        reset_control;
     else
        case CTRL_FETCHER_fsm is
           when S0_IDLE               =>
              if ind_wb_addr_i = '1' then
                 CTRL_FETCHER_fsm         <= S1_I2C_DRXA_RQT;
                 wb_master_i2c_stb_o      <= '1';
                 wb_master_i2c_cyc_o      <= '1';
                 wb_master_i2c_we_o       <= '0';
                 wb_master_i2c_addr_o     <= i2c_DRXA;
              else
              end if;
           when S1_I2C_DRXA_RQT       =>
                 CTRL_FETCHER_fsm         <= S2_I2C_DRXA_READ;
           when S2_I2C_DRXA_READ     =>
           -- Perform DRXA read
              if (wb_master_i2c_ack_i = '1') then
                 OP(31 downto 16)         <= wb_master_i2c_data_i(23 downto 8);
                 OP(12 downto 5)          <= wb_master_i2c_data_i(23 downto 16);
                 CTRL_FETCHER_fsm         <= S3_I2C_DRXA_FETCHED;
                 OP(4  downto 3)          <= "10";
              else
                 OP                       <= X"00000004";
                 CTRL_FETCHER_fsm         <= S7_WB_ERROR;
              end if;
              wb_master_i2c_stb_o       <= '0';
              wb_master_i2c_cyc_o       <= '0';
           when S3_I2C_DRXA_FETCHED    =>
              i2c_to_wb;
           when S4_WB_DATA_READ       =>
              update_DRB;
           when S5_I2C_DRD_ACK        =>
              wb_master_i2c_stb_o    <= '0';
              wb_master_i2c_cyc_o    <= '0';
              wb_master_i2c_sel_o    <= X"0";
              wb_master_i2c_we_o     <= '0';
              if wb_master_i2c_ack_i = '1' then
                 CTRL_FETCHER_fsm <= S6_DONE;
              else
                 CTRL_FETCHER_fsm <= S7_WB_ERROR;
              end if;
           when S5_I2C_WRITE_DATA     =>
              OP(4 downto 0)       <= "00010";
              CTRL_FETCHER_fsm     <= S6_DONE;
           when S6_DONE               =>
              OP                   <= (others => '0');
              CTRL_FETCHER_fsm     <= S0_IDLE;
           when S7_WB_ERROR           => 
              OP                   <= (others => '0');
              CTRL_FETCHER_fsm     <= S0_IDLE;
           when others =>
        end case;
     end if;

  else
  end if;

  end process;

end Behavioral;
