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

	PORT (
    SYSCLK 		: 	IN std_logic;
	 EPWMXA_IN	: 	IN std_logic;
	 EPWMXB_IN	:	IN std_logic;
	 
	 CHPDUTY	:	IN std_logic_vector(2 DOWNTO 0);
	 CHPFREQ	:	IN std_logic_vector(2 DOWNTO 0);
	 OSHTWTH:	IN std_logic_vector(3 DOWNTO 0);
	 CHPEN :  	IN std_logic;
	 
	 EPWMXA_OUT	: 	OUT std_logic;
	 EPWMXB_OUT	:	OUT std_logic
	);
END pwm_chopper;
 

ARCHITECTURE Behavioral OF pwm_chopper IS

	SIGNAL EPWMXA : std_logic := '0';
	SIGNAL EPWMXB : std_logic := '0';
	SIGNAL PSCLK : std_logic := '0';
	SIGNAL duty_clk : std_logic := '0';
	SIGNAL PWM_clk : std_logic := '0';

	FUNCTION maxCnt(	CHPFREQ : std_logic_vector(2 DOWNTO 0)) RETURN integer	IS
							VARIABLE a : integer;
	BEGIN
		CASE CHPFREQ IS
			WHEN "000" => a := 1;
			WHEN "001" => a := 2;
			WHEN "010" => a := 3;
			WHEN "011" => a := 4;
			WHEN "100" => a := 5;
			WHEN "101" => a := 6;
			WHEN "110" => a := 7;
			WHEN "111" => a := 8;
			WHEN OTHERS => NULL;
		END CASE;
		
		return (a*8/2);
		
	END FUNCTION;
	
	
BEGIN
	-- duty clock

	PROCESS(SYSCLK) 
	
	VARIABLE cnt: integer := 1;
	
	BEGIN
		IF(rising_edge(SYSCLK)) THEN			
			IF(cnt = (maxCnt(CHPFREQ))/(8)) THEN
				cnt := 1;			
				duty_clk <= NOT duty_clk;
			ELSE
				cnt := cnt + 1;
			END IF;
		END IF;
	END PROCESS;
	
	-- duty cycle
	
	PROCESS(PWM_clk, CHPDUTY)
	
	VARIABLE cnt: unsigned(2 DOWNTO 0) := (OTHERS => '0');
	VARIABLE PROC_CHPDUTY: std_logic_vector(2 DOWNTO 0) := CHPDUTY;
	
	BEGIN
		IF(rising_edge(PWM_clk)) THEN
			IF (cnt < "111") THEN
				cnt := cnt + 1;
			ELSE
				cnt := (OTHERS => '0');
			END IF;
			
			IF (CHPDUTY = "111") THEN
				PROC_CHPDUTY := PROC_CHPDUTY;
			ELSE
				PROC_CHPDUTY := CHPDUTY;
			END IF;
			
			IF (unsigned(PROC_CHPDUTY) >= cnt) THEN
				PSCLK <= '1';
			ELSE
				PSCLK <= '0';
			END IF;
		END IF;
	END PROCESS;
	
	-- first pulse delay
	
	PROCESS (SYSCLK, EPWMXA_IN, EPWMXB_IN)
	
	VARIABLE cnt1: unsigned(9 DOWNTO 0) := (OTHERS => '0');
	VARIABLE cnt2: unsigned(9 DOWNTO 0) := (OTHERS => '0'); 
	
	BEGIN
		IF rising_edge(SYSCLK) THEN
			IF(EPWMXA_IN = '0') THEN			
				EPWMXA <= '0';
				cnt1 := (OTHERS => '0');
			ELSIF(cnt1 < ((unsigned(OSHTWTH)+1)*8)) THEN
				cnt1 := cnt1 + 1;
				EPWMXA <= '1';
			ELSE
				EPWMXA <= PSCLK;
			END IF;
			
			IF(EPWMXB_IN = '0') THEN			
				EPWMXB <= '0';
				cnt2 := (OTHERS => '0');
			ELSIF(cnt2 < ((unsigned(OSHTWTH)+1)*8)) THEN
				cnt2 := cnt2 + 1;
				EPWMXB <= '1';
			ELSE
				EPWMXB <= PSCLK;
			END IF;			
		END IF;
	END PROCESS;
	
	-- output bypass

	EPWMXA_OUT <= EPWMXA WHEN CHPEN = '1' ELSE EPWMXA_IN;	
	EPWMXB_OUT <= EPWMXB WHEN CHPEN = '1' ELSE EPWMXB_IN;
	
	
	
	PWM_clk <= SYSCLK WHEN CHPFREQ = "000" ELSE duty_clk;
	
	
END Behavioral;