library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity PWM_generator_v1_0 is
	generic (
		-- Users to add parameters here
        CLK_FREQ : integer := 50;
        PWM_FREQ : integer := 20;
        PHASES : integer := 3;
        ACTIVE_H : integer := 0;
        ACTIVE_L : integer := 0;
        DEAD_TIME : integer := 0;
		-- User parameters ends
		-- Do not modify the parameters beyond this line


		-- Parameters of Axi Slave Bus Interface S00_AXI
		C_S00_AXI_DATA_WIDTH	: integer	:= 32;
		C_S00_AXI_ADDR_WIDTH	: integer	:= 4
	);
	port (
		-- Users to add ports here
        run : in std_logic;
        run_neg : in std_logic;
        clk : in std_logic;
        zero_align : in std_logic;
        duty_A : in std_logic_vector (15 downto 0);
        duty_B : in std_logic_vector (15 downto 0);
        duty_C : in std_logic_vector (15 downto 0);
        sync_out : out std_logic;
        PWM_AH : out std_logic;
        PWM_AL : out std_logic;
        PWM_BH : out std_logic;
        PWM_BL : out std_logic;
        PWM_CH : out std_logic;
        PWM_CL : out std_logic;
--        PWM_DH : out std_logic;
--        PWM_DL : out std_logic;
--        PWM_EH : out std_logic;
--        PWM_EL : out std_logic;
--        PWM_FH : out std_logic;
--        PWM_FL : out std_logic; 
--        PWM_GH : out std_logic;       
--		PWM_GL : out std_logic;
--		PWM_HH : out std_logic;
--        PWM_HL : out std_logic;
--        PWM_IH : out std_logic;
--		PWM_IL : out std_logic;
--		PWM_JH : out std_logic;
--        PWM_JL : out std_logic;
--        PWM_KH : out std_logic;
--        PWM_KL : out std_logic;
--        PWM_LH : out std_logic;
--        PWM_LL : out std_logic;
		-- User ports ends
		-- Do not modify the ports beyond this line
		
		-- Ports of Axi Slave Bus Interface S00_AXI
		s00_axi_aclk	: in std_logic;
		s00_axi_aresetn	: in std_logic;
		s00_axi_awaddr	: in std_logic_vector(C_S00_AXI_ADDR_WIDTH-1 downto 0);
		s00_axi_awprot	: in std_logic_vector(2 downto 0);
		s00_axi_awvalid	: in std_logic;
		s00_axi_awready	: out std_logic;
		s00_axi_wdata	: in std_logic_vector(C_S00_AXI_DATA_WIDTH-1 downto 0);
		s00_axi_wstrb	: in std_logic_vector((C_S00_AXI_DATA_WIDTH/8)-1 downto 0);
		s00_axi_wvalid	: in std_logic;
		s00_axi_wready	: out std_logic;
		s00_axi_bresp	: out std_logic_vector(1 downto 0);
		s00_axi_bvalid	: out std_logic;
		s00_axi_bready	: in std_logic;
		s00_axi_araddr	: in std_logic_vector(C_S00_AXI_ADDR_WIDTH-1 downto 0);
		s00_axi_arprot	: in std_logic_vector(2 downto 0);
		s00_axi_arvalid	: in std_logic;
		s00_axi_arready	: out std_logic;
		s00_axi_rdata	: out std_logic_vector(C_S00_AXI_DATA_WIDTH-1 downto 0);
		s00_axi_rresp	: out std_logic_vector(1 downto 0);
		s00_axi_rvalid	: out std_logic;
		s00_axi_rready	: in std_logic
	);
end PWM_generator_v1_0;

