----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    16:41:45 02/25/2014 
-- Design Name: 
-- Module Name:    reg_access - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all;
use IEEE.STD_LOGIC_ARITH.ALL;


-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity reg_access is
generic(	REG_WIDTH:integer:=64
			);
port (
		clk		: in std_logic;
		rst		: in std_logic;
		
	--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;
		
		
	--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 - da vemo kdaj smo dejansko prebral - zarad clear on read flagov
		
	--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;
		busy_w			: 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; --je na 1 dokler mora trajati read(dokler ne preberemo vseh paketkov)
		read_done			: in std_logic; --postavi na 1 za en clock ko se prebere en paketek
		
				--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;
		read_start			: out std_logic;		--Ko zapiemo Source read count
		write_start			: out std_logic; 		--Ko zapiemo Destination read count
		
			--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

--naslovi pod katerimi najdemo registre v BAR0
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";

--kje so posamezne zastavice v 'registru' zastavic
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;											--we=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)			                             opisi klele spodej so zamenjani:
			addr_wr1 		: in std_logic_vector(3 downto 0);				--6 bitni addres iz kompresorja (5 downto 2)-tapravi address za v ram;(1 downto 0) dejansko kater blok 16 bitov rabimo
			write_en1 		: in std_logic;										--write enable (primary) - kompresor core
			write_data1 	: in std_logic_vector(63 downto 0);				--16 data input iz kompresor core-a
			addr_rd1 		: in std_logic_vector(3 downto 0);
			out1 				: out std_logic_vector(63 downto 0);			--16 bitni out do kompresor core-a	
			busy1				: out std_logic;
			
			--Dual port :   (iz kompresor core)	
			addr_wr2 		: in std_logic_vector(5 downto 0);				--4 bitni addres iz pcie 
			write_en2		: in std_logic;										--write enable (dual) - pcie
			write_data2 	: in std_logic_vector(15 downto 0);				--64 data input iz pcie
			addr_rd2 		: in std_logic_vector(5 downto 0);	
			out2 				: out std_logic_vector(15 downto 0); 			--64 bitni out do pcie
			busy2				: out std_logic
);
end component;

-- start signals
signal read_start_i : std_logic:='0';
signal write_start_i: std_logic:='0';

--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 (compressor length)
signal comp_len_wr, comp_len_rd	: std_logic_vector(REG_WIDTH-1 downto 0);
signal comp_len_we	:	std_logic:='0';

--flags:
signal flag_vec	: std_logic_vector(REG_WIDTH-1 downto 0) := (others=>'0'); --vektor z vsemi zastavicami-za jih prebrat)
signal read_flag,write_flag :std_logic:='0';   -- Read-only
signal read_finished_flag, write_finished_flag : std_logic:='0'; -- Read-only, clear on read
signal no_more_data_flag:std_logic:='0';
signal freq_meas_flag, adaptive_flag : std_logic:='0'; 	-- R/W
--signal int_no_data_flag, int_write_finished_flag, int_read_finished_flag : std_logic;

--interrupt mask of flags:
signal int_no_data_mask 			: std_logic;
signal int_write_finished_mask 	: std_logic;
signal int_read_finished_mask 	: std_logic;

--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 NULL_DW : std_logic_vector(63 downto 0) := (others=>'0');

begin

--BRAM kjer imamo zapisane(in kamor zapisujemo) frekvence simbolov
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

	);
--naslov vira podatkov
source_addr: reg_PIO generic map (width => REG_WIDTH-1) port map (clk,src_addr_we,src_addr_wr, src_addr_rd,rst);
--delovni naslov vira podatkov
work_source_addr: reg_PIO generic map (width => REG_WIDTH-1) port map (clk,wsrc_addr_we,wsrc_addr_wr, wsrc_addr_rd,rst);
--koliko podatkov za prebrat iz vira (vse skupaj)
source_read_count: reg_PIO generic map (width => REG_WIDTH-1) port map (clk,src_rdcnt_we,src_rdcnt_wr, src_rdcnt_rd,rst);
--koliko podatkov imamo se za prebrat iz vira (koliko nam jih se ostane)
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);
--destination address:
dest_addr: reg_PIO generic map (width => REG_WIDTH-1) port map (clk,dst_addr_we,dst_addr_wr, dst_addr_rd,rst);
--work destination address:
work_dest_addr: reg_PIO generic map (width => REG_WIDTH-1) port map (clk,wdst_addr_we,wdst_addr_wr, wdst_addr_rd,rst);
--destination write count:
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 destination write count:
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);
--completed length (compressed length)
compl_len: reg_PIO generic map (width => REG_WIDTH-1) port map (clk,comp_len_we,comp_len_wr, comp_len_rd,rst);


--za pisat work registre:
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');

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 enabli
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);

--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;
--bram_pcie_we<=we_w and not addr_w(4);

RX_SIDE: process(clk,rst, we_w, addr_w) 
begin
if rst='1' then
	bram_pcie_we<='0';
	we_sig_vec<=(others=>'0');
