----------------------------------------------
-- MSS copyright 2015-2016
-- Filename: MATCHED_FILTERNx1.VHD
-- Authors: 
--		Alain Zarembowitch / MSS
-- Inheritance: MATCHED_FILTER32x2.VHD 4/2/15
-- Last edit date: 2/12/16
-- Revision: 4
--
-- Description: Detects a match for a SYNC_LENGTH-bit unique word.
-- The complex input is sampled at 1x symbol rate and hard-quantized (1-bit quantization
-- on the I and Q channel each).
-- 1-bit quantization makes this matched filter independent of input signal amplitude.
-- latency depends on SYNC_LENGTH and NSAMPLES_PER_BIT:
-- When SYNC_LENGTH=32:  (NSAMPLES_PER_BIT+3)* SAMPLES + 1 CLK
-- Maximum Frequency: 138.588MHz Spartan-6 L1
--
-- Rev 1 6/17/15 AZ
-- Added the FLIPPED feature to synchronize when reading forward or backwards.
-- 
-- Rev 2 9/20/15 AZ
-- Block spurious DETECT output at start
--
-- Rev 3 10/7/15 AZ
-- Added flexibility in selecting the sync word at runtime
--
-- Rev 4 2/12/16 AZ
-- Extended applicability to x1, x2, x4, etc input samples/bit
-- Correction MATCH(I) (one CLK off)
----------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;    
use IEEE.numeric_std.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity MATCHED_FILTERNx1 is
	generic (
		SYNC_LENGTH: integer range 16 to 128 := 32;
			-- sync word length, expressed in number of symbols.
			-- Typical value 32 for a 2048 symbol frame (1.5% overhead)
			-- MUST BE AN INTEGER MULTIPLE OF 16
			-- CURRENT CODE ONLY WORKS FOR 16,32,64,128. Minor mods needed for other multiples of 16
		SYNC_WORD: std_logic_vector(127 downto 0) := x"5A0FBE66000000000000000000000000";
			-- sync pattern. LEFT-aligned when SYNC_LENGTH is less than the maximum 128 bits.
			-- MSb is sent first.
		NSAMPLES_PER_BIT: integer := 1
			-- 
	);
    port (
	 	--// CLOCKS
		CLK: in std_logic;	-- reference clock, synchronous design

		--// INPUT DATA STREAM
		DATA_IN: in std_logic;     
			-- Input waveform, 1-bit quantization, 1x oversampling
			-- Read DATA_IN at rising edge of CLK when SAMPLE_CLK_IN = '1'
		SAMPLE_CLK_IN: in std_logic;
			-- nominally NSAMPLES_PER_BITx symbol rate.
			-- maximum: 1 samples/clk
			
		--// CONTROLS
		FLIPPED: in std_logic;
			-- '1' when SYNC_WORD is flipped (for example when reading backwards, in reverse time)

		--// OUTPUTS
		DETECT: out std_logic := '0';
			-- 1 sample wide pulse when more than 90% bits match. Wider to ensure alignment with confirmation circuit.
			-- Last bit of the input sync word + 4,5,6 SAMPLES + 1 CLK latency
		MF_OUT: out std_logic_vector(7 downto 0) := (others => '0');
			-- match filter output. Range 0 (perfect match) to SYNC_LENGTH (perfect match inverted)
			-- latency depends on SYNC_LENGTH and NSAMPLES_PER_BIT
			-- When SYNC_LENGTH=32:  (NSAMPLES_PER_BIT+3)* SAMPLES + 1 CLK
		MF_OUT_READY: out std_logic := '0';
			-- 1 CLK pulse when MF_OUT is ready
		SIGN_OUT: out std_logic := '0'
			-- '0' if aligned with SYNC_WORD, '1' if aligned with inverted SYNC_WORD
			-- read when MF_OUT_READY = '1'
			
	);
end entity;
  
architecture Behavioral of MATCHED_FILTERNx1 is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
component PC_16 is
	port (
		A: in std_logic_vector(15 downto 0);	-- input A
		O: out std_logic_vector(4 downto 0)	-- added value
	);
end component;