architecture arch_imp of PWM_generator_v1_0 is
    
    constant period : integer := CLK_FREQ * 1000 / PWM_FREQ;
    constant half_period : integer := period / 2;
    constant ACTIVE_H_b : std_logic := std_logic(to_unsigned(ACTIVE_H,1)(0));
    constant ACTIVE_L_b : std_logic := std_logic(to_unsigned(ACTIVE_L,1)(0));
    constant dt_count : unsigned (31 downto 0) := (to_unsigned(DEAD_TIME,16) *  to_unsigned(CLK_FREQ,16) / to_unsigned(1000,32)) + to_unsigned(1,32);
    
    signal duty_A_s : unsigned (15 downto 0);
    signal duty_B_s : unsigned (15 downto 0);
    signal duty_C_s : unsigned (15 downto 0);
--    signal duty_D_s : unsigned (15 downto 0);
--    signal duty_E_s : unsigned (15 downto 0);
--    signal duty_F_s : unsigned (15 downto 0);
--    signal duty_G_s : unsigned (15 downto 0);
--    signal duty_H_s : unsigned (15 downto 0);
--    signal duty_I_s : unsigned (15 downto 0);
--    signal duty_J_s : unsigned (15 downto 0);
--    signal duty_K_s : unsigned (15 downto 0);
--    signal duty_L_s : unsigned (15 downto 0);
    
    signal counter : unsigned (31 downto 0);
--    signal counter2 : unsigned (31 downto 0);
--    signal counter3 : unsigned (31 downto 0);
--    signal counter4 : unsigned (31 downto 0);
	-- component declaration
	component PWM_generator_v1_0_S00_AXI is
		generic (
		C_S_AXI_DATA_WIDTH	: integer	:= 32;
		C_S_AXI_ADDR_WIDTH	: integer	:= 4
		);
		port (
		S_AXI_ACLK	: in std_logic;
		S_AXI_ARESETN	: in std_logic;
		S_AXI_AWADDR	: in std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0);
		S_AXI_AWPROT	: in std_logic_vector(2 downto 0);
		S_AXI_AWVALID	: in std_logic;
		S_AXI_AWREADY	: out std_logic;
		S_AXI_WDATA	: in std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);
		S_AXI_WSTRB	: in std_logic_vector((C_S_AXI_DATA_WIDTH/8)-1 downto 0);
		S_AXI_WVALID	: in std_logic;
		S_AXI_WREADY	: out std_logic;
		S_AXI_BRESP	: out std_logic_vector(1 downto 0);
		S_AXI_BVALID	: out std_logic;
		S_AXI_BREADY	: in std_logic;
		S_AXI_ARADDR	: in std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0);
		S_AXI_ARPROT	: in std_logic_vector(2 downto 0);
		S_AXI_ARVALID	: in std_logic;
		S_AXI_ARREADY	: out std_logic;
		S_AXI_RDATA	: out std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);
		S_AXI_RRESP	: out std_logic_vector(1 downto 0);
		S_AXI_RVALID	: out std_logic;
		S_AXI_RREADY	: in std_logic
		);
	end component PWM_generator_v1_0_S00_AXI;
	
	component PWM_one_branch is
        generic(
            PHASES : integer;
            HALF_P : integer;
            ACTIVE_H : std_logic;
            ACTIVE_L : std_logic;
            DEAD_TIME_COUNT : unsigned
        );
        port (
        -- Users to add ports here
            run : in std_logic;
            run_neg : in std_logic;
            clk : in std_logic;    
            duty : in unsigned (15 downto 0);
            counter : in unsigned (31 downto 0);
            PWM_H : out std_logic;
            PWM_L : out std_logic
            
        );
    end component;

begin

