-------------------------------------------------------------------------------
--
-- Project name: JESD204 Receiver core
--
-- (C) Copyright 2018 DFC Design, s.r.o., Brno, Czech Republic
-- Author: Marek Kvas (m.kvas@dspfpga.com)
--
-------------------------------------------------------------------------------
--
-- This file implements axi slave register interface for accessing 
-- memory mapped registers. AXI4-Lite spec is used.
--
-- Core uses two clocks - AXI clock and system clock. AXI clock is supposed to
-- be free running clock. SYS_CLK is clock where most registers are.
-- This clock can be stopped or has strange frequency.
-- Dual clock should ensure that it can not break AXI and cause connected
-- processor to freeze. 
--
-- All registers must be written with 32 bit write operation as strobes are
-- not used.
--
-- Registers:
-- 0 - magic number and version register (AXI Domain)
-- 4 - Global reset register (AXI Domain)
--
--
--
--
--
--
-------------------------------------------------------------------------------


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

--use work.tb_print_pkg.all;


entity jesd204b_axi_reg is
   generic (
      g_magic_revision_reg : std_logic_vector(31 downto 0);

      g_stat_cnt_length    : integer;
      g_lane_count         : integer;

      g_axi_timeout        : integer;
      C_S_AXI_DATA_WIDTH   : integer := 32;
      C_S_AXI_ADDR_WIDTH   : integer := 10
           );
   port (
      SYS_CLK                 : in  std_logic;
      SYS_RST                 : in  std_logic;

      ------
      -- Registers in system domain
      -- Common to all lanes
      R_CTRL_SRST_WR             : out std_logic;
      R_CTRL_MONRST_WR           : out std_logic;
      
      R_CFG_F_RW                 : out std_logic_vector(7 downto 0);
      R_CFG_K_RW                 : out std_logic_vector(4 downto 0);
      R_CFG_FxK_RW               : out std_logic_vector(10 downto 0);
      R_CFG_SUBCLASS_RW          : out std_logic_vector(1 downto 0);
      R_CFG_ERR_REPORT_EN_RW     : out std_logic;
      R_CFG_REINIT_EN_RW         : out std_logic;
      R_CFG_ILAS_RW              : out std_logic;
      R_CFG_SCRAMBLING_EN_RW     : out std_logic;

      R_CFG_SYSREF_DELAY_RW      : out std_logic_vector(3 downto 0);
      R_CFG_SYSREF_ALLRESYNC_RW  : out std_logic;
      R_CFG_SYSREF_POLARITY_RW   : out std_logic;

      R_STATUS_SYSREF_SEEN_RO    : in  std_logic;
      R_STATUS_SYNC_OK_RO        : in  std_logic;

      R_STATUS_SYNC_ERR_RO       : in  std_logic;
      R_STATUS_SYNC_ERR_DIFF_RO  : in  std_logic_vector(10 downto 0);

      -- Specific to each lane
      R_LANE_ILAS_CFG_DONE_RO       : in std_logic_vector(g_lane_count-1 downto 0);
      R_LANE_ILAS_CFG_DATA_RO       : in std_logic_vector(
                           g_lane_count * 8*16 - 1 downto 0);   
      R_LANE_STATUS_RO           : in std_logic_vector(g_lane_count * 8 - 1
                                                                  downto 0);
      R_LANE_MON_PHYS_ERR_CNT_RO       : in std_logic_vector(
                           g_lane_count * g_stat_cnt_length - 1 downto 0);
      R_LANE_MON_PHYS_ERR_RES_CNT_RO   : in std_logic_vector(
                           g_lane_count * g_stat_cnt_length - 1 downto 0);
      R_LANE_MON_ALIGN_F_ERR_CNT_RO    : in std_logic_vector(
                           g_lane_count * g_stat_cnt_length - 1 downto 0);
      R_LANE_MON_ALIGN_MF_ERR_CNT_RO   : in std_logic_vector(
                           g_lane_count * g_stat_cnt_length - 1 downto 0);
      R_LANE_MON_REPLACE_F_CNT_RO      : in std_logic_vector(
                           g_lane_count * g_stat_cnt_length - 1 downto 0);
      R_LANE_MON_REPLACE_A_CNT_RO      : in std_logic_vector(
                           g_lane_count * g_stat_cnt_length - 1 downto 0);
      R_LANE_MON_UNKNOWN_K_CNT_RO      : in std_logic_vector(
                           g_lane_count * g_stat_cnt_length - 1 downto 0);
      R_LANE_MON_ILAS_ERR_CNT_RO       : in std_logic_vector(
                           g_lane_count * g_stat_cnt_length - 1 downto 0);


      ------
      -- Registers in AXI clock domain
      R_SW_GLOB_RST_WR  : out std_logic;


      -- AXI Lite slave interface for control purposes
      -- AXI Lite bus Slave interface
      S_AXI_ACLK        : in  std_logic;
      S_AXI_ARESETN     : in  std_logic;
      S_AXI_AWADDR      : in  std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0);
      S_AXI_AWPROT      : in  std_logic_vector(2 downto 0);
      S_AXI_AWVALID     : in  std_logic;
      S_AXI_AWREADY     : out std_logic;
      S_AXI_WDATA       : in  std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);
      S_AXI_WSTRB       : in std_logic_vector((C_S_AXI_DATA_WIDTH/8)-1 downto 0);
      S_AXI_WVALID      : in  std_logic;
      S_AXI_WREADY      : out std_logic;
      S_AXI_BRESP       : out std_logic_vector(1 downto 0);
      S_AXI_BVALID      : out std_logic;
      S_AXI_BREADY      : in  std_logic;
      S_AXI_ARADDR      : in  std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0);
      S_AXI_ARPROT      : in  std_logic_vector(2 downto 0);
      S_AXI_ARVALID     : in  std_logic;
      S_AXI_ARREADY     : out std_logic;
      S_AXI_RDATA       : out std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);
      S_AXI_RRESP       : out std_logic_vector(1 downto 0);
      S_AXI_RVALID      : out std_logic;
      S_AXI_RREADY      : in  std_logic
        );
