----------------------------------------------
-- MSS copyright 2001-2012
-- Filename: CODEGEN.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: none
-- Edit date: 4/30/12
-- Revision: 6
-- Description: 
--		Generation of pseudo random (PN) sequences for direct sequence spread-spectrum

-- Rev 0.002 AZ 2-14-02
-- Added start of sequence flag output.     
-- Corrected bugs.
--
-- Rev 0.003 AZ 7-7-02
-- Added GPS codes
--
-- Rev 4 AZ 2-15-04
-- Corrected bug for GPS codes: generator polynomials are G1: x204, G2: x3A6
-- Use CODE_SEL_D, G1_D, G2_D synchronous signals
--
-- Rev 5 AZ 8-16-08
-- Added truncated codes when SPREADING_FACTOR is less than the natural code length.
--
-- Rev 6 AZ 4/30/12 
-- Corrected terminology. changed SPREADING_FACTOR to CODE_PERIOD
---------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity CODEGEN is
  port (
	CLK: in  std_logic;   
		-- clock synchronous
	BIT_CLK_REQ: in std_logic;
		-- request for output code bit, 
		-- read output bit at rising_edge of CLK and BIT_CLK_REQ_D = '1'
	SYNC_RESET: in std_logic;
		-- synchronous reset for linear feedback shift register.
		-- 1 CLK wide pulse aligned with BIT_CLK_REQ.  

	--// code selection
 	CODE_PERIOD: in std_logic_vector(23 downto 0);
		-- Range 1 - 2^23-1
		-- Can be less than the natural length of the selected code. In which case, the code is truncated.
	CODE_SEL: in std_logic_vector(2 downto 0);
		-- code selection
		-- 000 = arbitrary
		-- 001 = Gold sequence
		-- 010 = maximal length sequence
		-- 011 = barker code
		-- 100 = GPS C/A codes
 	G1: in std_logic_vector(23 downto 0);
		-- generator polynomial 1
 	G2: in std_logic_vector(23 downto 0);
		-- generator polynomial 2 (if Gold Code selected)
		-- or GPS satellite bits 5-0 (if GPS C/A code selected)


	BIT_CLK_OUT: out std_logic;
		-- Latency between input clock and output clock is 1CLK.
  	CODE_START: out std_logic;
		-- 1 CLK wide pulse, aligned with BIT_CLK_OUT.
		-- Indicates that CODE_BIT is the first bit of the periodic code sequence.
	CODE_BIT: out std_logic	
		-- code sequence output. Read at rising edge of CLK when BIT_CLK_OUT = '1'
    );
end entity;

architecture behavior of CODEGEN is

-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
signal LFSR1_REG     : std_logic_vector(23 downto 0) := (others => '0');
signal LFSR1_TAPS     : std_logic_vector(23 downto 0) := (others => '0');
signal LFSR2_REG     : std_logic_vector(23 downto 0) := (others => '0');
signal LFSR2_TAPS     : std_logic_vector(23 downto 0) := (others => '0');
signal FEEDBACK1		: std_logic:= '0';
signal FEEDBACK2		: std_logic:= '0';
signal C16:	std_logic_vector(15 downto 0) := (others => '0');
signal GPS_CA_CODE_BIT: std_logic:= '0';
signal BIT_COUNT: std_logic_vector(23 downto 0) := (others => '0');
signal CODE_SEL_D: std_logic_vector(2 downto 0) := (others => '0');     
signal CODE_SEL_D2: std_logic_vector(2 downto 0) := (others => '0');     
signal G1_D: std_logic_vector(23 downto 0) := (others => '0');
signal G1_D2: std_logic_vector(23 downto 0) := (others => '0');
signal G2_D: std_logic_vector(23 downto 0) := (others => '0');
signal G2_D2: std_logic_vector(23 downto 0) := (others => '0');
signal CODE_PERIOD_D: std_logic_vector(23 downto 0) := (others => '0'); 
signal CODE_PERIOD_D2: std_logic_vector(23 downto 0) := (others => '0'); 
signal CHANGE_RESET: std_logic:= '0';
signal CODE_START_E: std_logic:= '0';
-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin

