----------------------------------------------------------------------------------
-- Company: IJS 
-- Engineer: Gasper Kojek
-- 
-- Create Date:    16:41:45 02/25/2014 
-- Last edit:      12:35    03/04/2014
-- Design Name:    Register Access
-- Module Name:    reg_access - Behavioral 
-- Project Name:    
-- Target Devices:  Xilinx Virtex 5
-- Tool versions:   Xilinx ISE 14.7
-- Description: 
--
-- Dependencies:    ram_wrap.vhd , reg_bram.vhd , reg_PIO.vhd
--
-- Revision: 1.0
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
use IEEE.STD_LOGIC_ARITH.ALL;



entity reg_access is
generic( REG_WIDTH: integer := 64 );
port (
        clk                : in std_logic;
        rst                : in std_logic; --active high
        
    --For RX ENGINE
        data_w             : in std_logic_vector (63 downto 0);
        addr_w             : in std_logic_vector (4 downto 0);
        we_w               : in std_logic;
        addr_error_w       : out std_logic;
        busy_w             : out std_logic;
        
        
    --For TX ENGINE
        data_r             : out std_logic_vector (63 downto 0);
        addr_r             : in  std_logic_vector (4 downto 0);
        busy_tx            : out std_logic;
        addr_error_r       : out std_logic;
        re                 : in  std_logic; --read enable - is high when pcie reads data from registers
        
    --BRAM - Comp Core side
        bram_data_r        : out std_logic_vector (15 downto 0);
        bram_addr_r        : in  std_logic_vector (5 downto 0);
        bram_data_w        : in  std_logic_vector (15 downto 0);
        bram_addr_w        : in  std_logic_vector (5 downto 0);
        we_core            : in  std_logic;
        busy_r             : out std_logic;

        
    --registri
        --read
        work_read_base     : out std_logic_vector (REG_WIDTH-1 downto 0);
        work_read_count    : out std_logic_vector (REG_WIDTH-1 downto 0);
        read_pck_len       : in  std_logic_vector (9 downto 0);
        read_f             : out std_logic; --is high while read is not finished(read count is not zero)
        read_done          : in  std_logic; --is high for 1 clock when a packet is read
        
        --write
        work_write_base    : out std_logic_vector (REG_WIDTH-1 downto 0);
        work_write_count   : out std_logic_vector (REG_WIDTH-1 downto 0);
        write_pck_len      : in  std_logic_vector (9 downto 0);
        write_f            : out std_logic;
        write_done         : in  std_logic;
        no_more_data       : in  std_logic;
        
        --settings
        freq_measure       : out std_logic;
        adaptive           : out std_logic;
            --interrupt masks
        int_no_data        : out std_logic;
        int_write_finished : out std_logic;
        int_read_finished  : out std_logic
        
        
);
end reg_access;

architecture rtl of reg_access is

--BAR0 register addresses
constant reg_src_addr   : std_logic_vector (4 downto 0) := "10000";
constant reg_wsrc_addr  : std_logic_vector (4 downto 0) := "10001";
constant reg_src_rdcnt  : std_logic_vector (4 downto 0) := "10010";
constant reg_wsrc_rdcnt : std_logic_vector (4 downto 0) := "10011";
constant reg_dst_addr   : std_logic_vector (4 downto 0) := "10100";
constant reg_wdst_addr  : std_logic_vector (4 downto 0) := "10101";
constant reg_dst_wrcnt  : std_logic_vector (4 downto 0) := "10110";
constant reg_wdst_wrcnt : std_logic_vector (4 downto 0) := "10111";
constant reg_comp_len   : std_logic_vector (4 downto 0) := "11000";
constant reg_status     : std_logic_vector (4 downto 0) := "11001";

--location of flags in the flag vector
constant read_mask           : integer :=0;
constant write_mask          : integer :=1;
constant read_finished_mask  : integer :=2;
constant write_finished_mask : integer :=3;
constant no_more_data_mask   : integer :=4;
constant freq_meas_mask      : integer :=5;
constant adaptive_mask       : integer :=6;