end entity;




architecture synthesis of jesd204b_axi_reg is

   constant LC_MAGIC_VERSION_REG_ADDR  : integer := 0;
   constant LC_RESET_REG_ADDR          : integer := 1;

--------------------------------------------------------------------------------
   function sl(idx : integer; len : integer; vec : std_logic_vector) 
                                       return std_logic_vector is
      variable res : std_logic_vector(len - 1 downto 0);
   begin
      res := vec(len * (idx + 1) - 1 downto len * idx);
      return res;
   end function;
--------------------------------------------------------------------------------
   -- Signals for AXI state machines
   type read_fsm_type is (R_RESET, R_IDLE, R_READ, R_PRESENT);
   signal read_fsm_cur : read_fsm_type;

   signal axi_arready_i             : std_logic;
   signal axi_rvalid_i              : std_logic;
   signal axi_rresp_i               : std_logic_vector(S_AXI_RRESP'range);

   type write_fsm_type is (W_RESET, W_IDLE, W_DATA, W_HANDSHAKE, W_RESP);
   signal write_fsm_cur : write_fsm_type;

   signal axi_awready_i             : std_logic;
   signal axi_wready_i              : std_logic;
   signal axi_bvalid_i              : std_logic;
   signal write_data_latched        : std_logic;

   signal write_data                : std_logic_vector(S_AXI_WDATA'range);
   signal write_strb                : std_logic_vector(S_AXI_WSTRB'range);
   signal write_addr                : std_logic_vector(S_AXI_AWADDR'range);
   signal write_addr_axi            : std_logic_vector(S_AXI_AWADDR'range);

   signal reg_read_req              : std_logic := '0';
   signal reg_read_req_meta         : std_logic := '0';
   signal reg_read_req_sys          : std_logic := '0';
   signal reg_read_req_sys_d        : std_logic := '0';
   signal reg_read_req_sys_dd       : std_logic := '0';
   signal reg_read_req_sys_hold     : std_logic := '0';
   signal reg_read_ack              : std_logic := '0';
   signal reg_read_ack_meta         : std_logic := '0';

   signal reg_write_req             : std_logic := '0';
   signal reg_write_req_meta        : std_logic := '0';
   signal reg_write_req_sys         : std_logic := '0';
   signal reg_write_req_sys_d       : std_logic := '0';
   signal reg_write_ack             : std_logic := '0';
   signal reg_write_ack_meta        : std_logic := '0';
   signal axi_bresp_i               : std_logic_vector(S_AXI_BRESP'range);

   signal axi_rdata_i               : std_logic_vector(S_AXI_RDATA'range);
   signal read_addr                 : std_logic_vector(S_AXI_ARADDR'range);   
   signal reg_read_data             : std_logic_vector(S_AXI_RDATA'range);


   signal handshake_tmo_rd          : std_logic;
   signal handshake_tmo_wr          : std_logic;
   signal handshake_tmo             : std_logic;
   signal handshake_tmo_elapsed     : std_logic;
   signal handshake_tmo_cnt         : integer range 0 to g_axi_timeout - 1;


--------------------------------------------------------------------------------
   signal reg_sw_glob_rst                 : std_logic := '1';

   signal reg_ctrl_srst_wr                : std_logic := '1';
   signal reg_ctrl_srst_wr_dl             : std_logic_vector(3 downto 0) := (others => '1');
   signal reg_ctrl_monrst_wr              : std_logic;

   subtype lane_status_type is std_logic_vector(g_lane_count * 8 - 1 downto 0);

   signal reg_cfg_f_rw                 : std_logic_vector(7 downto 0);
   signal reg_cfg_k_rw                 : std_logic_vector(4 downto 0);
   signal reg_cfg_fxk_rw               : std_logic_vector(10 downto 0);
   signal reg_cfg_subclass_rw          : std_logic_vector(1 downto 0);
   signal reg_cfg_err_report_en_rw     : std_logic;
   signal reg_cfg_reinit_en_rw         : std_logic;
   signal reg_cfg_ilas_rw              : std_logic;
   signal reg_cfg_scrambling_en_rw     : std_logic;
   signal reg_cfg_sysref_delay_rw      : std_logic_vector(3 downto 0);
   signal reg_cfg_sysref_allresync_rw  : std_logic;
   signal reg_cfg_sysref_polarity_rw   : std_logic;   
   
   
   ------------------------------------------------------------------------   

   -- There are several CDCs, mark them properly for vivado
   attribute ASYNC_REG           : string;
   attribute ASYNC_REG of reg_read_req_meta     : signal is "true";
   attribute ASYNC_REG of reg_read_req_sys      : signal is "true";
   attribute ASYNC_REG of reg_read_ack_meta     : signal is "true";
   attribute ASYNC_REG of reg_read_ack          : signal is "true";
   attribute ASYNC_REG of reg_write_req_meta    : signal is "true";
   attribute ASYNC_REG of reg_write_req_sys     : signal is "true";
   attribute ASYNC_REG of reg_write_ack_meta    : signal is "true";
   attribute ASYNC_REG of reg_write_ack         : signal is "true";

   -- Add by Jakub Hadamek
   attribute ASYNC_REG of read_addr             : signal is "true";
   attribute ASYNC_REG of reg_read_data         : signal is "true";
   attribute ASYNC_REG of axi_rdata_i           : signal is "true";
   
   

begin

   assert C_S_AXI_DATA_WIDTH = 32 report "AXI data must be of 32 bit width" 
         severity error;
   assert C_S_AXI_ADDR_WIDTH = 32 report 
      "AXI addres must be at least 10 bit width" severity error;

--------------------------------------------------------------------------------
-- READ handling
--------------------------------------------------------------------------------
   -- Read state machine
   read_fsm_proc : process (S_AXI_ACLK)
   begin
      if rising_edge(S_AXI_ACLK) then
         if S_AXI_ARESETN = '0' then
            axi_arready_i <= '0';
            axi_rvalid_i <= '0';
            read_fsm_cur <= R_RESET;
            reg_read_req <= '0';
            handshake_tmo_rd <= '0';
         else
            case read_fsm_cur is
               when R_RESET =>
                  read_fsm_cur <= R_IDLE;
                  axi_arready_i <= '1';
               when R_IDLE =>
                  axi_rdata_i <= (others => '0');
                  if S_AXI_ARVALID = '1' then
                     -- There is a valid address on the bus, so catch it and read
                     -- Registers placed in AXI clock domain are handled here.
                     -- The other domain may have no clock.
                     case to_integer(unsigned(S_AXI_ARADDR(S_AXI_ARADDR'left 
                                                            downto 2))) is
                     when LC_MAGIC_VERSION_REG_ADDR =>
                        axi_rdata_i <= g_magic_revision_reg; -- Magic and rev.
                        axi_rvalid_i <= '1';
                        read_fsm_cur <= R_PRESENT;
                     when LC_RESET_REG_ADDR => 
                        axi_rdata_i(0) <= reg_sw_glob_rst;
                        axi_rvalid_i <= '1';
                        read_fsm_cur <= R_PRESENT;
                     when others =>
                        -- registers in the other domain are handled here
                        read_fsm_cur <= R_READ;
                        read_addr <= S_AXI_ARADDR;
                        reg_read_req <= not reg_read_req;
                        handshake_tmo_rd <= '1';
                     end case;
                     -- We are not ready for at least one cycle
                     axi_arready_i <= '0';
                  end if;                  
               when R_READ =>
                  if reg_read_ack = reg_read_req or 
                     handshake_tmo_elapsed = '1' then
                     if handshake_tmo_elapsed = '0' then
                        axi_rdata_i  <= reg_read_data; -- Crossing clocks
                     else
                        axi_rdata_i  <= (others => '1');
                     end if;
                     axi_rvalid_i <= '1';
                     read_fsm_cur <= R_PRESENT;
                     handshake_tmo_rd <= '0';
                  end if;
               when R_PRESENT =>
                  if S_AXI_RREADY = '1' then
                     -- Master consumed data, wait for another transaction
                     axi_arready_i <= '1';
                     axi_rvalid_i <= '0';
                     read_fsm_cur <= R_IDLE;
                  end if;
            end case;
         end if;
      end if;
   end process;
   axi_rresp_i <= (others => '0'); -- Read is always OK.

   -- Re-clock request to sys domain
   sys2axi_rdreq_proc : process(SYS_CLK)
   begin
      if rising_edge(SYS_CLK) then
         reg_read_req_meta <= reg_read_req;
         reg_read_req_sys <= reg_read_req_meta;
         reg_read_req_sys_d <= reg_read_req_sys;
         if reg_read_req_sys_hold = '0' then
            reg_read_req_sys_dd <=  reg_read_req_sys_d;
         end if;
      end if;
   end process;

   -- Re-clock ack back to axi domain
   axi2sys_rdreq_proc : process(S_AXI_ACLK)
   begin
      if rising_edge(S_AXI_ACLK) then
         reg_read_ack_meta <= reg_read_req_sys_dd;
         reg_read_ack <= reg_read_ack_meta;
      end if;
   end process;


   --Register sub-space in system clock domain
   read_data_mux_proc : process (SYS_CLK)
      variable reg_addr_v  : integer range 0 to 63;
      variable lane_addr_v : integer range 0 to g_lane_count;
   begin
      if rising_edge(SYS_CLK) then
         reg_read_req_sys_hold <= '0';
         
   
         if (reg_read_req_sys /= reg_read_req_sys_d) or 
            reg_read_req_sys_hold = '1' then

            reg_read_data <= (others => '0');
            -- We don't have to consider lowest two bits (strobes)
            reg_addr_v := to_integer(unsigned(read_addr(7 downto 2)));
            if unsigned(read_addr(read_addr'left  downto 8)) = 0 then
               -- Registers common for whole core
               case reg_addr_v is
                 -- when 0  => -- Magic and rev. handled in AXI domain
                  when 8  => reg_read_data <= 
                        (0 => reg_ctrl_srst_wr_dl(0),
                         others => '0');
   
                  when 9  => 
                        reg_read_data(23) <= reg_cfg_scrambling_en_rw;
                        reg_read_data(22) <= reg_cfg_ilas_rw;
                        reg_read_data(21) <= reg_cfg_reinit_en_rw;
                        reg_read_data(20) <= reg_cfg_err_report_en_rw;
                        reg_read_data(17 downto 16) <= reg_cfg_subclass_rw;
                        reg_read_data(12 downto 8) <= reg_cfg_k_rw;
                        reg_read_data(7 downto 0) <= reg_cfg_f_rw;
   
                  when 10 => 
                        reg_read_data(9) <= reg_cfg_sysref_polarity_rw;
                        reg_read_data(8) <= reg_cfg_sysref_allresync_rw;
                        reg_read_data(3 downto 0) <= reg_cfg_sysref_delay_rw;

                  when 11 => 
                        reg_read_data(25 downto 15) <= R_STATUS_SYNC_ERR_DIFF_RO;
                        reg_read_data(2) <= R_STATUS_SYNC_ERR_RO;
                        reg_read_data(1) <= R_STATUS_SYSREF_SEEN_RO;
                        reg_read_data(0) <= R_STATUS_SYNC_OK_RO;
                  when others =>
                     reg_read_data <= x"ffffffff";
               end case;
            else
               -- Registers specific for each lane
               lane_addr_v := to_integer(unsigned(
                                 read_addr(read_addr'left downto 8))) - 1;
               case reg_addr_v is
                  when 0 =>
                     reg_read_data(g_stat_cnt_length - 1 downto 0) <= 
                        sl(lane_addr_v, g_stat_cnt_length, 
                           R_LANE_STATUS_RO);
                  when 4 =>
                     reg_read_data <= sl(0, reg_read_data'length,
                           sl(lane_addr_v, R_LANE_ILAS_CFG_DATA_RO'length, 
                              R_LANE_ILAS_CFG_DATA_RO));
                  when 5 =>
                     reg_read_data <= sl(1, reg_read_data'length,
                           sl(lane_addr_v, R_LANE_ILAS_CFG_DATA_RO'length, 
                              R_LANE_ILAS_CFG_DATA_RO));
                  when 6 =>
                     reg_read_data <= sl(2, reg_read_data'length,
                           sl(lane_addr_v, R_LANE_ILAS_CFG_DATA_RO'length, 
                              R_LANE_ILAS_CFG_DATA_RO));
                  when 7 =>
                     reg_read_data <= sl(3, reg_read_data'length,
                           sl(lane_addr_v, R_LANE_ILAS_CFG_DATA_RO'length, 
                              R_LANE_ILAS_CFG_DATA_RO));                         
                  when 8 =>
                     reg_read_data(g_stat_cnt_length - 1 downto 0) <= 
                        sl(lane_addr_v, g_stat_cnt_length, 
                           R_LANE_MON_PHYS_ERR_CNT_RO);
                  when 9 =>
                     reg_read_data(g_stat_cnt_length - 1 downto 0) <= 
                        sl(lane_addr_v, g_stat_cnt_length, 
                           R_LANE_MON_PHYS_ERR_RES_CNT_RO);
                  when 10 =>
                     reg_read_data(g_stat_cnt_length - 1 downto 0) <= 
                        sl(lane_addr_v, g_stat_cnt_length, 
                           R_LANE_MON_ALIGN_F_ERR_CNT_RO);
                  when 11 =>
                     reg_read_data(g_stat_cnt_length - 1 downto 0) <= 
                        sl(lane_addr_v, g_stat_cnt_length, 
                           R_LANE_MON_REPLACE_F_CNT_RO);
                  when 12 =>
                     reg_read_data(g_stat_cnt_length - 1 downto 0) <= 
                        sl(lane_addr_v, g_stat_cnt_length, 
                           R_LANE_MON_REPLACE_A_CNT_RO);
                  when 13 =>
                     reg_read_data(g_stat_cnt_length - 1 downto 0) <= 
                        sl(lane_addr_v, g_stat_cnt_length, 
                           R_LANE_MON_UNKNOWN_K_CNT_RO);
                  when 14 =>
                     reg_read_data(g_stat_cnt_length - 1 downto 0) <= 
                        sl(lane_addr_v, g_stat_cnt_length, 
                           R_LANE_MON_ILAS_ERR_CNT_RO);
                  

                  when others =>
                     reg_read_data <= x"ffffffff";
               end case;
            end if;
         end if;
      end if; -- clk
   end process;


--------------------------------------------------------------------------------
-- WRITE handling
--------------------------------------------------------------------------------
   -- Write state machine
   write_fsm_proc : process (S_AXI_ACLK)
   begin
      if rising_edge(S_AXI_ACLK) then
         if S_AXI_ARESETN = '0' then
            axi_awready_i <= '0';
            axi_wready_i <= '0';
            axi_bvalid_i <= '0';
            handshake_tmo_wr <= '0';
            write_fsm_cur <= W_RESET;
            reg_write_req <= '0';

            -- AXI domain register initialization
            reg_sw_glob_rst <= '1';
         else

            case write_fsm_cur is
               when W_RESET => 
                  -- Make sure ready is always high in idle
                  write_fsm_cur <= W_IDLE;
                  axi_awready_i <= '1';
                  axi_wready_i <= '1';
                  write_data_latched <= '0';
               when W_IDLE =>
                  -- Handle address - we can actually update it always
                  write_addr <= S_AXI_AWADDR;
                  write_addr_axi <= S_AXI_AWADDR;
                  if S_AXI_AWVALID = '1' then
                     axi_awready_i <= '0';
                     if S_AXI_WVALID = '0' and write_data_latched = '0' then
                        -- We need to wait for data
                        write_fsm_cur <= W_DATA;
                     else
                        case to_integer(unsigned(S_AXI_AWADDR(S_AXI_AWADDR'left 
                                                            downto 2))) is
                        when LC_RESET_REG_ADDR =>
                           reg_sw_glob_rst <= S_AXI_WDATA(0);
                           axi_bvalid_i <= '1';
                           write_fsm_cur <= W_RESP;
                        when others =>
                           -- Data are already valid, so 
                           -- we can respond immediately
                           write_fsm_cur <= W_HANDSHAKE;
                           handshake_tmo_wr <= '1';
                        
                           -- Start real write to registers
                           reg_write_req <= not reg_write_req;

                        end case;
                     end if;
                  end if;

                  -- Latch data when valid
                  if S_AXI_WVALID = '1' then
                     write_data_latched <= '1';
                     write_data <= S_AXI_WDATA;
                     write_strb <= S_AXI_WSTRB;
                     axi_awready_i <= '0';
                  end if;
               when W_DATA =>
                  -- Latch data as soon as valid; address is already here
                  if S_AXI_WVALID = '1' then
                     case to_integer(unsigned(write_addr_axi(write_addr_axi'left 
                                                            downto 2))) is
                     when LC_RESET_REG_ADDR =>
                        reg_sw_glob_rst <= S_AXI_WDATA(0);
                        axi_bvalid_i <= '1';
                        write_fsm_cur <= W_RESP;
                     
                     when others =>
                        write_fsm_cur <= W_HANDSHAKE;
                         -- Start real write to registers by req inversion
                        reg_write_req <= not reg_write_req;
                        handshake_tmo_wr <= '1';
                        -- Latch data
                        write_data <= S_AXI_WDATA;
                        write_strb <= S_AXI_WSTRB;
                        axi_awready_i <= '0';
                     end case;
                  end if;
               when W_HANDSHAKE =>
                  if reg_write_ack = reg_write_req or 
                     handshake_tmo_elapsed = '1' then
                     handshake_tmo_wr <= '0';
                     axi_bvalid_i <= '1';
                     write_fsm_cur <= W_RESP;
                  end if;
               when W_RESP =>
                  if S_AXI_BREADY = '1' then
                     axi_bvalid_i <= '0';
                     axi_awready_i <= '1';
                     axi_awready_i <= '1';

                     write_fsm_cur <= W_IDLE;
                  end if;
                  -- This can be done always
                  write_data_latched <= '0';
            end case;
         end if;
      end if;
   end process;
   -- Response is always OK (regardless handshake timeout)
   axi_bresp_i <= (others => '0');
   
   -- Re-clock write request to system domain
   axi2sys_wrreq_proc : process (SYS_CLK)
   begin
      if rising_edge(SYS_CLK) then
         reg_write_req_meta <= reg_write_req;
         reg_write_req_sys <= reg_write_req_meta;
         reg_write_req_sys_d <= reg_write_req_sys;
      end if;
   end process;
   -- Re-clock write ack back to axi domain
   sys2axi_wrack_proc : process (S_AXI_ACLK)
   begin
      if rising_edge(S_AXI_ACLK) then
         reg_write_ack_meta <= reg_write_req_sys_d;
         reg_write_ack <= reg_write_ack_meta;
      end if;
   end process;


   -- Writable registers handling
   wr_handling_proc : process (SYS_CLK, SYS_RST)
      variable reg_cfg_fxk_rw_v : unsigned(reg_cfg_f_rw'length + 1 +
                                  reg_cfg_k_rw'length + 1 - 1 downto 0);
      variable f_v : unsigned(reg_cfg_f_rw'left + 1 downto 0);
      variable k_v : unsigned(reg_cfg_k_rw'left + 1 downto 0);
   begin
      if rising_edge(SYS_CLK) then
         if reg_ctrl_srst_wr = '1' then
            reg_ctrl_srst_wr_dl <= (others => '1');
         else
            reg_ctrl_srst_wr_dl <= '0' & 
                        reg_ctrl_srst_wr_dl(reg_ctrl_srst_wr_dl'left downto 1);
         end if;

         -- TODO: redoo this multiplication
         f_v := unsigned("0" & reg_cfg_f_rw) + 1;
         k_v := unsigned("0" & reg_cfg_k_rw) + 1;
         --print("reg_cfg_f_rw " & hstr(reg_cfg_f_rw));
         --print("reg_cfg_k_rw " & hstr(reg_cfg_k_rw));
         --print("f_v " & hstr(f_v));
         --print("k_v " & hstr(k_v));
         reg_cfg_fxk_rw_v := f_v * k_v;
         --print("reg_cfg_fxk_rw_v " & hstr(reg_cfg_fxk_rw_v));
         reg_cfg_fxk_rw <= std_logic_vector(
                              reg_cfg_fxk_rw_v(reg_cfg_fxk_rw'range));

         reg_ctrl_monrst_wr <= '0';

         if SYS_RST = '1' then
            reg_ctrl_srst_wr                <= '1';
            reg_ctrl_srst_wr_dl             <= (others => '1');

         elsif reg_write_req_sys /= reg_write_req_sys_d  then
            -- Don't decode 2 LSBs as they are in strobes
            case to_integer(unsigned(write_addr(write_addr'left downto 2))) is
               when 8  => 
                     reg_ctrl_monrst_wr        <= write_data(1);
                     reg_ctrl_srst_wr        <= write_data(0);
                                       
               when 9  =>
                     reg_cfg_scrambling_en_rw <= write_data(23);
                     reg_cfg_ilas_rw <= write_data(22);
                     reg_cfg_reinit_en_rw <= write_data(21);
                     reg_cfg_err_report_en_rw <= write_data(20);
                     reg_cfg_subclass_rw <= write_data(17 downto 16);
                     reg_cfg_k_rw <= write_data(12 downto 8);
                     reg_cfg_f_rw <= write_data(7 downto 0);
           
               when 10 => 
                     reg_cfg_sysref_polarity_rw <= write_data(9);
                     reg_cfg_sysref_allresync_rw <= write_data(8);
                     reg_cfg_sysref_delay_rw <= write_data(3 downto 0); 
               --when 11 => read only

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

   

   -- Axi handshake timeout counter - axi clock domain
   handshake_tmo <= handshake_tmo_wr or handshake_tmo_rd;
   axi_handshake_tmo_proc : process (S_AXI_ACLK)
   begin
      if rising_edge(S_AXI_ACLK) then
         if handshake_tmo = '0' then
            handshake_tmo_cnt <= g_axi_timeout - 1;
            handshake_tmo_elapsed <= '0';
         elsif handshake_tmo_cnt > 0 then
            handshake_tmo_cnt <= handshake_tmo_cnt - 1;
         else
            handshake_tmo_elapsed <= '1';
         end if;
      end if;
   end process;


   -- Outputs assignments
   -- AXI -----
   S_AXI_AWREADY  <= axi_arready_i;
   S_AXI_WREADY   <= axi_wready_i;
   S_AXI_BRESP    <= axi_bresp_i;
   S_AXI_BVALID   <= axi_bvalid_i;
   S_AXI_ARREADY  <= axi_arready_i;
   S_AXI_RDATA    <= axi_rdata_i;
   S_AXI_RRESP    <= axi_rresp_i;
   S_AXI_RVALID   <= axi_rvalid_i;
   -----------
   -- Registers
   R_SW_GLOB_RST_WR           <= reg_sw_glob_rst;
   R_CTRL_SRST_WR             <= reg_ctrl_srst_wr_dl(0);
   R_CTRL_MONRST_WR           <= reg_ctrl_monrst_wr;
   
   R_CFG_F_RW                 <= reg_cfg_f_rw;
   R_CFG_K_RW                 <= reg_cfg_k_rw;
   R_CFG_FxK_RW               <= reg_cfg_fxk_rw;
   R_CFG_SUBCLASS_RW          <= reg_cfg_subclass_rw;
   R_CFG_ERR_REPORT_EN_RW     <= reg_cfg_err_report_en_rw;
   R_CFG_REINIT_EN_RW         <= reg_cfg_reinit_en_rw;
   R_CFG_ILAS_RW              <= reg_cfg_ilas_rw;
   R_CFG_SCRAMBLING_EN_RW     <= reg_cfg_scrambling_en_rw;
   R_CFG_SYSREF_DELAY_RW      <= reg_cfg_sysref_delay_rw;
   R_CFG_SYSREF_ALLRESYNC_RW  <= reg_cfg_sysref_allresync_rw;
   R_CFG_SYSREF_POLARITY_RW   <= reg_cfg_sysref_polarity_rw;
   

end architecture;




























