--// Detect change in configuration
RECLOCK_001: process(CLK, CODE_SEL, G1, G2, CODE_PERIOD)
begin
	if rising_edge(CLK) then
		CODE_SEL_D <= CODE_SEL;    
		CODE_SEL_D2 <= CODE_SEL_D;    
		CODE_PERIOD_D <= CODE_PERIOD;    
		CODE_PERIOD_D2 <= CODE_PERIOD_D;    
		G1_D2 <= G1_D;
		G2_D2 <= G2_D;
		
		if(CODE_SEL_D /= CODE_SEL_D2) or (G1_D /= G1_D2) or (G2_D /= G2_D2) or (CODE_PERIOD_D /= CODE_PERIOD_D2) then
			CHANGE_RESET <= '1';
		else
			CHANGE_RESET <= '0';
		end if;

		if(CODE_SEL_D = 4) then 
			-- generator polynomials G1/G2 are implied if the GPS codes are selected
			-- G1 = 1 + x^3 + x^10 -> x204
			-- G2 = 1 + x^2 + x^3 + x^6 + x^8 + x^9 + x^10 -> x3A6
			G1_D <= x"000204";
			G2_D <= x"0003A6";
		else
			-- other cases (Maximal length sequence, Gold codes).
			G1_D <= G1;
			G2_D <= G2;
		end if;
	end if;
end process;                  



--// linear feedback shift register 1
LFSR1_GEN: process(CLK, FEEDBACK1)
begin
	if rising_edge(CLK) then
		BIT_CLK_OUT <= BIT_CLK_REQ;	-- 1 CLK latency between module input and output

		if(CODE_START_E = '1') then
			-- force start of code. (includes reset, configuration change, periodic start of code, truncation)
			LFSR1_REG <= (others => '1');
		elsif(BIT_CLK_REQ = '1') then
			LFSR1_REG(23 downto 1) <= LFSR1_REG(22 downto 0);
			LFSR1_REG(0) <= FEEDBACK1;
	  	end if;
	end if;
end process;

-- Mask all unused taps
LFSR1_TAPS <= (LFSR1_REG and G1_D);

LFSR1_GEN_002: process(LFSR1_TAPS)
variable V: std_logic;
begin
	V := '0';
  for I in 0 to 23 loop
     V := V xor LFSR1_TAPS(I);
  end loop;
  FEEDBACK1 <= V;
end process;

--// linear feedback shift register 2
LFSR2_GEN: process(CLK, FEEDBACK2)
begin
	if rising_edge(CLK) then
		if(CODE_START_E = '1') then
			-- force start of code. (includes reset, configuration change, periodic start of code, truncation)
			LFSR2_REG <= (others => '1');
		elsif(BIT_CLK_REQ = '1') then
			LFSR2_REG(23 downto 1) <= LFSR2_REG(22 downto 0);
			LFSR2_REG(0) <= FEEDBACK2;
	  	end if;
	end if;
end process;

-- Mask all unused taps
LFSR2_TAPS <= LFSR2_REG and G2_D;

LFSR2_GEN_002: process(LFSR2_TAPS)
variable V: std_logic;
begin
	V := '0';
  for I in 0 to 23 loop
     V := V xor LFSR2_TAPS(I);
  end loop;
  FEEDBACK2 <= V;
end process;

