-------------------------------------------------------------------------------
-- Title      : DIOT WIC Demonstrator
-- Project    : Distributed I/O Tier
-------------------------------------------------------------------------------
-- File       : diot_wic_top.vhd
-- Author     : Greg Daniluk <grzegorz.daniluk@cern.ch>
-- Company    : CERN (BE-CO-HT)
-- Standard   : VHDL
-------------------------------------------------------------------------------
-- Description:
-- DIOT top module with for Warm Interlocks Controller demo.
-------------------------------------------------------------------------------
--
-- Copyright (c) 2018 CERN
--
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 0.51 (the License) (which enables you, at your option,
-- to treat this file as licensed under the Apache License 2.0); you may not
-- use this file except in compliance with the License. You may obtain a copy
-- of the License at http://solderpad.org/licenses/SHL-0.51.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

use work.wishbone_pkg.all;
use work.genram_pkg.all;
use work.secded_32b_pkg.all;
use work.fip_urv_regs_pkg.all;

entity diot_hydra2_top is
  generic (
    --  Version as reported in the memory map.
    --  This is declared as a generic so that it is visible at the top
    --  of the file, but it shouldn't be redefined.
    g_VERSION : std_logic_vector(31 downto 0) := x"2023_03_30";

    --  Size of management cpu instruction RAM (in log2 of bytes).
    g_MGMT_IRAM_LOG_SIZE : natural := 12;
    --  Size of management cpu data RAM (in log2 of bytes).
    g_MGMT_DRAM_LOG_SIZE : natural := 10;
    --  Size of application cpu instruction RAM (in log2 of bytes).
    g_APPL_IRAM_LOG_SIZE : natural := 12;
    --  Size of application cpu data RAM (in log2 of bytes).
    g_APPL_DRAM_LOG_SIZE : natural := 12);
  port (
    clk_40m_i : in std_logic;
    devrst_n : in std_logic;

    -- nanoFIP Wishbone
    dat_i  : in  std_logic_vector(7 downto 0);
    dat_o  : out std_logic_vector(7 downto 0);
    adr_o  : out std_logic_vector(9 downto 0);
    stb_o  : out std_logic;
    wclk_o : out std_logic;
    we_o   : out std_logic;
    cyc_o  : out std_logic;
    ack_i  : in  std_logic;
    rst_o  : out std_logic;     --  Active high
    -- nanoFIP control
    rstin_o    : out std_logic; --  Active low
    -- rston_i    : in std_logic;  --  Active low
    nostat_o   : out std_logic;
    var3_acc_o : out std_logic;
    var3_rdy_i : in  std_logic;
    var2_acc_o : out std_logic;
    var2_rdy_i : in  std_logic;
    var1_acc_o : out std_logic;
    var1_rdy_i : in  std_logic;
    u_errflag_i : in  std_logic;
    p3_lgth_o  : out std_logic_vector(2 downto 0);

    --  Monimod i2c (#0)
    monimod_scl_b : inout std_logic;
    monimod_sda_b : inout std_logic;

    --  Control monimod power
    monimod_shdn_o : out std_logic;

    --  Backplane i2c (#1)
    backplane_scl_b : inout std_logic;
    backplane_sda_b : inout std_logic;

    --  psu (aka mon) i2c (#2)
    psu_scl_b : inout std_logic;
    psu_sda_b : inout std_logic;

    --  pcbver i2c (#3)
    pcbver_scl_b : inout std_logic;
    pcbver_sda_b : inout std_logic;

    mon_gpio_pres_i : in std_logic_vector(1 downto 0);
    pwr_cyc_req_o : out std_logic_vector(1 downto 0);

    --  Control fan power.
    fan_rstn_o : out std_logic;
    fan_io0_o : out std_logic;
    fan_io1_o : out std_logic;

    --  Flash
    flash_ss_n_o    : out std_logic;
    flash_sclk_o    : out std_logic;
    flash_mosi_o    : out std_logic;
    flash_miso_i    : in  std_logic;

--    shared_bus : inout std_logic_vector(3 downto 0);
--    irq_n_i    : in  std_logic;

    servmod_b : inout std_logic_vector(8 downto 1);
    servmod_dir_o : out std_logic;

    s1_p_i  : in  std_logic_vector(15 downto 0);
    s1_n_o  : out std_logic_vector(15 downto 0);
    s2_p_i  : in  std_logic_vector(15 downto 0);
    s2_n_o  : out std_logic_vector(15 downto 0);
    s3_p_i  : in  std_logic_vector(15 downto 0);
    s3_n_o  : out std_logic_vector(15 downto 0);
    s4_p_i  : in  std_logic_vector(15 downto 0);
    s4_n_o  : out std_logic_vector(15 downto 0);
    s5_p_i  : in  std_logic_vector(15 downto 0);
    s5_n_o  : out std_logic_vector(15 downto 0);
    s6_p_i  : in  std_logic_vector(15 downto 0);
    s6_n_o  : out std_logic_vector(15 downto 0);
    s7_p_i  : in  std_logic_vector(15 downto 0);
    s7_n_o  : out std_logic_vector(15 downto 0);
    s8_p_i  : in  std_logic_vector(15 downto 0);
    s8_n_o  : out std_logic_vector(15 downto 0);

    --  Front panel leds
    leds_o  : out std_logic_vector(4 downto 1)
  );
end diot_hydra2_top;

architecture rtl of diot_hydra2_top is
  constant c_SLOT_NUM     : integer := 8;

  signal clk_sys : std_logic;
  signal clk_cpu : std_logic_vector(3 downto 1);

  signal led_init, led_clk, led_rst : std_logic;
  signal leds_usr   : std_logic_vector(4 downto 1);
  signal servmod_dir : std_logic;
  signal servmod_out : std_logic_vector(8 downto 1);

  subtype t_rst_counter is natural range 0 to 40_000_000 - 1;
  signal rst_counter : t_rst_counter;

  --  Reset sources
  signal pon_rsti_n : std_logic;
  --  gbl_rst_n is the full reset, enabled at power-on.
  signal gbl_rst_n : std_logic;
  --  hydra_rst_n is set at restart of mgmt cpu.
  signal hydra_rst_n : std_logic;

  -- app
  signal app_regs_out : t_app_master_out_array(1 downto 0);
  signal app_regs_in : t_app_master_in_array(1 downto 0);

  type t_slot_vec is array (c_SLOT_NUM-1 downto 0) of std_logic_vector (31 downto 0);

  signal slots_loops        : t_slot_vec;
  signal slots_relays       : t_slot_vec;
  signal var1_rdy2, var3_rdy2, var3_clr_out, var3_clr_wr : std_logic;

  signal wb_fip_in : t_wishbone_slave_in;
  signal wb_fip_out : t_wishbone_slave_out;
  signal sel : std_logic_vector(3 downto 0);
  signal unc_dat : std_logic_vector(31 downto 8);
  signal unc_adr : std_logic_vector(31 downto 0);
  signal u_errflag, u_errflag_d : std_logic;

  signal var1_acc, var1_rdy, var1_read : std_logic;
  signal var3_acc, var3_rdy, var3_written : std_logic;

  --  shmem
  signal sh_mem_mgmt_in : t_wishbone_slave_in;
  signal sh_mem_mgmt_out : t_wishbone_slave_out;

  -- i2c
  signal wb_i2c_in : t_wishbone_slave_in;
  signal wb_i2c_out : t_wishbone_slave_out;
  signal i2c_sda_in, i2c_sda_out, i2c_sda_oen : std_logic_vector(3 downto 0);
  signal i2c_scl_in, i2c_scl_out, i2c_scl_oen : std_logic_vector(3 downto 0);

  --  flash
  signal wb_spi_in : t_wishbone_slave_in;
  signal wb_spi_out : t_wishbone_slave_out;

  --  User to eNVM
  signal wb_envm_in : t_wishbone_slave_in;
  signal wb_envm_out : t_wishbone_slave_out;

  --  MEM bus for FIP cpu to write into app memory.
  signal app_mem_wb_in : t_wishbone_slave_in_array(0 to 1);
  signal app_mem_wb_out : t_wishbone_slave_out_array(0 to 1);

  signal sh_mem_app_in : t_wishbone_slave_in_array(0 to 1);
  signal sh_mem_app_out : t_wishbone_slave_out_array(0 to 1);

  signal fip_cyc : std_logic;
  signal fip_stb, fip_stb_d : std_logic;
  signal fip_we : std_logic;
  signal fip_ack : std_logic;
  signal fip_adr : std_logic_vector (9 downto 0);
  signal fip_dato : std_logic_vector (7 downto 0);
  signal fip_dati : std_logic_vector (7 downto 0);

  -- MSS
  signal fic0_haddr     : std_logic_vector(31 downto 0);
  signal fic0_hburst    : std_logic_vector(2 downto 0);
  signal fic0_hmastlock : std_logic;
  signal fic0_hprot     : std_logic_vector(3 downto 0);
  signal fic0_hsize     : std_logic_vector(2 downto 0);
  signal fic0_htrans    : std_logic_vector(1 downto 0);
  signal fic0_hwdata    : std_logic_vector(31 downto 0);
  signal fic0_hwrite    : std_logic;
  signal fic0_hrdata    : std_logic_vector(31 downto 0);
  signal fic0_hready    : std_logic;
  signal fic0_hresp     : std_logic_vector(1 downto 0);

  signal restart_stb, restart : std_logic;
  signal restart_val : std_logic_vector(31 downto 0);
  signal reset_count : std_logic_vector(31 downto 0);
  signal hydra_rst : std_logic;

  signal iram_copy, dram_clear, iram_force_err : std_logic;
  signal iram_page, src_page : std_logic_vector(7 downto 0);
  signal iram_src : std_logic;
  signal nbr_wf_err : std_logic_vector (31 downto 0);
  signal nbr_wf_err_clr : std_logic;

  signal led_rx_ok, led_rx_err : std_logic;

  signal spi_en, spi_mosi, spi_miso, spi_sck, spi_csn : std_logic;
  signal flash_ss_n : std_logic;
  signal flash_sclk : std_logic;
  signal flash_mosi : std_logic;
  signal flash_miso : std_logic;

  signal shmem_regs_ram_ecc_se, shmem_regs_ram_scrub_se : std_logic_vector (31 downto 0);
  signal shmem_regs_ram_scrub_cycle, shmem_regs_ram_scrub_de_last : std_logic_vector (15 downto 0);
  signal shmem_regs_ram_scrub_period : std_logic_vector(15 downto 0);
  signal shmem_regs_scrub_cfg_ram_en, shmem_regs_scrub_cfg_ram_wr : std_logic;
  signal shmem_regs_scrub_cfg_ram_in, shmem_regs_scrub_cfg_ram_out : std_logic;
  signal shmem_regs_ecc_mask : std_logic_vector(7 downto 0);

  signal i2c_monimod_off, i2c_fantray_off : std_logic;
begin
  --  Reset generation
  gbl_rst_n <= pon_rsti_n;

  hydra_rst_n <= gbl_rst_n and (not restart);

  --  Fip reset
  rst_o <= not gbl_rst_n;
  rstin_o <= '1';

-------------------------------------------------------------------------------
-- Synchronizers for RDY signals (40MHz clock domain in nanoFIP
-------------------------------------------------------------------------------

  process (clk_sys)
  begin
    if rising_edge(clk_sys) then
      if gbl_rst_n = '0' then
        --  Variable was not read.
        var1_read <= '0';
        var3_written <= '0';
      else
        if var1_rdy = '0' then
          --  A new value will be written.
          var1_read <= '0';
        elsif var1_acc = '1' then
          --  Variable is (being) read.
          var1_read <= '1';
        end if;

        if var3_acc = '1' or (var3_clr_out = '1' and var3_clr_wr = '1') then
          var3_written <= '1';
        elsif var3_rdy = '0' then
          var3_written <= '0';
        end if;
      end if;
    end if;
  end process;

  --  A variable is ready if it the data are ready from nanofip and not yet read.
  var1_rdy2 <= var1_rdy and not var1_read;
  var3_rdy2 <= var3_rdy and not var3_written;

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

  inst_hydra_core: entity work.diot_hydra2_core
    generic map (
      g_IRAM_LOG_SIZE => g_MGMT_IRAM_LOG_SIZE,
      g_DRAM_LOG_SIZE => g_MGMT_DRAM_LOG_SIZE
    )
    port map (
      clk_sys_i => clk_sys,
      clk_cpu_i => clk_cpu,
      rst_n_i => hydra_rst_n,
      iram_copy_i => iram_copy,
      dram_clear_i => dram_clear,
      iram_err_i => iram_force_err,
      dwb_o => wb_fip_in,
      dwb_i => wb_fip_out,

      envm_i => wb_envm_in,
      envm_o => wb_envm_out,

      cpu_flt_o => open,
      cpu_rst_o => hydra_rst,
      iram_src_i => iram_src,
      iram_page_i => iram_page,
      src_page_i => src_page,

      spi_en_o => spi_en,
      spi_sck_o => spi_sck,
      spi_mosi_o => spi_mosi,
      spi_miso_i => spi_miso,
      spi_csn_o => spi_csn,

      fic0_ready_i   => '1',

      fic0_haddr_o     => fic0_haddr,
      fic0_hburst_o    => fic0_hburst,
      fic0_hmastlock_o => fic0_hmastlock,
      fic0_hprot_o     => fic0_hprot,
      fic0_hsize_o     => fic0_hsize,
      fic0_htrans_o    => fic0_htrans,
      fic0_hwdata_o    => fic0_hwdata,
      fic0_hwrite_o    => fic0_hwrite,
      fic0_hrdata_i    => fic0_hrdata,
      fic0_hready_i    => fic0_hready,
      fic0_hresp_i     => fic0_hresp,

      led_o   => led_init
    );

  inst_errflag_sync: entity work.gc_sync
    port map (
      clk_i => clk_sys,
      rst_n_a_i => gbl_rst_n,
      d_i => u_errflag_i,
      q_o => u_errflag
    );

  p_errflag: process (clk_sys)
  begin
    if rising_edge(clk_sys) then
      if gbl_rst_n = '0' then
        nbr_wf_err <= (others => '0');
        u_errflag_d <= '1';
      else
        if u_errflag = '1' and u_errflag_d = '0' then
          --  Only count rising edges
          nbr_wf_err <= std_logic_vector(unsigned(nbr_wf_err) + 1);
        end if;
        u_errflag_d <= u_errflag;
      end if;
    end if;
  end process;

  p_reset_count: process (clk_sys)
  begin
    if rising_edge(clk_sys) then
      if gbl_rst_n = '0' then
        reset_count <= (others => '0');
      else
        if hydra_rst = '1' then
          reset_count <= std_logic_vector(unsigned(reset_count) + 1);
        end if;
      end if;
    end if;
  end process;

  inst_fip_regs: entity work.fip_urv_regs
    port map (
      rst_n_i => gbl_rst_n,
      clk_i => clk_sys,
      wb_i => wb_fip_in,
      wb_o => wb_fip_out,

      version_i => g_version,
      config_ram_app0_iram_i => std_logic_vector(to_unsigned(g_APPL_IRAM_LOG_SIZE - 8, 4)),
      config_ram_app0_dram_i => std_logic_vector(to_unsigned(g_APPL_DRAM_LOG_SIZE - 8, 4)),
      config_ram_mgmt_iram_i => std_logic_vector(to_unsigned(g_MGMT_IRAM_LOG_SIZE - 8, 4)),
      config_ram_mgmt_dram_i => std_logic_vector(to_unsigned(g_MGMT_IRAM_LOG_SIZE - 8, 4)),

      fip_var_length_o => p3_lgth_o,
      fip_status_var1_rdy_i => var1_rdy2,
      fip_status_var3_rdy_i => var3_rdy2,
      fip_var1_acc_o => var1_acc,
      fip_var3_acc_o => var3_acc,
      fip_var3_clr_i => '0',
      fip_var3_clr_o => var3_clr_out,
      fip_var3_wr_o => var3_clr_wr,

      app_i => app_regs_in,
      app_o => app_regs_out,

      servmod_in_line_i => servmod_b,
      servmod_out_line_o => servmod_out,
      servmod_ctrl_dir_o => servmod_dir,
      psu_stat_pres_i => mon_gpio_pres_i,
      psu_ctrl_pwr_cyc_req_o => pwr_cyc_req_o,
      leds_val_o => leds_usr,
      restart_o => restart_val,
      restart_wr_o => restart_stb,
      restart_cfg_iram_copy_o => iram_copy,
      restart_cfg_dram_clear_o => dram_clear,
      restart_cfg_iram_err_o => iram_force_err,
      restart_cfg_iram_src_o => iram_src,
      restart_cfg_iram_page_o => iram_page,
      restart_cfg_src_page_o => src_page,
      reset_count_i => reset_count,
      monimod_shdn_val_o => monimod_shdn_o,
      i2c_off_monimod_o => i2c_monimod_off,
      i2c_off_fantray_o => i2c_fantray_off,
      fip_err_i => nbr_wf_err,
      fip_err_wr_o => nbr_wf_err_clr,
      fip_err_o => open,
      boards_0_pins_i (15 downto 0) => slots_loops(0)(15 downto 0),
      boards_0_pins_i (31 downto 16) => slots_relays(0)(15 downto 0),
      boards_1_pins_i (15 downto 0) => slots_loops(1)(15 downto 0),
      boards_1_pins_i (31 downto 16) => slots_relays(1)(15 downto 0),
      boards_2_pins_i (15 downto 0) => slots_loops(2)(15 downto 0),
      boards_2_pins_i (31 downto 16) => slots_relays(2)(15 downto 0),
      boards_3_pins_i (15 downto 0) => slots_loops(3)(15 downto 0),
      boards_3_pins_i (31 downto 16) => slots_relays(3)(15 downto 0),
      boards_4_pins_i (15 downto 0) => slots_loops(4)(15 downto 0),
      boards_4_pins_i (31 downto 16) => slots_relays(4)(15 downto 0),
      boards_5_pins_i (15 downto 0) => slots_loops(5)(15 downto 0),
      boards_5_pins_i (31 downto 16) => slots_relays(5)(15 downto 0),
      boards_6_pins_i (15 downto 0) => slots_loops(6)(15 downto 0),
      boards_6_pins_i (31 downto 16) => slots_relays(6)(15 downto 0),
      boards_7_pins_i (15 downto 0) => slots_loops(7)(15 downto 0),
      boards_7_pins_i (31 downto 16) => slots_relays(7)(15 downto 0),
      shmem_regs_ram_ecc_se_i => shmem_regs_ram_ecc_se,
      shmem_regs_ram_scrub_se_i => shmem_regs_ram_scrub_se,
      shmem_regs_ram_scrub_de_last_i => shmem_regs_ram_scrub_de_last,
      shmem_regs_ram_scrub_cycle_i => shmem_regs_ram_scrub_cycle,
      shmem_regs_ram_scrub_period_o => shmem_regs_ram_scrub_period,
      shmem_regs_scrub_cfg_ram_en_i => shmem_regs_scrub_cfg_ram_in,
      shmem_regs_scrub_cfg_ram_en_o => shmem_regs_scrub_cfg_ram_out,
      shmem_regs_scrub_cfg_wr_o => shmem_regs_scrub_cfg_ram_wr,
      shmem_regs_ecc_mask_o => shmem_regs_ecc_mask,
      i2c_i => wb_i2c_out,
      i2c_o => wb_i2c_in,
      spi_i => wb_spi_out,
      spi_o => wb_spi_in,
      fip_reg_i.dat (7 downto 0) => fip_dati,
      fip_reg_i.dat (31 downto 8) => x"00_00_00",
      fip_reg_i.ack => fip_ack,
      fip_reg_i.err => '0',
      fip_reg_i.rty => '0',
      fip_reg_i.stall => '0',
      fip_reg_o.dat (7 downto 0) => fip_dato,
      fip_reg_o.dat (31 downto 8) => unc_dat,
      fip_reg_o.adr (1 downto 0) => unc_adr (1 downto 0),
      fip_reg_o.adr (11 downto 2) => fip_adr,
      fip_reg_o.adr (31 downto 12) => unc_adr (31 downto 12),
      fip_reg_o.cyc => fip_cyc,
      fip_reg_o.stb => fip_stb,
      fip_reg_o.sel => sel,
      fip_reg_o.we => fip_we,
      fan_ctrl_io0_o => fan_io0_o,
      fan_ctrl_io1_o => fan_io1_o,
      fan_ctrl_rstn_o => fan_rstn_o,
      sh_mem_i => sh_mem_mgmt_out,
      sh_mem_o => sh_mem_mgmt_in,
      app0_mem_i => app_mem_wb_out(0),
      app0_mem_o => app_mem_wb_in(0),
      app1_mem_i => app_mem_wb_out(1),
      app1_mem_o => app_mem_wb_in(1),
      envm_i => wb_envm_out,
      envm_o => wb_envm_in
    );

  restart <= '1' when restart_stb = '1' and restart_val = x"8e51a610" else '0';

  servmod_dir_o <= servmod_dir;
  servmod_b <= servmod_out when servmod_dir = '1' else (others => 'Z');

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

  gen_app: for i in 0 to 1 generate
    signal app_rst_in, app_rst_n              : std_logic;
    signal app_flt_in, app_flt                : std_logic;
    signal app_cpu_rst_n                      : std_logic;
    signal app_iram_ecc                       : std_logic_vector(6 downto 0);

    --  IO bus for the app cpu
    signal wb_app_in : t_wishbone_slave_in;
    signal wb_app_out : t_wishbone_slave_out;
    signal app_mem_wb_we : std_logic;

    --  Mailboxes (from the FIP cpu POV)
    signal mbox_out_reg                       : std_logic_vector(31 downto 0);
    signal mbox_out_rd                        : std_logic;
    signal mbox_out_rdy                       : std_logic;
    signal mbox_in_reg                        : std_logic_vector(31 downto 0);
    signal mbox_in_dat                        : std_logic_vector(31 downto 0);
    signal mbox_in_wr                         : std_logic;
    signal mbox_in_rdy                        : std_logic;

    signal local_slots_relays       : t_slot_vec;
  begin
    app_mem_wb_we <= app_mem_wb_in(i).stb and app_mem_wb_in(i).cyc and app_mem_wb_in(i).we;
    app_mem_wb_out(i) <= (ack => app_mem_wb_in(i).stb and app_mem_wb_in(i).cyc,
      err => '0', rty => '0', stall => '0', dat => (others => '0'));
    app_iram_ecc <= f_calc_ecc (app_mem_wb_in(i).dat);

    inst_app_hydra: entity work.hydra_core
      generic map (
        g_iram_log_size => g_APPL_IRAM_LOG_SIZE, --  TODO: 1Kb for app0, 4Kb for app1.
        g_dram_log_size => g_APPL_DRAM_LOG_SIZE,
        g_triple_core => (i = 0)
        )
      port map (
        clk_sys_i => clk_sys,
        clk_cpu_i => clk_cpu,
        rst_n_i => app_rst_n,
        cpu_rst_n_i => app_cpu_rst_n,
        dwb_o => wb_app_in,
        dwb_i => wb_app_out,
        iram_addr_i => app_mem_wb_in(i).adr (g_APPL_IRAM_LOG_SIZE - 1 downto 2),
        iram_we_i => app_mem_wb_we,
        iram_data_i => app_mem_wb_in(i).dat,
        iram_ecc_i => app_iram_ecc,
        dram_clr_i => '1',
        cpu_flt_o => app_flt_in,
        cpu_rst_o => app_rst_in,
        reset_cause_o => app_regs_in(i).reset_cause,
        nbr_iram_ecc_corr_o => app_regs_in(i).iram_ecc_se,
        nbr_iram_scrub_corr_o => app_regs_in(i).iram_scrub_se,
        nbr_iram_scrub_uncorr_last_o => app_regs_in(i).iram_scrub_de_last,
        nbr_iram_scrub_cycle_o => app_regs_in(i).iram_scrub_cycle,
        iram_scrub_en_o => app_regs_in(i).scrub_cfg_iram_en,
        nbr_dram_ecc_corr_o => app_regs_in(i).dram_ecc_se,
        nbr_dram_scrub_corr_o => app_regs_in(i).dram_scrub_se,
        nbr_dram_scrub_uncorr_last_o => app_regs_in(i).dram_scrub_de_last,
        nbr_dram_scrub_cycle_o => app_regs_in(i).dram_scrub_cycle,
        dram_scrub_en_o => app_regs_in(i).scrub_cfg_dram_en,
        nbr_cpu_fault_o => app_regs_in(i).nbr_cpu_fault
        );

    app_rst_n <= gbl_rst_n and (not app_regs_out(i).cr_sys_rst);
    app_cpu_rst_n <= (not app_regs_out(i).cr_cpu_rst) and (not app_flt);

    process (clk_sys)
    begin
      if rising_edge(clk_sys) then
        if gbl_rst_n = '0' or app_regs_out(i).cr_sys_rst = '1' then
          --  Clear fault on reset
          app_flt <= '0';
        else
          app_flt <= app_flt or app_flt_in;
        end if;
      end if;
    end process;

    app_regs_in(i).sr_fault <= app_flt;

    inst_app_regs: entity work.app_urv_regs
      port map (
        rst_n_i => app_rst_n,
        clk_i => clk_sys,
        wb_i => wb_app_in,
        wb_o => wb_app_out,
        sh_mem_i => sh_mem_app_out(i),
        sh_mem_o => sh_mem_app_in(i),
        mailboxes_mboxout_o  => mbox_in_dat,
        mailboxes_mboxout_i  => mbox_in_reg,
        mailboxes_mboxout_wr_o => mbox_in_wr,
        mailboxes_mboxin_i   => mbox_out_reg,
        mailboxes_mboxin_rd_o => mbox_out_rd,
        mailboxes_status_mbin_i => mbox_out_rdy,
        mailboxes_status_mbout_i => mbox_in_rdy,
        presence_en_i => servmod_b,
        loops_0_pins_i => slots_loops(0),
        loops_1_pins_i => slots_loops(1),
        loops_2_pins_i => slots_loops(2),
        loops_3_pins_i => slots_loops(3),
        loops_4_pins_i => slots_loops(4),
        loops_5_pins_i => slots_loops(5),
        loops_6_pins_i => slots_loops(6),
        loops_7_pins_i => slots_loops(7),
        relays_0_pins_o => local_slots_relays(0),
        relays_1_pins_o => local_slots_relays(1),
        relays_2_pins_o => local_slots_relays(2),
        relays_3_pins_o => local_slots_relays(3),
        relays_4_pins_o => local_slots_relays(4),
        relays_5_pins_o => local_slots_relays(5),
        relays_6_pins_o => local_slots_relays(6),
        relays_7_pins_o => local_slots_relays(7)
        );

    --  Only app 0 drives relays
    gen_relays: if i = 0 generate
      slots_relays <= local_slots_relays;
    end generate;

    --  Mailboxes (from FIP POV).
    process(clk_sys)
    begin
      if rising_edge(clk_sys) then
        if gbl_rst_n = '0' then
          mbox_in_rdy <= '0';
          mbox_out_rdy <= '0';
          mbox_out_reg <= (others => '0');
          mbox_in_reg <= x"11_22_00_00";
        else
          if app_regs_out(i).mailboxes_mboxout_wr = '1' then
            mbox_out_rdy <= '1';
            mbox_out_reg <= app_regs_out(i).mailboxes_mboxout;
          elsif mbox_out_rd = '1' then
            mbox_out_rdy <= '0';
          end if;

          if mbox_in_wr = '1' then
            mbox_in_rdy <= '1';
            mbox_in_reg <= mbox_in_dat;
          elsif app_regs_out(i).mailboxes_mboxin_rd = '1' then
            mbox_in_rdy <= '0';
          end if;
        end if;
      end if;
    end process;

    app_regs_in(i).mailboxes_mboxin <= mbox_in_reg;
    app_regs_in(i).mailboxes_mboxout <= mbox_out_reg;
    app_regs_in(i).mailboxes_status_mbin <= mbox_in_rdy;
    app_regs_in(i).mailboxes_status_mbout <= mbox_out_rdy;
  end generate;

  inst_sh_mem: entity work.hydra_dpram
    generic map (
      g_ram_log_size => 10
    )
    port map (
      clk_i => clk_sys,
      rst_n_i => gbl_rst_n,
      cpu_rst_n_i => hydra_rst_n,
      porta_o => sh_mem_mgmt_out,
      porta_i => sh_mem_mgmt_in,
      porta_ecc_i => shmem_regs_ecc_mask(6 downto 0),
      portb_o => sh_mem_app_out(0),
      portb_i => sh_mem_app_in(0),
      nbr_ecc_corr_o => shmem_regs_ram_ecc_se,
      nbr_scrub_corr_o => shmem_regs_ram_scrub_se,
      nbr_scrub_uncorr_last_o => shmem_regs_ram_scrub_de_last,
      nbr_scrub_cycle_o => shmem_regs_ram_scrub_cycle,
      scrub_en_i => shmem_regs_scrub_cfg_ram_en,
      scrub_period_i => shmem_regs_ram_scrub_period
    );

  p_shmem_scrub: process (clk_sys)
  begin
    if rising_edge(clk_sys) then
      if hydra_rst_n = '0' then
        shmem_regs_scrub_cfg_ram_en <= '0';
      else
        if (shmem_regs_scrub_cfg_ram_out and shmem_regs_scrub_cfg_ram_wr) = '1' then
          shmem_regs_scrub_cfg_ram_en <= '1';
        end if;
      end if;
    end if;
  end process;

  inst_i2c: entity work.xwb_i2c_master
    generic map (
      g_interface_mode => CLASSIC,
      g_address_granularity => BYTE,
      g_num_interfaces => 4
    )
    port map (
      clk_sys_i => clk_sys,
      rst_n_i => gbl_rst_n,
      slave_i => wb_i2c_in,
      slave_o => wb_i2c_out,
      desc_o => open,
      int_o => open,
      scl_pad_i => i2c_scl_in,
      scl_pad_o => i2c_scl_out,
      scl_padoen_o => i2c_scl_oen,
      sda_pad_i => i2c_sda_in,
      sda_pad_o => i2c_sda_out,
      sda_padoen_o => i2c_sda_oen
    );

  monimod_sda_b <= '0' when i2c_monimod_off = '1'
              else i2c_sda_out(0) when i2c_sda_oen(0) = '0' else 'Z';
  i2c_sda_in(0) <= monimod_sda_b;

  monimod_scl_b <= '0' when i2c_monimod_off = '1'
              else i2c_scl_out(0) when i2c_scl_oen(0) = '0' else 'Z';
  i2c_scl_in(0) <= monimod_scl_b;

  backplane_sda_b <= '0' when i2c_sda_oen(1) = '0' else 'Z';
  i2c_sda_in(1) <= backplane_sda_b;

  backplane_scl_b <= '0' when i2c_scl_oen(1) = '0' else 'Z';
  i2c_scl_in(1) <= backplane_scl_b;

  psu_sda_b <= '0' when i2c_fantray_off = '1'
              else '0' when i2c_sda_oen(2) = '0' else 'Z';
  i2c_sda_in(2) <= psu_sda_b;

  psu_scl_b <= '0' when i2c_fantray_off = '1'
              else '0' when i2c_scl_oen(2) = '0' else 'Z';
  i2c_scl_in(2) <= psu_scl_b;

  pcbver_sda_b <= '0' when i2c_sda_oen(3) = '0' else 'Z';
  i2c_sda_in(3) <= pcbver_sda_b;

  pcbver_scl_b <= '0' when i2c_scl_oen(3) = '0' else 'Z';
  i2c_scl_in(3) <= pcbver_scl_b;


  inst_spi: entity work.xwb_spi
    generic map (
      g_interface_mode => CLASSIC,
      g_address_granularity => BYTE,
      g_num_slaves => 1
    )
    port map (
      clk_sys_i => clk_sys,
      rst_n_i => gbl_rst_n,
      slave_i => wb_spi_in,
      slave_o => wb_spi_out,
      desc_o => open,
      int_o => open,
      pad_cs_o(0) => flash_ss_n,
      pad_sclk_o => flash_sclk,
      pad_mosi_o => flash_mosi,
      pad_miso_i => flash_miso
    );

  flash_ss_n_o <= spi_csn when spi_en = '1' else flash_ss_n;
  flash_sclk_o <= spi_sck when spi_en = '1' else flash_sclk;
  flash_mosi_o <= spi_mosi when spi_en = '1' else flash_mosi;
  flash_miso <= flash_miso_i;
  spi_miso <= flash_miso_i;

  -------------------------------------------------------------------------------
  GEN_REAL_IO: block
    type t_slv16_arr is array (natural range <>) of std_logic_vector (15 downto 0);

    signal inp : t_slv16_arr(c_SLOT_NUM-1 downto 0);
  begin
    -- slots assignment
    inp (0) <= s1_p_i;
    inp (1) <= s2_p_i;
    inp (2) <= s3_p_i;
    inp (3) <= s4_p_i;
    inp (4) <= s5_p_i;
    inp (5) <= s6_p_i;
    inp (6) <= s7_p_i;
    inp (7) <= s8_p_i;

    gen_slots: for k in inp'range generate
      gen_slots_0: for i in 15 downto 0 generate
        inst_sync: entity work.gc_sync
          port map (
            clk_i => clk_sys,
            rst_n_a_i => gbl_rst_n,
            d_i => inp(k)(i),
            q_o => slots_loops(k)(i)
          );
      end generate;
      slots_loops(k)(31 downto 16) <= (others =>'0');
    end generate;

    s1_n_o <= slots_relays(0)(15 downto 0);
    s2_n_o <= slots_relays(1)(15 downto 0);
    s3_n_o <= slots_relays(2)(15 downto 0);
    s4_n_o <= slots_relays(3)(15 downto 0);
    s5_n_o <= slots_relays(4)(15 downto 0);
    s6_n_o <= slots_relays(5)(15 downto 0);
    s7_n_o <= slots_relays(6)(15 downto 0);
    s8_n_o <= slots_relays(7)(15 downto 0);
  end block;

  g_rx_leds: if false generate
    process (clk_sys)
      subtype t_counter is natural range 0 to 2**22;
      variable cnt_err, cnt_ok : t_counter;
    begin
      if rising_edge(clk_sys) then
        if gbl_rst_n = '0' then
          cnt_err := 0;
          cnt_ok := 0;

          leds_o(1) <= '0';
          leds_o(2) <= '0';
        else
          if led_rx_err = '1' then
            cnt_err := t_counter'high;
          end if;
          if cnt_err = 0 then
            leds_o (1) <= '0';
          else
            leds_o (1) <= '1';
            cnt_err := cnt_err - 1;
          end if;

          if led_rx_ok = '1' then
            cnt_ok := t_counter'high;
          end if;
          if cnt_ok = 0 then
            leds_o (2) <= '0';
          else
            leds_o (2) <= '1';
            cnt_ok := cnt_ok - 1;
          end if;
        end if;
      end if;
    end process;
  end generate;

  --  Leds: assign to '1' to switch on the led, 'Z' to switch off.
  -- leds_o(1) <= '1' when rst_n = '0' or leds(0) = '1' else 'Z';

  g_counter: if true generate
    process (clk_sys) is
      variable cnt : unsigned(26 downto 0) := (others => '0');
    begin
      if rising_edge(clk_sys) then
        cnt := cnt + 1;
        if cnt = 39_999_999 then
          led_clk <= '0';
        elsif cnt = 79_999_999 then
          led_clk <= '1';
          cnt := (others => '0');
        end if;
      end if;
    end process;
  end generate;

  p_led_rst: process (clk_sys)
  begin
    if rising_edge(clk_sys) then
      if gbl_rst_n = '0' then
        rst_counter <= t_rst_counter'high;
        led_rst <= '1';
      else
        if rst_counter = 0 then
          led_rst <= '0';
        else
          rst_counter <= rst_counter - 1;
        end if;
      end if;
    end if;
  end process;

  leds_o(1) <= leds_usr(1);
  leds_o(2) <= leds_usr(2); --led_clk; --KB: leds removed in order to control them from SW
  leds_o(3) <= leds_usr(3); --led_rst;
  leds_o(4) <= leds_usr(4); --led_init;

  --  Nanofip mezzanine
  --  Simple speed adapter for WB with nanofip.
  process (clk_sys)
  begin
    if rising_edge(clk_sys) then
      if gbl_rst_n = '0' then
        cyc_o <= '0';
        stb_o <= '0';
        we_o <= '0';
        fip_stb_d <= '0';
      else
        cyc_o <= fip_cyc;
        stb_o <= fip_stb_d;
        we_o  <= fip_we;
        dat_o <= fip_dato;
        adr_o <= fip_adr;
        fip_dati <= dat_i;
        --  Delay stb so that all the other signals are stable when stb raising edge is detected.
        fip_stb_d <= fip_stb;
      end if;
    end if;
  end process;

  inst_fip_ack: entity work.gc_sync
  port map (
    clk_i => clk_sys,
    rst_n_a_i => gbl_rst_n,
    d_i => ack_i,
    q_o => fip_ack
  );

  inst_var1_rdy: entity work.gc_sync
  port map (
    clk_i => clk_sys,
    rst_n_a_i => gbl_rst_n,
    d_i => var1_rdy_i,
    q_o => var1_rdy
  );

  inst_var3_rdy: entity work.gc_sync
  port map (
    clk_i => clk_sys,
    rst_n_a_i => gbl_rst_n,
    d_i => var3_rdy_i,
    q_o => var3_rdy
  );

  wclk_o <= clk_sys;

  nostat_o <= '1'; --  Do not append status byte at the end of var3.
  var3_acc_o <= var3_acc;
  var2_acc_o <= '0';
  var1_acc_o <= var1_acc;

  inst_hpms: entity work.diot_hydra2_periph
    port map (
      clk_i => clk_40m_i,
      devrst_n => devrst_n,
      HPMS_FIC_0_USER_MASTER_HADDR_M0     => fic0_haddr,
      HPMS_FIC_0_USER_MASTER_HBURST_M0    => fic0_hburst,
      HPMS_FIC_0_USER_MASTER_HMASTLOCK_M0 => fic0_hmastlock,
      HPMS_FIC_0_USER_MASTER_HPROT_M0     => fic0_hprot,
      HPMS_FIC_0_USER_MASTER_HSIZE_M0     => fic0_hsize,
      HPMS_FIC_0_USER_MASTER_HTRANS_M0    => fic0_htrans,
      HPMS_FIC_0_USER_MASTER_HWDATA_M0    => fic0_hwdata,
      HPMS_FIC_0_USER_MASTER_HWRITE_M0    => fic0_hwrite,

      HPMS_FIC_0_USER_MASTER_HRDATA_M0    => fic0_hrdata,
      HPMS_FIC_0_USER_MASTER_HREADY_M0    => fic0_hready,
      HPMS_FIC_0_USER_MASTER_HRESP_M0     => fic0_hresp,
      power_on_reset_n => pon_rsti_n,

      clk_sys_o => clk_sys,
      clk_cpu1_o => clk_cpu(1),
      clk_cpu2_o => clk_cpu(2),
      clk_cpu3_o => clk_cpu(3)
    );
end rtl;
