--------------------------------------------------------------------------------
-- CERN (BE-CO-HT)
-- Top level entity of CONV-TTL-BLO
-- http://www.ohwr.org/projects/conv-ttl-blo
--------------------------------------------------------------------------------
--
-- unit name: conv_ttl_blo.vhd
--
-- author: Theodor-Adrian Stana (t.stana@cern.ch)
--
-- version: 1.0
--
-- description: Top entity of CONV-TTL-BLO
--
-- dependencies:
--
-- references:
--   [1] ELMA, Access to board data using SNMP and I2C
--       http://www.ohwr.org/documents/227
--
--------------------------------------------------------------------------------
-- GNU LESSER GENERAL PUBLIC LICENSE
--------------------------------------------------------------------------------
-- This source file is free software; you can redistribute it and/or modify it
-- under the terms of the GNU Lesser General Public License as published by the
-- Free Software Foundation; either version 2.1 of the License, or (at your
-- option) any later version. This source is distributed in the hope that it
-- will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-- See the GNU Lesser General Public License for more details. You should have
-- received a copy of the GNU Lesser General Public License along with this
-- source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
--------------------------------------------------------------------------------
library ieee;
library unisim;

use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use unisim.vcomponents.all;

use work.bicolor_led_ctrl_pkg.all;
use work.wishbone_pkg.all;
use work.gencores_pkg.all;
use work.genram_pkg.all;

entity conv_ttl_blo is
  generic
  (
    g_nr_ttl_chan : natural := 6;
    g_nr_inv_chan : natural := 4
  );
  port
  (
    -- Clock lines
    fpga_clk_p_i           : in  std_logic; --Using the 125MHz clock
    fpga_clk_n_i           : in  std_logic;

    -- LEDs
    led_ctrl0_o            : out std_logic;
    led_ctrl0_oen_o        : out std_logic;
    led_ctrl1_o            : out std_logic;
    led_ctrl1_oen_o        : out std_logic;
    led_multicast_2_0_o    : out std_logic;
    led_multicast_3_1_o    : out std_logic;
    led_wr_gmt_ttl_ttln_o  : out std_logic;
    led_wr_link_syserror_o : out std_logic;
    led_wr_ok_syspw_o      : out std_logic;
    led_wr_ownaddr_i2c_o   : out std_logic;
 
    -- Lines for the i2c_slave
    scl_i                  : in  std_logic;
    scl_o                  : out std_logic;
    scl_oe_o               : out std_logic;
    sda_i                  : in  std_logic;
    sda_o                  : out std_logic;
    sda_oe_o               : out std_logic;
    fpga_ga_i              : in  std_logic_vector(4 downto 0);
    fpga_gap_i             : in  std_logic;

    -- It allows power sequencing of the 24V rail after a security delay
    mr_n_o                 : out std_logic
  );
end conv_ttl_blo;