--// short codes (up to 16 bits)
SHORT_CODE_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
	  	if(CODE_START_E = '1') then
			-- force start of code. (includes reset, configuration change, periodic start of code, truncation)
			if(CODE_PERIOD(3 downto 0) = 11) and (CODE_SEL_D = 3) then
				-- 11-bit Barker code
				C16 <= "0000010110111000";	-- 0x5B8
			--elsif(CODE_PERIOD(3 downto 0) = 13) and (CODE_SEL_D = 3) then
			else
				-- 13-bit Barker code
				C16 <= "0001111100110101";	-- 0x1F35
		  	end if;
		elsif(BIT_CLK_REQ = '1') then
			-- regular permutation
			C16(15 downto 1) <= C16(14 downto 0);
			if(CODE_PERIOD(3 downto 0) = 11) and (CODE_SEL_D = 3) then
				-- length 11
				C16(0) <= C16(10);
			--elsif(CODE_PERIOD(3 downto 0) = 13) and (CODE_SEL_D = 3) then
			else
				-- length 13
				C16(0) <= C16(12);
			end if;
	  	end if;
		
	end if;
end process;

--// GPS C/A codes
GPS_CA_CODES_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(BIT_CLK_REQ = '1') and (CODE_SEL_D = 4) then
			-- GPS code selection depends on the GPS PRN signal number (in G2(5:0))
			case(G2(5 downto 0)) is 
				when ("000001") => GPS_CA_CODE_BIT <= LFSR2_REG(1) xor LFSR2_REG(5);
				when ("000010") => GPS_CA_CODE_BIT <= LFSR2_REG(2) xor LFSR2_REG(6);
				when ("000011") => GPS_CA_CODE_BIT <= LFSR2_REG(3) xor LFSR2_REG(7);
				when ("000100") => GPS_CA_CODE_BIT <= LFSR2_REG(4) xor LFSR2_REG(8);
				when ("000101") => GPS_CA_CODE_BIT <= LFSR2_REG(0) xor LFSR2_REG(8);
				when ("000110") => GPS_CA_CODE_BIT <= LFSR2_REG(1) xor LFSR2_REG(9);
				when ("000111") => GPS_CA_CODE_BIT <= LFSR2_REG(0) xor LFSR2_REG(7);
				when ("001000") => GPS_CA_CODE_BIT <= LFSR2_REG(1) xor LFSR2_REG(8);
				when ("001001") => GPS_CA_CODE_BIT <= LFSR2_REG(2) xor LFSR2_REG(9);
				when ("001010") => GPS_CA_CODE_BIT <= LFSR2_REG(1) xor LFSR2_REG(2);
				when ("001011") => GPS_CA_CODE_BIT <= LFSR2_REG(2) xor LFSR2_REG(3);
				when ("001100") => GPS_CA_CODE_BIT <= LFSR2_REG(4) xor LFSR2_REG(5);
				when ("001101") => GPS_CA_CODE_BIT <= LFSR2_REG(5) xor LFSR2_REG(6);
				when ("001110") => GPS_CA_CODE_BIT <= LFSR2_REG(6) xor LFSR2_REG(7);
				when ("001111") => GPS_CA_CODE_BIT <= LFSR2_REG(7) xor LFSR2_REG(8);
				when ("010000") => GPS_CA_CODE_BIT <= LFSR2_REG(8) xor LFSR2_REG(9);
				when ("010001") => GPS_CA_CODE_BIT <= LFSR2_REG(0) xor LFSR2_REG(3);
				when ("010010") => GPS_CA_CODE_BIT <= LFSR2_REG(1) xor LFSR2_REG(4);
				when ("010011") => GPS_CA_CODE_BIT <= LFSR2_REG(2) xor LFSR2_REG(5);
				when ("010100") => GPS_CA_CODE_BIT <= LFSR2_REG(3) xor LFSR2_REG(6);
				when ("010101") => GPS_CA_CODE_BIT <= LFSR2_REG(4) xor LFSR2_REG(7);
				when ("010110") => GPS_CA_CODE_BIT <= LFSR2_REG(5) xor LFSR2_REG(8);
				when ("010111") => GPS_CA_CODE_BIT <= LFSR2_REG(0) xor LFSR2_REG(2);
				when ("011000") => GPS_CA_CODE_BIT <= LFSR2_REG(3) xor LFSR2_REG(5);
				when ("011001") => GPS_CA_CODE_BIT <= LFSR2_REG(4) xor LFSR2_REG(6);
				when ("011010") => GPS_CA_CODE_BIT <= LFSR2_REG(5) xor LFSR2_REG(7);
				when ("011011") => GPS_CA_CODE_BIT <= LFSR2_REG(6) xor LFSR2_REG(8);
				when ("011100") => GPS_CA_CODE_BIT <= LFSR2_REG(7) xor LFSR2_REG(9);
				when ("011101") => GPS_CA_CODE_BIT <= LFSR2_REG(0) xor LFSR2_REG(5);
				when ("011110") => GPS_CA_CODE_BIT <= LFSR2_REG(1) xor LFSR2_REG(6);
				when ("011111") => GPS_CA_CODE_BIT <= LFSR2_REG(2) xor LFSR2_REG(7);
				when ("100000") => GPS_CA_CODE_BIT <= LFSR2_REG(3) xor LFSR2_REG(8);
				when ("100001") => GPS_CA_CODE_BIT <= LFSR2_REG(4) xor LFSR2_REG(9);
				when ("100010") => GPS_CA_CODE_BIT <= LFSR2_REG(3) xor LFSR2_REG(9);
				when ("100011") => GPS_CA_CODE_BIT <= LFSR2_REG(0) xor LFSR2_REG(6);
				when ("100100") => GPS_CA_CODE_BIT <= LFSR2_REG(1) xor LFSR2_REG(7);
				when others => GPS_CA_CODE_BIT <= LFSR2_REG(3) xor LFSR2_REG(9);
				--when ("100101") => GPS_CA_CODE_BIT <= LFSR2_REG() xor LFSR2_REG();
	
			end case;
	 	end if;
 	end if;