-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
signal SHIFT_REG: std_logic_vector((SYNC_LENGTH*NSAMPLES_PER_BIT - 1) downto 0) := (others => '0');
signal MATCH: std_logic_vector((SYNC_LENGTH - 1) downto 0) := (others => '0');
constant NMF: integer := (SYNC_LENGTH/16);	-- SYNC_LENGTH MUST BE AN INTEGER MULTIPLE OF 16
type SLV16xNMFtype is array (integer range 0 to (NMF-1)) of std_logic_vector(15 downto 0);
signal MATCH16: SLV16xNMFtype:= (others => (others => '0'));
type SLV5xNMFtype is array (integer range 0 to (NMF-1)) of std_logic_vector(4 downto 0);
signal PARTIAL_CORRELATION: SLV5xNMFtype:= (others => (others => '0'));
signal PARTIAL_CORRELATION_D: SLV5xNMFtype:= (others => (others => '0'));
signal PARTIAL_CORRELATION2_0: std_logic_vector(5 downto 0) := (others => '0');
signal PARTIAL_CORRELATION2_1: std_logic_vector(5 downto 0) := (others => '0');
signal PARTIAL_CORRELATION2_2: std_logic_vector(5 downto 0) := (others => '0');
signal PARTIAL_CORRELATION2_3: std_logic_vector(5 downto 0) := (others => '0');
signal PARTIAL_CORRELATION3_0: std_logic_vector(6 downto 0) := (others => '0');
signal PARTIAL_CORRELATION3_1: std_logic_vector(6 downto 0) := (others => '0');
signal MF1: std_logic_vector(7 downto 0) := (others => '0');
signal DETECT1: std_logic := '0';
signal SIGN1: std_logic := '0';
signal FIRST_CNTR: unsigned(7 downto 0) := (others => '0');
signal BLOCK_OUTPUT_N: std_logic := '0';
-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin 

-- left shift input samples
-- latency: 1 SAMPLE_CLK_IN
SHIFT_IN: process (CLK)
begin
	if rising_edge(CLK) then 
		if(SAMPLE_CLK_IN = '1') then 
			SHIFT_REG(SHIFT_REG'left downto 1) <= SHIFT_REG((SHIFT_REG'left-1) downto 0);
			SHIFT_REG(0) <= DATA_IN;
		end if;
	end if;
end process;      
    
