LIBRARY ieee;
USE ieee.std_logic_1164. all;
USE ieee.numeric_std.all;
 
ENTITY time_base IS

	PORT (
    SYSCLK		: 	IN std_logic;								--! System clock.
	 EPWMxSYNCI	: 	IN std_logic; 								--! Time-base synchronization input.
	 EPWMxSYNCO	: 	OUT std_logic; 							--! Time-base synchronization output.
	 PHSEN		: 	IN std_logic; 								--! Counter Register Load From Phase Register Enable
	 PRDLD		: 	IN std_logic; 								--! Active Period Register Load From Shadow Register Select
	 SYNCOSEL	: 	IN std_logic_vector(1 DOWNTO 0); 	--! Synchronization Output Select. These bits select the source of the EPWMxSYNCO signal.
	 CTRMODE		:	IN std_logic_vector(1 DOWNTO 0);		--! Counter Mode
	 TBPRD		:	IN std_logic_vector(15 DOWNTO 0); 	--! These bits determine the period of the time-base counter. This sets the PWM frequency.
	 TBPHS		:	IN std_logic_vector(15 DOWNTO 0); 	--! These bits set time-base counter phase of the selected ePWM relative to the time-base that is supplying the synchronization input signal.
	 CLKDIV  	: 	IN std_logic_vector(2 DOWNTO 0); 	--! Time-base Clock Prescale Bits 2^CLKDIV
	 HSPCLKDIV 	: 	IN std_logic_vector(2 DOWNTO 0); 	--! Highspeed Time-base prescale bits 2*HSPCLKDIV
	 
	 CMPBE	:	IN std_logic;									--! CTR = CMPB
	 CTR_dir :  OUT std_logic;									--! Time-base counter direction.
	 CTR_zero:  OUT std_logic;									--! Time-base counter equal to zero.
	 CTR_prd :  OUT std_logic;									--! Time-base counter equal to the specified period.
	 TBCLK	:  OUT std_logic; 								--! Time-base clock.
	 CTR		:	OUT std_logic_vector(15 DOWNTO 0));		--! Time-base counter value.
	
END time_base;
 

ARCHITECTURE Behavioral OF time_base IS
	
	TYPE state_type IS (s0, s1);	--! state machine for counter UP/DOWN
	SIGNAL state : state_type := s0;
	
	SIGNAL TBPRD_active: std_logic_vector(15 DOWNTO 0) := (OTHERS => '0');	--! Active TBPRD register
	SIGNAL TBPHS_active: std_logic_vector(15 DOWNTO 0) := (OTHERS => '0');	--! Active TBPHS register
	
	SIGNAL iCTR_zero	:	std_logic	:=	'0'; 	--! Internal TBCTR = 0
	
	SIGNAL tb_clk : std_logic 			:= '0';	--! Internal prescaled clock
	
	--! Function for divide system clock
	
	FUNCTION maxCnt(	CLKDIV : std_logic_vector(2 DOWNTO 0);	
							HSPCLKDIV : std_logic_vector(2 DOWNTO 0)) RETURN integer	IS
							VARIABLE a : integer;
							VARIABLE b : integer;
	BEGIN
		CASE CLKDIV IS
			WHEN "000" => a := 1;
			WHEN "001" => a := 2;
			WHEN "010" => a := 4;
			WHEN "011" => a := 8;
			WHEN "100" => a := 16;
			WHEN "101" => a := 32;
			WHEN "110" => a := 64;
			WHEN "111" => a := 128;
			WHEN OTHERS => NULL;
		END CASE;
		
		CASE HSPCLKDIV IS
			WHEN "000" => b := 1;
			WHEN "001" => b := 2;
			WHEN "010" => b := 4;
			WHEN "011" => b := 6;
			WHEN "100" => b := 8;
			WHEN "101" => b := 10;
			WHEN "110" => b := 12;
			WHEN "111" => b := 14;
			WHEN OTHERS => NULL;
		END CASE;
		
		return a*b;
		
	END FUNCTION;
		
	