component reg_PIO is
generic (width: integer :=15);
    port (
        clk : in std_logic;
        we  : in std_logic;                                --write enable(active high)
        D   : in std_logic_vector (width downto 0);        --data in
        Q   : out std_logic_vector (width downto 0);       --data out
        rst : in std_logic
        );
end component;

component ram_wrap is
    port (
        clk         : in std_logic;
        
        --Primary port :  (pcie)
        addr_wr1    : in std_logic_vector (3 downto 0);              --4 bit write address
        write_en1   : in std_logic;                                  --write enable (primary) - active high
        write_data1 : in std_logic_vector (63 downto 0);             --64 bit write data
        addr_rd1    : in std_logic_vector (3 downto 0);              --4 bit read address
        out1        : out std_logic_vector (63 downto 0);            --64 bit read data   
        busy1       : out std_logic;                                 --read bram is busy (output data not valid)
        
        --Dual port :   (iz kompresor core) 
        addr_wr2    : in std_logic_vector (5 downto 0);              --6 bit write address - (5 downto 2) goes to BRAM, (1 downto 0) chooses which 16 bits are chosen from BRAM address 
        write_en2   : in std_logic;                                  --write enable (dual) - active high
        write_data2 : in std_logic_vector (15 downto 0);             --16 bit write data from compressor core
        addr_rd2    : in std_logic_vector (5 downto 0);              --6 bit read address - (5 downto 2) goes to BRAM, (1 downto 0) chooses which 16 bits are chosen from BRAM address
        out2        : out std_logic_vector (15 downto 0);            --16 bit read data output for compressor core
        busy2       : out std_logic                                  --read bram is busy (output data not valid)
        );
end component;

--za zapis v registre
signal we_sig_vec                   : std_logic_vector (9 downto 0) := (others=>'0');

--za pcie
signal bram_data_w2, bram_data_r2   : std_logic_vector (63 downto 0);
signal bram_addr_w2, bram_addr_r2   : std_logic_vector (3 downto 0);
signal bram_pcie_we, bram_pcie_busy : std_logic := '0';

--source address:
signal src_addr_wr, src_addr_rd     : std_logic_vector (REG_WIDTH-1 downto 0);
signal src_addr_we                  : std_logic := '0';

--work source address:
signal wsrc_addr_wr, wsrc_addr_rd   : std_logic_vector (REG_WIDTH-1 downto 0);
signal wsrc_addr_we                 : std_logic :='0';

--source read count:
signal src_rdcnt_wr, src_rdcnt_rd   : std_logic_vector (REG_WIDTH-1 downto 0);
signal src_rdcnt_we                 : std_logic := '0';

--work source read count:
signal wsrc_rdcnt_wr, wsrc_rdcnt_rd : std_logic_vector (REG_WIDTH-1 downto 0);
signal wsrc_rdcnt_we                : std_logic := '0';

--destination address:
signal dst_addr_wr, dst_addr_rd     : std_logic_vector (REG_WIDTH-1 downto 0);
signal dst_addr_we                  : std_logic := '0';

--work destination address:
signal wdst_addr_wr, wdst_addr_rd   : std_logic_vector (REG_WIDTH-1 downto 0);
signal wdst_addr_we                 : std_logic := '0';

--destination write count:
signal dst_wrcnt_wr, dst_wrcnt_rd   : std_logic_vector (REG_WIDTH-1 downto 0);
signal dst_wrcnt_we                 : std_logic := '0';

--work destination write count:
signal wdst_wrcnt_wr, wdst_wrcnt_rd : std_logic_vector (REG_WIDTH-1 downto 0);
signal wdst_wrcnt_we                : std_logic := '0';

--completed length
signal comp_len_wr, comp_len_rd     : std_logic_vector (REG_WIDTH-1 downto 0);
signal comp_len_we                  : std_logic := '0';