-- Instantiation of Axi Bus Interface S00_AXI
PWM_generator_v1_0_S00_AXI_inst : PWM_generator_v1_0_S00_AXI
	generic map (
		C_S_AXI_DATA_WIDTH	=> C_S00_AXI_DATA_WIDTH,
		C_S_AXI_ADDR_WIDTH	=> C_S00_AXI_ADDR_WIDTH
	)
	port map (
		S_AXI_ACLK	=> s00_axi_aclk,
		S_AXI_ARESETN	=> s00_axi_aresetn,
		S_AXI_AWADDR	=> s00_axi_awaddr,
		S_AXI_AWPROT	=> s00_axi_awprot,
		S_AXI_AWVALID	=> s00_axi_awvalid,
		S_AXI_AWREADY	=> s00_axi_awready,
		S_AXI_WDATA	=> s00_axi_wdata,
		S_AXI_WSTRB	=> s00_axi_wstrb,
		S_AXI_WVALID	=> s00_axi_wvalid,
		S_AXI_WREADY	=> s00_axi_wready,
		S_AXI_BRESP	=> s00_axi_bresp,
		S_AXI_BVALID	=> s00_axi_bvalid,
		S_AXI_BREADY	=> s00_axi_bready,
		S_AXI_ARADDR	=> s00_axi_araddr,
		S_AXI_ARPROT	=> s00_axi_arprot,
		S_AXI_ARVALID	=> s00_axi_arvalid,
		S_AXI_ARREADY	=> s00_axi_arready,
		S_AXI_RDATA	=> s00_axi_rdata,
		S_AXI_RRESP	=> s00_axi_rresp,
		S_AXI_RVALID	=> s00_axi_rvalid,
		S_AXI_RREADY	=> s00_axi_rready
	);

	-- Add user logic here
	PWM_A : PWM_one_branch
        generic map(
            PHASES => PHASES,
            HALF_P => half_period,
            ACTIVE_H => ACTIVE_H_b,
            ACTIVE_L => ACTIVE_L_b,
            DEAD_TIME_COUNT => dt_count
        )
        port map(
            run => run,
            run_neg => run_neg,
            clk => clk,    
            duty => duty_A_s,
            counter => counter,
            PWM_H => PWM_AH,
            PWM_L => PWM_AL   
        );
        
    PWM_B : PWM_one_branch
        generic map(
            PHASES => PHASES,
            HALF_P => half_period,
            ACTIVE_H => ACTIVE_H_b,
            ACTIVE_L => ACTIVE_L_b,
            DEAD_TIME_COUNT => dt_count
        )
        port map(
            run => run,
            run_neg => run_neg,
            clk => clk,    
            duty => duty_B_s,
            counter => counter,
            PWM_H => PWM_BH,
            PWM_L => PWM_BL   
        ); 
        
    PWM_C : PWM_one_branch
        generic map(
            PHASES => PHASES,
            HALF_P => half_period,
            ACTIVE_H => ACTIVE_H_b,
            ACTIVE_L => ACTIVE_L_b,
            DEAD_TIME_COUNT => dt_count
        )
        port map(
            run => run,
            run_neg => run_neg,
            clk => clk,    
            duty => duty_C_s,
            counter => counter,
            PWM_H => PWM_CH,
            PWM_L => PWM_CL   
        );
        
--    PH4to6: if PHASES > 3 generate
--        PWM_D : PWM_one_branch
--            generic map(
--                PHASES => PHASES,
--                HALF_P => half_period,
--                ACTIVE_H => ACTIVE_H_b,
--                ACTIVE_L => ACTIVE_L_b,
--                DEAD_TIME_COUNT => dt_count
--            )
--            port map(
--                run => run,
--                run_neg => run_neg,
--                clk => clk,    
--                duty => duty_D_s,
--                counter => counter2,
--                PWM_H => PWM_DH,
--                PWM_L => PWM_DL   
--            );
            
--        PWM_E : PWM_one_branch
--            generic map(
--                PHASES => PHASES,
--                HALF_P => half_period,
--                ACTIVE_H => ACTIVE_H_b,
--                ACTIVE_L => ACTIVE_L_b,
--                DEAD_TIME_COUNT => dt_count
--            )
--            port map(
--                run => run,
--                run_neg => run_neg,
--                clk => clk,    
--                duty => duty_E_s,
--                counter => counter2,
--                PWM_H => PWM_EH,
--                PWM_L => PWM_EL   
--            ); 
            