end process;
    
--// Select output
SELECT_OUTPUT_001: process(CODE_SEL_D, LFSR1_REG, LFSR2_REG, C16, GPS_CA_CODE_BIT)
begin
	if(CODE_SEL_D = 1) then
		-- Gold sequence
		CODE_BIT <= LFSR1_REG(0) xor LFSR2_REG(0);
 	elsif (CODE_SEL_D = 2) then
		-- Maximal length sequence
		CODE_BIT <= LFSR1_REG(0);
	elsif (CODE_SEL_D = 3) then
		-- Barker code
		CODE_BIT <= C16(0);
	else
--	elsif (CODE_SEL_D = 4) then
		-- GPS C/A codes  
		-- Even though the G2i bit is to be xored with the G1(9), LFSR1_REG has been shifted once
		-- by the time the output bit is ready (one clock after BIT_CLK_REQ)
		CODE_BIT <= LFSR1_REG(10) xor GPS_CA_CODE_BIT;
	end if;
end process;

--// Count the number of bits in the sequence. Raise a flag for the first one
BIT_COUNT_GEN: process(CLK)
begin
  	if rising_edge(CLK) then
		if(SYNC_RESET = '1') or (CHANGE_RESET = '1') then
			-- initialize to 1
			BIT_COUNT <= "000000000000000000000001";
		elsif(BIT_CLK_REQ = '1') then
			if(BIT_COUNT = CODE_PERIOD) then
				-- reset count
				BIT_COUNT <= "000000000000000000000001";
		  	else
				BIT_COUNT <= BIT_COUNT + 1;
			end if;
	  	end if;
	  	
	  	if(CODE_START_E = '1') then
	  		CODE_START <= '1';
	  	else 
	  		CODE_START <= '0';
	  	end if;
	end if;
end process;

CODE_START_E <= '1' when (BIT_CLK_REQ = '1') and (BIT_COUNT = 1) else '0';


end behavior;