BEGIN

	--! Clock prescale SYSCLK => TBCLK
	
	PROCESS(SYSCLK, CLKDIV, HSPCLKDIV, tb_clk) 
	
	VARIABLE cnt: integer := 1;
	
	BEGIN
		
		IF(maxCnt(CLKDIV, HSPCLKDIV) = 1) THEN
			tb_clk <= SYSCLK;
	
		ELSIF(rising_edge(SYSCLK)) THEN
			IF(cnt = (maxCnt(CLKDIV, HSPCLKDIV)/2)) THEN
				cnt := 1;			
				tb_clk <= NOT tb_clk;
			ELSE
				cnt := cnt + 1;
			END IF;
		END IF;
		
		TBCLK <= tb_clk;
		
	END PROCESS;
	
	--! Main counter process

	PROCESS(tb_clk, EPWMxSYNCI, iCTR_zero) 
	
	VARIABLE cnt: unsigned(15 DOWNTO 0) := (OTHERS => '0');
	
	BEGIN
			
		IF rising_edge(tb_clk) THEN
			
			IF (EPWMxSYNCI = '1' AND PHSEN = '1') THEN
				cnt := unsigned(TBPHS_active);
			END IF;
			
			CASE CTRMODE IS
			
				--! 00 Counter up mode
				WHEN "00" =>
					IF(cnt < unsigned(TBPRD_active)) THEN
						cnt := cnt + 1;
						CTR_Dir <= '1';
					ELSE
						cnt := (OTHERS => '0');
					END IF;
				
				--! 01 - Counter down mode
				WHEN "01" =>
					IF(cnt > 0) THEN
						cnt := cnt - 1;
						CTR_Dir <= '0';
					ELSE
						cnt := unsigned(TBPRD_active);
					END IF;
				
				--! 10 - Counter up/down mode
				WHEN "10" =>
					CASE state IS
						WHEN s0 =>
							IF(cnt < unsigned(TBPRD_active)) THEN
								cnt := cnt + 1;
								CTR_Dir <= '1';
							ELSE
								cnt := cnt - 1;
								CTR_Dir <= '0';
								state <= s1;
							END IF;
					
						WHEN s1 =>
							IF(cnt > 0) THEN
								cnt := cnt - 1;
								CTR_Dir <= '0';
							ELSE
								cnt := cnt + 1;
								CTR_Dir <= '1';
								state <= s0;
							END IF;
					END CASE;
				
				--! 11 - Freeze
				WHEN OTHERS =>
					cnt := cnt;
					
			END CASE;
			
			--! TBRCTR = 0?
			IF (cnt = 0) THEN
				iCTR_zero <= '1';
			ELSE
				iCTR_zero <= '0';
			END IF;
			
			--! TBRCTR = TBPRD?
			IF (cnt = unsigned(TBPRD_active)) THEN
				CTR_prd <= '1';
			ELSE
				CTR_prd <= '0';
			END IF;
			
		END IF;
		
 		CTR 		<= std_logic_vector(cnt);
		CTR_zero <= iCTR_zero;

	END PROCESS;
	
	--! Sync out multiplexer
	
	PROCESS (SYNCOSEL, EPWMxSYNCI, CMPBE, iCTR_zero)
	BEGIN
		CASE SYNCOSEL IS
			WHEN "00" => 	EPWMxSYNCO <= EPWMxSYNCI;				
			WHEN "01" => 	EPWMxSYNCO <= iCTR_zero;				
			WHEN "10" => 	EPWMxSYNCO <= CMPBE;
			WHEN OTHERS => EPWMxSYNCO <= '0';
		END CASE;
	END PROCESS;
	
	--! Shadow registers
	
	TBPHS_active <= 	TBPHS WHEN (PHSEN = '1') ELSE 
							NULL;
	
	TBPRD_active <= 	TBPRD WHEN (PRDLD = '0') ELSE
							TBPRD WHEN (iCTR_zero = '1' AND PRDLD = '1') ELSE
							NULL;
	
END Behavioral;