else
	bram_pcie_we<=we_w and not addr_w(4);
	if rising_edge(clk) then
		if (addr_w>x"19") then
			addr_error_w<='1';
		elsif we_w='1' then
			--registers
			case ( addr_w ) is
				when reg_src_addr=>		--source_address
					src_addr_wr<=data_w;
					we_sig_vec<=(0=>we_w, others=>'0');
					addr_error_w<='0';
				when reg_src_rdcnt=>		--source read count
					src_rdcnt_wr<=data_w;
					we_sig_vec<=(1=>we_w, others=>'0');
					addr_error_w<='0';
				when reg_dst_addr=>		--destination address
					dst_addr_wr<=data_w;
					we_sig_vec<=(2=>we_w, others=>'0');	
					addr_error_w<='0';
				when reg_dst_wrcnt=>		--destination write count
					dst_wrcnt_wr<=data_w;
					we_sig_vec<=(3=>we_w, others=>'0');
					addr_error_w<='0';
				when reg_status=>		--status register -delamo v posebej procesih
					addr_error_w<='0';
				when others=>					--error
					addr_error_w<=addr_w(4);
					we_sig_vec<=(others=>'0');	
			end case;
		else 
			we_sig_vec<=(others=>'0');
			addr_error_w<='0';
		end if;
	end if;
end if;
end process;

delayed_address: process (clk, addr_r)
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;

--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
			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';
			

--TX_SIDE: process(clk)
--begin
--if rising_edge(clk) then
--	if (addr_r>x"19") then
--		addr_error_r<='1';
--	elsif ( addr_r(4) = '0' ) then		--BRAM
--		addr_error_r<='0';
--		bram_addr_r2<=addr_r(3 downto 0);
--		data_r<=bram_data_r2;
--		busy_tx<=bram_pcie_we;
--	else										--registers
--		addr_error_r<='0';
--		case ( addr_r ) is
--			when reg_src_addr=>		--source_address
--				data_r<=src_addr_rd;
--				busy_tx<=src_addr_we;
--			when reg_wsrc_addr=>		--work source address
--				data_r<=wsrc_addr_rd;
--				busy_tx<=wsrc_addr_we;
--			when reg_src_rdcnt=>		--source read count
--				data_r<=src_rdcnt_rd;
--				busy_tx<=src_rdcnt_we;
--			when reg_wsrc_rdcnt=>		--work source read count
--				data_r<=wsrc_rdcnt_rd;
--				busy_tx<=wsrc_rdcnt_we;
--			when reg_dst_addr=>		--destination address
--				data_r<=dst_addr_rd;
--				busy_tx<=dst_addr_we;
--			when reg_wdst_addr=>		--work destination address
--				data_r<=wdst_addr_rd;
--				busy_tx<=wdst_addr_we;
--			when reg_dst_wrcnt=>		--destination write count
--				data_r<=dst_wrcnt_rd;
--				busy_tx<=dst_wrcnt_we;
--			when reg_wdst_wrcnt=>		--work destination write count
--				data_r<=wdst_wrcnt_rd;
--				busy_tx<=wdst_wrcnt_we;
--			when reg_comp_len=>		--completed length (compressor length)
--				data_r<=comp_len_rd;
--				busy_tx<=comp_len_we;
--			when reg_status=>		--status register --zastavice brisemo v posebej procesih
--				data_r<=flag_vec;	
--			when others=>		--error
--		end case;
--	end if;
--end if;
--end process;

--flag connections
--flag_vec<=(	read_mask				=> read_flag,
--				write_mask				=> write_flag,
--				read_finished_mask	=> read_finished_flag,
--				write_finished_mask	=> write_finished_flag,
--				no_more_data_mask		=> no_more_data_flag,
--				freq_meas_mask			=> freq_meas_flag,
--				adaptive_mask			=> adaptive_flag,
--				others					=> '0');
				
write_f<=flag_vec(write_mask);
read_f<=flag_vec(read_mask);
freq_measure<=flag_vec(freq_meas_mask);
adaptive<=flag_vec(adaptive_mask);
-- maske za interrupte so v zgornjih 32 bitih flag_vec - istolene z samimi biti za katere nastavljamo ali proijo interrupte
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';

zastavice: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;
--		flag_vec(read_finished_mask) <= src_rdcnt_nonzero and not wsrc_rdcnt_nonzero and not status_read;
--		flag_vec(write_finished_mask) <= dst_wrcnt_nonzero and not wdst_wrcnt_nonzero and not status_read;
		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;

--zastavice:process (clk, rst)
--begin
--if rst='1' then
--	write_flag <= '0';
--	read_flag <= '0';
--	read_fin_internal <= '0';
--	no_more_data_flag <= '0';
--	write_finished_flag <= '0';
--	read_finished_flag <= '0';
--	freq_meas_flag <= '0';
--	adaptive_flag <= '0';
--elsif rising_edge(clk) then
--		read_flag <= wsrc_rdcnt_nonzero;
--		write_flag <= wdst_wrcnt_nonzero;
--		no_more_data_flag <= no_more_data;
--		read_finished_flag <= src_rdcnt_nonzero and not wsrc_rdcnt_nonzero and not status_read;
--		write_finished_flag <= dst_wrcnt_nonzero and not wdst_wrcnt_nonzero and not status_read;
--		
--		if status_write='1' then
--			freq_meas_flag <= data_w(freq_meas_mask);
--			adaptive_flag <= data_w(adaptive_mask);	
--		end if;
--		
--		if (wsrc_rdcnt_rd=0 and src_rdcnt_rd>0) then 
--			read_fin_internal <= '1'; 
--		elsif ( write_finished_flag='1' ) then
--			read_fin_internal <= '0'; 
--		end if;
--		
--end if;
--end process;

end rtl;