--flags/status/interrupt mask:
signal flag_vec                     : std_logic_vector (REG_WIDTH-1 downto 0) := (others=>'0'); --flag/status/interrupt mask vector

--internal flags/signals:
signal read_fin_internal            : std_logic := '0';
signal wsrc_rdcnt_nonzero           : std_logic;
signal wdst_wrcnt_nonzero           : std_logic;
signal src_rdcnt_nonzero            : std_logic;
signal dst_wrcnt_nonzero            : std_logic;
signal status_read                  : std_logic;
signal status_write                 : std_logic;
signal addr_r_sig, addr_r_delayed   : std_logic_vector (4 downto 0);
signal register_hit                 : std_logic;

-- NULL signal
signal NULL_DW : std_logic_vector (63 downto 0) := (others=>'0');

begin

--BRAM - symbol frequencies holder
frekv: ram_wrap port map (
            clk         => clk,
        --Primary port :  (pcie)            
            addr_wr1    => bram_addr_w2, 
            write_en1   => bram_pcie_we,
            write_data1 => bram_data_w2,
            addr_rd1    => bram_addr_r2,     
            out1        => bram_data_r2,
            busy1       => bram_pcie_busy,
            
        --Dual port :   (kompresor core)
            addr_wr2    => bram_addr_w,
            write_en2   => we_core,
            write_data2 => bram_data_w,               
            addr_rd2    => bram_addr_r,
            out2        => bram_data_r,  
            busy2       => busy_r
            );

source_addr:            reg_PIO generic map (width => REG_WIDTH-1) port map (clk, src_addr_we,   src_addr_wr,   src_addr_rd,rst);
work_source_addr:       reg_PIO generic map (width => REG_WIDTH-1) port map (clk, wsrc_addr_we,  wsrc_addr_wr,  wsrc_addr_rd,rst);
source_read_count:      reg_PIO generic map (width => REG_WIDTH-1) port map (clk, src_rdcnt_we,  src_rdcnt_wr,  src_rdcnt_rd,rst);
source_work_read_count: reg_PIO generic map (width => REG_WIDTH-1) port map (clk, wsrc_rdcnt_we, wsrc_rdcnt_wr, wsrc_rdcnt_rd,rst);
dest_addr:              reg_PIO generic map (width => REG_WIDTH-1) port map (clk, dst_addr_we,   dst_addr_wr,   dst_addr_rd,rst);
work_dest_addr:         reg_PIO generic map (width => REG_WIDTH-1) port map (clk, wdst_addr_we,  wdst_addr_wr,  wdst_addr_rd,rst);
dest_write_count:       reg_PIO generic map (width => REG_WIDTH-1) port map (clk, dst_wrcnt_we,  dst_wrcnt_wr,  dst_wrcnt_rd,rst);
work_dest_write_count:  reg_PIO generic map (width => REG_WIDTH-1) port map (clk, wdst_wrcnt_we, wdst_wrcnt_wr, wdst_wrcnt_rd,rst);
compl_len:              reg_PIO generic map (width => REG_WIDTH-1) port map (clk, comp_len_we,   comp_len_wr,   comp_len_rd,rst);

busy_w <=   we_core when addr_w(4)='0' else
            read_done   when addr_w=reg_src_addr  else
            read_done  when addr_w=reg_src_rdcnt else
            write_done   when addr_w=reg_dst_addr  else
            write_done  when addr_w=reg_dst_wrcnt else
            '0';

--core side connections
work_read_base   <= wsrc_addr_rd;
work_read_count  <= wsrc_rdcnt_rd;
work_write_base  <= wdst_addr_rd;
work_write_count <= wdst_wrcnt_rd;
bram_addr_w2     <= addr_w(3 downto 0);
bram_data_w2     <= data_w;
write_f          <= flag_vec (write_mask);
read_f           <= flag_vec (read_mask);
freq_measure     <= flag_vec (freq_meas_mask);
adaptive         <= flag_vec (adaptive_mask);