--        PWM_F : PWM_one_branch
--            generic map(
--                PHASES => PHASES,
--                HALF_P => half_period,
--                ACTIVE_H => ACTIVE_H_b,
--                ACTIVE_L => ACTIVE_L_b,
--                DEAD_TIME_COUNT => dt_count
--            )
--            port map(
--                run => run,
--                run_neg => run_neg,
--                clk => clk,    
--                duty => duty_F_s,
--                counter => counter2,
--                PWM_H => PWM_FH,
--                PWM_L => PWM_FL   
--            );
--    end generate PH4to6; 
    
--    PH7to9: if PHASES > 6 generate
--        PWM_G : PWM_one_branch
--            generic map(
--                PHASES => PHASES,
--                HALF_P => half_period,
--                ACTIVE_H => ACTIVE_H_b,
--                ACTIVE_L => ACTIVE_L_b,
--                DEAD_TIME_COUNT => dt_count
--            )
--            port map(
--                run => run,
--                run_neg => run_neg,
--                clk => clk,    
--                duty => duty_G_s,
--                counter => counter3,
--                PWM_H => PWM_GH,
--                PWM_L => PWM_GL   
--            );
            
--        PWM_H : PWM_one_branch
--            generic map(
--                PHASES => PHASES,
--                HALF_P => half_period,
--                ACTIVE_H => ACTIVE_H_b,
--                ACTIVE_L => ACTIVE_L_b,
--                DEAD_TIME_COUNT => dt_count
--            )
--            port map(
--                run => run,
--                run_neg => run_neg,
--                clk => clk,    
--                duty => duty_H_s,
--                counter => counter3,
--                PWM_H => PWM_HH,
--                PWM_L => PWM_HL   
--            ); 
            
--        PWM_I : PWM_one_branch
--            generic map(
--                PHASES => PHASES,
--                HALF_P => half_period,
--                ACTIVE_H => ACTIVE_H_b,
--                ACTIVE_L => ACTIVE_L_b,
--                DEAD_TIME_COUNT => dt_count
--            )
--            port map(
--                run => run,
--                run_neg => run_neg,
--                clk => clk,    
--                duty => duty_I_s,
--                counter => counter3,
--                PWM_H => PWM_IH,
--                PWM_L => PWM_IL   
--            );
--    end generate PH7to9;
    
--    PH10to12: if PHASES>9 generate
--        PWM_J : PWM_one_branch
--            generic map(
--                PHASES => PHASES,
--                HALF_P => half_period,
--                ACTIVE_H => ACTIVE_H_b,
--                ACTIVE_L => ACTIVE_L_b,
--                DEAD_TIME_COUNT => dt_count
--            )
--            port map(
--                run => run,
--                run_neg => run_neg,
--                clk => clk,    
--                duty => duty_J_s,
--                counter => counter4,
--                PWM_H => PWM_JH,
--                PWM_L => PWM_JL   
--            );
            
--        PWM_K : PWM_one_branch
--            generic map(
--                PHASES => PHASES,
--                HALF_P => half_period,
--                ACTIVE_H => ACTIVE_H_b,
--                ACTIVE_L => ACTIVE_L_b,
--                DEAD_TIME_COUNT => dt_count
--            )
--            port map(
--                run => run,
--                run_neg => run_neg,
--                clk => clk,    
--                duty => duty_K_s,
--                counter => counter4,
--                PWM_H => PWM_KH,
--                PWM_L => PWM_KL   
--            ); 
            
--        PWM_L : PWM_one_branch
--            generic map(
--                PHASES => PHASES,
--                HALF_P => half_period,
--                ACTIVE_H => ACTIVE_H_b,
--                ACTIVE_L => ACTIVE_L_b,
--                DEAD_TIME_COUNT => dt_count
--            )
--            port map(
--                run => run,
--                run_neg => run_neg,
--                clk => clk,    
--                duty => duty_L_s,
--                counter => counter4,
--                PWM_H => PWM_LH,
--                PWM_L => PWM_LL   
--            );
--    end generate PH10to12;         
        
    duty_A_s <= unsigned(not duty_A(15) & duty_A(14 downto 0)) when zero_align = '0' else to_unsigned(48495,16);
    duty_B_s <= unsigned(not duty_B(15) & duty_B(14 downto 0)) when zero_align = '0' else to_unsigned(24903,16);
    duty_C_s <= unsigned(not duty_C(15) & duty_C(14 downto 0)) when zero_align = '0' else to_unsigned(24903,16);