architecture behav of conv_ttl_blo is

  --============================================================================
  -- Constant declarations
  --============================================================================
  -- Number of Wishbone masters and slaves, for wb_crossbar
  constant c_nr_masters      : natural :=  1;
  constant c_nr_slaves       : natural :=  1;

  -----------------------------------------
  -- Memory map
  -- * all registers are word-addressable
  -- * all registers are word-aligned
  -----------------------------------------
  --    MEM       [000-FFF]
  -----------------------------------------
  -- slave order definitions
  constant c_slv_mem : natural := 0;

  -- base address definitions
  constant c_addr_mem : t_wishbone_address := x"00000000";

  -- address mask definitions
  constant c_mask_mem : t_wishbone_address := x"00000000";

  -- addresses constant for Wishbone crossbar
  constant c_addresses : t_wishbone_address_array(c_nr_slaves-1 downto 0)
                  := (
                       c_slv_mem => c_addr_mem
                     );

  -- masks constant for Wishbone crossbar
  constant c_masks : t_wishbone_address_array(c_nr_slaves-1 downto 0)
                  := (
                       c_slv_mem => c_mask_mem
                     );

  --============================================================================
  -- Component declarations
  --============================================================================
  -- Reset generator component
  -- (use: global reset generation, output reset generation)
  component reset_gen is
    generic
    (
      -- Reset time in number of clk_i cycles
      g_reset_time : positive := 5_000_000
    );
    port
    (
      clk_i   : in  std_logic;
      rst_i   : in  std_logic;
      rst_n_o : out std_logic
    );
  end component reset_gen;

  -- I2C bridge
  -- (use: convert I2C transfers into WB transfers on memmapped registers)
  component vbcp_wb is
    port
    (
      -- Clock, reset
      clk_i      : in  std_logic;
      rst_n_i    : in  std_logic;

      -- I2C lines
      sda_en_o   : out std_logic;
      sda_i      : in  std_logic;
      sda_o      : out std_logic;
      scl_en_o   : out std_logic;
      scl_i      : in  std_logic;
      scl_o      : out std_logic;

      -- I2C address and status
      i2c_addr_i : in  std_logic_vector(6 downto 0);
      tip_o : out std_logic;
      err_o  : out std_logic;

      -- Wishbone master signals
      wbm_stb_o  : out std_logic;
      wbm_cyc_o  : out std_logic;
      wbm_sel_o  : out std_logic_vector(3 downto 0);
      wbm_we_o   : out std_logic;
      wbm_dat_i  : in  std_logic_vector(31 downto 0);
      wbm_dat_o  : out std_logic_vector(31 downto 0);
      wbm_adr_o  : out std_logic_vector(31 downto 0);
      wbm_ack_i  : in  std_logic;
      wbm_rty_i  : in  std_logic;
      wbm_err_i  : in  std_logic
    );
  end component vbcp_wb;

  --============================================================================
  -- Signal declarations
  --============================================================================
  -- Clock signals
  signal clk125                    : std_logic;

  -- Reset signals
  signal rst_n, rst                 : std_logic;

  -- Wishbone crossbar signals
  signal xbar_slave_in       : t_wishbone_slave_in_array  (c_nr_masters - 1 downto 0);
  signal xbar_slave_out      : t_wishbone_slave_out_array (c_nr_masters - 1 downto 0);
  signal xbar_master_in      : t_wishbone_master_in_array (c_nr_slaves  - 1 downto 0);
  signal xbar_master_out     : t_wishbone_master_out_array(c_nr_slaves  - 1 downto 0);

  -- RAM signals
  signal ram_we              : std_logic;
  signal ram_ack             : std_logic;

  -- Signal for controlling the bicolor LED matrix
  signal bicolor_led_state          : std_logic_vector(23 downto 0);

  -- I2C bridge signals
  signal vbcp_done            : std_logic;
  signal vbcp_err             : std_logic;
  signal i2c_err_led         : std_logic;
  signal i2c_up              : std_logic;
  signal i2c_addr            : std_logic_vector(6 downto 0);
  signal led_i2c             : std_logic;
  signal led_i2c_clkdiv      : unsigned(22 downto 0);
  signal led_i2c_cnt         : unsigned( 2 downto 0);
  signal blink_state         : std_logic;

begin

  --============================================================================
  -- Generate 125 MHz global signal from differential lines
  --============================================================================
  cmp_125_diff_buf : IBUFGDS
  generic map
  (
    DIFF_TERM    => TRUE,
    IBUF_LOW_PWR => TRUE
  )
  port map
  (
    I  => fpga_clk_p_i,
    IB => fpga_clk_n_i,
    O  => clk125
  );

  --============================================================================
  -- Internal and external reset generation
  --============================================================================
  -- Configure reset generator for 96ms power-on reset
  cmp_reset_gen : reset_gen
    generic map
    (
      -- Reset time: 12 * 8ns * (10**6) = 96 ms
      g_reset_time => 12*(10**6)
    )
    port map
    (
      clk_i   => clk125,
      rst_i   => '0',
      rst_n_o => rst_n
    );