--work registers control:
wsrc_addr_wr  <= (wsrc_addr_rd+read_pck_len)   when (flag_vec(read_mask)='1')  else src_addr_wr;
wsrc_rdcnt_wr <= (wsrc_rdcnt_rd-read_pck_len)  when (flag_vec(read_mask)='1')  else src_rdcnt_wr;
wdst_addr_wr  <= (wdst_addr_rd+write_pck_len)  when (flag_vec(write_mask)='1') else dst_addr_wr;
wdst_wrcnt_wr <= (wdst_wrcnt_rd-write_pck_len) when (flag_vec(write_mask)='1') else dst_wrcnt_wr;
comp_len_wr   <= (comp_len_rd+write_pck_len)   when (flag_vec(write_mask)='1') else (others=>'0');
-- work write enable
wsrc_addr_we  <= read_done  or (we_w and we_sig_vec(1));
wsrc_rdcnt_we <= read_done  or (we_w and we_sig_vec(1));
wdst_addr_we  <= write_done or (we_w and we_sig_vec(3));
wdst_wrcnt_we <= write_done or (we_w and we_sig_vec(3));
comp_len_we   <= write_done or (we_w and we_sig_vec(3));
--non work write enable
src_addr_we   <= we_sig_vec(0);
src_rdcnt_we  <= we_sig_vec(1);
dst_addr_we   <= we_sig_vec(2);
dst_wrcnt_we  <= we_sig_vec(3);

--PCIe receive side:
--RX SIDE:
bram_pcie_we   <= we_w and not addr_w(4) and not rst; 
we_sig_vec <=   (others=>'0')          when rst='1'              else
                (0=>we_w, others=>'0') when addr_w=reg_src_addr  else
                (1=>we_w, others=>'0') when addr_w=reg_src_rdcnt else
                (2=>we_w, others=>'0') when addr_w=reg_dst_addr  else
                (3=>we_w, others=>'0') when addr_w=reg_dst_wrcnt else
                (others=>'0');                
src_addr_wr    <= data_w when addr_w=reg_src_addr  else src_addr_rd; 
src_rdcnt_wr   <= data_w when addr_w=reg_src_rdcnt else src_rdcnt_rd; 
dst_addr_wr    <= data_w when addr_w=reg_dst_addr  else dst_addr_rd; 
dst_wrcnt_wr   <= data_w when addr_w=reg_dst_wrcnt else dst_wrcnt_rd;           

register_hit <= '1' when (addr_w=reg_src_addr or addr_w=reg_src_rdcnt or addr_w=reg_dst_addr or addr_w=reg_dst_wrcnt or addr_w=reg_status) else '0';
addr_error_w <= '0' when register_hit='1' else addr_w(4);



delayed_address: process (clk, addr_r)  --for synchronising BRAM and register read delay
begin
    if rising_edge(clk) then
        addr_r_delayed <= addr_r;
    end if;
end process;

addr_r_sig <= addr_r when addr_r(4)='0' else addr_r_delayed;

-- PCIe transmit side
--TX SIDE:
bram_addr_r2 <= addr_r_sig(3 downto 0);
data_r       <= bram_data_r2    when addr_r_sig(4) = '0'         else
                src_addr_rd     when addr_r_sig = reg_src_addr   else
                wsrc_addr_rd    when addr_r_sig = reg_wsrc_addr  else
                src_rdcnt_rd    when addr_r_sig = reg_src_rdcnt  else
                wsrc_rdcnt_rd   when addr_r_sig = reg_wsrc_rdcnt else
                dst_addr_rd     when addr_r_sig = reg_dst_addr   else
                wdst_addr_rd    when addr_r_sig = reg_wdst_addr  else
                dst_wrcnt_rd    when addr_r_sig = reg_dst_wrcnt  else
                wdst_wrcnt_rd   when addr_r_sig = reg_wdst_wrcnt else
                comp_len_rd     when addr_r_sig = reg_comp_len   else
                flag_vec        when addr_r_sig = reg_status     else
                NULL_DW;