--    duty_D_s <= unsigned(not duty_A(15) & duty_A(14 downto 0)) when zero_align = '0' else to_unsigned(32768,16);
--    duty_E_s <= unsigned(not duty_B(15) & duty_B(14 downto 0)) when zero_align = '0' else to_unsigned(32768,16);
--    duty_F_s <= unsigned(not duty_C(15) & duty_C(14 downto 0)) when zero_align = '0' else to_unsigned(32768,16);
--    duty_G_s <= unsigned(not duty_A(15) & duty_A(14 downto 0)) when zero_align = '0' else to_unsigned(32768,16);
--    duty_H_s <= unsigned(not duty_B(15) & duty_B(14 downto 0)) when zero_align = '0' else to_unsigned(32768,16);
--    duty_I_s <= unsigned(not duty_C(15) & duty_C(14 downto 0)) when zero_align = '0' else to_unsigned(32768,16);
--    duty_J_s <= unsigned(not duty_A(15) & duty_A(14 downto 0)) when zero_align = '0' else to_unsigned(32768,16);
--    duty_K_s <= unsigned(not duty_B(15) & duty_B(14 downto 0)) when zero_align = '0' else to_unsigned(32768,16);
--    duty_L_s <= unsigned(not duty_C(15) & duty_C(14 downto 0)) when zero_align = '0' else to_unsigned(32768,16);
    
--    counter2 <= counter - to_unsigned(period/2,32) when PHASES=6 and counter >= to_unsigned(period/2,32) else
--                counter + to_unsigned(period,32)-to_unsigned(period/2,32) when PHASES=6 and counter < to_unsigned(period/2,32) else
--                counter - to_unsigned(period/3,32) when PHASES=9 and counter >= to_unsigned(period/3,32) else
--                counter + to_unsigned(period,32)-to_unsigned(period/3,32) when PHASES=9 and counter < to_unsigned(period/3,32) else
--                counter - to_unsigned(period/4,32) when PHASES=12 and counter >= to_unsigned(period/4,32) else
--                counter + to_unsigned(period,32)-to_unsigned(period/4,32) when PHASES=12 and counter < to_unsigned(period/4,32) else
--                to_unsigned(0,32);
                
--    counter3 <= counter - to_unsigned(period*2/3,32) when PHASES=9 and counter >= to_unsigned(period*2/3,32) else
--                counter + to_unsigned(period,32)-to_unsigned(period*2/3,32) when PHASES=9 and counter < to_unsigned(period*2/3,32) else
--                counter - to_unsigned(period/2,32) when PHASES=12 and counter >= to_unsigned(period/2,32) else
--                counter + to_unsigned(period,32)-to_unsigned(period/2,32) when PHASES=12 and counter < to_unsigned(period/2,32) else
--                to_unsigned(0,32);  
                          
--	counter4 <= counter - to_unsigned(period*3/4,32) when PHASES=12 and counter >= to_unsigned(period*3/4,32) else
--                counter + to_unsigned(period,32)-to_unsigned(period*3/4,32) when PHASES=12 and counter < to_unsigned(period*3/4,32) else
--                to_unsigned(0,32);           
                                
    process (clk)
        variable cnt : integer range 0 to period;
    BEGIN
        if run='0' then
            cnt := 0;
        elsif (clk'event and clk = '1') then
            if cnt = period then
                cnt := 0;
                sync_out <= '1';
            else    
                cnt := cnt + 1;
                sync_out <= '0';
            end if;    
        end if;          
        counter <= to_unsigned(cnt,32);
    end process;
	-- User logic ends

end arch_imp;