--  rst  <= not rst_n;
  mr_n_o <= rst_n;

  --============================================================================
  -- I2C bridge logic
  --============================================================================
  -- Set the I2C address signal according to ELMA protocol [1]
  i2c_addr <= "10" & fpga_ga_i;

  -- Instantiate VBCP bridge component
  cmp_i2c_bridge : vbcp_wb
    port map
    (
      -- Clock, reset
      clk_i      => clk125,
      rst_n_i    => rst_n,

      -- I2C lines
      sda_en_o   => sda_oe_o,
      sda_i      => sda_i,
      sda_o      => sda_o,
      scl_en_o   => scl_oe_o,
      scl_i      => scl_i,
      scl_o      => scl_o,

      -- I2C address and status
      i2c_addr_i => i2c_addr,
      tip_o      => vbcp_done,
      err_o      => vbcp_err,

      -- Wishbone master signals
      wbm_stb_o  => xbar_slave_in(0).stb,
      wbm_cyc_o  => xbar_slave_in(0).cyc,
      wbm_sel_o  => xbar_slave_in(0).sel,
      wbm_we_o   => xbar_slave_in(0).we,
      wbm_dat_i  => xbar_slave_out(0).dat,
      wbm_dat_o  => xbar_slave_in(0).dat,
      wbm_adr_o  => xbar_slave_in(0).adr,
      wbm_ack_i  => xbar_slave_out(0).ack,
      wbm_rty_i  => xbar_slave_out(0).rty,
      wbm_err_i  => xbar_slave_out(0).err
    );

  -- Process to blink the LED for a finite amount of time when the vbcp_done
  -- signal is set.
  p_i2c_blink : process(clk125)
  begin
    if rising_edge(clk125) then
      if (rst_n = '0') then
        led_i2c_clkdiv <= (others => '0');
        led_i2c_cnt      <= (others => '0');
        led_i2c          <= '0';
        blink_state      <= '0';
      else
        case blink_state is

          when '0' =>
            led_i2c <= '0';
            if (vbcp_done = '1') then
              blink_state <= '1';
            end if;

          when '1' =>
            led_i2c_clkdiv <= led_i2c_clkdiv + 1;
            if (led_i2c_clkdiv = 2499999) then
              led_i2c_clkdiv <= (others => '0');
              led_i2c_cnt    <= led_i2c_cnt + 1;
              led_i2c        <= not led_i2c;
              if (led_i2c_cnt = 7) then
                led_i2c_cnt <= (others => '0');
                blink_state <= '0';
              end if;
            end if;

          when others =>
            blink_state <= '0';

        end case;
      end if;
    end if;
  end process p_i2c_blink;

  -- Process to set the I2C error LED signal for display on the front panel
  -- of the front module. The I2C error signal is permanently set once an
  -- error is detected from the bridge module.
  p_i2c_err_led : process (clk125) is
  begin
  if rising_edge(clk125) then
    if (rst_n = '0') then
      i2c_err_led <= '0';
    elsif (vbcp_err = '1') then
      i2c_err_led <= '1';
    end if;
  end if;
  end process p_i2c_err_led;

  --============================================================================
  -- Instantiation and connection of the main Wishbone crossbar
  --============================================================================
  xbar_master_in(0).int <= '0';
  xbar_master_in(0).err <= '0';

  cmp_wb_crossbar : xwb_crossbar
  generic map
  (
    g_num_masters => c_nr_masters,
    g_num_slaves  => c_nr_slaves,
    g_registered  => false,
    g_address     => c_addresses,
    g_mask        => c_masks
  )
  port map
  (
    clk_sys_i => clk125,
    rst_n_i   => rst_n,
    slave_i   => xbar_slave_in,
    slave_o   => xbar_slave_out,
    master_i  => xbar_master_in,
    master_o  => xbar_master_out
  );

  --============================================================================
  -- Instantiate single-port RAM
  --============================================================================
  cmp_memory: generic_spram
    generic map (
      g_data_width => 32,
      g_size       => 2**12
    )
    port map (
      rst_n_i => rst_n,
      clk_i   => clk125,
      bwe_i   => (others => '0'),
      we_i    => ram_we,
      a_i     => xbar_master_out(c_slv_mem).adr(11 downto 0),
      d_i     => xbar_master_out(c_slv_mem).dat,
      q_o     => xbar_master_in(c_slv_mem).dat
    );

  ram_we <= xbar_master_out(c_slv_mem).we and xbar_master_out(c_slv_mem).stb and
            xbar_master_out(c_slv_mem).cyc;

  xbar_master_in(c_slv_mem).ack <= ram_ack;
  xbar_master_in(c_slv_mem).err <= '0';

  p_ram_ack : process (clk125) is
  begin
    if rising_edge(clk125) then
      if (rst_n = '0') then
        ram_ack <= '0';
      else
        ram_ack <= '0';
        if (xbar_master_out(c_slv_mem).stb = '1') and
           (xbar_master_out(c_slv_mem).cyc = '1') then
          ram_ack <= '1';
        end if;
      end if;
    end if;
  end process p_ram_ack;

  --============================================================================
  -- Bicolor LED matrix logic
  --============================================================================
  -- Bicolor LED controls, corresponding to the column orders on the
  -- bicolor_led_ctrl unit.

  -- WR address
  bicolor_led_state( 1 downto  0) <= c_LED_OFF;

  -- WR GMT
  bicolor_led_state( 3 downto  2) <= c_LED_OFF;

  -- WR link
  bicolor_led_state( 5 downto  4) <= c_LED_OFF;

  -- WR OK
  bicolor_led_state( 7 downto  6) <= c_LED_OFF;

  -- MULTICAST 0
  bicolor_led_state( 9 downto  8) <= c_LED_OFF;

  -- MULTICAST 1
  bicolor_led_state(11 downto 10) <= c_LED_OFF;


  -- I2C
  bicolor_led_state(13 downto 12) <= c_LED_GREEN when (led_i2c = '1') else
                                     c_LED_RED   when (i2c_err_led = '1') else
                                     c_LED_OFF;

  -- State of TTL/TTL_N switch
  bicolor_led_state(15 downto 14) <= c_LED_OFF;

  -- System error
  bicolor_led_state(17 downto 16) <= c_LED_OFF;

  -- System power
  bicolor_led_state(19 downto 18) <= c_LED_GREEN;

  -- MULTICAST 2
  bicolor_led_state(21 downto 20) <= c_LED_OFF;

  -- MULTICAST 3
  bicolor_led_state(23 downto 22) <= c_LED_OFF;

  cmp_bicolor_led_ctrl : bicolor_led_ctrl
    generic map
    (
      g_NB_COLUMN    => 6,
      g_NB_LINE      => 2,
      g_clk_freq     => 125000000,
      g_refresh_rate => 250
    )
    port map
    (
      clk_i           => clk125,
      rst_n_i         => rst_n,
      led_intensity_i => "1111111",
      led_state_i     => bicolor_led_state,
      column_o(0)     => led_wr_ownaddr_i2c_o,
      column_o(1)     => led_wr_gmt_ttl_ttln_o,
      column_o(2)     => led_wr_link_syserror_o,
      column_o(3)     => led_wr_ok_syspw_o,
      column_o(4)     => led_multicast_2_0_o,
      column_o(5)     => led_multicast_3_1_o,
      line_o(0)       => led_ctrl0_o,
      line_o(1)       => led_ctrl1_o,
      line_oen_o(0)   => led_ctrl0_oen_o,
      line_oen_o(1)   => led_ctrl1_oen_o
    );

end behav;