busy_tx     <=  bram_pcie_we    when addr_r_sig(4) = '0'         else --FIXME a bi mogl bit to en clock zamaknjeno?
                src_addr_we     when addr_r_sig = reg_src_addr   else
                wsrc_addr_we    when addr_r_sig = reg_wsrc_addr  else
                src_rdcnt_we    when addr_r_sig = reg_src_rdcnt  else
                wsrc_addr_we    when addr_r_sig = reg_wsrc_rdcnt else
                dst_addr_we     when addr_r_sig = reg_dst_addr   else
                wdst_addr_we    when addr_r_sig = reg_wdst_addr  else
                dst_wrcnt_we    when addr_r_sig = reg_dst_wrcnt  else
                wdst_wrcnt_we   when addr_r_sig = reg_wdst_wrcnt else
                comp_len_we     when addr_r_sig = reg_comp_len   else
                '0';
            
addr_error_r <= '1' when addr_r>x"19" else '0';
                
-- interrupt masks are in upper 32 bits (same spot as trigger)
int_no_data        <= flag_vec (no_more_data_mask+32);
int_write_finished <= flag_vec (write_finished_mask+32);
int_read_finished  <= flag_vec (read_finished_mask+32);

wsrc_rdcnt_nonzero <= '1'  when (wsrc_rdcnt_rd>0) else '0';
wdst_wrcnt_nonzero <= '1'  when (wdst_wrcnt_rd>0) else '0';
src_rdcnt_nonzero  <= '1'  when (src_rdcnt_rd>0)  else '0';
dst_wrcnt_nonzero  <= '1'  when (dst_wrcnt_rd>0)  else '0';
status_read        <= re   when (addr_r_sig="11001") else '0';
status_write       <= we_w when (addr_w="11001")  else '0';

flag_ctrl:process (clk, rst)
begin
    if rst='1' then
        flag_vec(write_mask) <= '0';
        flag_vec(read_mask)  <= '0';
        read_fin_internal    <= '0';
        flag_vec(no_more_data_mask)   <= '0';
        flag_vec(write_finished_mask) <= '0';
        flag_vec(read_finished_mask)  <= '0';
        flag_vec(freq_meas_mask) <= '0';
        flag_vec(adaptive_mask)  <= '0';
    elsif rising_edge(clk) then
        flag_vec(read_mask)  <= wsrc_rdcnt_nonzero and not flag_vec(read_finished_mask);
        flag_vec(write_mask) <= wdst_wrcnt_nonzero and read_fin_internal and not flag_vec(write_finished_mask);
        flag_vec(no_more_data_mask) <= no_more_data;

        if status_read='1' then
            flag_vec(read_finished_mask)  <= '0';
            flag_vec(write_finished_mask) <= '0';
        else 
            if src_rdcnt_nonzero='1' and wsrc_rdcnt_nonzero='0' then
                flag_vec(read_finished_mask) <= '1';
            end if;
            if dst_wrcnt_nonzero='1' and wdst_wrcnt_nonzero='0' then
                flag_vec(write_finished_mask) <= '1';
            end if;
        end if;
        
        if status_write='1' then
            flag_vec(freq_meas_mask) <= data_w(freq_meas_mask);
            flag_vec(adaptive_mask)  <= data_w(adaptive_mask);
            flag_vec(no_more_data_mask+32)   <= data_w(no_more_data_mask+32);
            flag_vec(read_finished_mask+32)  <= data_w(read_finished_mask+32);
            flag_vec(write_finished_mask+32) <= data_w(write_finished_mask+32);
        end if;
        
        if ( flag_vec(write_finished_mask)='1' ) then
            read_fin_internal <= '0'; 
        elsif (wsrc_rdcnt_rd=0 and src_rdcnt_rd>0 and flag_vec(read_mask)='1') then 
            read_fin_internal <= '1'; 
        end if;
        
    end if;
end process;

end rtl;