-- match filter taps
-- latency: (NSAMPLES_PER_BIT-1)* SAMPLE_CLK_IN
MATCH_001: process(SHIFT_REG)
begin
	for I in 0 to (SYNC_LENGTH-1) loop
		if(FLIPPED = '0') then
			-- forward
			MATCH(I) <= SHIFT_REG(SHIFT_REG'left - I*NSAMPLES_PER_BIT) xor SYNC_WORD(127-I);
		else
			MATCH(I) <= SHIFT_REG(SHIFT_REG'left - I*NSAMPLES_PER_BIT) xor SYNC_WORD(128-SYNC_LENGTH+I);	-- 2/12/16
		end if;
	end loop;
end process;

-- make groups of 16
MATCH_002: process(MATCH)
begin
	for I in 0 to (NMF-1) loop
		for J in 0 to 15 loop
			MATCH16(I)(J) <= MATCH((I*16) + J);
		end loop;
	end loop;
end process;

-- compute partial sums. Output ranges from 0 to 16
PC16_001: for I in 0 to (NMF-1) generate
	PC16x: PC_16 port map(A => MATCH16(I), O => PARTIAL_CORRELATION(I));
end generate;
	
-- reclock
-- latency: 1 SAMPLE_CLK_IN
PC16_002: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then 
			for I in 0 to (NMF-1) loop
				PARTIAL_CORRELATION_D(I) <= PARTIAL_CORRELATION(I);
			end loop;
		end if;
	end if;
end process;

-- sum all partial correlations
-- latency: 1 SAMPLE_CLK_IN
SUM_ALL_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then 
			if(NMF >= 2) then
				PARTIAL_CORRELATION2_0 <= ("0" & PARTIAL_CORRELATION_D(0)) + ("0" & PARTIAL_CORRELATION_D(1));
			end if;
			if(NMF >= 4) then
				PARTIAL_CORRELATION2_1 <= ("0" & PARTIAL_CORRELATION_D(2)) + ("0" & PARTIAL_CORRELATION_D(3));
			end if;
			if(NMF >= 6) then
				PARTIAL_CORRELATION2_2 <= ("0" & PARTIAL_CORRELATION_D(4)) + ("0" & PARTIAL_CORRELATION_D(5));
			end if;
			if(NMF >= 8) then
				PARTIAL_CORRELATION2_3 <= ("0" & PARTIAL_CORRELATION_D(6)) + ("0" & PARTIAL_CORRELATION_D(7));
			end if;
		end if;
	end if;
end process;

-- latency: 1 SAMPLE_CLK_IN
SUM_ALL_002: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then 
			if(NMF >= 4) then
				PARTIAL_CORRELATION3_0 <= ("0" & PARTIAL_CORRELATION2_0) + ("0" & PARTIAL_CORRELATION2_1);
			end if;
			if(NMF >= 8) then
				PARTIAL_CORRELATION3_1 <= ("0" & PARTIAL_CORRELATION2_2) + ("0" & PARTIAL_CORRELATION2_3);
			end if;
		end if;
	end if;
end process;

-- latency: 1 SAMPLE_CLK_IN
SUM_ALL_003: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then 
			if(NMF = 1) then
				MF1 <= "000" & PARTIAL_CORRELATION_D(0);
			end if;
			if(NMF = 2) then
				MF1 <= "00" & PARTIAL_CORRELATION2_0;
			end if;
			if(NMF = 4) then
				MF1 <= "0" & PARTIAL_CORRELATION3_0;
			end if;
			if(NMF = 8) then
				MF1 <= ("0" & PARTIAL_CORRELATION3_0) + ("0" & PARTIAL_CORRELATION3_1);
			end if;
		end if;
	end if;
end process;

-- detection threshold is 90%
DETECT_GEN_001: process(MF1)
begin
    if(NMF = 1) then
        if(MF1 < 3) or (MF1 > 13) then
            DETECT1 <= '1';
        else
            DETECT1 <= '0';
        end if;
    end if;
    if(NMF = 2) then
        if(MF1 < 4) or (MF1 > 28) then
            DETECT1 <= '1';
        else
            DETECT1 <= '0';
        end if;
    end if;
    if(NMF = 4) then
        if(MF1 < 7) or (MF1 > 57) then
            DETECT1 <= '1';
        else
            DETECT1 <= '0';
        end if;
    end if;
    if(NMF = 8) then
        if(MF1 < 14) or (MF1 > 114) then
            DETECT1 <= '1';
        else
            DETECT1 <= '0';
        end if;
    end if;
end process;

-- match sync word or inverse?
-- '0' if aligned with SYNC_WORD, '1' if aligned with inverted SYNC_WORD
DETECT_GEN_002: process(MF1)
begin
    if(NMF = 1) then
        if(MF1 < 8) then
            SIGN1 <= '0';
        else
            SIGN1 <= '1';
        end if;
    end if;
    if(NMF = 2) then
        if(MF1 < 16) then
			SIGN1 <= '0';
		else
			SIGN1 <= '1';
    	end if;
    end if;
    if(NMF = 4) then
        if(MF1 < 32) then
			SIGN1 <= '0';
		else
			SIGN1 <= '1';
    	end if;
    end if;
    if(NMF = 8) then
        if(MF1 < 64) then
			SIGN1 <= '0';
		else
			SIGN1 <= '1';
    	end if;
    end if;
end process;

-- latency: 1 SAMPLE_CLK_IN
DETECT_GEN_003: process(CLK)
begin
	if rising_edge(CLK) then
        MF_OUT_READY <= SAMPLE_CLK_IN;

		if(SAMPLE_CLK_IN = '1') and (BLOCK_OUTPUT_N = '1') then 
			DETECT <= DETECT1;
			MF_OUT <= MF1;
			if(DETECT1 = '1') then
				SIGN_OUT <= SIGN1;
			end if;
    	end if;
    end if;
end process;

-- block the output until we have received the first SYNC_LENGTH - 1 input bits
BLOCK_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then
			if(FIRST_CNTR < (SYNC_LENGTH-2)) then
				FIRST_CNTR <= FIRST_CNTR + 1;
				BLOCK_OUTPUT_N <= '0';
			else
				BLOCK_OUTPUT_N <= '1';
			end if;
		end if;
   end if;
end process;


end  Behavioral;




